VirtualBox

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

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

MachineImpl.cpp doesn't seem to need GuestImpl.h (Guest* lives in VBoxC.dll).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 508.6 KB
Line 
1/* $Id: MachineImpl.cpp 55642 2015-05-04 13:04:51Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69#include <iprt/base64.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mTripleFaultReset = false;
197 mHPETEnabled = false;
198
199 /* default boot order: floppy - DVD - HDD */
200 mBootOrder[0] = DeviceType_Floppy;
201 mBootOrder[1] = DeviceType_DVD;
202 mBootOrder[2] = DeviceType_HardDisk;
203 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
204 mBootOrder[i] = DeviceType_Null;
205
206 mClipboardMode = ClipboardMode_Disabled;
207 mDnDMode = DnDMode_Disabled;
208 mGuestPropertyNotificationPatterns = "";
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 /* Maximum CPU execution cap by default. */
224 mCpuExecutionCap = 100;
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HDData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::MediaData::MediaData()
236{
237}
238
239Machine::MediaData::~MediaData()
240{
241}
242
243/////////////////////////////////////////////////////////////////////////////
244// Machine class
245/////////////////////////////////////////////////////////////////////////////
246
247// constructor / destructor
248/////////////////////////////////////////////////////////////////////////////
249
250Machine::Machine() :
251#ifdef VBOX_WITH_RESOURCE_USAGE_API
252 mCollectorGuest(NULL),
253#endif
254 mPeer(NULL),
255 mParent(NULL),
256 mSerialPorts(),
257 mParallelPorts(),
258 uRegistryNeedsSaving(0)
259{}
260
261Machine::~Machine()
262{}
263
264HRESULT Machine::FinalConstruct()
265{
266 LogFlowThisFunc(("\n"));
267 return BaseFinalConstruct();
268}
269
270void Machine::FinalRelease()
271{
272 LogFlowThisFunc(("\n"));
273 uninit();
274 BaseFinalRelease();
275}
276
277/**
278 * Initializes a new machine instance; this init() variant creates a new, empty machine.
279 * This gets called from VirtualBox::CreateMachine().
280 *
281 * @param aParent Associated parent object
282 * @param strConfigFile Local file system path to the VM settings file (can
283 * be relative to the VirtualBox config directory).
284 * @param strName name for the machine
285 * @param llGroups list of groups for the machine
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Apply BIOS defaults */
349 mBIOSSettings->i_applyDefaults(aOsType);
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Let the OS type select 64-bit ness. */
360 mHWData->mLongMode = aOsType->i_is64Bit()
361 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 }
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 i_allowStateModification();
367
368 /* commit all changes made during the initialization */
369 i_commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param aConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = i_registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 i_allowStateModification();
469
470 i_commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->i_unregisterMachineMedia(i_getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent
607 * @return
608 */
609HRESULT Machine::initImpl(VirtualBox *aParent,
610 const Utf8Str &strConfigFile)
611{
612 LogFlowThisFuncEnter();
613
614 AssertReturn(aParent, E_INVALIDARG);
615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
616
617 HRESULT rc = S_OK;
618
619 /* share the parent weakly */
620 unconst(mParent) = aParent;
621
622 /* allocate the essential machine data structure (the rest will be
623 * allocated later by initDataAndChildObjects() */
624 mData.allocate();
625
626 /* memorize the config file name (as provided) */
627 mData->m_strConfigFile = strConfigFile;
628
629 /* get the full file name */
630 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
631 if (RT_FAILURE(vrc1))
632 return setError(VBOX_E_FILE_ERROR,
633 tr("Invalid machine settings file name '%s' (%Rrc)"),
634 strConfigFile.c_str(),
635 vrc1);
636
637 LogFlowThisFuncLeave();
638
639 return rc;
640}
641
642/**
643 * Tries to create a machine settings file in the path stored in the machine
644 * instance data. Used when a new machine is created to fail gracefully if
645 * the settings file could not be written (e.g. because machine dir is read-only).
646 * @return
647 */
648HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
649{
650 HRESULT rc = S_OK;
651
652 // when we create a new machine, we must be able to create the settings file
653 RTFILE f = NIL_RTFILE;
654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
655 if ( RT_SUCCESS(vrc)
656 || vrc == VERR_SHARING_VIOLATION
657 )
658 {
659 if (RT_SUCCESS(vrc))
660 RTFileClose(f);
661 if (!fForceOverwrite)
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Machine settings file '%s' already exists"),
664 mData->m_strConfigFileFull.c_str());
665 else
666 {
667 /* try to delete the config file, as otherwise the creation
668 * of a new settings file will fail. */
669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
670 if (RT_FAILURE(vrc2))
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Could not delete the existing settings file '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(), vrc2);
674 }
675 }
676 else if ( vrc != VERR_FILE_NOT_FOUND
677 && vrc != VERR_PATH_NOT_FOUND
678 )
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Invalid machine settings file name '%s' (%Rrc)"),
681 mData->m_strConfigFileFull.c_str(),
682 vrc);
683 return rc;
684}
685
686/**
687 * Initializes the registered machine by loading the settings file.
688 * This method is separated from #init() in order to make it possible to
689 * retry the operation after VirtualBox startup instead of refusing to
690 * startup the whole VirtualBox server in case if the settings file of some
691 * registered VM is invalid or inaccessible.
692 *
693 * @note Must be always called from this object's write lock
694 * (unless called from #init() that doesn't need any locking).
695 * @note Locks the mUSBController method for writing.
696 * @note Subclasses must not call this method.
697 */
698HRESULT Machine::i_registeredInit()
699{
700 AssertReturn(!i_isSessionMachine(), E_FAIL);
701 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
702 AssertReturn(mData->mUuid.isValid(), E_FAIL);
703 AssertReturn(!mData->mAccessible, E_FAIL);
704
705 HRESULT rc = initDataAndChildObjects();
706
707 if (SUCCEEDED(rc))
708 {
709 /* Temporarily reset the registered flag in order to let setters
710 * potentially called from loadSettings() succeed (isMutable() used in
711 * all setters will return FALSE for a Machine instance if mRegistered
712 * is TRUE). */
713 mData->mRegistered = FALSE;
714
715 try
716 {
717 // load and parse machine XML; this will throw on XML or logic errors
718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
719
720 if (mData->mUuid != mData->pMachineConfigFile->uuid)
721 throw setError(E_FAIL,
722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
723 mData->pMachineConfigFile->uuid.raw(),
724 mData->m_strConfigFileFull.c_str(),
725 mData->mUuid.toString().c_str(),
726 mParent->i_settingsFilePath().c_str());
727
728 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
729 NULL /* const Guid *puuidRegistry */);
730 if (FAILED(rc)) throw rc;
731 }
732 catch (HRESULT err)
733 {
734 /* we assume that error info is set by the thrower */
735 rc = err;
736 }
737 catch (...)
738 {
739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
740 }
741
742 /* Restore the registered flag (even on failure) */
743 mData->mRegistered = TRUE;
744 }
745
746 if (SUCCEEDED(rc))
747 {
748 /* Set mAccessible to TRUE only if we successfully locked and loaded
749 * the settings file */
750 mData->mAccessible = TRUE;
751
752 /* commit all changes made during loading the settings file */
753 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
754 /// @todo r=klaus for some reason the settings loading logic backs up
755 // the settings, and therefore a commit is needed. Should probably be changed.
756 }
757 else
758 {
759 /* If the machine is registered, then, instead of returning a
760 * failure, we mark it as inaccessible and set the result to
761 * success to give it a try later */
762
763 /* fetch the current error info */
764 mData->mAccessError = com::ErrorInfo();
765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
766 mData->mUuid.raw(),
767 mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 i_rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->i_unregisterMachineMedia(i_getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 i_saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!i_isSnapshotMachine());
820 Assert(!i_isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it does
835 * VirtualBox::addCaller() for the duration of the
836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 LogWarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(i_getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->i_unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
879 i_rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// Wrapped IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
894{
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 aParent = pVirtualBox;
898
899 return S_OK;
900}
901
902
903HRESULT Machine::getAccessible(BOOL *aAccessible)
904{
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->i_dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = i_registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->i_onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
959 {
960 /* return shortly */
961 aAccessError = NULL;
962 return S_OK;
963 }
964
965 HRESULT rc = S_OK;
966
967 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
968 rc = errorInfo.createObject();
969 if (SUCCEEDED(rc))
970 {
971 errorInfo->init(mData->mAccessError.getResultCode(),
972 mData->mAccessError.getInterfaceID().ref(),
973 Utf8Str(mData->mAccessError.getComponent()).c_str(),
974 Utf8Str(mData->mAccessError.getText()));
975 aAccessError = errorInfo;
976 }
977
978 return rc;
979}
980
981HRESULT Machine::getName(com::Utf8Str &aName)
982{
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 aName = mUserData->s.strName;
986
987 return S_OK;
988}
989
990HRESULT Machine::setName(const com::Utf8Str &aName)
991{
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995
996 if (test.isValid())
997 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
998
999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 HRESULT rc = i_checkStateDependency(MutableStateDep);
1002 if (FAILED(rc)) return rc;
1003
1004 i_setModified(IsModified_MachineData);
1005 mUserData.backup();
1006 mUserData->s.strName = aName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1012{
1013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 aDescription = mUserData->s.strDescription;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1021{
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 // this can be done in principle in any state as it doesn't affect the VM
1025 // significantly, but play safe by not messing around while complex
1026 // activities are going on
1027 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1028 if (FAILED(rc)) return rc;
1029
1030 i_setModified(IsModified_MachineData);
1031 mUserData.backup();
1032 mUserData->s.strDescription = aDescription;
1033
1034 return S_OK;
1035}
1036
1037HRESULT Machine::getId(com::Guid &aId)
1038{
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 aId = mData->mUuid;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049 aGroups.resize(mUserData->s.llGroups.size());
1050 size_t i = 0;
1051 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1052 it != mUserData->s.llGroups.end(); ++it, ++i)
1053 aGroups[i] = (*it);
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1059{
1060 StringsList llGroups;
1061 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1062 if (FAILED(rc))
1063 return rc;
1064
1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1066
1067 rc = i_checkStateDependency(MutableOrSavedStateDep);
1068 if (FAILED(rc)) return rc;
1069
1070 i_setModified(IsModified_MachineData);
1071 mUserData.backup();
1072 mUserData->s.llGroups = llGroups;
1073
1074 return S_OK;
1075}
1076
1077HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1078{
1079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1080
1081 aOSTypeId = mUserData->s.strOsType;
1082
1083 return S_OK;
1084}
1085
1086HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1087{
1088 /* look up the object by Id to check it is valid */
1089 ComPtr<IGuestOSType> guestOSType;
1090 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1091 if (FAILED(rc)) return rc;
1092
1093 /* when setting, always use the "etalon" value for consistency -- lookup
1094 * by ID is case-insensitive and the input value may have different case */
1095 Bstr osTypeId;
1096 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1097 if (FAILED(rc)) return rc;
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 rc = i_checkStateDependency(MutableStateDep);
1102 if (FAILED(rc)) return rc;
1103
1104 i_setModified(IsModified_MachineData);
1105 mUserData.backup();
1106 mUserData->s.strOsType = osTypeId;
1107
1108 return S_OK;
1109}
1110
1111HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1112{
1113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 *aFirmwareType = mHWData->mFirmwareType;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1121{
1122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 HRESULT rc = i_checkStateDependency(MutableStateDep);
1125 if (FAILED(rc)) return rc;
1126
1127 i_setModified(IsModified_MachineData);
1128 mHWData.backup();
1129 mHWData->mFirmwareType = aFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1135{
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1144{
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 HRESULT rc = i_checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 i_setModified(IsModified_MachineData);
1151 mHWData.backup();
1152 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aPointingHIDType = mHWData->mPointingHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mPointingHIDType = aPointingHIDType;
1176
1177 return S_OK;
1178}
1179
1180HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1181{
1182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 *aChipsetType = mHWData->mChipsetType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1190{
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = i_checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 if (aChipsetType != mHWData->mChipsetType)
1197 {
1198 i_setModified(IsModified_MachineData);
1199 mHWData.backup();
1200 mHWData->mChipsetType = aChipsetType;
1201
1202 // Resize network adapter array, to be finalized on commit/rollback.
1203 // We must not throw away entries yet, otherwise settings are lost
1204 // without a way to roll back.
1205 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1206 size_t oldCount = mNetworkAdapters.size();
1207 if (newCount > oldCount)
1208 {
1209 mNetworkAdapters.resize(newCount);
1210 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1211 {
1212 unconst(mNetworkAdapters[slot]).createObject();
1213 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1214 }
1215 }
1216 }
1217
1218 return S_OK;
1219}
1220
1221HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1222{
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 *aParavirtProvider = mHWData->mParavirtProvider;
1226
1227 return S_OK;
1228}
1229
1230HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1231{
1232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 HRESULT rc = i_checkStateDependency(MutableStateDep);
1235 if (FAILED(rc)) return rc;
1236
1237 if (aParavirtProvider != mHWData->mParavirtProvider)
1238 {
1239 i_setModified(IsModified_MachineData);
1240 mHWData.backup();
1241 mHWData->mParavirtProvider = aParavirtProvider;
1242 }
1243
1244 return S_OK;
1245}
1246
1247HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1248{
1249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 *aParavirtProvider = mHWData->mParavirtProvider;
1252 switch (mHWData->mParavirtProvider)
1253 {
1254 case ParavirtProvider_None:
1255 case ParavirtProvider_HyperV:
1256 case ParavirtProvider_KVM:
1257 case ParavirtProvider_Minimal:
1258 break;
1259
1260 /* Resolve dynamic provider types to the effective types. */
1261 default:
1262 {
1263 ComPtr<IGuestOSType> ptrGuestOSType;
1264 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1266
1267 Bstr guestTypeFamilyId;
1268 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1269 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1270 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1271
1272 switch (mHWData->mParavirtProvider)
1273 {
1274 case ParavirtProvider_Legacy:
1275 {
1276 if (fOsXGuest)
1277 *aParavirtProvider = ParavirtProvider_Minimal;
1278 else
1279 *aParavirtProvider = ParavirtProvider_None;
1280 break;
1281 }
1282
1283 case ParavirtProvider_Default:
1284 {
1285 if (fOsXGuest)
1286 *aParavirtProvider = ParavirtProvider_Minimal;
1287 else if ( mUserData->s.strOsType == "Windows10"
1288 || mUserData->s.strOsType == "Windows10_64"
1289 || mUserData->s.strOsType == "Windows81"
1290 || mUserData->s.strOsType == "Windows81_64"
1291 || mUserData->s.strOsType == "Windows8"
1292 || mUserData->s.strOsType == "Windows8_64"
1293 || mUserData->s.strOsType == "Windows7"
1294 || mUserData->s.strOsType == "Windows7_64"
1295 || mUserData->s.strOsType == "WindowsVista"
1296 || mUserData->s.strOsType == "WindowsVista_64"
1297 || mUserData->s.strOsType == "Windows2012"
1298 || mUserData->s.strOsType == "Windows2012_64"
1299 || mUserData->s.strOsType == "Windows2008"
1300 || mUserData->s.strOsType == "Windows2008_64")
1301 {
1302 *aParavirtProvider = ParavirtProvider_HyperV;
1303 }
1304 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1305 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1306 || mUserData->s.strOsType == "Linux"
1307 || mUserData->s.strOsType == "Linux_64"
1308 || mUserData->s.strOsType == "ArchLinux"
1309 || mUserData->s.strOsType == "ArchLinux_64"
1310 || mUserData->s.strOsType == "Debian"
1311 || mUserData->s.strOsType == "Debian_64"
1312 || mUserData->s.strOsType == "Fedora"
1313 || mUserData->s.strOsType == "Fedora_64"
1314 || mUserData->s.strOsType == "Gentoo"
1315 || mUserData->s.strOsType == "Gentoo_64"
1316 || mUserData->s.strOsType == "Mandriva"
1317 || mUserData->s.strOsType == "Mandriva_64"
1318 || mUserData->s.strOsType == "OpenSUSE"
1319 || mUserData->s.strOsType == "OpenSUSE_64"
1320 || mUserData->s.strOsType == "Oracle"
1321 || mUserData->s.strOsType == "Oracle_64"
1322 || mUserData->s.strOsType == "RedHat"
1323 || mUserData->s.strOsType == "RedHat_64"
1324 || mUserData->s.strOsType == "Turbolinux"
1325 || mUserData->s.strOsType == "Turbolinux_64"
1326 || mUserData->s.strOsType == "Ubuntu"
1327 || mUserData->s.strOsType == "Ubuntu_64"
1328 || mUserData->s.strOsType == "Xandros"
1329 || mUserData->s.strOsType == "Xandros_64")
1330 {
1331 *aParavirtProvider = ParavirtProvider_KVM;
1332 }
1333 else
1334 *aParavirtProvider = ParavirtProvider_None;
1335 break;
1336 }
1337 }
1338 break;
1339 }
1340 }
1341
1342 Assert( *aParavirtProvider == ParavirtProvider_None
1343 || *aParavirtProvider == ParavirtProvider_Minimal
1344 || *aParavirtProvider == ParavirtProvider_HyperV
1345 || *aParavirtProvider == ParavirtProvider_KVM);
1346 return S_OK;
1347}
1348
1349HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1350{
1351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 aHardwareVersion = mHWData->mHWVersion;
1354
1355 return S_OK;
1356}
1357
1358HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1359{
1360 /* check known version */
1361 Utf8Str hwVersion = aHardwareVersion;
1362 if ( hwVersion.compare("1") != 0
1363 && hwVersion.compare("2") != 0)
1364 return setError(E_INVALIDARG,
1365 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1366
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 HRESULT rc = i_checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 i_setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 mHWData->mHWVersion = aHardwareVersion;
1375
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 if (!mHWData->mHardwareUUID.isZero())
1384 aHardwareUUID = mHWData->mHardwareUUID;
1385 else
1386 aHardwareUUID = mData->mUuid;
1387
1388 return S_OK;
1389}
1390
1391HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1392{
1393 if (!aHardwareUUID.isValid())
1394 return E_INVALIDARG;
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 HRESULT rc = i_checkStateDependency(MutableStateDep);
1399 if (FAILED(rc)) return rc;
1400
1401 i_setModified(IsModified_MachineData);
1402 mHWData.backup();
1403 if (aHardwareUUID == mData->mUuid)
1404 mHWData->mHardwareUUID.clear();
1405 else
1406 mHWData->mHardwareUUID = aHardwareUUID;
1407
1408 return S_OK;
1409}
1410
1411HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1412{
1413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 *aMemorySize = mHWData->mMemorySize;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::setMemorySize(ULONG aMemorySize)
1421{
1422 /* check RAM limits */
1423 if ( aMemorySize < MM_RAM_MIN_IN_MB
1424 || aMemorySize > MM_RAM_MAX_IN_MB
1425 )
1426 return setError(E_INVALIDARG,
1427 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1428 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1429
1430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 HRESULT rc = i_checkStateDependency(MutableStateDep);
1433 if (FAILED(rc)) return rc;
1434
1435 i_setModified(IsModified_MachineData);
1436 mHWData.backup();
1437 mHWData->mMemorySize = aMemorySize;
1438
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 *aCPUCount = mHWData->mCPUCount;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setCPUCount(ULONG aCPUCount)
1452{
1453 /* check CPU limits */
1454 if ( aCPUCount < SchemaDefs::MinCPUCount
1455 || aCPUCount > SchemaDefs::MaxCPUCount
1456 )
1457 return setError(E_INVALIDARG,
1458 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1459 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1460
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1464 if (mHWData->mCPUHotPlugEnabled)
1465 {
1466 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1467 {
1468 if (mHWData->mCPUAttached[idx])
1469 return setError(E_INVALIDARG,
1470 tr("There is still a CPU attached to socket %lu."
1471 "Detach the CPU before removing the socket"),
1472 aCPUCount, idx+1);
1473 }
1474 }
1475
1476 HRESULT rc = i_checkStateDependency(MutableStateDep);
1477 if (FAILED(rc)) return rc;
1478
1479 i_setModified(IsModified_MachineData);
1480 mHWData.backup();
1481 mHWData->mCPUCount = aCPUCount;
1482
1483 return S_OK;
1484}
1485
1486HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1487{
1488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1491
1492 return S_OK;
1493}
1494
1495HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1496{
1497 HRESULT rc = S_OK;
1498
1499 /* check throttle limits */
1500 if ( aCPUExecutionCap < 1
1501 || aCPUExecutionCap > 100
1502 )
1503 return setError(E_INVALIDARG,
1504 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1505 aCPUExecutionCap, 1, 100);
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 alock.release();
1510 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1511 alock.acquire();
1512 if (FAILED(rc)) return rc;
1513
1514 i_setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1517
1518 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1519 if (Global::IsOnline(mData->mMachineState))
1520 i_saveSettings(NULL);
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1539
1540 rc = i_checkStateDependency(MutableStateDep);
1541 if (FAILED(rc)) return rc;
1542
1543 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1544 {
1545 if (aCPUHotPlugEnabled)
1546 {
1547 i_setModified(IsModified_MachineData);
1548 mHWData.backup();
1549
1550 /* Add the amount of CPUs currently attached */
1551 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1552 mHWData->mCPUAttached[i] = true;
1553 }
1554 else
1555 {
1556 /*
1557 * We can disable hotplug only if the amount of maximum CPUs is equal
1558 * to the amount of attached CPUs
1559 */
1560 unsigned cCpusAttached = 0;
1561 unsigned iHighestId = 0;
1562
1563 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1564 {
1565 if (mHWData->mCPUAttached[i])
1566 {
1567 cCpusAttached++;
1568 iHighestId = i;
1569 }
1570 }
1571
1572 if ( (cCpusAttached != mHWData->mCPUCount)
1573 || (iHighestId >= mHWData->mCPUCount))
1574 return setError(E_INVALIDARG,
1575 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1576
1577 i_setModified(IsModified_MachineData);
1578 mHWData.backup();
1579 }
1580 }
1581
1582 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1583
1584 return rc;
1585}
1586
1587HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1588{
1589#ifdef VBOX_WITH_USB_CARDREADER
1590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1591
1592 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1593
1594 return S_OK;
1595#else
1596 NOREF(aEmulatedUSBCardReaderEnabled);
1597 return E_NOTIMPL;
1598#endif
1599}
1600
1601HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1602{
1603#ifdef VBOX_WITH_USB_CARDREADER
1604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1605
1606 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1607 if (FAILED(rc)) return rc;
1608
1609 i_setModified(IsModified_MachineData);
1610 mHWData.backup();
1611 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1612
1613 return S_OK;
1614#else
1615 NOREF(aEmulatedUSBCardReaderEnabled);
1616 return E_NOTIMPL;
1617#endif
1618}
1619
1620HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1621{
1622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 *aHPETEnabled = mHWData->mHPETEnabled;
1625
1626 return S_OK;
1627}
1628
1629HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1630{
1631 HRESULT rc = S_OK;
1632
1633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1634
1635 rc = i_checkStateDependency(MutableStateDep);
1636 if (FAILED(rc)) return rc;
1637
1638 i_setModified(IsModified_MachineData);
1639 mHWData.backup();
1640
1641 mHWData->mHPETEnabled = aHPETEnabled;
1642
1643 return rc;
1644}
1645
1646HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1647{
1648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1649
1650 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1651 return S_OK;
1652}
1653
1654HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1655{
1656 HRESULT rc = S_OK;
1657
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659
1660 i_setModified(IsModified_MachineData);
1661 mHWData.backup();
1662 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1663
1664 alock.release();
1665 rc = i_onVideoCaptureChange();
1666 alock.acquire();
1667 if (FAILED(rc))
1668 {
1669 /*
1670 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1671 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1672 * determine if it should start or stop capturing. Therefore we need to manually
1673 * undo change.
1674 */
1675 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1676 return rc;
1677 }
1678
1679 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1680 if (Global::IsOnline(mData->mMachineState))
1681 i_saveSettings(NULL);
1682
1683 return rc;
1684}
1685
1686HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1687{
1688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1689 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1690 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1691 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1692 return S_OK;
1693}
1694
1695HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1696{
1697 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1698 bool fChanged = false;
1699
1700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1701
1702 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1703 {
1704 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1705 {
1706 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1707 fChanged = true;
1708 }
1709 }
1710 if (fChanged)
1711 {
1712 alock.release();
1713 HRESULT rc = i_onVideoCaptureChange();
1714 alock.acquire();
1715 if (FAILED(rc)) return rc;
1716 i_setModified(IsModified_MachineData);
1717
1718 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1719 if (Global::IsOnline(mData->mMachineState))
1720 i_saveSettings(NULL);
1721 }
1722
1723 return S_OK;
1724}
1725
1726HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1727{
1728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1729 if (mHWData->mVideoCaptureFile.isEmpty())
1730 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1731 else
1732 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1733 return S_OK;
1734}
1735
1736HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1737{
1738 Utf8Str strFile(aVideoCaptureFile);
1739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1740
1741 if ( Global::IsOnline(mData->mMachineState)
1742 && mHWData->mVideoCaptureEnabled)
1743 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1744
1745 if (!RTPathStartsWithRoot(strFile.c_str()))
1746 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1747
1748 if (!strFile.isEmpty())
1749 {
1750 Utf8Str defaultFile;
1751 i_getDefaultVideoCaptureFile(defaultFile);
1752 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1753 strFile.setNull();
1754 }
1755
1756 i_setModified(IsModified_MachineData);
1757 mHWData.backup();
1758 mHWData->mVideoCaptureFile = strFile;
1759
1760 return S_OK;
1761}
1762
1763HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1767 return S_OK;
1768}
1769
1770HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1771{
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 if ( Global::IsOnline(mData->mMachineState)
1775 && mHWData->mVideoCaptureEnabled)
1776 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1786{
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1789 return S_OK;
1790}
1791
1792HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1793{
1794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 if ( Global::IsOnline(mData->mMachineState)
1797 && mHWData->mVideoCaptureEnabled)
1798 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1799
1800 i_setModified(IsModified_MachineData);
1801 mHWData.backup();
1802 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1808{
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1811 return S_OK;
1812}
1813
1814HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1815{
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 i_setModified(IsModified_MachineData);
1823 mHWData.backup();
1824 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1833 return S_OK;
1834}
1835
1836HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1837{
1838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 if ( Global::IsOnline(mData->mMachineState)
1841 && mHWData->mVideoCaptureEnabled)
1842 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1855 return S_OK;
1856}
1857
1858HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1859{
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 i_setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1877 return S_OK;
1878}
1879
1880HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1881{
1882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 if ( Global::IsOnline(mData->mMachineState)
1885 && mHWData->mVideoCaptureEnabled)
1886 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1887
1888 i_setModified(IsModified_MachineData);
1889 mHWData.backup();
1890 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1896{
1897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1900 return S_OK;
1901}
1902
1903HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1904{
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 if ( Global::IsOnline(mData->mMachineState)
1908 && mHWData->mVideoCaptureEnabled)
1909 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1910
1911 i_setModified(IsModified_MachineData);
1912 mHWData.backup();
1913 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1914
1915 return S_OK;
1916}
1917
1918HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1919{
1920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1921
1922 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1923
1924 return S_OK;
1925}
1926
1927HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1928{
1929 switch (aGraphicsControllerType)
1930 {
1931 case GraphicsControllerType_Null:
1932 case GraphicsControllerType_VBoxVGA:
1933#ifdef VBOX_WITH_VMSVGA
1934 case GraphicsControllerType_VMSVGA:
1935#endif
1936 break;
1937 default:
1938 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1939 }
1940
1941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1942
1943 HRESULT rc = i_checkStateDependency(MutableStateDep);
1944 if (FAILED(rc)) return rc;
1945
1946 i_setModified(IsModified_MachineData);
1947 mHWData.backup();
1948 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1949
1950 return S_OK;
1951}
1952
1953HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1954{
1955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 *aVRAMSize = mHWData->mVRAMSize;
1958
1959 return S_OK;
1960}
1961
1962HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1963{
1964 /* check VRAM limits */
1965 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1966 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1967 return setError(E_INVALIDARG,
1968 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1969 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1970
1971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 HRESULT rc = i_checkStateDependency(MutableStateDep);
1974 if (FAILED(rc)) return rc;
1975
1976 i_setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mVRAMSize = aVRAMSize;
1979
1980 return S_OK;
1981}
1982
1983/** @todo this method should not be public */
1984HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1985{
1986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1989
1990 return S_OK;
1991}
1992
1993/**
1994 * Set the memory balloon size.
1995 *
1996 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1997 * we have to make sure that we never call IGuest from here.
1998 */
1999HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2000{
2001 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2002#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2003 /* check limits */
2004 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2005 return setError(E_INVALIDARG,
2006 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2007 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2008
2009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2014
2015 return S_OK;
2016#else
2017 NOREF(aMemoryBalloonSize);
2018 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2019#endif
2020}
2021
2022HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2023{
2024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2027 return S_OK;
2028}
2029
2030HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2031{
2032#ifdef VBOX_WITH_PAGE_SHARING
2033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2039 return S_OK;
2040#else
2041 NOREF(aPageFusionEnabled);
2042 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2043#endif
2044}
2045
2046HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2047{
2048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2051
2052 return S_OK;
2053}
2054
2055HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2056{
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 HRESULT rc = i_checkStateDependency(MutableStateDep);
2060 if (FAILED(rc)) return rc;
2061
2062 /** @todo check validity! */
2063
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2067
2068 return S_OK;
2069}
2070
2071
2072HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2073{
2074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2077
2078 return S_OK;
2079}
2080
2081HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2082{
2083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 HRESULT rc = i_checkStateDependency(MutableStateDep);
2086 if (FAILED(rc)) return rc;
2087
2088 /** @todo check validity! */
2089 i_setModified(IsModified_MachineData);
2090 mHWData.backup();
2091 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2092
2093 return S_OK;
2094}
2095
2096HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2097{
2098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2099
2100 *aMonitorCount = mHWData->mMonitorCount;
2101
2102 return S_OK;
2103}
2104
2105HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2106{
2107 /* make sure monitor count is a sensible number */
2108 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2109 return setError(E_INVALIDARG,
2110 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2111 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2112
2113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 HRESULT rc = i_checkStateDependency(MutableStateDep);
2116 if (FAILED(rc)) return rc;
2117
2118 i_setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mMonitorCount = aMonitorCount;
2121
2122 return S_OK;
2123}
2124
2125HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2126{
2127 /* mBIOSSettings is constant during life time, no need to lock */
2128 aBIOSSettings = mBIOSSettings;
2129
2130 return S_OK;
2131}
2132
2133HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2134{
2135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 switch (aProperty)
2138 {
2139 case CPUPropertyType_PAE:
2140 *aValue = mHWData->mPAEEnabled;
2141 break;
2142
2143 case CPUPropertyType_Synthetic:
2144 *aValue = mHWData->mSyntheticCpu;
2145 break;
2146
2147 case CPUPropertyType_LongMode:
2148 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2149 *aValue = TRUE;
2150 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2151 *aValue = FALSE;
2152#if HC_ARCH_BITS == 64
2153 else
2154 *aValue = TRUE;
2155#else
2156 else
2157 {
2158 *aValue = FALSE;
2159
2160 ComPtr<IGuestOSType> ptrGuestOSType;
2161 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2162 if (SUCCEEDED(hrc2))
2163 {
2164 BOOL fIs64Bit = FALSE;
2165 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2166 if (SUCCEEDED(hrc2) && fIs64Bit)
2167 {
2168 ComObjPtr<Host> ptrHost = mParent->i_host();
2169 alock.release();
2170
2171 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2172 if (FAILED(hrc2))
2173 *aValue = FALSE;
2174 }
2175 }
2176 }
2177#endif
2178 break;
2179
2180 case CPUPropertyType_TripleFaultReset:
2181 *aValue = mHWData->mTripleFaultReset;
2182 break;
2183
2184 default:
2185 return E_INVALIDARG;
2186 }
2187 return S_OK;
2188}
2189
2190HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2191{
2192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2193
2194 HRESULT rc = i_checkStateDependency(MutableStateDep);
2195 if (FAILED(rc)) return rc;
2196
2197 switch (aProperty)
2198 {
2199 case CPUPropertyType_PAE:
2200 i_setModified(IsModified_MachineData);
2201 mHWData.backup();
2202 mHWData->mPAEEnabled = !!aValue;
2203 break;
2204
2205 case CPUPropertyType_Synthetic:
2206 i_setModified(IsModified_MachineData);
2207 mHWData.backup();
2208 mHWData->mSyntheticCpu = !!aValue;
2209 break;
2210
2211 case CPUPropertyType_LongMode:
2212 i_setModified(IsModified_MachineData);
2213 mHWData.backup();
2214 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2215 break;
2216
2217 case CPUPropertyType_TripleFaultReset:
2218 i_setModified(IsModified_MachineData);
2219 mHWData.backup();
2220 mHWData->mTripleFaultReset = !!aValue;
2221 break;
2222
2223 default:
2224 return E_INVALIDARG;
2225 }
2226 return S_OK;
2227}
2228
2229HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2230{
2231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
2233 switch(aId)
2234 {
2235 case 0x0:
2236 case 0x1:
2237 case 0x2:
2238 case 0x3:
2239 case 0x4:
2240 case 0x5:
2241 case 0x6:
2242 case 0x7:
2243 case 0x8:
2244 case 0x9:
2245 case 0xA:
2246 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2247 return E_INVALIDARG;
2248
2249 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2250 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2251 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2252 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2253 break;
2254
2255 case 0x80000000:
2256 case 0x80000001:
2257 case 0x80000002:
2258 case 0x80000003:
2259 case 0x80000004:
2260 case 0x80000005:
2261 case 0x80000006:
2262 case 0x80000007:
2263 case 0x80000008:
2264 case 0x80000009:
2265 case 0x8000000A:
2266 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2267 return E_INVALIDARG;
2268
2269 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2270 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2271 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2272 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2273 break;
2274
2275 default:
2276 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2277 }
2278 return S_OK;
2279}
2280
2281
2282HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2283{
2284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2285
2286 HRESULT rc = i_checkStateDependency(MutableStateDep);
2287 if (FAILED(rc)) return rc;
2288
2289 switch(aId)
2290 {
2291 case 0x0:
2292 case 0x1:
2293 case 0x2:
2294 case 0x3:
2295 case 0x4:
2296 case 0x5:
2297 case 0x6:
2298 case 0x7:
2299 case 0x8:
2300 case 0x9:
2301 case 0xA:
2302 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2303 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2304 i_setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2307 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2308 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2309 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2310 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2311 break;
2312
2313 case 0x80000000:
2314 case 0x80000001:
2315 case 0x80000002:
2316 case 0x80000003:
2317 case 0x80000004:
2318 case 0x80000005:
2319 case 0x80000006:
2320 case 0x80000007:
2321 case 0x80000008:
2322 case 0x80000009:
2323 case 0x8000000A:
2324 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2325 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2329 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2330 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2331 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2332 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2333 break;
2334
2335 default:
2336 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2337 }
2338 return S_OK;
2339}
2340
2341HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2342{
2343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2344
2345 HRESULT rc = i_checkStateDependency(MutableStateDep);
2346 if (FAILED(rc)) return rc;
2347
2348 switch(aId)
2349 {
2350 case 0x0:
2351 case 0x1:
2352 case 0x2:
2353 case 0x3:
2354 case 0x4:
2355 case 0x5:
2356 case 0x6:
2357 case 0x7:
2358 case 0x8:
2359 case 0x9:
2360 case 0xA:
2361 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2362 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2363 i_setModified(IsModified_MachineData);
2364 mHWData.backup();
2365 /* Invalidate leaf. */
2366 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2367 break;
2368
2369 case 0x80000000:
2370 case 0x80000001:
2371 case 0x80000002:
2372 case 0x80000003:
2373 case 0x80000004:
2374 case 0x80000005:
2375 case 0x80000006:
2376 case 0x80000007:
2377 case 0x80000008:
2378 case 0x80000009:
2379 case 0x8000000A:
2380 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2381 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2382 i_setModified(IsModified_MachineData);
2383 mHWData.backup();
2384 /* Invalidate leaf. */
2385 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2386 break;
2387
2388 default:
2389 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2390 }
2391 return S_OK;
2392}
2393
2394HRESULT Machine::removeAllCPUIDLeaves()
2395{
2396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 HRESULT rc = i_checkStateDependency(MutableStateDep);
2399 if (FAILED(rc)) return rc;
2400
2401 i_setModified(IsModified_MachineData);
2402 mHWData.backup();
2403
2404 /* Invalidate all standard leafs. */
2405 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2406 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2407
2408 /* Invalidate all extended leafs. */
2409 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2410 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2411
2412 return S_OK;
2413}
2414HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2415{
2416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2417
2418 switch(aProperty)
2419 {
2420 case HWVirtExPropertyType_Enabled:
2421 *aValue = mHWData->mHWVirtExEnabled;
2422 break;
2423
2424 case HWVirtExPropertyType_VPID:
2425 *aValue = mHWData->mHWVirtExVPIDEnabled;
2426 break;
2427
2428 case HWVirtExPropertyType_NestedPaging:
2429 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2430 break;
2431
2432 case HWVirtExPropertyType_UnrestrictedExecution:
2433 *aValue = mHWData->mHWVirtExUXEnabled;
2434 break;
2435
2436 case HWVirtExPropertyType_LargePages:
2437 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2438#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2439 *aValue = FALSE;
2440#endif
2441 break;
2442
2443 case HWVirtExPropertyType_Force:
2444 *aValue = mHWData->mHWVirtExForceEnabled;
2445 break;
2446
2447 default:
2448 return E_INVALIDARG;
2449 }
2450 return S_OK;
2451}
2452
2453HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2454{
2455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2456
2457 HRESULT rc = i_checkStateDependency(MutableStateDep);
2458 if (FAILED(rc)) return rc;
2459
2460 switch(aProperty)
2461 {
2462 case HWVirtExPropertyType_Enabled:
2463 i_setModified(IsModified_MachineData);
2464 mHWData.backup();
2465 mHWData->mHWVirtExEnabled = !!aValue;
2466 break;
2467
2468 case HWVirtExPropertyType_VPID:
2469 i_setModified(IsModified_MachineData);
2470 mHWData.backup();
2471 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2472 break;
2473
2474 case HWVirtExPropertyType_NestedPaging:
2475 i_setModified(IsModified_MachineData);
2476 mHWData.backup();
2477 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2478 break;
2479
2480 case HWVirtExPropertyType_UnrestrictedExecution:
2481 i_setModified(IsModified_MachineData);
2482 mHWData.backup();
2483 mHWData->mHWVirtExUXEnabled = !!aValue;
2484 break;
2485
2486 case HWVirtExPropertyType_LargePages:
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2490 break;
2491
2492 case HWVirtExPropertyType_Force:
2493 i_setModified(IsModified_MachineData);
2494 mHWData.backup();
2495 mHWData->mHWVirtExForceEnabled = !!aValue;
2496 break;
2497
2498 default:
2499 return E_INVALIDARG;
2500 }
2501
2502 return S_OK;
2503}
2504
2505HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2506{
2507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2515{
2516 /* @todo (r=dmik):
2517 * 1. Allow to change the name of the snapshot folder containing snapshots
2518 * 2. Rename the folder on disk instead of just changing the property
2519 * value (to be smart and not to leave garbage). Note that it cannot be
2520 * done here because the change may be rolled back. Thus, the right
2521 * place is #saveSettings().
2522 */
2523
2524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2525
2526 HRESULT rc = i_checkStateDependency(MutableStateDep);
2527 if (FAILED(rc)) return rc;
2528
2529 if (!mData->mCurrentSnapshot.isNull())
2530 return setError(E_FAIL,
2531 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2532
2533 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2534
2535 if (strSnapshotFolder.isEmpty())
2536 strSnapshotFolder = "Snapshots";
2537 int vrc = i_calculateFullPath(strSnapshotFolder,
2538 strSnapshotFolder);
2539 if (RT_FAILURE(vrc))
2540 return setError(E_FAIL,
2541 tr("Invalid snapshot folder '%s' (%Rrc)"),
2542 strSnapshotFolder.c_str(), vrc);
2543
2544 i_setModified(IsModified_MachineData);
2545 mUserData.backup();
2546
2547 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2548
2549 return S_OK;
2550}
2551
2552HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2553{
2554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2555
2556 aMediumAttachments.resize(mMediaData->mAttachments.size());
2557 size_t i = 0;
2558 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2559 it != mMediaData->mAttachments.end(); ++it, ++i)
2560 aMediumAttachments[i] = *it;
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 Assert(!!mVRDEServer);
2570
2571 aVRDEServer = mVRDEServer;
2572
2573 return S_OK;
2574}
2575
2576HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2577{
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 aAudioAdapter = mAudioAdapter;
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2586{
2587#ifdef VBOX_WITH_VUSB
2588 clearError();
2589 MultiResult rc(S_OK);
2590
2591# ifdef VBOX_WITH_USB
2592 rc = mParent->i_host()->i_checkUSBProxyService();
2593 if (FAILED(rc)) return rc;
2594# endif
2595
2596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2597
2598 USBControllerList data = *mUSBControllers.data();
2599 aUSBControllers.resize(data.size());
2600 size_t i = 0;
2601 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2602 aUSBControllers[i] = *it;
2603
2604 return S_OK;
2605#else
2606 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2607 * extended error info to indicate that USB is simply not available
2608 * (w/o treating it as a failure), for example, as in OSE */
2609 NOREF(aUSBControllers);
2610 ReturnComNotImplemented();
2611#endif /* VBOX_WITH_VUSB */
2612}
2613
2614HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2615{
2616#ifdef VBOX_WITH_VUSB
2617 clearError();
2618 MultiResult rc(S_OK);
2619
2620# ifdef VBOX_WITH_USB
2621 rc = mParent->i_host()->i_checkUSBProxyService();
2622 if (FAILED(rc)) return rc;
2623# endif
2624
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 aUSBDeviceFilters = mUSBDeviceFilters;
2628 return rc;
2629#else
2630 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2631 * extended error info to indicate that USB is simply not available
2632 * (w/o treating it as a failure), for example, as in OSE */
2633 NOREF(aUSBDeviceFilters);
2634 ReturnComNotImplemented();
2635#endif /* VBOX_WITH_VUSB */
2636}
2637
2638HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 aSettingsFilePath = mData->m_strConfigFileFull;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2652 if (FAILED(rc)) return rc;
2653
2654 if (!mData->pMachineConfigFile->fileExists())
2655 // this is a new machine, and no config file exists yet:
2656 *aSettingsModified = TRUE;
2657 else
2658 *aSettingsModified = (mData->flModifications != 0);
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2664{
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 *aSessionState = mData->mSession.mState;
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2674{
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 aSessionType = mData->mSession.mType;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 *aSessionPID = mData->mSession.mPID;
2687
2688 return S_OK;
2689}
2690
2691HRESULT Machine::getState(MachineState_T *aState)
2692{
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 *aState = mData->mMachineState;
2696 Assert(mData->mMachineState != MachineState_Null);
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2702{
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2706
2707 return S_OK;
2708}
2709
2710HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2711{
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 aStateFilePath = mSSData->strStateFilePath;
2715
2716 return S_OK;
2717}
2718
2719HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2720{
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 i_getLogFolder(aLogFolder);
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aCurrentSnapshot = mData->mCurrentSnapshot;
2733
2734 return S_OK;
2735}
2736
2737HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2738{
2739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2740
2741 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2742 ? 0
2743 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2749{
2750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 /* Note: for machines with no snapshots, we always return FALSE
2753 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2754 * reasons :) */
2755
2756 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2757 ? FALSE
2758 : mData->mCurrentStateModified;
2759
2760 return S_OK;
2761}
2762
2763HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2764{
2765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2766
2767 aSharedFolders.resize(mHWData->mSharedFolders.size());
2768 size_t i = 0;
2769 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2770 it != mHWData->mSharedFolders.end(); ++i, ++it)
2771 aSharedFolders[i] = *it;
2772
2773 return S_OK;
2774}
2775
2776HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2777{
2778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2779
2780 *aClipboardMode = mHWData->mClipboardMode;
2781
2782 return S_OK;
2783}
2784
2785HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2786{
2787 HRESULT rc = S_OK;
2788
2789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 alock.release();
2792 rc = i_onClipboardModeChange(aClipboardMode);
2793 alock.acquire();
2794 if (FAILED(rc)) return rc;
2795
2796 i_setModified(IsModified_MachineData);
2797 mHWData.backup();
2798 mHWData->mClipboardMode = aClipboardMode;
2799
2800 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2801 if (Global::IsOnline(mData->mMachineState))
2802 i_saveSettings(NULL);
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 *aDnDMode = mHWData->mDnDMode;
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2817{
2818 HRESULT rc = S_OK;
2819
2820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 alock.release();
2823 rc = i_onDnDModeChange(aDnDMode);
2824
2825 alock.acquire();
2826 if (FAILED(rc)) return rc;
2827
2828 i_setModified(IsModified_MachineData);
2829 mHWData.backup();
2830 mHWData->mDnDMode = aDnDMode;
2831
2832 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2833 if (Global::IsOnline(mData->mMachineState))
2834 i_saveSettings(NULL);
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2840{
2841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2842
2843 try
2844 {
2845 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2846 }
2847 catch (...)
2848 {
2849 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2850 }
2851
2852 return S_OK;
2853}
2854
2855HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2856{
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2860 if (FAILED(rc)) return rc;
2861
2862 i_setModified(IsModified_MachineData);
2863 mHWData.backup();
2864 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2865 return rc;
2866}
2867
2868HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2869{
2870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2871 StorageControllerList data = *mStorageControllers.data();
2872 size_t i = 0;
2873 aStorageControllers.resize(data.size());
2874 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2875 aStorageControllers[i] = *it;
2876 return S_OK;
2877}
2878
2879HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2880{
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882
2883 *aEnabled = mUserData->s.fTeleporterEnabled;
2884
2885 return S_OK;
2886}
2887
2888HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2889{
2890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2891
2892 /* Only allow it to be set to true when PoweredOff or Aborted.
2893 (Clearing it is always permitted.) */
2894 if ( aTeleporterEnabled
2895 && mData->mRegistered
2896 && ( !i_isSessionMachine()
2897 || ( mData->mMachineState != MachineState_PoweredOff
2898 && mData->mMachineState != MachineState_Teleported
2899 && mData->mMachineState != MachineState_Aborted
2900 )
2901 )
2902 )
2903 return setError(VBOX_E_INVALID_VM_STATE,
2904 tr("The machine is not powered off (state is %s)"),
2905 Global::stringifyMachineState(mData->mMachineState));
2906
2907 i_setModified(IsModified_MachineData);
2908 mUserData.backup();
2909 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2910
2911 return S_OK;
2912}
2913
2914HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2915{
2916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2919
2920 return S_OK;
2921}
2922
2923HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2924{
2925 if (aTeleporterPort >= _64K)
2926 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2927
2928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2931 if (FAILED(rc)) return rc;
2932
2933 i_setModified(IsModified_MachineData);
2934 mUserData.backup();
2935 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2936
2937 return S_OK;
2938}
2939
2940HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2941{
2942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2943
2944 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2945
2946 return S_OK;
2947}
2948
2949HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2950{
2951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2952
2953 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2954 if (FAILED(rc)) return rc;
2955
2956 i_setModified(IsModified_MachineData);
2957 mUserData.backup();
2958 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2959
2960 return S_OK;
2961}
2962
2963HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2964{
2965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2966 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2972{
2973 /*
2974 * Hash the password first.
2975 */
2976 com::Utf8Str aT = aTeleporterPassword;
2977
2978 if (!aT.isEmpty())
2979 {
2980 if (VBoxIsPasswordHashed(&aT))
2981 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2982 VBoxHashPassword(&aT);
2983 }
2984
2985 /*
2986 * Do the update.
2987 */
2988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2989 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2990 if (SUCCEEDED(hrc))
2991 {
2992 i_setModified(IsModified_MachineData);
2993 mUserData.backup();
2994 mUserData->s.strTeleporterPassword = aT;
2995 }
2996
2997 return hrc;
2998}
2999
3000HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3001{
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3005 return S_OK;
3006}
3007
3008HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3009{
3010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 /* @todo deal with running state change. */
3013 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3014 if (FAILED(rc)) return rc;
3015
3016 i_setModified(IsModified_MachineData);
3017 mUserData.backup();
3018 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3019 return S_OK;
3020}
3021
3022HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3023{
3024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3025
3026 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3027 return S_OK;
3028}
3029
3030HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
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.strFaultToleranceAddress = aFaultToleranceAddress;
3041 return S_OK;
3042}
3043
3044HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3045{
3046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3047
3048 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3049 return S_OK;
3050}
3051
3052HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3053{
3054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 /* @todo deal with running state change. */
3057 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3058 if (FAILED(rc)) return rc;
3059
3060 i_setModified(IsModified_MachineData);
3061 mUserData.backup();
3062 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3063 return S_OK;
3064}
3065
3066HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3067{
3068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3069
3070 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3071
3072 return S_OK;
3073}
3074
3075HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3076{
3077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3078
3079 /* @todo deal with running state change. */
3080 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3081 if (FAILED(rc)) return rc;
3082
3083 i_setModified(IsModified_MachineData);
3084 mUserData.backup();
3085 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3086
3087 return S_OK;
3088}
3089
3090HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3091{
3092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3095 return S_OK;
3096}
3097
3098HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3099{
3100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 /* @todo deal with running state change. */
3103 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3104 if (FAILED(rc)) return rc;
3105
3106 i_setModified(IsModified_MachineData);
3107 mUserData.backup();
3108 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3109 return S_OK;
3110}
3111
3112HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3113{
3114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3117
3118 return S_OK;
3119}
3120
3121HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3122{
3123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 /* Only allow it to be set to true when PoweredOff or Aborted.
3126 (Clearing it is always permitted.) */
3127 if ( aRTCUseUTC
3128 && mData->mRegistered
3129 && ( !i_isSessionMachine()
3130 || ( mData->mMachineState != MachineState_PoweredOff
3131 && mData->mMachineState != MachineState_Teleported
3132 && mData->mMachineState != MachineState_Aborted
3133 )
3134 )
3135 )
3136 return setError(VBOX_E_INVALID_VM_STATE,
3137 tr("The machine is not powered off (state is %s)"),
3138 Global::stringifyMachineState(mData->mMachineState));
3139
3140 i_setModified(IsModified_MachineData);
3141 mUserData.backup();
3142 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3143
3144 return S_OK;
3145}
3146
3147HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3148{
3149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3152
3153 return S_OK;
3154}
3155
3156HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3157{
3158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3159
3160 HRESULT rc = i_checkStateDependency(MutableStateDep);
3161 if (FAILED(rc)) return rc;
3162
3163 i_setModified(IsModified_MachineData);
3164 mHWData.backup();
3165 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3166
3167 return S_OK;
3168}
3169
3170HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3171{
3172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3173
3174 *aIOCacheSize = mHWData->mIOCacheSize;
3175
3176 return S_OK;
3177}
3178
3179HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3180{
3181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3182
3183 HRESULT rc = i_checkStateDependency(MutableStateDep);
3184 if (FAILED(rc)) return rc;
3185
3186 i_setModified(IsModified_MachineData);
3187 mHWData.backup();
3188 mHWData->mIOCacheSize = aIOCacheSize;
3189
3190 return S_OK;
3191}
3192
3193
3194/**
3195 * @note Locks objects!
3196 */
3197HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3198 LockType_T aLockType)
3199
3200{
3201 /* check the session state */
3202 SessionState_T state;
3203 HRESULT rc = aSession->COMGETTER(State)(&state);
3204 if (FAILED(rc)) return rc;
3205
3206 if (state != SessionState_Unlocked)
3207 return setError(VBOX_E_INVALID_OBJECT_STATE,
3208 tr("The given session is busy"));
3209
3210 // get the client's IInternalSessionControl interface
3211 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3212 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3213 E_INVALIDARG);
3214
3215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3216
3217 if (!mData->mRegistered)
3218 return setError(E_UNEXPECTED,
3219 tr("The machine '%s' is not registered"),
3220 mUserData->s.strName.c_str());
3221
3222 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3223
3224 SessionState_T oldState = mData->mSession.mState;
3225 /* Hack: in case the session is closing and there is a progress object
3226 * which allows waiting for the session to be closed, take the opportunity
3227 * and do a limited wait (max. 1 second). This helps a lot when the system
3228 * is busy and thus session closing can take a little while. */
3229 if ( mData->mSession.mState == SessionState_Unlocking
3230 && mData->mSession.mProgress)
3231 {
3232 alock.release();
3233 mData->mSession.mProgress->WaitForCompletion(1000);
3234 alock.acquire();
3235 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3236 }
3237
3238 // try again now
3239 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3240 // (i.e. session machine exists)
3241 && (aLockType == LockType_Shared) // caller wants a shared link to the
3242 // existing session that holds the write lock:
3243 )
3244 {
3245 // OK, share the session... we are now dealing with three processes:
3246 // 1) VBoxSVC (where this code runs);
3247 // 2) process C: the caller's client process (who wants a shared session);
3248 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3249
3250 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3251 ComAssertRet(mData->mSession.mLockType == LockType_Write || mData->mSession.mLockType == LockType_VM, E_FAIL);
3252 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3253 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3254 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3255 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3256
3257 /*
3258 * Release the lock before calling the client process. It's safe here
3259 * since the only thing to do after we get the lock again is to add
3260 * the remote control to the list (which doesn't directly influence
3261 * anything).
3262 */
3263 alock.release();
3264
3265 // get the console of the session holding the write lock (this is a remote call)
3266 ComPtr<IConsole> pConsoleW;
3267 if (mData->mSession.mLockType == LockType_VM)
3268 {
3269 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3270 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3271 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3272 if (FAILED(rc))
3273 // the failure may occur w/o any error info (from RPC), so provide one
3274 return setError(VBOX_E_VM_ERROR,
3275 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3276 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3277 }
3278
3279 // share the session machine and W's console with the caller's session
3280 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3281 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3282 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3283
3284 if (FAILED(rc))
3285 // the failure may occur w/o any error info (from RPC), so provide one
3286 return setError(VBOX_E_VM_ERROR,
3287 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3288 alock.acquire();
3289
3290 // need to revalidate the state after acquiring the lock again
3291 if (mData->mSession.mState != SessionState_Locked)
3292 {
3293 pSessionControl->Uninitialize();
3294 return setError(VBOX_E_INVALID_SESSION_STATE,
3295 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3296 mUserData->s.strName.c_str());
3297 }
3298
3299 // add the caller's session to the list
3300 mData->mSession.mRemoteControls.push_back(pSessionControl);
3301 }
3302 else if ( mData->mSession.mState == SessionState_Locked
3303 || mData->mSession.mState == SessionState_Unlocking
3304 )
3305 {
3306 // sharing not permitted, or machine still unlocking:
3307 return setError(VBOX_E_INVALID_OBJECT_STATE,
3308 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3309 mUserData->s.strName.c_str());
3310 }
3311 else
3312 {
3313 // machine is not locked: then write-lock the machine (create the session machine)
3314
3315 // must not be busy
3316 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3317
3318 // get the caller's session PID
3319 RTPROCESS pid = NIL_RTPROCESS;
3320 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3321 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3322 Assert(pid != NIL_RTPROCESS);
3323
3324 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3325
3326 if (fLaunchingVMProcess)
3327 {
3328 if (mData->mSession.mPID == NIL_RTPROCESS)
3329 {
3330 // two or more clients racing for a lock, the one which set the
3331 // session state to Spawning will win, the others will get an
3332 // error as we can't decide here if waiting a little would help
3333 // (only for shared locks this would avoid an error)
3334 return setError(VBOX_E_INVALID_OBJECT_STATE,
3335 tr("The machine '%s' already has a lock request pending"),
3336 mUserData->s.strName.c_str());
3337 }
3338
3339 // this machine is awaiting for a spawning session to be opened:
3340 // then the calling process must be the one that got started by
3341 // LaunchVMProcess()
3342
3343 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3344 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3345
3346#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3347 /* Hardened windows builds spawns three processes when a VM is
3348 launched, the 3rd one is the one that will end up here. */
3349 RTPROCESS ppid;
3350 int rc = RTProcQueryParent(pid, &ppid);
3351 if (RT_SUCCESS(rc))
3352 rc = RTProcQueryParent(ppid, &ppid);
3353 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3354 || rc == VERR_ACCESS_DENIED)
3355 {
3356 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3357 mData->mSession.mPID = pid;
3358 }
3359#endif
3360
3361 if (mData->mSession.mPID != pid)
3362 return setError(E_ACCESSDENIED,
3363 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3364 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3365 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3366 }
3367
3368 // create the mutable SessionMachine from the current machine
3369 ComObjPtr<SessionMachine> sessionMachine;
3370 sessionMachine.createObject();
3371 rc = sessionMachine->init(this);
3372 AssertComRC(rc);
3373
3374 /* NOTE: doing return from this function after this point but
3375 * before the end is forbidden since it may call SessionMachine::uninit()
3376 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3377 * lock while still holding the Machine lock in alock so that a deadlock
3378 * is possible due to the wrong lock order. */
3379
3380 if (SUCCEEDED(rc))
3381 {
3382 /*
3383 * Set the session state to Spawning to protect against subsequent
3384 * attempts to open a session and to unregister the machine after
3385 * we release the lock.
3386 */
3387 SessionState_T origState = mData->mSession.mState;
3388 mData->mSession.mState = SessionState_Spawning;
3389
3390#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3391 /* Get the client token ID to be passed to the client process */
3392 Utf8Str strTokenId;
3393 sessionMachine->i_getTokenId(strTokenId);
3394 Assert(!strTokenId.isEmpty());
3395#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3396 /* Get the client token to be passed to the client process */
3397 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3398 /* The token is now "owned" by pToken, fix refcount */
3399 if (!pToken.isNull())
3400 pToken->Release();
3401#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3402
3403 /*
3404 * Release the lock before calling the client process -- it will call
3405 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3406 * because the state is Spawning, so that LaunchVMProcess() and
3407 * LockMachine() calls will fail. This method, called before we
3408 * acquire the lock again, will fail because of the wrong PID.
3409 *
3410 * Note that mData->mSession.mRemoteControls accessed outside
3411 * the lock may not be modified when state is Spawning, so it's safe.
3412 */
3413 alock.release();
3414
3415 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3416#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3417 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3418#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3419 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3420 /* Now the token is owned by the client process. */
3421 pToken.setNull();
3422#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3423 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3424
3425 /* The failure may occur w/o any error info (from RPC), so provide one */
3426 if (FAILED(rc))
3427 setError(VBOX_E_VM_ERROR,
3428 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3429
3430 if ( SUCCEEDED(rc)
3431 && fLaunchingVMProcess
3432 )
3433 {
3434 /* complete the remote session initialization */
3435
3436 /* get the console from the direct session */
3437 ComPtr<IConsole> console;
3438 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3439 ComAssertComRC(rc);
3440
3441 if (SUCCEEDED(rc) && !console)
3442 {
3443 ComAssert(!!console);
3444 rc = E_FAIL;
3445 }
3446
3447 /* assign machine & console to the remote session */
3448 if (SUCCEEDED(rc))
3449 {
3450 /*
3451 * after LaunchVMProcess(), the first and the only
3452 * entry in remoteControls is that remote session
3453 */
3454 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3455 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3456 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3457
3458 /* The failure may occur w/o any error info (from RPC), so provide one */
3459 if (FAILED(rc))
3460 setError(VBOX_E_VM_ERROR,
3461 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3462 }
3463
3464 if (FAILED(rc))
3465 pSessionControl->Uninitialize();
3466 }
3467
3468 /* acquire the lock again */
3469 alock.acquire();
3470
3471 /* Restore the session state */
3472 mData->mSession.mState = origState;
3473 }
3474
3475 // finalize spawning anyway (this is why we don't return on errors above)
3476 if (fLaunchingVMProcess)
3477 {
3478 /* Note that the progress object is finalized later */
3479 /** @todo Consider checking mData->mSession.mProgress for cancellation
3480 * around here. */
3481
3482 /* We don't reset mSession.mPID here because it is necessary for
3483 * SessionMachine::uninit() to reap the child process later. */
3484
3485 if (FAILED(rc))
3486 {
3487 /* Close the remote session, remove the remote control from the list
3488 * and reset session state to Closed (@note keep the code in sync
3489 * with the relevant part in checkForSpawnFailure()). */
3490
3491 Assert(mData->mSession.mRemoteControls.size() == 1);
3492 if (mData->mSession.mRemoteControls.size() == 1)
3493 {
3494 ErrorInfoKeeper eik;
3495 mData->mSession.mRemoteControls.front()->Uninitialize();
3496 }
3497
3498 mData->mSession.mRemoteControls.clear();
3499 mData->mSession.mState = SessionState_Unlocked;
3500 }
3501 }
3502 else
3503 {
3504 /* memorize PID of the directly opened session */
3505 if (SUCCEEDED(rc))
3506 mData->mSession.mPID = pid;
3507 }
3508
3509 if (SUCCEEDED(rc))
3510 {
3511 mData->mSession.mLockType = aLockType;
3512 /* memorize the direct session control and cache IUnknown for it */
3513 mData->mSession.mDirectControl = pSessionControl;
3514 mData->mSession.mState = SessionState_Locked;
3515 /* associate the SessionMachine with this Machine */
3516 mData->mSession.mMachine = sessionMachine;
3517
3518 /* request an IUnknown pointer early from the remote party for later
3519 * identity checks (it will be internally cached within mDirectControl
3520 * at least on XPCOM) */
3521 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3522 NOREF(unk);
3523 }
3524
3525 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3526 * would break the lock order */
3527 alock.release();
3528
3529 /* uninitialize the created session machine on failure */
3530 if (FAILED(rc))
3531 sessionMachine->uninit();
3532 }
3533
3534 if (SUCCEEDED(rc))
3535 {
3536 /*
3537 * tell the client watcher thread to update the set of
3538 * machines that have open sessions
3539 */
3540 mParent->i_updateClientWatcher();
3541
3542 if (oldState != SessionState_Locked)
3543 /* fire an event */
3544 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3545 }
3546
3547 return rc;
3548}
3549
3550/**
3551 * @note Locks objects!
3552 */
3553HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3554 const com::Utf8Str &aType,
3555 const com::Utf8Str &aEnvironment,
3556 ComPtr<IProgress> &aProgress)
3557{
3558 Utf8Str strFrontend(aType);
3559 /* "emergencystop" doesn't need the session, so skip the checks/interface
3560 * retrieval. This code doesn't quite fit in here, but introducing a
3561 * special API method would be even more effort, and would require explicit
3562 * support by every API client. It's better to hide the feature a bit. */
3563 if (strFrontend != "emergencystop")
3564 CheckComArgNotNull(aSession);
3565
3566 HRESULT rc = S_OK;
3567 if (strFrontend.isEmpty())
3568 {
3569 Bstr bstrFrontend;
3570 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3571 if (FAILED(rc))
3572 return rc;
3573 strFrontend = bstrFrontend;
3574 if (strFrontend.isEmpty())
3575 {
3576 ComPtr<ISystemProperties> systemProperties;
3577 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3578 if (FAILED(rc))
3579 return rc;
3580 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3581 if (FAILED(rc))
3582 return rc;
3583 strFrontend = bstrFrontend;
3584 }
3585 /* paranoia - emergencystop is not a valid default */
3586 if (strFrontend == "emergencystop")
3587 strFrontend = Utf8Str::Empty;
3588 }
3589 /* default frontend: Qt GUI */
3590 if (strFrontend.isEmpty())
3591 strFrontend = "GUI/Qt";
3592
3593 if (strFrontend != "emergencystop")
3594 {
3595 /* check the session state */
3596 SessionState_T state;
3597 rc = aSession->COMGETTER(State)(&state);
3598 if (FAILED(rc))
3599 return rc;
3600
3601 if (state != SessionState_Unlocked)
3602 return setError(VBOX_E_INVALID_OBJECT_STATE,
3603 tr("The given session is busy"));
3604
3605 /* get the IInternalSessionControl interface */
3606 ComPtr<IInternalSessionControl> control(aSession);
3607 ComAssertMsgRet(!control.isNull(),
3608 ("No IInternalSessionControl interface"),
3609 E_INVALIDARG);
3610
3611 /* get the teleporter enable state for the progress object init. */
3612 BOOL fTeleporterEnabled;
3613 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3614 if (FAILED(rc))
3615 return rc;
3616
3617 /* create a progress object */
3618 ComObjPtr<ProgressProxy> progress;
3619 progress.createObject();
3620 rc = progress->init(mParent,
3621 static_cast<IMachine*>(this),
3622 Bstr(tr("Starting VM")).raw(),
3623 TRUE /* aCancelable */,
3624 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3625 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3626 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3627 2 /* uFirstOperationWeight */,
3628 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3629
3630 if (SUCCEEDED(rc))
3631 {
3632 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3633 if (SUCCEEDED(rc))
3634 {
3635 aProgress = progress;
3636
3637 /* signal the client watcher thread */
3638 mParent->i_updateClientWatcher();
3639
3640 /* fire an event */
3641 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3642 }
3643 }
3644 }
3645 else
3646 {
3647 /* no progress object - either instant success or failure */
3648 aProgress = NULL;
3649
3650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3651
3652 if (mData->mSession.mState != SessionState_Locked)
3653 return setError(VBOX_E_INVALID_OBJECT_STATE,
3654 tr("The machine '%s' is not locked by a session"),
3655 mUserData->s.strName.c_str());
3656
3657 /* must have a VM process associated - do not kill normal API clients
3658 * with an open session */
3659 if (!Global::IsOnline(mData->mMachineState))
3660 return setError(VBOX_E_INVALID_OBJECT_STATE,
3661 tr("The machine '%s' does not have a VM process"),
3662 mUserData->s.strName.c_str());
3663
3664 /* forcibly terminate the VM process */
3665 if (mData->mSession.mPID != NIL_RTPROCESS)
3666 RTProcTerminate(mData->mSession.mPID);
3667
3668 /* signal the client watcher thread, as most likely the client has
3669 * been terminated */
3670 mParent->i_updateClientWatcher();
3671 }
3672
3673 return rc;
3674}
3675
3676HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3677{
3678 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3679 return setError(E_INVALIDARG,
3680 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3681 aPosition, SchemaDefs::MaxBootPosition);
3682
3683 if (aDevice == DeviceType_USB)
3684 return setError(E_NOTIMPL,
3685 tr("Booting from USB device is currently not supported"));
3686
3687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3688
3689 HRESULT rc = i_checkStateDependency(MutableStateDep);
3690 if (FAILED(rc)) return rc;
3691
3692 i_setModified(IsModified_MachineData);
3693 mHWData.backup();
3694 mHWData->mBootOrder[aPosition - 1] = aDevice;
3695
3696 return S_OK;
3697}
3698
3699HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3700{
3701 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3702 return setError(E_INVALIDARG,
3703 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3704 aPosition, SchemaDefs::MaxBootPosition);
3705
3706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3707
3708 *aDevice = mHWData->mBootOrder[aPosition - 1];
3709
3710 return S_OK;
3711}
3712
3713HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3714 LONG aControllerPort,
3715 LONG aDevice,
3716 DeviceType_T aType,
3717 const ComPtr<IMedium> &aMedium)
3718{
3719 IMedium *aM = aMedium;
3720 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3721 aName.c_str(), aControllerPort, aDevice, aType, aM));
3722
3723 // request the host lock first, since might be calling Host methods for getting host drives;
3724 // next, protect the media tree all the while we're in here, as well as our member variables
3725 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3726 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3727
3728 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3729 if (FAILED(rc)) return rc;
3730
3731 /// @todo NEWMEDIA implicit machine registration
3732 if (!mData->mRegistered)
3733 return setError(VBOX_E_INVALID_OBJECT_STATE,
3734 tr("Cannot attach storage devices to an unregistered machine"));
3735
3736 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3737
3738 /* Check for an existing controller. */
3739 ComObjPtr<StorageController> ctl;
3740 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3741 if (FAILED(rc)) return rc;
3742
3743 StorageControllerType_T ctrlType;
3744 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3745 if (FAILED(rc))
3746 return setError(E_FAIL,
3747 tr("Could not get type of controller '%s'"),
3748 aName.c_str());
3749
3750 bool fSilent = false;
3751 Utf8Str strReconfig;
3752
3753 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3754 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3755 if ( mData->mMachineState == MachineState_Paused
3756 && strReconfig == "1")
3757 fSilent = true;
3758
3759 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3760 bool fHotplug = false;
3761 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3762 fHotplug = true;
3763
3764 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3765 return setError(VBOX_E_INVALID_VM_STATE,
3766 tr("Controller '%s' does not support hotplugging"),
3767 aName.c_str());
3768
3769 // check that the port and device are not out of range
3770 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3771 if (FAILED(rc)) return rc;
3772
3773 /* check if the device slot is already busy */
3774 MediumAttachment *pAttachTemp;
3775 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3776 Bstr(aName).raw(),
3777 aControllerPort,
3778 aDevice)))
3779 {
3780 Medium *pMedium = pAttachTemp->i_getMedium();
3781 if (pMedium)
3782 {
3783 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3784 return setError(VBOX_E_OBJECT_IN_USE,
3785 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3786 pMedium->i_getLocationFull().c_str(),
3787 aControllerPort,
3788 aDevice,
3789 aName.c_str());
3790 }
3791 else
3792 return setError(VBOX_E_OBJECT_IN_USE,
3793 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3794 aControllerPort, aDevice, aName.c_str());
3795 }
3796
3797 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3798 if (aMedium && medium.isNull())
3799 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3800
3801 AutoCaller mediumCaller(medium);
3802 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3803
3804 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3805
3806 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3807 && !medium.isNull()
3808 )
3809 return setError(VBOX_E_OBJECT_IN_USE,
3810 tr("Medium '%s' is already attached to this virtual machine"),
3811 medium->i_getLocationFull().c_str());
3812
3813 if (!medium.isNull())
3814 {
3815 MediumType_T mtype = medium->i_getType();
3816 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3817 // For DVDs it's not written to the config file, so needs no global config
3818 // version bump. For floppies it's a new attribute "type", which is ignored
3819 // by older VirtualBox version, so needs no global config version bump either.
3820 // For hard disks this type is not accepted.
3821 if (mtype == MediumType_MultiAttach)
3822 {
3823 // This type is new with VirtualBox 4.0 and therefore requires settings
3824 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3825 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3826 // two reasons: The medium type is a property of the media registry tree, which
3827 // can reside in the global config file (for pre-4.0 media); we would therefore
3828 // possibly need to bump the global config version. We don't want to do that though
3829 // because that might make downgrading to pre-4.0 impossible.
3830 // As a result, we can only use these two new types if the medium is NOT in the
3831 // global registry:
3832 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3833 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3834 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3835 )
3836 return setError(VBOX_E_INVALID_OBJECT_STATE,
3837 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3838 "to machines that were created with VirtualBox 4.0 or later"),
3839 medium->i_getLocationFull().c_str());
3840 }
3841 }
3842
3843 bool fIndirect = false;
3844 if (!medium.isNull())
3845 fIndirect = medium->i_isReadOnly();
3846 bool associate = true;
3847
3848 do
3849 {
3850 if ( aType == DeviceType_HardDisk
3851 && mMediaData.isBackedUp())
3852 {
3853 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3854
3855 /* check if the medium was attached to the VM before we started
3856 * changing attachments in which case the attachment just needs to
3857 * be restored */
3858 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3859 {
3860 AssertReturn(!fIndirect, E_FAIL);
3861
3862 /* see if it's the same bus/channel/device */
3863 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3864 {
3865 /* the simplest case: restore the whole attachment
3866 * and return, nothing else to do */
3867 mMediaData->mAttachments.push_back(pAttachTemp);
3868
3869 /* Reattach the medium to the VM. */
3870 if (fHotplug || fSilent)
3871 {
3872 mediumLock.release();
3873 treeLock.release();
3874 alock.release();
3875
3876 MediumLockList *pMediumLockList(new MediumLockList());
3877
3878 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3879 true /* fMediumLockWrite */,
3880 false /* fMediumLockWriteAll */,
3881 NULL,
3882 *pMediumLockList);
3883 alock.acquire();
3884 if (FAILED(rc))
3885 delete pMediumLockList;
3886 else
3887 {
3888 mData->mSession.mLockedMedia.Unlock();
3889 alock.release();
3890 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3891 mData->mSession.mLockedMedia.Lock();
3892 alock.acquire();
3893 }
3894 alock.release();
3895
3896 if (SUCCEEDED(rc))
3897 {
3898 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3899 /* Remove lock list in case of error. */
3900 if (FAILED(rc))
3901 {
3902 mData->mSession.mLockedMedia.Unlock();
3903 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3904 mData->mSession.mLockedMedia.Lock();
3905 }
3906 }
3907 }
3908
3909 return S_OK;
3910 }
3911
3912 /* bus/channel/device differ; we need a new attachment object,
3913 * but don't try to associate it again */
3914 associate = false;
3915 break;
3916 }
3917 }
3918
3919 /* go further only if the attachment is to be indirect */
3920 if (!fIndirect)
3921 break;
3922
3923 /* perform the so called smart attachment logic for indirect
3924 * attachments. Note that smart attachment is only applicable to base
3925 * hard disks. */
3926
3927 if (medium->i_getParent().isNull())
3928 {
3929 /* first, investigate the backup copy of the current hard disk
3930 * attachments to make it possible to re-attach existing diffs to
3931 * another device slot w/o losing their contents */
3932 if (mMediaData.isBackedUp())
3933 {
3934 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3935
3936 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3937 uint32_t foundLevel = 0;
3938
3939 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3940 {
3941 uint32_t level = 0;
3942 MediumAttachment *pAttach = *it;
3943 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3944 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3945 if (pMedium.isNull())
3946 continue;
3947
3948 if (pMedium->i_getBase(&level) == medium)
3949 {
3950 /* skip the hard disk if its currently attached (we
3951 * cannot attach the same hard disk twice) */
3952 if (i_findAttachment(mMediaData->mAttachments,
3953 pMedium))
3954 continue;
3955
3956 /* matched device, channel and bus (i.e. attached to the
3957 * same place) will win and immediately stop the search;
3958 * otherwise the attachment that has the youngest
3959 * descendant of medium will be used
3960 */
3961 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3962 {
3963 /* the simplest case: restore the whole attachment
3964 * and return, nothing else to do */
3965 mMediaData->mAttachments.push_back(*it);
3966
3967 /* Reattach the medium to the VM. */
3968 if (fHotplug || fSilent)
3969 {
3970 mediumLock.release();
3971 treeLock.release();
3972 alock.release();
3973
3974 MediumLockList *pMediumLockList(new MediumLockList());
3975
3976 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3977 true /* fMediumLockWrite */,
3978 false /* fMediumLockWriteAll */,
3979 NULL,
3980 *pMediumLockList);
3981 alock.acquire();
3982 if (FAILED(rc))
3983 delete pMediumLockList;
3984 else
3985 {
3986 mData->mSession.mLockedMedia.Unlock();
3987 alock.release();
3988 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3989 mData->mSession.mLockedMedia.Lock();
3990 alock.acquire();
3991 }
3992 alock.release();
3993
3994 if (SUCCEEDED(rc))
3995 {
3996 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3997 /* Remove lock list in case of error. */
3998 if (FAILED(rc))
3999 {
4000 mData->mSession.mLockedMedia.Unlock();
4001 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4002 mData->mSession.mLockedMedia.Lock();
4003 }
4004 }
4005 }
4006
4007 return S_OK;
4008 }
4009 else if ( foundIt == oldAtts.end()
4010 || level > foundLevel /* prefer younger */
4011 )
4012 {
4013 foundIt = it;
4014 foundLevel = level;
4015 }
4016 }
4017 }
4018
4019 if (foundIt != oldAtts.end())
4020 {
4021 /* use the previously attached hard disk */
4022 medium = (*foundIt)->i_getMedium();
4023 mediumCaller.attach(medium);
4024 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4025 mediumLock.attach(medium);
4026 /* not implicit, doesn't require association with this VM */
4027 fIndirect = false;
4028 associate = false;
4029 /* go right to the MediumAttachment creation */
4030 break;
4031 }
4032 }
4033
4034 /* must give up the medium lock and medium tree lock as below we
4035 * go over snapshots, which needs a lock with higher lock order. */
4036 mediumLock.release();
4037 treeLock.release();
4038
4039 /* then, search through snapshots for the best diff in the given
4040 * hard disk's chain to base the new diff on */
4041
4042 ComObjPtr<Medium> base;
4043 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4044 while (snap)
4045 {
4046 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4047
4048 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4049
4050 MediumAttachment *pAttachFound = NULL;
4051 uint32_t foundLevel = 0;
4052
4053 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4054 {
4055 MediumAttachment *pAttach = *it;
4056 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4057 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4058 if (pMedium.isNull())
4059 continue;
4060
4061 uint32_t level = 0;
4062 if (pMedium->i_getBase(&level) == medium)
4063 {
4064 /* matched device, channel and bus (i.e. attached to the
4065 * same place) will win and immediately stop the search;
4066 * otherwise the attachment that has the youngest
4067 * descendant of medium will be used
4068 */
4069 if ( pAttach->i_getDevice() == aDevice
4070 && pAttach->i_getPort() == aControllerPort
4071 && pAttach->i_getControllerName() == aName
4072 )
4073 {
4074 pAttachFound = pAttach;
4075 break;
4076 }
4077 else if ( !pAttachFound
4078 || level > foundLevel /* prefer younger */
4079 )
4080 {
4081 pAttachFound = pAttach;
4082 foundLevel = level;
4083 }
4084 }
4085 }
4086
4087 if (pAttachFound)
4088 {
4089 base = pAttachFound->i_getMedium();
4090 break;
4091 }
4092
4093 snap = snap->i_getParent();
4094 }
4095
4096 /* re-lock medium tree and the medium, as we need it below */
4097 treeLock.acquire();
4098 mediumLock.acquire();
4099
4100 /* found a suitable diff, use it as a base */
4101 if (!base.isNull())
4102 {
4103 medium = base;
4104 mediumCaller.attach(medium);
4105 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4106 mediumLock.attach(medium);
4107 }
4108 }
4109
4110 Utf8Str strFullSnapshotFolder;
4111 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4112
4113 ComObjPtr<Medium> diff;
4114 diff.createObject();
4115 // store this diff in the same registry as the parent
4116 Guid uuidRegistryParent;
4117 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4118 {
4119 // parent image has no registry: this can happen if we're attaching a new immutable
4120 // image that has not yet been attached (medium then points to the base and we're
4121 // creating the diff image for the immutable, and the parent is not yet registered);
4122 // put the parent in the machine registry then
4123 mediumLock.release();
4124 treeLock.release();
4125 alock.release();
4126 i_addMediumToRegistry(medium);
4127 alock.acquire();
4128 treeLock.acquire();
4129 mediumLock.acquire();
4130 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4131 }
4132 rc = diff->init(mParent,
4133 medium->i_getPreferredDiffFormat(),
4134 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4135 uuidRegistryParent,
4136 DeviceType_HardDisk);
4137 if (FAILED(rc)) return rc;
4138
4139 /* Apply the normal locking logic to the entire chain. */
4140 MediumLockList *pMediumLockList(new MediumLockList());
4141 mediumLock.release();
4142 treeLock.release();
4143 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4144 true /* fMediumLockWrite */,
4145 false /* fMediumLockWriteAll */,
4146 medium,
4147 *pMediumLockList);
4148 treeLock.acquire();
4149 mediumLock.acquire();
4150 if (SUCCEEDED(rc))
4151 {
4152 mediumLock.release();
4153 treeLock.release();
4154 rc = pMediumLockList->Lock();
4155 treeLock.acquire();
4156 mediumLock.acquire();
4157 if (FAILED(rc))
4158 setError(rc,
4159 tr("Could not lock medium when creating diff '%s'"),
4160 diff->i_getLocationFull().c_str());
4161 else
4162 {
4163 /* will release the lock before the potentially lengthy
4164 * operation, so protect with the special state */
4165 MachineState_T oldState = mData->mMachineState;
4166 i_setMachineState(MachineState_SettingUp);
4167
4168 mediumLock.release();
4169 treeLock.release();
4170 alock.release();
4171
4172 rc = medium->i_createDiffStorage(diff,
4173 MediumVariant_Standard,
4174 pMediumLockList,
4175 NULL /* aProgress */,
4176 true /* aWait */);
4177
4178 alock.acquire();
4179 treeLock.acquire();
4180 mediumLock.acquire();
4181
4182 i_setMachineState(oldState);
4183 }
4184 }
4185
4186 /* Unlock the media and free the associated memory. */
4187 delete pMediumLockList;
4188
4189 if (FAILED(rc)) return rc;
4190
4191 /* use the created diff for the actual attachment */
4192 medium = diff;
4193 mediumCaller.attach(medium);
4194 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4195 mediumLock.attach(medium);
4196 }
4197 while (0);
4198
4199 ComObjPtr<MediumAttachment> attachment;
4200 attachment.createObject();
4201 rc = attachment->init(this,
4202 medium,
4203 aName,
4204 aControllerPort,
4205 aDevice,
4206 aType,
4207 fIndirect,
4208 false /* fPassthrough */,
4209 false /* fTempEject */,
4210 false /* fNonRotational */,
4211 false /* fDiscard */,
4212 fHotplug /* fHotPluggable */,
4213 Utf8Str::Empty);
4214 if (FAILED(rc)) return rc;
4215
4216 if (associate && !medium.isNull())
4217 {
4218 // as the last step, associate the medium to the VM
4219 rc = medium->i_addBackReference(mData->mUuid);
4220 // here we can fail because of Deleting, or being in process of creating a Diff
4221 if (FAILED(rc)) return rc;
4222
4223 mediumLock.release();
4224 treeLock.release();
4225 alock.release();
4226 i_addMediumToRegistry(medium);
4227 alock.acquire();
4228 treeLock.acquire();
4229 mediumLock.acquire();
4230 }
4231
4232 /* success: finally remember the attachment */
4233 i_setModified(IsModified_Storage);
4234 mMediaData.backup();
4235 mMediaData->mAttachments.push_back(attachment);
4236
4237 mediumLock.release();
4238 treeLock.release();
4239 alock.release();
4240
4241 if (fHotplug || fSilent)
4242 {
4243 if (!medium.isNull())
4244 {
4245 MediumLockList *pMediumLockList(new MediumLockList());
4246
4247 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4248 true /* fMediumLockWrite */,
4249 false /* fMediumLockWriteAll */,
4250 NULL,
4251 *pMediumLockList);
4252 alock.acquire();
4253 if (FAILED(rc))
4254 delete pMediumLockList;
4255 else
4256 {
4257 mData->mSession.mLockedMedia.Unlock();
4258 alock.release();
4259 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4260 mData->mSession.mLockedMedia.Lock();
4261 alock.acquire();
4262 }
4263 alock.release();
4264 }
4265
4266 if (SUCCEEDED(rc))
4267 {
4268 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4269 /* Remove lock list in case of error. */
4270 if (FAILED(rc))
4271 {
4272 mData->mSession.mLockedMedia.Unlock();
4273 mData->mSession.mLockedMedia.Remove(attachment);
4274 mData->mSession.mLockedMedia.Lock();
4275 }
4276 }
4277 }
4278
4279 /* Save modified registries, but skip this machine as it's the caller's
4280 * job to save its settings like all other settings changes. */
4281 mParent->i_unmarkRegistryModified(i_getId());
4282 mParent->i_saveModifiedRegistries();
4283
4284 return rc;
4285}
4286
4287HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4288 LONG aDevice)
4289{
4290 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4291 aName.c_str(), aControllerPort, aDevice));
4292
4293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4294
4295 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4296 if (FAILED(rc)) return rc;
4297
4298 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4299
4300 /* Check for an existing controller. */
4301 ComObjPtr<StorageController> ctl;
4302 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4303 if (FAILED(rc)) return rc;
4304
4305 StorageControllerType_T ctrlType;
4306 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4307 if (FAILED(rc))
4308 return setError(E_FAIL,
4309 tr("Could not get type of controller '%s'"),
4310 aName.c_str());
4311
4312 bool fSilent = false;
4313 Utf8Str strReconfig;
4314
4315 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4316 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4317 if ( mData->mMachineState == MachineState_Paused
4318 && strReconfig == "1")
4319 fSilent = true;
4320
4321 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4322 bool fHotplug = false;
4323 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4324 fHotplug = true;
4325
4326 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4327 return setError(VBOX_E_INVALID_VM_STATE,
4328 tr("Controller '%s' does not support hotplugging"),
4329 aName.c_str());
4330
4331 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4332 Bstr(aName).raw(),
4333 aControllerPort,
4334 aDevice);
4335 if (!pAttach)
4336 return setError(VBOX_E_OBJECT_NOT_FOUND,
4337 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4338 aDevice, aControllerPort, aName.c_str());
4339
4340 if (fHotplug && !pAttach->i_getHotPluggable())
4341 return setError(VBOX_E_NOT_SUPPORTED,
4342 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4343 aDevice, aControllerPort, aName.c_str());
4344
4345 /*
4346 * The VM has to detach the device before we delete any implicit diffs.
4347 * If this fails we can roll back without loosing data.
4348 */
4349 if (fHotplug || fSilent)
4350 {
4351 alock.release();
4352 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4353 alock.acquire();
4354 }
4355 if (FAILED(rc)) return rc;
4356
4357 /* If we are here everything went well and we can delete the implicit now. */
4358 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4359
4360 alock.release();
4361
4362 /* Save modified registries, but skip this machine as it's the caller's
4363 * job to save its settings like all other settings changes. */
4364 mParent->i_unmarkRegistryModified(i_getId());
4365 mParent->i_saveModifiedRegistries();
4366
4367 return rc;
4368}
4369
4370HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4371 LONG aDevice, BOOL aPassthrough)
4372{
4373 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4374 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4375
4376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4377
4378 HRESULT rc = i_checkStateDependency(MutableStateDep);
4379 if (FAILED(rc)) return rc;
4380
4381 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4382
4383 if (Global::IsOnlineOrTransient(mData->mMachineState))
4384 return setError(VBOX_E_INVALID_VM_STATE,
4385 tr("Invalid machine state: %s"),
4386 Global::stringifyMachineState(mData->mMachineState));
4387
4388 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4389 Bstr(aName).raw(),
4390 aControllerPort,
4391 aDevice);
4392 if (!pAttach)
4393 return setError(VBOX_E_OBJECT_NOT_FOUND,
4394 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4395 aDevice, aControllerPort, aName.c_str());
4396
4397
4398 i_setModified(IsModified_Storage);
4399 mMediaData.backup();
4400
4401 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4402
4403 if (pAttach->i_getType() != DeviceType_DVD)
4404 return setError(E_INVALIDARG,
4405 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4406 aDevice, aControllerPort, aName.c_str());
4407 pAttach->i_updatePassthrough(!!aPassthrough);
4408
4409 return S_OK;
4410}
4411
4412HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4413 LONG aDevice, BOOL aTemporaryEject)
4414{
4415
4416 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4417 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4418
4419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4420
4421 HRESULT rc = i_checkStateDependency(MutableStateDep);
4422 if (FAILED(rc)) return rc;
4423
4424 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4425 Bstr(aName).raw(),
4426 aControllerPort,
4427 aDevice);
4428 if (!pAttach)
4429 return setError(VBOX_E_OBJECT_NOT_FOUND,
4430 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4431 aDevice, aControllerPort, aName.c_str());
4432
4433
4434 i_setModified(IsModified_Storage);
4435 mMediaData.backup();
4436
4437 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4438
4439 if (pAttach->i_getType() != DeviceType_DVD)
4440 return setError(E_INVALIDARG,
4441 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4442 aDevice, aControllerPort, aName.c_str());
4443 pAttach->i_updateTempEject(!!aTemporaryEject);
4444
4445 return S_OK;
4446}
4447
4448HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4449 LONG aDevice, BOOL aNonRotational)
4450{
4451
4452 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4453 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4454
4455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4456
4457 HRESULT rc = i_checkStateDependency(MutableStateDep);
4458 if (FAILED(rc)) return rc;
4459
4460 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4461
4462 if (Global::IsOnlineOrTransient(mData->mMachineState))
4463 return setError(VBOX_E_INVALID_VM_STATE,
4464 tr("Invalid machine state: %s"),
4465 Global::stringifyMachineState(mData->mMachineState));
4466
4467 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4468 Bstr(aName).raw(),
4469 aControllerPort,
4470 aDevice);
4471 if (!pAttach)
4472 return setError(VBOX_E_OBJECT_NOT_FOUND,
4473 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4474 aDevice, aControllerPort, aName.c_str());
4475
4476
4477 i_setModified(IsModified_Storage);
4478 mMediaData.backup();
4479
4480 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4481
4482 if (pAttach->i_getType() != DeviceType_HardDisk)
4483 return setError(E_INVALIDARG,
4484 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"),
4485 aDevice, aControllerPort, aName.c_str());
4486 pAttach->i_updateNonRotational(!!aNonRotational);
4487
4488 return S_OK;
4489}
4490
4491HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4492 LONG aDevice, BOOL aDiscard)
4493{
4494
4495 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4496 aName.c_str(), aControllerPort, aDevice, aDiscard));
4497
4498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4499
4500 HRESULT rc = i_checkStateDependency(MutableStateDep);
4501 if (FAILED(rc)) return rc;
4502
4503 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4504
4505 if (Global::IsOnlineOrTransient(mData->mMachineState))
4506 return setError(VBOX_E_INVALID_VM_STATE,
4507 tr("Invalid machine state: %s"),
4508 Global::stringifyMachineState(mData->mMachineState));
4509
4510 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4511 Bstr(aName).raw(),
4512 aControllerPort,
4513 aDevice);
4514 if (!pAttach)
4515 return setError(VBOX_E_OBJECT_NOT_FOUND,
4516 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4517 aDevice, aControllerPort, aName.c_str());
4518
4519
4520 i_setModified(IsModified_Storage);
4521 mMediaData.backup();
4522
4523 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4524
4525 if (pAttach->i_getType() != DeviceType_HardDisk)
4526 return setError(E_INVALIDARG,
4527 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"),
4528 aDevice, aControllerPort, aName.c_str());
4529 pAttach->i_updateDiscard(!!aDiscard);
4530
4531 return S_OK;
4532}
4533
4534HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4535 LONG aDevice, BOOL aHotPluggable)
4536{
4537 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4538 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4539
4540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4541
4542 HRESULT rc = i_checkStateDependency(MutableStateDep);
4543 if (FAILED(rc)) return rc;
4544
4545 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4546
4547 if (Global::IsOnlineOrTransient(mData->mMachineState))
4548 return setError(VBOX_E_INVALID_VM_STATE,
4549 tr("Invalid machine state: %s"),
4550 Global::stringifyMachineState(mData->mMachineState));
4551
4552 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4553 Bstr(aName).raw(),
4554 aControllerPort,
4555 aDevice);
4556 if (!pAttach)
4557 return setError(VBOX_E_OBJECT_NOT_FOUND,
4558 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4559 aDevice, aControllerPort, aName.c_str());
4560
4561 /* Check for an existing controller. */
4562 ComObjPtr<StorageController> ctl;
4563 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4564 if (FAILED(rc)) return rc;
4565
4566 StorageControllerType_T ctrlType;
4567 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4568 if (FAILED(rc))
4569 return setError(E_FAIL,
4570 tr("Could not get type of controller '%s'"),
4571 aName.c_str());
4572
4573 if (!i_isControllerHotplugCapable(ctrlType))
4574 return setError(VBOX_E_NOT_SUPPORTED,
4575 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4576 aName.c_str());
4577
4578 i_setModified(IsModified_Storage);
4579 mMediaData.backup();
4580
4581 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4582
4583 if (pAttach->i_getType() == DeviceType_Floppy)
4584 return setError(E_INVALIDARG,
4585 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"),
4586 aDevice, aControllerPort, aName.c_str());
4587 pAttach->i_updateHotPluggable(!!aHotPluggable);
4588
4589 return S_OK;
4590}
4591
4592HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4593 LONG aDevice)
4594{
4595 int rc = S_OK;
4596 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4597 aName.c_str(), aControllerPort, aDevice));
4598
4599 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4600
4601 return rc;
4602}
4603
4604HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4605 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4606{
4607 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4608 aName.c_str(), aControllerPort, aDevice));
4609
4610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4611
4612 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4613 if (FAILED(rc)) return rc;
4614
4615 if (Global::IsOnlineOrTransient(mData->mMachineState))
4616 return setError(VBOX_E_INVALID_VM_STATE,
4617 tr("Invalid machine state: %s"),
4618 Global::stringifyMachineState(mData->mMachineState));
4619
4620 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4621 Bstr(aName).raw(),
4622 aControllerPort,
4623 aDevice);
4624 if (!pAttach)
4625 return setError(VBOX_E_OBJECT_NOT_FOUND,
4626 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4627 aDevice, aControllerPort, aName.c_str());
4628
4629
4630 i_setModified(IsModified_Storage);
4631 mMediaData.backup();
4632
4633 IBandwidthGroup *iB = aBandwidthGroup;
4634 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4635 if (aBandwidthGroup && group.isNull())
4636 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4637
4638 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4639
4640 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4641 if (strBandwidthGroupOld.isNotEmpty())
4642 {
4643 /* Get the bandwidth group object and release it - this must not fail. */
4644 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4645 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4646 Assert(SUCCEEDED(rc));
4647
4648 pBandwidthGroupOld->i_release();
4649 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4650 }
4651
4652 if (!group.isNull())
4653 {
4654 group->i_reference();
4655 pAttach->i_updateBandwidthGroup(group->i_getName());
4656 }
4657
4658 return S_OK;
4659}
4660
4661HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4662 LONG aControllerPort,
4663 LONG aDevice,
4664 DeviceType_T aType)
4665{
4666 HRESULT rc = S_OK;
4667
4668 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4669 aName.c_str(), aControllerPort, aDevice, aType));
4670
4671 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4672
4673 return rc;
4674}
4675
4676
4677HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4678 LONG aControllerPort,
4679 LONG aDevice,
4680 BOOL aForce)
4681{
4682 int rc = S_OK;
4683 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4684 aName.c_str(), aControllerPort, aForce));
4685
4686 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4687
4688 return rc;
4689}
4690
4691HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4692 LONG aControllerPort,
4693 LONG aDevice,
4694 const ComPtr<IMedium> &aMedium,
4695 BOOL aForce)
4696{
4697 int rc = S_OK;
4698 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4699 aName.c_str(), aControllerPort, aDevice, aForce));
4700
4701 // request the host lock first, since might be calling Host methods for getting host drives;
4702 // next, protect the media tree all the while we're in here, as well as our member variables
4703 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4704 this->lockHandle(),
4705 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4706
4707 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4708 Bstr(aName).raw(),
4709 aControllerPort,
4710 aDevice);
4711 if (pAttach.isNull())
4712 return setError(VBOX_E_OBJECT_NOT_FOUND,
4713 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4714 aDevice, aControllerPort, aName.c_str());
4715
4716 /* Remember previously mounted medium. The medium before taking the
4717 * backup is not necessarily the same thing. */
4718 ComObjPtr<Medium> oldmedium;
4719 oldmedium = pAttach->i_getMedium();
4720
4721 IMedium *iM = aMedium;
4722 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4723 if (aMedium && pMedium.isNull())
4724 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4725
4726 AutoCaller mediumCaller(pMedium);
4727 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4728
4729 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4730 if (pMedium)
4731 {
4732 DeviceType_T mediumType = pAttach->i_getType();
4733 switch (mediumType)
4734 {
4735 case DeviceType_DVD:
4736 case DeviceType_Floppy:
4737 break;
4738
4739 default:
4740 return setError(VBOX_E_INVALID_OBJECT_STATE,
4741 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4742 aControllerPort,
4743 aDevice,
4744 aName.c_str());
4745 }
4746 }
4747
4748 i_setModified(IsModified_Storage);
4749 mMediaData.backup();
4750
4751 {
4752 // The backup operation makes the pAttach reference point to the
4753 // old settings. Re-get the correct reference.
4754 pAttach = i_findAttachment(mMediaData->mAttachments,
4755 Bstr(aName).raw(),
4756 aControllerPort,
4757 aDevice);
4758 if (!oldmedium.isNull())
4759 oldmedium->i_removeBackReference(mData->mUuid);
4760 if (!pMedium.isNull())
4761 {
4762 pMedium->i_addBackReference(mData->mUuid);
4763
4764 mediumLock.release();
4765 multiLock.release();
4766 i_addMediumToRegistry(pMedium);
4767 multiLock.acquire();
4768 mediumLock.acquire();
4769 }
4770
4771 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4772 pAttach->i_updateMedium(pMedium);
4773 }
4774
4775 i_setModified(IsModified_Storage);
4776
4777 mediumLock.release();
4778 multiLock.release();
4779 rc = i_onMediumChange(pAttach, aForce);
4780 multiLock.acquire();
4781 mediumLock.acquire();
4782
4783 /* On error roll back this change only. */
4784 if (FAILED(rc))
4785 {
4786 if (!pMedium.isNull())
4787 pMedium->i_removeBackReference(mData->mUuid);
4788 pAttach = i_findAttachment(mMediaData->mAttachments,
4789 Bstr(aName).raw(),
4790 aControllerPort,
4791 aDevice);
4792 /* If the attachment is gone in the meantime, bail out. */
4793 if (pAttach.isNull())
4794 return rc;
4795 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4796 if (!oldmedium.isNull())
4797 oldmedium->i_addBackReference(mData->mUuid);
4798 pAttach->i_updateMedium(oldmedium);
4799 }
4800
4801 mediumLock.release();
4802 multiLock.release();
4803
4804 /* Save modified registries, but skip this machine as it's the caller's
4805 * job to save its settings like all other settings changes. */
4806 mParent->i_unmarkRegistryModified(i_getId());
4807 mParent->i_saveModifiedRegistries();
4808
4809 return rc;
4810}
4811HRESULT Machine::getMedium(const com::Utf8Str &aName,
4812 LONG aControllerPort,
4813 LONG aDevice,
4814 ComPtr<IMedium> &aMedium)
4815{
4816 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4817 aName.c_str(), aControllerPort, aDevice));
4818
4819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4820
4821 aMedium = NULL;
4822
4823 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4824 Bstr(aName).raw(),
4825 aControllerPort,
4826 aDevice);
4827 if (pAttach.isNull())
4828 return setError(VBOX_E_OBJECT_NOT_FOUND,
4829 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4830 aDevice, aControllerPort, aName.c_str());
4831
4832 aMedium = pAttach->i_getMedium();
4833
4834 return S_OK;
4835}
4836
4837HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4838{
4839
4840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4841
4842 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4843
4844 return S_OK;
4845}
4846
4847HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4848{
4849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4850
4851 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4852
4853 return S_OK;
4854}
4855
4856HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4857{
4858 /* Do not assert if slot is out of range, just return the advertised
4859 status. testdriver/vbox.py triggers this in logVmInfo. */
4860 if (aSlot >= mNetworkAdapters.size())
4861 return setError(E_INVALIDARG,
4862 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4863 aSlot, mNetworkAdapters.size());
4864
4865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4866
4867 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4868
4869 return S_OK;
4870}
4871
4872HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4873{
4874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4875
4876 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4877 size_t i = 0;
4878 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4879 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4880 ++it, ++i)
4881 aKeys[i] = it->first;
4882
4883 return S_OK;
4884}
4885
4886 /**
4887 * @note Locks this object for reading.
4888 */
4889HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4890 com::Utf8Str &aValue)
4891{
4892 /* start with nothing found */
4893 aValue = "";
4894
4895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4896
4897 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4898 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4899 // found:
4900 aValue = it->second; // source is a Utf8Str
4901
4902 /* return the result to caller (may be empty) */
4903 return S_OK;
4904}
4905
4906 /**
4907 * @note Locks mParent for writing + this object for writing.
4908 */
4909HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4910{
4911 Utf8Str strOldValue; // empty
4912
4913 // locking note: we only hold the read lock briefly to look up the old value,
4914 // then release it and call the onExtraCanChange callbacks. There is a small
4915 // chance of a race insofar as the callback might be called twice if two callers
4916 // change the same key at the same time, but that's a much better solution
4917 // than the deadlock we had here before. The actual changing of the extradata
4918 // is then performed under the write lock and race-free.
4919
4920 // look up the old value first; if nothing has changed then we need not do anything
4921 {
4922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4923
4924 // For snapshots don't even think about allowing changes, extradata
4925 // is global for a machine, so there is nothing snapshot specific.
4926 if (i_isSnapshotMachine())
4927 return setError(VBOX_E_INVALID_VM_STATE,
4928 tr("Cannot set extradata for a snapshot"));
4929
4930 // check if the right IMachine instance is used
4931 if (mData->mRegistered && !i_isSessionMachine())
4932 return setError(VBOX_E_INVALID_VM_STATE,
4933 tr("Cannot set extradata for an immutable machine"));
4934
4935 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4936 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4937 strOldValue = it->second;
4938 }
4939
4940 bool fChanged;
4941 if ((fChanged = (strOldValue != aValue)))
4942 {
4943 // ask for permission from all listeners outside the locks;
4944 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4945 // lock to copy the list of callbacks to invoke
4946 Bstr error;
4947 Bstr bstrValue(aValue);
4948
4949 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4950 {
4951 const char *sep = error.isEmpty() ? "" : ": ";
4952 CBSTR err = error.raw();
4953 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4954 sep, err));
4955 return setError(E_ACCESSDENIED,
4956 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4957 aKey.c_str(),
4958 aValue.c_str(),
4959 sep,
4960 err);
4961 }
4962
4963 // data is changing and change not vetoed: then write it out under the lock
4964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4965
4966 if (aValue.isEmpty())
4967 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4968 else
4969 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4970 // creates a new key if needed
4971
4972 bool fNeedsGlobalSaveSettings = false;
4973 // This saving of settings is tricky: there is no "old state" for the
4974 // extradata items at all (unlike all other settings), so the old/new
4975 // settings comparison would give a wrong result!
4976 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4977
4978 if (fNeedsGlobalSaveSettings)
4979 {
4980 // save the global settings; for that we should hold only the VirtualBox lock
4981 alock.release();
4982 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4983 mParent->i_saveSettings();
4984 }
4985 }
4986
4987 // fire notification outside the lock
4988 if (fChanged)
4989 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4990
4991 return S_OK;
4992}
4993
4994HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4995{
4996 aProgress = NULL;
4997 NOREF(aSettingsFilePath);
4998 ReturnComNotImplemented();
4999}
5000
5001HRESULT Machine::saveSettings()
5002{
5003 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5004
5005 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5006 if (FAILED(rc)) return rc;
5007
5008 /* the settings file path may never be null */
5009 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5010
5011 /* save all VM data excluding snapshots */
5012 bool fNeedsGlobalSaveSettings = false;
5013 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5014 mlock.release();
5015
5016 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5017 {
5018 // save the global settings; for that we should hold only the VirtualBox lock
5019 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5020 rc = mParent->i_saveSettings();
5021 }
5022
5023 return rc;
5024}
5025
5026
5027HRESULT Machine::discardSettings()
5028{
5029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5030
5031 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5032 if (FAILED(rc)) return rc;
5033
5034 /*
5035 * during this rollback, the session will be notified if data has
5036 * been actually changed
5037 */
5038 i_rollback(true /* aNotify */);
5039
5040 return S_OK;
5041}
5042
5043/** @note Locks objects! */
5044HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5045 std::vector<ComPtr<IMedium> > &aMedia)
5046{
5047 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5048 AutoLimitedCaller autoCaller(this);
5049 AssertComRCReturnRC(autoCaller.rc());
5050
5051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5052
5053 Guid id(i_getId());
5054
5055 if (mData->mSession.mState != SessionState_Unlocked)
5056 return setError(VBOX_E_INVALID_OBJECT_STATE,
5057 tr("Cannot unregister the machine '%s' while it is locked"),
5058 mUserData->s.strName.c_str());
5059
5060 // wait for state dependents to drop to zero
5061 i_ensureNoStateDependencies();
5062
5063 if (!mData->mAccessible)
5064 {
5065 // inaccessible maschines can only be unregistered; uninitialize ourselves
5066 // here because currently there may be no unregistered that are inaccessible
5067 // (this state combination is not supported). Note releasing the caller and
5068 // leaving the lock before calling uninit()
5069 alock.release();
5070 autoCaller.release();
5071
5072 uninit();
5073
5074 mParent->i_unregisterMachine(this, id);
5075 // calls VirtualBox::i_saveSettings()
5076
5077 return S_OK;
5078 }
5079
5080 HRESULT rc = S_OK;
5081
5082 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5083 // discard saved state
5084 if (mData->mMachineState == MachineState_Saved)
5085 {
5086 // add the saved state file to the list of files the caller should delete
5087 Assert(!mSSData->strStateFilePath.isEmpty());
5088 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5089
5090 mSSData->strStateFilePath.setNull();
5091
5092 // unconditionally set the machine state to powered off, we now
5093 // know no session has locked the machine
5094 mData->mMachineState = MachineState_PoweredOff;
5095 }
5096
5097 size_t cSnapshots = 0;
5098 if (mData->mFirstSnapshot)
5099 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5100 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5101 // fail now before we start detaching media
5102 return setError(VBOX_E_INVALID_OBJECT_STATE,
5103 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5104 mUserData->s.strName.c_str(), cSnapshots);
5105
5106 // This list collects the medium objects from all medium attachments
5107 // which we will detach from the machine and its snapshots, in a specific
5108 // order which allows for closing all media without getting "media in use"
5109 // errors, simply by going through the list from the front to the back:
5110 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5111 // and must be closed before the parent media from the snapshots, or closing the parents
5112 // will fail because they still have children);
5113 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5114 // the root ("first") snapshot of the machine.
5115 MediaList llMedia;
5116
5117 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5118 && mMediaData->mAttachments.size()
5119 )
5120 {
5121 // we have media attachments: detach them all and add the Medium objects to our list
5122 if (aCleanupMode != CleanupMode_UnregisterOnly)
5123 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5124 else
5125 return setError(VBOX_E_INVALID_OBJECT_STATE,
5126 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5127 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5128 }
5129
5130 if (cSnapshots)
5131 {
5132 // add the media from the medium attachments of the snapshots to llMedia
5133 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5134 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5135 // into the children first
5136
5137 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5138 MachineState_T oldState = mData->mMachineState;
5139 mData->mMachineState = MachineState_DeletingSnapshot;
5140
5141 // make a copy of the first snapshot so the refcount does not drop to 0
5142 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5143 // because of the AutoCaller voodoo)
5144 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5145
5146 // GO!
5147 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5148
5149 mData->mMachineState = oldState;
5150 }
5151
5152 if (FAILED(rc))
5153 {
5154 i_rollbackMedia();
5155 return rc;
5156 }
5157
5158 // commit all the media changes made above
5159 i_commitMedia();
5160
5161 mData->mRegistered = false;
5162
5163 // machine lock no longer needed
5164 alock.release();
5165
5166 // return media to caller
5167 size_t i = 0;
5168 aMedia.resize(llMedia.size());
5169 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5170 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5171
5172 mParent->i_unregisterMachine(this, id);
5173 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5174
5175 return S_OK;
5176}
5177
5178/**
5179 * Task record for deleting a machine config.
5180 */
5181struct Machine::DeleteConfigTask
5182 : public Machine::Task
5183{
5184 DeleteConfigTask(Machine *m,
5185 Progress *p,
5186 const Utf8Str &t,
5187 const RTCList<ComPtr<IMedium> > &llMediums,
5188 const StringsList &llFilesToDelete)
5189 : Task(m, p, t),
5190 m_llMediums(llMediums),
5191 m_llFilesToDelete(llFilesToDelete)
5192 {}
5193
5194 void handler()
5195 {
5196 m_pMachine->i_deleteConfigHandler(*this);
5197 }
5198
5199 RTCList<ComPtr<IMedium> > m_llMediums;
5200 StringsList m_llFilesToDelete;
5201};
5202
5203/**
5204 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5205 * SessionMachine::taskHandler().
5206 *
5207 * @note Locks this object for writing.
5208 *
5209 * @param task
5210 * @return
5211 */
5212void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5213{
5214 LogFlowThisFuncEnter();
5215
5216 AutoCaller autoCaller(this);
5217 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5218 if (FAILED(autoCaller.rc()))
5219 {
5220 /* we might have been uninitialized because the session was accidentally
5221 * closed by the client, so don't assert */
5222 HRESULT rc = setError(E_FAIL,
5223 tr("The session has been accidentally closed"));
5224 task.m_pProgress->i_notifyComplete(rc);
5225 LogFlowThisFuncLeave();
5226 return;
5227 }
5228
5229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5230
5231 HRESULT rc = S_OK;
5232
5233 try
5234 {
5235 ULONG uLogHistoryCount = 3;
5236 ComPtr<ISystemProperties> systemProperties;
5237 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5238 if (FAILED(rc)) throw rc;
5239
5240 if (!systemProperties.isNull())
5241 {
5242 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5243 if (FAILED(rc)) throw rc;
5244 }
5245
5246 MachineState_T oldState = mData->mMachineState;
5247 i_setMachineState(MachineState_SettingUp);
5248 alock.release();
5249 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5250 {
5251 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5252 {
5253 AutoCaller mac(pMedium);
5254 if (FAILED(mac.rc())) throw mac.rc();
5255 Utf8Str strLocation = pMedium->i_getLocationFull();
5256 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5257 if (FAILED(rc)) throw rc;
5258 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5259 }
5260 if (pMedium->i_isMediumFormatFile())
5261 {
5262 ComPtr<IProgress> pProgress2;
5263 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5264 if (FAILED(rc)) throw rc;
5265 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5266 if (FAILED(rc)) throw rc;
5267 /* Check the result of the asynchronous process. */
5268 LONG iRc;
5269 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5270 if (FAILED(rc)) throw rc;
5271 /* If the thread of the progress object has an error, then
5272 * retrieve the error info from there, or it'll be lost. */
5273 if (FAILED(iRc))
5274 throw setError(ProgressErrorInfo(pProgress2));
5275 }
5276
5277 /* Close the medium, deliberately without checking the return
5278 * code, and without leaving any trace in the error info, as
5279 * a failure here is a very minor issue, which shouldn't happen
5280 * as above we even managed to delete the medium. */
5281 {
5282 ErrorInfoKeeper eik;
5283 pMedium->Close();
5284 }
5285 }
5286 i_setMachineState(oldState);
5287 alock.acquire();
5288
5289 // delete the files pushed on the task list by Machine::Delete()
5290 // (this includes saved states of the machine and snapshots and
5291 // medium storage files from the IMedium list passed in, and the
5292 // machine XML file)
5293 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5294 while (it != task.m_llFilesToDelete.end())
5295 {
5296 const Utf8Str &strFile = *it;
5297 LogFunc(("Deleting file %s\n", strFile.c_str()));
5298 int vrc = RTFileDelete(strFile.c_str());
5299 if (RT_FAILURE(vrc))
5300 throw setError(VBOX_E_IPRT_ERROR,
5301 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5302
5303 ++it;
5304 if (it == task.m_llFilesToDelete.end())
5305 {
5306 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5307 if (FAILED(rc)) throw rc;
5308 break;
5309 }
5310
5311 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5312 if (FAILED(rc)) throw rc;
5313 }
5314
5315 /* delete the settings only when the file actually exists */
5316 if (mData->pMachineConfigFile->fileExists())
5317 {
5318 /* Delete any backup or uncommitted XML files. Ignore failures.
5319 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5320 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5321 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5322 RTFileDelete(otherXml.c_str());
5323 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5324 RTFileDelete(otherXml.c_str());
5325
5326 /* delete the Logs folder, nothing important should be left
5327 * there (we don't check for errors because the user might have
5328 * some private files there that we don't want to delete) */
5329 Utf8Str logFolder;
5330 getLogFolder(logFolder);
5331 Assert(logFolder.length());
5332 if (RTDirExists(logFolder.c_str()))
5333 {
5334 /* Delete all VBox.log[.N] files from the Logs folder
5335 * (this must be in sync with the rotation logic in
5336 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5337 * files that may have been created by the GUI. */
5338 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5339 logFolder.c_str(), RTPATH_DELIMITER);
5340 RTFileDelete(log.c_str());
5341 log = Utf8StrFmt("%s%cVBox.png",
5342 logFolder.c_str(), RTPATH_DELIMITER);
5343 RTFileDelete(log.c_str());
5344 for (int i = uLogHistoryCount; i > 0; i--)
5345 {
5346 log = Utf8StrFmt("%s%cVBox.log.%d",
5347 logFolder.c_str(), RTPATH_DELIMITER, i);
5348 RTFileDelete(log.c_str());
5349 log = Utf8StrFmt("%s%cVBox.png.%d",
5350 logFolder.c_str(), RTPATH_DELIMITER, i);
5351 RTFileDelete(log.c_str());
5352 }
5353#if defined(RT_OS_WINDOWS)
5354 log = Utf8StrFmt("%s%cVBoxStartup.log",
5355 logFolder.c_str(), RTPATH_DELIMITER);
5356 RTFileDelete(log.c_str());
5357#endif
5358
5359 RTDirRemove(logFolder.c_str());
5360 }
5361
5362 /* delete the Snapshots folder, nothing important should be left
5363 * there (we don't check for errors because the user might have
5364 * some private files there that we don't want to delete) */
5365 Utf8Str strFullSnapshotFolder;
5366 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5367 Assert(!strFullSnapshotFolder.isEmpty());
5368 if (RTDirExists(strFullSnapshotFolder.c_str()))
5369 RTDirRemove(strFullSnapshotFolder.c_str());
5370
5371 // delete the directory that contains the settings file, but only
5372 // if it matches the VM name
5373 Utf8Str settingsDir;
5374 if (i_isInOwnDir(&settingsDir))
5375 RTDirRemove(settingsDir.c_str());
5376 }
5377
5378 alock.release();
5379
5380 mParent->i_saveModifiedRegistries();
5381 }
5382 catch (HRESULT aRC) { rc = aRC; }
5383
5384 task.m_pProgress->i_notifyComplete(rc);
5385
5386 LogFlowThisFuncLeave();
5387}
5388
5389HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5390{
5391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5392
5393 HRESULT rc = i_checkStateDependency(MutableStateDep);
5394 if (FAILED(rc)) return rc;
5395
5396 if (mData->mRegistered)
5397 return setError(VBOX_E_INVALID_VM_STATE,
5398 tr("Cannot delete settings of a registered machine"));
5399
5400 // collect files to delete
5401 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5402 if (mData->pMachineConfigFile->fileExists())
5403 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5404
5405 RTCList<ComPtr<IMedium> > llMediums;
5406 for (size_t i = 0; i < aMedia.size(); ++i)
5407 {
5408 IMedium *pIMedium(aMedia[i]);
5409 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5410 if (pMedium.isNull())
5411 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5412 SafeArray<BSTR> ids;
5413 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5414 if (FAILED(rc)) return rc;
5415 /* At this point the medium should not have any back references
5416 * anymore. If it has it is attached to another VM and *must* not
5417 * deleted. */
5418 if (ids.size() < 1)
5419 llMediums.append(pMedium);
5420 }
5421
5422 ComObjPtr<Progress> pProgress;
5423 pProgress.createObject();
5424 rc = pProgress->init(i_getVirtualBox(),
5425 static_cast<IMachine*>(this) /* aInitiator */,
5426 Bstr(tr("Deleting files")).raw(),
5427 true /* fCancellable */,
5428 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5429 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5430 if (FAILED(rc))
5431 return rc;
5432
5433 /* create and start the task on a separate thread (note that it will not
5434 * start working until we release alock) */
5435 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5436 rc = pTask->createThread();
5437 if (FAILED(rc))
5438 return rc;
5439
5440 pProgress.queryInterfaceTo(aProgress.asOutParam());
5441
5442 LogFlowFuncLeave();
5443
5444 return S_OK;
5445}
5446
5447HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5448{
5449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5450
5451 ComObjPtr<Snapshot> pSnapshot;
5452 HRESULT rc;
5453
5454 if (aNameOrId.isEmpty())
5455 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5456 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5457 else
5458 {
5459 Guid uuid(aNameOrId);
5460 if (uuid.isValid())
5461 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5462 else
5463 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5464 }
5465 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5466
5467 return rc;
5468}
5469
5470HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5471{
5472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5473
5474 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5475 if (FAILED(rc)) return rc;
5476
5477 ComObjPtr<SharedFolder> sharedFolder;
5478 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5479 if (SUCCEEDED(rc))
5480 return setError(VBOX_E_OBJECT_IN_USE,
5481 tr("Shared folder named '%s' already exists"),
5482 aName.c_str());
5483
5484 sharedFolder.createObject();
5485 rc = sharedFolder->init(i_getMachine(),
5486 aName,
5487 aHostPath,
5488 !!aWritable,
5489 !!aAutomount,
5490 true /* fFailOnError */);
5491 if (FAILED(rc)) return rc;
5492
5493 i_setModified(IsModified_SharedFolders);
5494 mHWData.backup();
5495 mHWData->mSharedFolders.push_back(sharedFolder);
5496
5497 /* inform the direct session if any */
5498 alock.release();
5499 i_onSharedFolderChange();
5500
5501 return S_OK;
5502}
5503
5504HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5505{
5506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5507
5508 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5509 if (FAILED(rc)) return rc;
5510
5511 ComObjPtr<SharedFolder> sharedFolder;
5512 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5513 if (FAILED(rc)) return rc;
5514
5515 i_setModified(IsModified_SharedFolders);
5516 mHWData.backup();
5517 mHWData->mSharedFolders.remove(sharedFolder);
5518
5519 /* inform the direct session if any */
5520 alock.release();
5521 i_onSharedFolderChange();
5522
5523 return S_OK;
5524}
5525
5526HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5527{
5528 /* start with No */
5529 *aCanShow = FALSE;
5530
5531 ComPtr<IInternalSessionControl> directControl;
5532 {
5533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5534
5535 if (mData->mSession.mState != SessionState_Locked)
5536 return setError(VBOX_E_INVALID_VM_STATE,
5537 tr("Machine is not locked for session (session state: %s)"),
5538 Global::stringifySessionState(mData->mSession.mState));
5539
5540 if (mData->mSession.mLockType == LockType_VM)
5541 directControl = mData->mSession.mDirectControl;
5542 }
5543
5544 /* ignore calls made after #OnSessionEnd() is called */
5545 if (!directControl)
5546 return S_OK;
5547
5548 LONG64 dummy;
5549 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5550}
5551
5552HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5553{
5554 ComPtr<IInternalSessionControl> directControl;
5555 {
5556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5557
5558 if (mData->mSession.mState != SessionState_Locked)
5559 return setError(E_FAIL,
5560 tr("Machine is not locked for session (session state: %s)"),
5561 Global::stringifySessionState(mData->mSession.mState));
5562
5563 if (mData->mSession.mLockType == LockType_VM)
5564 directControl = mData->mSession.mDirectControl;
5565 }
5566
5567 /* ignore calls made after #OnSessionEnd() is called */
5568 if (!directControl)
5569 return S_OK;
5570
5571 BOOL dummy;
5572 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5573}
5574
5575#ifdef VBOX_WITH_GUEST_PROPS
5576/**
5577 * Look up a guest property in VBoxSVC's internal structures.
5578 */
5579HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5580 com::Utf8Str &aValue,
5581 LONG64 *aTimestamp,
5582 com::Utf8Str &aFlags) const
5583{
5584 using namespace guestProp;
5585
5586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5587 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5588
5589 if (it != mHWData->mGuestProperties.end())
5590 {
5591 char szFlags[MAX_FLAGS_LEN + 1];
5592 aValue = it->second.strValue;
5593 *aTimestamp = it->second.mTimestamp;
5594 writeFlags(it->second.mFlags, szFlags);
5595 aFlags = Utf8Str(szFlags);
5596 }
5597
5598 return S_OK;
5599}
5600
5601/**
5602 * Query the VM that a guest property belongs to for the property.
5603 * @returns E_ACCESSDENIED if the VM process is not available or not
5604 * currently handling queries and the lookup should then be done in
5605 * VBoxSVC.
5606 */
5607HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5608 com::Utf8Str &aValue,
5609 LONG64 *aTimestamp,
5610 com::Utf8Str &aFlags) const
5611{
5612 HRESULT rc = S_OK;
5613 BSTR bValue = NULL;
5614 BSTR bFlags = NULL;
5615
5616 ComPtr<IInternalSessionControl> directControl;
5617 {
5618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5619 if (mData->mSession.mLockType == LockType_VM)
5620 directControl = mData->mSession.mDirectControl;
5621 }
5622
5623 /* ignore calls made after #OnSessionEnd() is called */
5624 if (!directControl)
5625 rc = E_ACCESSDENIED;
5626 else
5627 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5628 0 /* accessMode */,
5629 &bValue, aTimestamp, &bFlags);
5630
5631 aValue = bValue;
5632 aFlags = bFlags;
5633
5634 return rc;
5635}
5636#endif // VBOX_WITH_GUEST_PROPS
5637
5638HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5639 com::Utf8Str &aValue,
5640 LONG64 *aTimestamp,
5641 com::Utf8Str &aFlags)
5642{
5643#ifndef VBOX_WITH_GUEST_PROPS
5644 ReturnComNotImplemented();
5645#else // VBOX_WITH_GUEST_PROPS
5646
5647 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5648
5649 if (rc == E_ACCESSDENIED)
5650 /* The VM is not running or the service is not (yet) accessible */
5651 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5652 return rc;
5653#endif // VBOX_WITH_GUEST_PROPS
5654}
5655
5656HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5657{
5658 LONG64 dummyTimestamp;
5659 com::Utf8Str dummyFlags;
5660 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5661 return rc;
5662
5663}
5664HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5665{
5666 com::Utf8Str dummyFlags;
5667 com::Utf8Str dummyValue;
5668 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5669 return rc;
5670}
5671
5672#ifdef VBOX_WITH_GUEST_PROPS
5673/**
5674 * Set a guest property in VBoxSVC's internal structures.
5675 */
5676HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5677 const com::Utf8Str &aFlags, bool fDelete)
5678{
5679 using namespace guestProp;
5680
5681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5682 HRESULT rc = S_OK;
5683
5684 rc = i_checkStateDependency(MutableOrSavedStateDep);
5685 if (FAILED(rc)) return rc;
5686
5687 try
5688 {
5689 uint32_t fFlags = NILFLAG;
5690 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5691 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5692
5693 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5694 if (it == mHWData->mGuestProperties.end())
5695 {
5696 if (!fDelete)
5697 {
5698 i_setModified(IsModified_MachineData);
5699 mHWData.backupEx();
5700
5701 RTTIMESPEC time;
5702 HWData::GuestProperty prop;
5703 prop.strValue = Bstr(aValue).raw();
5704 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5705 prop.mFlags = fFlags;
5706 mHWData->mGuestProperties[aName] = prop;
5707 }
5708 }
5709 else
5710 {
5711 if (it->second.mFlags & (RDONLYHOST))
5712 {
5713 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5714 }
5715 else
5716 {
5717 i_setModified(IsModified_MachineData);
5718 mHWData.backupEx();
5719
5720 /* The backupEx() operation invalidates our iterator,
5721 * so get a new one. */
5722 it = mHWData->mGuestProperties.find(aName);
5723 Assert(it != mHWData->mGuestProperties.end());
5724
5725 if (!fDelete)
5726 {
5727 RTTIMESPEC time;
5728 it->second.strValue = aValue;
5729 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5730 it->second.mFlags = fFlags;
5731 }
5732 else
5733 mHWData->mGuestProperties.erase(it);
5734 }
5735 }
5736
5737 if ( SUCCEEDED(rc)
5738 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5739 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5740 RTSTR_MAX,
5741 aName.c_str(),
5742 RTSTR_MAX,
5743 NULL)
5744 )
5745 )
5746 {
5747 alock.release();
5748
5749 mParent->i_onGuestPropertyChange(mData->mUuid,
5750 Bstr(aName).raw(),
5751 Bstr(aValue).raw(),
5752 Bstr(aFlags).raw());
5753 }
5754 }
5755 catch (std::bad_alloc &)
5756 {
5757 rc = E_OUTOFMEMORY;
5758 }
5759
5760 return rc;
5761}
5762
5763/**
5764 * Set a property on the VM that that property belongs to.
5765 * @returns E_ACCESSDENIED if the VM process is not available or not
5766 * currently handling queries and the setting should then be done in
5767 * VBoxSVC.
5768 */
5769HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5770 const com::Utf8Str &aFlags, bool fDelete)
5771{
5772 HRESULT rc;
5773
5774 try
5775 {
5776 ComPtr<IInternalSessionControl> directControl;
5777 {
5778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5779 if (mData->mSession.mLockType == LockType_VM)
5780 directControl = mData->mSession.mDirectControl;
5781 }
5782
5783 BSTR dummy = NULL; /* will not be changed (setter) */
5784 LONG64 dummy64;
5785 if (!directControl)
5786 rc = E_ACCESSDENIED;
5787 else
5788 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5789 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5790 fDelete? 2: 1 /* accessMode */,
5791 &dummy, &dummy64, &dummy);
5792 }
5793 catch (std::bad_alloc &)
5794 {
5795 rc = E_OUTOFMEMORY;
5796 }
5797
5798 return rc;
5799}
5800#endif // VBOX_WITH_GUEST_PROPS
5801
5802HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5803 const com::Utf8Str &aFlags)
5804{
5805#ifndef VBOX_WITH_GUEST_PROPS
5806 ReturnComNotImplemented();
5807#else // VBOX_WITH_GUEST_PROPS
5808 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5809 if (rc == E_ACCESSDENIED)
5810 /* The VM is not running or the service is not (yet) accessible */
5811 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5812 return rc;
5813#endif // VBOX_WITH_GUEST_PROPS
5814}
5815
5816HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5817{
5818 return setGuestProperty(aProperty, aValue, "");
5819}
5820
5821HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5822{
5823#ifndef VBOX_WITH_GUEST_PROPS
5824 ReturnComNotImplemented();
5825#else // VBOX_WITH_GUEST_PROPS
5826 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5827 if (rc == E_ACCESSDENIED)
5828 /* The VM is not running or the service is not (yet) accessible */
5829 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5830 return rc;
5831#endif // VBOX_WITH_GUEST_PROPS
5832}
5833
5834#ifdef VBOX_WITH_GUEST_PROPS
5835/**
5836 * Enumerate the guest properties in VBoxSVC's internal structures.
5837 */
5838HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5839 std::vector<com::Utf8Str> &aNames,
5840 std::vector<com::Utf8Str> &aValues,
5841 std::vector<LONG64> &aTimestamps,
5842 std::vector<com::Utf8Str> &aFlags)
5843{
5844 using namespace guestProp;
5845
5846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5847 Utf8Str strPatterns(aPatterns);
5848
5849 HWData::GuestPropertyMap propMap;
5850
5851 /*
5852 * Look for matching patterns and build up a list.
5853 */
5854 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5855 while (it != mHWData->mGuestProperties.end())
5856 {
5857 if ( strPatterns.isEmpty()
5858 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5859 RTSTR_MAX,
5860 it->first.c_str(),
5861 RTSTR_MAX,
5862 NULL)
5863 )
5864 propMap.insert(*it);
5865 it++;
5866 }
5867
5868 alock.release();
5869
5870 /*
5871 * And build up the arrays for returning the property information.
5872 */
5873 size_t cEntries = propMap.size();
5874
5875 aNames.resize(cEntries);
5876 aValues.resize(cEntries);
5877 aTimestamps.resize(cEntries);
5878 aFlags.resize(cEntries);
5879
5880 char szFlags[MAX_FLAGS_LEN + 1];
5881 size_t i= 0;
5882 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5883 {
5884 aNames[i] = it->first;
5885 aValues[i] = it->second.strValue;
5886 aTimestamps[i] = it->second.mTimestamp;
5887 writeFlags(it->second.mFlags, szFlags);
5888 aFlags[i] = Utf8Str(szFlags);
5889 }
5890
5891 return S_OK;
5892}
5893
5894/**
5895 * Enumerate the properties managed by a VM.
5896 * @returns E_ACCESSDENIED if the VM process is not available or not
5897 * currently handling queries and the setting should then be done in
5898 * VBoxSVC.
5899 */
5900HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5901 std::vector<com::Utf8Str> &aNames,
5902 std::vector<com::Utf8Str> &aValues,
5903 std::vector<LONG64> &aTimestamps,
5904 std::vector<com::Utf8Str> &aFlags)
5905{
5906 HRESULT rc;
5907 ComPtr<IInternalSessionControl> directControl;
5908 {
5909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5910 if (mData->mSession.mLockType == LockType_VM)
5911 directControl = mData->mSession.mDirectControl;
5912 }
5913
5914 com::SafeArray<BSTR> bNames;
5915 com::SafeArray<BSTR> bValues;
5916 com::SafeArray<LONG64> bTimestamps;
5917 com::SafeArray<BSTR> bFlags;
5918
5919 if (!directControl)
5920 rc = E_ACCESSDENIED;
5921 else
5922 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5923 ComSafeArrayAsOutParam(bNames),
5924 ComSafeArrayAsOutParam(bValues),
5925 ComSafeArrayAsOutParam(bTimestamps),
5926 ComSafeArrayAsOutParam(bFlags));
5927 size_t i;
5928 aNames.resize(bNames.size());
5929 for (i = 0; i < bNames.size(); ++i)
5930 aNames[i] = Utf8Str(bNames[i]);
5931 aValues.resize(bValues.size());
5932 for (i = 0; i < bValues.size(); ++i)
5933 aValues[i] = Utf8Str(bValues[i]);
5934 aTimestamps.resize(bTimestamps.size());
5935 for (i = 0; i < bTimestamps.size(); ++i)
5936 aTimestamps[i] = bTimestamps[i];
5937 aFlags.resize(bFlags.size());
5938 for (i = 0; i < bFlags.size(); ++i)
5939 aFlags[i] = Utf8Str(bFlags[i]);
5940
5941 return rc;
5942}
5943#endif // VBOX_WITH_GUEST_PROPS
5944HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5945 std::vector<com::Utf8Str> &aNames,
5946 std::vector<com::Utf8Str> &aValues,
5947 std::vector<LONG64> &aTimestamps,
5948 std::vector<com::Utf8Str> &aFlags)
5949{
5950#ifndef VBOX_WITH_GUEST_PROPS
5951 ReturnComNotImplemented();
5952#else // VBOX_WITH_GUEST_PROPS
5953
5954 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5955
5956 if (rc == E_ACCESSDENIED)
5957 /* The VM is not running or the service is not (yet) accessible */
5958 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5959 return rc;
5960#endif // VBOX_WITH_GUEST_PROPS
5961}
5962
5963HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5964 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5965{
5966 MediaData::AttachmentList atts;
5967
5968 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5969 if (FAILED(rc)) return rc;
5970
5971 size_t i = 0;
5972 aMediumAttachments.resize(atts.size());
5973 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5974 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5975
5976 return S_OK;
5977}
5978
5979HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5980 LONG aControllerPort,
5981 LONG aDevice,
5982 ComPtr<IMediumAttachment> &aAttachment)
5983{
5984 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5985 aName.c_str(), aControllerPort, aDevice));
5986
5987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5988
5989 aAttachment = NULL;
5990
5991 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5992 Bstr(aName).raw(),
5993 aControllerPort,
5994 aDevice);
5995 if (pAttach.isNull())
5996 return setError(VBOX_E_OBJECT_NOT_FOUND,
5997 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5998 aDevice, aControllerPort, aName.c_str());
5999
6000 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6001
6002 return S_OK;
6003}
6004
6005
6006HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6007 StorageBus_T aConnectionType,
6008 ComPtr<IStorageController> &aController)
6009{
6010 if ( (aConnectionType <= StorageBus_Null)
6011 || (aConnectionType > StorageBus_USB))
6012 return setError(E_INVALIDARG,
6013 tr("Invalid connection type: %d"),
6014 aConnectionType);
6015
6016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6017
6018 HRESULT rc = i_checkStateDependency(MutableStateDep);
6019 if (FAILED(rc)) return rc;
6020
6021 /* try to find one with the name first. */
6022 ComObjPtr<StorageController> ctrl;
6023
6024 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6025 if (SUCCEEDED(rc))
6026 return setError(VBOX_E_OBJECT_IN_USE,
6027 tr("Storage controller named '%s' already exists"),
6028 aName.c_str());
6029
6030 ctrl.createObject();
6031
6032 /* get a new instance number for the storage controller */
6033 ULONG ulInstance = 0;
6034 bool fBootable = true;
6035 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6036 it != mStorageControllers->end();
6037 ++it)
6038 {
6039 if ((*it)->i_getStorageBus() == aConnectionType)
6040 {
6041 ULONG ulCurInst = (*it)->i_getInstance();
6042
6043 if (ulCurInst >= ulInstance)
6044 ulInstance = ulCurInst + 1;
6045
6046 /* Only one controller of each type can be marked as bootable. */
6047 if ((*it)->i_getBootable())
6048 fBootable = false;
6049 }
6050 }
6051
6052 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6053 if (FAILED(rc)) return rc;
6054
6055 i_setModified(IsModified_Storage);
6056 mStorageControllers.backup();
6057 mStorageControllers->push_back(ctrl);
6058
6059 ctrl.queryInterfaceTo(aController.asOutParam());
6060
6061 /* inform the direct session if any */
6062 alock.release();
6063 i_onStorageControllerChange();
6064
6065 return S_OK;
6066}
6067
6068HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6069 ComPtr<IStorageController> &aStorageController)
6070{
6071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6072
6073 ComObjPtr<StorageController> ctrl;
6074
6075 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6076 if (SUCCEEDED(rc))
6077 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6078
6079 return rc;
6080}
6081
6082HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6083 ComPtr<IStorageController> &aStorageController)
6084{
6085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6086
6087 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6088 it != mStorageControllers->end();
6089 ++it)
6090 {
6091 if ((*it)->i_getInstance() == aInstance)
6092 {
6093 (*it).queryInterfaceTo(aStorageController.asOutParam());
6094 return S_OK;
6095 }
6096 }
6097
6098 return setError(VBOX_E_OBJECT_NOT_FOUND,
6099 tr("Could not find a storage controller with instance number '%lu'"),
6100 aInstance);
6101}
6102
6103HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6104{
6105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6106
6107 HRESULT rc = i_checkStateDependency(MutableStateDep);
6108 if (FAILED(rc)) return rc;
6109
6110 ComObjPtr<StorageController> ctrl;
6111
6112 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6113 if (SUCCEEDED(rc))
6114 {
6115 /* Ensure that only one controller of each type is marked as bootable. */
6116 if (aBootable == TRUE)
6117 {
6118 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6119 it != mStorageControllers->end();
6120 ++it)
6121 {
6122 ComObjPtr<StorageController> aCtrl = (*it);
6123
6124 if ( (aCtrl->i_getName() != aName)
6125 && aCtrl->i_getBootable() == TRUE
6126 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6127 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6128 {
6129 aCtrl->i_setBootable(FALSE);
6130 break;
6131 }
6132 }
6133 }
6134
6135 if (SUCCEEDED(rc))
6136 {
6137 ctrl->i_setBootable(aBootable);
6138 i_setModified(IsModified_Storage);
6139 }
6140 }
6141
6142 if (SUCCEEDED(rc))
6143 {
6144 /* inform the direct session if any */
6145 alock.release();
6146 i_onStorageControllerChange();
6147 }
6148
6149 return rc;
6150}
6151
6152HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6153{
6154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6155
6156 HRESULT rc = i_checkStateDependency(MutableStateDep);
6157 if (FAILED(rc)) return rc;
6158
6159 ComObjPtr<StorageController> ctrl;
6160 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6161 if (FAILED(rc)) return rc;
6162
6163 {
6164 /* find all attached devices to the appropriate storage controller and detach them all */
6165 // make a temporary list because detachDevice invalidates iterators into
6166 // mMediaData->mAttachments
6167 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6168
6169 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6170 it != llAttachments2.end();
6171 ++it)
6172 {
6173 MediumAttachment *pAttachTemp = *it;
6174
6175 AutoCaller localAutoCaller(pAttachTemp);
6176 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6177
6178 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6179
6180 if (pAttachTemp->i_getControllerName() == aName)
6181 {
6182 rc = i_detachDevice(pAttachTemp, alock, NULL);
6183 if (FAILED(rc)) return rc;
6184 }
6185 }
6186 }
6187
6188 /* We can remove it now. */
6189 i_setModified(IsModified_Storage);
6190 mStorageControllers.backup();
6191
6192 ctrl->i_unshare();
6193
6194 mStorageControllers->remove(ctrl);
6195
6196 /* inform the direct session if any */
6197 alock.release();
6198 i_onStorageControllerChange();
6199
6200 return S_OK;
6201}
6202
6203HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6204 ComPtr<IUSBController> &aController)
6205{
6206 if ( (aType <= USBControllerType_Null)
6207 || (aType >= USBControllerType_Last))
6208 return setError(E_INVALIDARG,
6209 tr("Invalid USB controller type: %d"),
6210 aType);
6211
6212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6213
6214 HRESULT rc = i_checkStateDependency(MutableStateDep);
6215 if (FAILED(rc)) return rc;
6216
6217 /* try to find one with the same type first. */
6218 ComObjPtr<USBController> ctrl;
6219
6220 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6221 if (SUCCEEDED(rc))
6222 return setError(VBOX_E_OBJECT_IN_USE,
6223 tr("USB controller named '%s' already exists"),
6224 aName.c_str());
6225
6226 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6227 ULONG maxInstances;
6228 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6229 if (FAILED(rc))
6230 return rc;
6231
6232 ULONG cInstances = i_getUSBControllerCountByType(aType);
6233 if (cInstances >= maxInstances)
6234 return setError(E_INVALIDARG,
6235 tr("Too many USB controllers of this type"));
6236
6237 ctrl.createObject();
6238
6239 rc = ctrl->init(this, aName, aType);
6240 if (FAILED(rc)) return rc;
6241
6242 i_setModified(IsModified_USB);
6243 mUSBControllers.backup();
6244 mUSBControllers->push_back(ctrl);
6245
6246 ctrl.queryInterfaceTo(aController.asOutParam());
6247
6248 /* inform the direct session if any */
6249 alock.release();
6250 i_onUSBControllerChange();
6251
6252 return S_OK;
6253}
6254
6255HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6256{
6257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6258
6259 ComObjPtr<USBController> ctrl;
6260
6261 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6262 if (SUCCEEDED(rc))
6263 ctrl.queryInterfaceTo(aController.asOutParam());
6264
6265 return rc;
6266}
6267
6268HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6269 ULONG *aControllers)
6270{
6271 if ( (aType <= USBControllerType_Null)
6272 || (aType >= USBControllerType_Last))
6273 return setError(E_INVALIDARG,
6274 tr("Invalid USB controller type: %d"),
6275 aType);
6276
6277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6278
6279 ComObjPtr<USBController> ctrl;
6280
6281 *aControllers = i_getUSBControllerCountByType(aType);
6282
6283 return S_OK;
6284}
6285
6286HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6287{
6288
6289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6290
6291 HRESULT rc = i_checkStateDependency(MutableStateDep);
6292 if (FAILED(rc)) return rc;
6293
6294 ComObjPtr<USBController> ctrl;
6295 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6296 if (FAILED(rc)) return rc;
6297
6298 i_setModified(IsModified_USB);
6299 mUSBControllers.backup();
6300
6301 ctrl->i_unshare();
6302
6303 mUSBControllers->remove(ctrl);
6304
6305 /* inform the direct session if any */
6306 alock.release();
6307 i_onUSBControllerChange();
6308
6309 return S_OK;
6310}
6311
6312HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6313 ULONG *aOriginX,
6314 ULONG *aOriginY,
6315 ULONG *aWidth,
6316 ULONG *aHeight,
6317 BOOL *aEnabled)
6318{
6319 uint32_t u32OriginX= 0;
6320 uint32_t u32OriginY= 0;
6321 uint32_t u32Width = 0;
6322 uint32_t u32Height = 0;
6323 uint16_t u16Flags = 0;
6324
6325 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6326 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6327 if (RT_FAILURE(vrc))
6328 {
6329#ifdef RT_OS_WINDOWS
6330 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6331 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6332 * So just assign fEnable to TRUE again.
6333 * The right fix would be to change GUI API wrappers to make sure that parameters
6334 * are changed only if API succeeds.
6335 */
6336 *aEnabled = TRUE;
6337#endif
6338 return setError(VBOX_E_IPRT_ERROR,
6339 tr("Saved guest size is not available (%Rrc)"),
6340 vrc);
6341 }
6342
6343 *aOriginX = u32OriginX;
6344 *aOriginY = u32OriginY;
6345 *aWidth = u32Width;
6346 *aHeight = u32Height;
6347 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6348
6349 return S_OK;
6350}
6351
6352HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6353{
6354 if (aScreenId != 0)
6355 return E_NOTIMPL;
6356
6357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6358
6359 uint8_t *pu8Data = NULL;
6360 uint32_t cbData = 0;
6361 uint32_t u32Width = 0;
6362 uint32_t u32Height = 0;
6363
6364 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6365
6366 if (RT_FAILURE(vrc))
6367 return setError(VBOX_E_IPRT_ERROR,
6368 tr("Saved screenshot data is not available (%Rrc)"),
6369 vrc);
6370
6371 *aSize = cbData;
6372 *aWidth = u32Width;
6373 *aHeight = u32Height;
6374
6375 freeSavedDisplayScreenshot(pu8Data);
6376
6377 return S_OK;
6378}
6379
6380HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6381 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6382{
6383 if (aScreenId != 0)
6384 return E_NOTIMPL;
6385
6386 if ( aBitmapFormat != BitmapFormat_BGR0
6387 && aBitmapFormat != BitmapFormat_BGRA
6388 && aBitmapFormat != BitmapFormat_RGBA
6389 && aBitmapFormat != BitmapFormat_PNG)
6390 return setError(E_NOTIMPL,
6391 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6392
6393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6394
6395 uint8_t *pu8Data = NULL;
6396 uint32_t cbData = 0;
6397 uint32_t u32Width = 0;
6398 uint32_t u32Height = 0;
6399
6400 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6401
6402 if (RT_FAILURE(vrc))
6403 return setError(VBOX_E_IPRT_ERROR,
6404 tr("Saved thumbnail data is not available (%Rrc)"),
6405 vrc);
6406
6407 HRESULT hr = S_OK;
6408
6409 *aWidth = u32Width;
6410 *aHeight = u32Height;
6411
6412 if (cbData > 0)
6413 {
6414 /* Convert pixels to the format expected by the API caller. */
6415 if (aBitmapFormat == BitmapFormat_BGR0)
6416 {
6417 /* [0] B, [1] G, [2] R, [3] 0. */
6418 aData.resize(cbData);
6419 memcpy(&aData.front(), pu8Data, cbData);
6420 }
6421 else if (aBitmapFormat == BitmapFormat_BGRA)
6422 {
6423 /* [0] B, [1] G, [2] R, [3] A. */
6424 aData.resize(cbData);
6425 for (uint32_t i = 0; i < cbData; i += 4)
6426 {
6427 aData[i] = pu8Data[i];
6428 aData[i + 1] = pu8Data[i + 1];
6429 aData[i + 2] = pu8Data[i + 2];
6430 aData[i + 3] = 0xff;
6431 }
6432 }
6433 else if (aBitmapFormat == BitmapFormat_RGBA)
6434 {
6435 /* [0] R, [1] G, [2] B, [3] A. */
6436 aData.resize(cbData);
6437 for (uint32_t i = 0; i < cbData; i += 4)
6438 {
6439 aData[i] = pu8Data[i + 2];
6440 aData[i + 1] = pu8Data[i + 1];
6441 aData[i + 2] = pu8Data[i];
6442 aData[i + 3] = 0xff;
6443 }
6444 }
6445 else if (aBitmapFormat == BitmapFormat_PNG)
6446 {
6447 uint8_t *pu8PNG = NULL;
6448 uint32_t cbPNG = 0;
6449 uint32_t cxPNG = 0;
6450 uint32_t cyPNG = 0;
6451
6452 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6453
6454 if (RT_SUCCESS(vrc))
6455 {
6456 aData.resize(cbPNG);
6457 if (cbPNG)
6458 memcpy(&aData.front(), pu8PNG, cbPNG);
6459 }
6460 else
6461 hr = setError(VBOX_E_IPRT_ERROR,
6462 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6463 vrc);
6464
6465 RTMemFree(pu8PNG);
6466 }
6467 }
6468
6469 freeSavedDisplayScreenshot(pu8Data);
6470
6471 return hr;
6472}
6473
6474HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6475{
6476 if (aScreenId != 0)
6477 return E_NOTIMPL;
6478
6479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6480
6481 uint8_t *pu8Data = NULL;
6482 uint32_t cbData = 0;
6483 uint32_t u32Width = 0;
6484 uint32_t u32Height = 0;
6485
6486 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6487
6488 if (RT_FAILURE(vrc))
6489 return setError(VBOX_E_IPRT_ERROR,
6490 tr("Saved screenshot data is not available (%Rrc)"),
6491 vrc);
6492
6493 *aSize = cbData;
6494 *aWidth = u32Width;
6495 *aHeight = u32Height;
6496
6497 freeSavedDisplayScreenshot(pu8Data);
6498
6499 return S_OK;
6500}
6501
6502HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6503{
6504 if (aScreenId != 0)
6505 return E_NOTIMPL;
6506
6507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6508
6509 uint8_t *pu8Data = NULL;
6510 uint32_t cbData = 0;
6511 uint32_t u32Width = 0;
6512 uint32_t u32Height = 0;
6513
6514 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6515
6516 if (RT_FAILURE(vrc))
6517 return setError(VBOX_E_IPRT_ERROR,
6518 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6519 vrc);
6520
6521 *aWidth = u32Width;
6522 *aHeight = u32Height;
6523
6524 aData.resize(cbData);
6525 if (cbData)
6526 memcpy(&aData.front(), pu8Data, cbData);
6527
6528 freeSavedDisplayScreenshot(pu8Data);
6529
6530 return S_OK;
6531}
6532
6533HRESULT Machine::hotPlugCPU(ULONG aCpu)
6534{
6535 HRESULT rc = S_OK;
6536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6537
6538 if (!mHWData->mCPUHotPlugEnabled)
6539 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6540
6541 if (aCpu >= mHWData->mCPUCount)
6542 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6543
6544 if (mHWData->mCPUAttached[aCpu])
6545 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6546
6547 alock.release();
6548 rc = i_onCPUChange(aCpu, false);
6549 alock.acquire();
6550 if (FAILED(rc)) return rc;
6551
6552 i_setModified(IsModified_MachineData);
6553 mHWData.backup();
6554 mHWData->mCPUAttached[aCpu] = true;
6555
6556 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6557 if (Global::IsOnline(mData->mMachineState))
6558 i_saveSettings(NULL);
6559
6560 return S_OK;
6561}
6562
6563HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6564{
6565 HRESULT rc = S_OK;
6566
6567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6568
6569 if (!mHWData->mCPUHotPlugEnabled)
6570 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6571
6572 if (aCpu >= SchemaDefs::MaxCPUCount)
6573 return setError(E_INVALIDARG,
6574 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6575 SchemaDefs::MaxCPUCount);
6576
6577 if (!mHWData->mCPUAttached[aCpu])
6578 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6579
6580 /* CPU 0 can't be detached */
6581 if (aCpu == 0)
6582 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6583
6584 alock.release();
6585 rc = i_onCPUChange(aCpu, true);
6586 alock.acquire();
6587 if (FAILED(rc)) return rc;
6588
6589 i_setModified(IsModified_MachineData);
6590 mHWData.backup();
6591 mHWData->mCPUAttached[aCpu] = false;
6592
6593 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6594 if (Global::IsOnline(mData->mMachineState))
6595 i_saveSettings(NULL);
6596
6597 return S_OK;
6598}
6599
6600HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6601{
6602 *aAttached = false;
6603
6604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6605
6606 /* If hotplug is enabled the CPU is always enabled. */
6607 if (!mHWData->mCPUHotPlugEnabled)
6608 {
6609 if (aCpu < mHWData->mCPUCount)
6610 *aAttached = true;
6611 }
6612 else
6613 {
6614 if (aCpu < SchemaDefs::MaxCPUCount)
6615 *aAttached = mHWData->mCPUAttached[aCpu];
6616 }
6617
6618 return S_OK;
6619}
6620
6621HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6622{
6623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6624
6625 Utf8Str log = i_queryLogFilename(aIdx);
6626 if (!RTFileExists(log.c_str()))
6627 log.setNull();
6628 aFilename = log;
6629
6630 return S_OK;
6631}
6632
6633HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6634{
6635 if (aSize < 0)
6636 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6637
6638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6639
6640 HRESULT rc = S_OK;
6641 Utf8Str log = i_queryLogFilename(aIdx);
6642
6643 /* do not unnecessarily hold the lock while doing something which does
6644 * not need the lock and potentially takes a long time. */
6645 alock.release();
6646
6647 /* Limit the chunk size to 32K for now, as that gives better performance
6648 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6649 * One byte expands to approx. 25 bytes of breathtaking XML. */
6650 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6651 aData.resize(cbData);
6652
6653 RTFILE LogFile;
6654 int vrc = RTFileOpen(&LogFile, log.c_str(),
6655 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6656 if (RT_SUCCESS(vrc))
6657 {
6658 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6659 if (RT_SUCCESS(vrc))
6660 aData.resize(cbData);
6661 else
6662 rc = setError(VBOX_E_IPRT_ERROR,
6663 tr("Could not read log file '%s' (%Rrc)"),
6664 log.c_str(), vrc);
6665 RTFileClose(LogFile);
6666 }
6667 else
6668 rc = setError(VBOX_E_IPRT_ERROR,
6669 tr("Could not open log file '%s' (%Rrc)"),
6670 log.c_str(), vrc);
6671
6672 if (FAILED(rc))
6673 aData.resize(0);
6674
6675 return rc;
6676}
6677
6678
6679/**
6680 * Currently this method doesn't attach device to the running VM,
6681 * just makes sure it's plugged on next VM start.
6682 */
6683HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6684{
6685 // lock scope
6686 {
6687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6688
6689 HRESULT rc = i_checkStateDependency(MutableStateDep);
6690 if (FAILED(rc)) return rc;
6691
6692 ChipsetType_T aChipset = ChipsetType_PIIX3;
6693 COMGETTER(ChipsetType)(&aChipset);
6694
6695 if (aChipset != ChipsetType_ICH9)
6696 {
6697 return setError(E_INVALIDARG,
6698 tr("Host PCI attachment only supported with ICH9 chipset"));
6699 }
6700
6701 // check if device with this host PCI address already attached
6702 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6703 it != mHWData->mPCIDeviceAssignments.end();
6704 ++it)
6705 {
6706 LONG iHostAddress = -1;
6707 ComPtr<PCIDeviceAttachment> pAttach;
6708 pAttach = *it;
6709 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6710 if (iHostAddress == aHostAddress)
6711 return setError(E_INVALIDARG,
6712 tr("Device with host PCI address already attached to this VM"));
6713 }
6714
6715 ComObjPtr<PCIDeviceAttachment> pda;
6716 char name[32];
6717
6718 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6719 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6720 Bstr bname(name);
6721 pda.createObject();
6722 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6723 i_setModified(IsModified_MachineData);
6724 mHWData.backup();
6725 mHWData->mPCIDeviceAssignments.push_back(pda);
6726 }
6727
6728 return S_OK;
6729}
6730
6731/**
6732 * Currently this method doesn't detach device from the running VM,
6733 * just makes sure it's not plugged on next VM start.
6734 */
6735HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6736{
6737 ComObjPtr<PCIDeviceAttachment> pAttach;
6738 bool fRemoved = false;
6739 HRESULT rc;
6740
6741 // lock scope
6742 {
6743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6744
6745 rc = i_checkStateDependency(MutableStateDep);
6746 if (FAILED(rc)) return rc;
6747
6748 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6749 it != mHWData->mPCIDeviceAssignments.end();
6750 ++it)
6751 {
6752 LONG iHostAddress = -1;
6753 pAttach = *it;
6754 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6755 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6756 {
6757 i_setModified(IsModified_MachineData);
6758 mHWData.backup();
6759 mHWData->mPCIDeviceAssignments.remove(pAttach);
6760 fRemoved = true;
6761 break;
6762 }
6763 }
6764 }
6765
6766
6767 /* Fire event outside of the lock */
6768 if (fRemoved)
6769 {
6770 Assert(!pAttach.isNull());
6771 ComPtr<IEventSource> es;
6772 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6773 Assert(SUCCEEDED(rc));
6774 Bstr mid;
6775 rc = this->COMGETTER(Id)(mid.asOutParam());
6776 Assert(SUCCEEDED(rc));
6777 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6778 }
6779
6780 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6781 tr("No host PCI device %08x attached"),
6782 aHostAddress
6783 );
6784}
6785
6786HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6787{
6788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6789
6790 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6791
6792 size_t i = 0;
6793 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6794 it != mHWData->mPCIDeviceAssignments.end();
6795 ++i, ++it)
6796 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6797
6798 return S_OK;
6799}
6800
6801HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6802{
6803 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6804
6805 return S_OK;
6806}
6807
6808HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6809{
6810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6811
6812 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6813
6814 return S_OK;
6815}
6816
6817HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6818{
6819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6820 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6821 if (SUCCEEDED(hrc))
6822 {
6823 hrc = mHWData.backupEx();
6824 if (SUCCEEDED(hrc))
6825 {
6826 i_setModified(IsModified_MachineData);
6827 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6828 }
6829 }
6830 return hrc;
6831}
6832
6833HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6834{
6835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6836 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6837 return S_OK;
6838}
6839
6840HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6841{
6842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6843 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6844 if (SUCCEEDED(hrc))
6845 {
6846 hrc = mHWData.backupEx();
6847 if (SUCCEEDED(hrc))
6848 {
6849 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6850 if (SUCCEEDED(hrc))
6851 i_setModified(IsModified_MachineData);
6852 }
6853 }
6854 return hrc;
6855}
6856
6857HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6858{
6859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6860
6861 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6862
6863 return S_OK;
6864}
6865
6866HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6867{
6868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6869 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6870 if (SUCCEEDED(hrc))
6871 {
6872 hrc = mHWData.backupEx();
6873 if (SUCCEEDED(hrc))
6874 {
6875 i_setModified(IsModified_MachineData);
6876 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6877 }
6878 }
6879 return hrc;
6880}
6881
6882HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6883{
6884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6885
6886 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6887
6888 return S_OK;
6889}
6890
6891HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6892{
6893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6894
6895 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6896 if ( SUCCEEDED(hrc)
6897 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6898 {
6899 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6900 int vrc;
6901
6902 if (aAutostartEnabled)
6903 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6904 else
6905 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6906
6907 if (RT_SUCCESS(vrc))
6908 {
6909 hrc = mHWData.backupEx();
6910 if (SUCCEEDED(hrc))
6911 {
6912 i_setModified(IsModified_MachineData);
6913 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6914 }
6915 }
6916 else if (vrc == VERR_NOT_SUPPORTED)
6917 hrc = setError(VBOX_E_NOT_SUPPORTED,
6918 tr("The VM autostart feature is not supported on this platform"));
6919 else if (vrc == VERR_PATH_NOT_FOUND)
6920 hrc = setError(E_FAIL,
6921 tr("The path to the autostart database is not set"));
6922 else
6923 hrc = setError(E_UNEXPECTED,
6924 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6925 aAutostartEnabled ? "Adding" : "Removing",
6926 mUserData->s.strName.c_str(), vrc);
6927 }
6928 return hrc;
6929}
6930
6931HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6932{
6933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6934
6935 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6936
6937 return S_OK;
6938}
6939
6940HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6941{
6942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6943 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6944 if (SUCCEEDED(hrc))
6945 {
6946 hrc = mHWData.backupEx();
6947 if (SUCCEEDED(hrc))
6948 {
6949 i_setModified(IsModified_MachineData);
6950 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6951 }
6952 }
6953 return hrc;
6954}
6955
6956HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6957{
6958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6959
6960 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6961
6962 return S_OK;
6963}
6964
6965HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6966{
6967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6968 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6969 if ( SUCCEEDED(hrc)
6970 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6971 {
6972 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6973 int vrc;
6974
6975 if (aAutostopType != AutostopType_Disabled)
6976 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6977 else
6978 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6979
6980 if (RT_SUCCESS(vrc))
6981 {
6982 hrc = mHWData.backupEx();
6983 if (SUCCEEDED(hrc))
6984 {
6985 i_setModified(IsModified_MachineData);
6986 mHWData->mAutostart.enmAutostopType = aAutostopType;
6987 }
6988 }
6989 else if (vrc == VERR_NOT_SUPPORTED)
6990 hrc = setError(VBOX_E_NOT_SUPPORTED,
6991 tr("The VM autostop feature is not supported on this platform"));
6992 else if (vrc == VERR_PATH_NOT_FOUND)
6993 hrc = setError(E_FAIL,
6994 tr("The path to the autostart database is not set"));
6995 else
6996 hrc = setError(E_UNEXPECTED,
6997 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6998 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6999 mUserData->s.strName.c_str(), vrc);
7000 }
7001 return hrc;
7002}
7003
7004HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7005{
7006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7007
7008 aDefaultFrontend = mHWData->mDefaultFrontend;
7009
7010 return S_OK;
7011}
7012
7013HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7014{
7015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7016 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7017 if (SUCCEEDED(hrc))
7018 {
7019 hrc = mHWData.backupEx();
7020 if (SUCCEEDED(hrc))
7021 {
7022 i_setModified(IsModified_MachineData);
7023 mHWData->mDefaultFrontend = aDefaultFrontend;
7024 }
7025 }
7026 return hrc;
7027}
7028
7029HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7030{
7031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7032 size_t cbIcon = mUserData->mIcon.size();
7033 aIcon.resize(cbIcon);
7034 if (cbIcon)
7035 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7036 return S_OK;
7037}
7038
7039HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7040{
7041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7042 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7043 if (SUCCEEDED(hrc))
7044 {
7045 i_setModified(IsModified_MachineData);
7046 mUserData.backup();
7047 size_t cbIcon = aIcon.size();
7048 mUserData->mIcon.resize(cbIcon);
7049 if (cbIcon)
7050 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7051 }
7052 return hrc;
7053}
7054
7055HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7056{
7057#ifdef VBOX_WITH_USB
7058 *aUSBProxyAvailable = true;
7059#else
7060 *aUSBProxyAvailable = false;
7061#endif
7062 return S_OK;
7063}
7064
7065HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7066 ComPtr<IProgress> &aProgress)
7067{
7068 ComObjPtr<Progress> pP;
7069 Progress *ppP = pP;
7070 IProgress *iP = static_cast<IProgress *>(ppP);
7071 IProgress **pProgress = &iP;
7072
7073 IMachine *pTarget = aTarget;
7074
7075 /* Convert the options. */
7076 RTCList<CloneOptions_T> optList;
7077 if (aOptions.size())
7078 for (size_t i = 0; i < aOptions.size(); ++i)
7079 optList.append(aOptions[i]);
7080
7081 if (optList.contains(CloneOptions_Link))
7082 {
7083 if (!i_isSnapshotMachine())
7084 return setError(E_INVALIDARG,
7085 tr("Linked clone can only be created from a snapshot"));
7086 if (aMode != CloneMode_MachineState)
7087 return setError(E_INVALIDARG,
7088 tr("Linked clone can only be created for a single machine state"));
7089 }
7090 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7091
7092 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7093
7094 HRESULT rc = pWorker->start(pProgress);
7095
7096 pP = static_cast<Progress *>(*pProgress);
7097 pP.queryInterfaceTo(aProgress.asOutParam());
7098
7099 return rc;
7100
7101}
7102
7103HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7104{
7105 NOREF(aProgress);
7106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7107
7108 // This check should always fail.
7109 HRESULT rc = i_checkStateDependency(MutableStateDep);
7110 if (FAILED(rc)) return rc;
7111
7112 AssertFailedReturn(E_NOTIMPL);
7113}
7114
7115HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7116{
7117 NOREF(aSavedStateFile);
7118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7119
7120 // This check should always fail.
7121 HRESULT rc = i_checkStateDependency(MutableStateDep);
7122 if (FAILED(rc)) return rc;
7123
7124 AssertFailedReturn(E_NOTIMPL);
7125}
7126
7127HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7128{
7129 NOREF(aFRemoveFile);
7130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7131
7132 // This check should always fail.
7133 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7134 if (FAILED(rc)) return rc;
7135
7136 AssertFailedReturn(E_NOTIMPL);
7137}
7138
7139// public methods for internal purposes
7140/////////////////////////////////////////////////////////////////////////////
7141
7142/**
7143 * Adds the given IsModified_* flag to the dirty flags of the machine.
7144 * This must be called either during i_loadSettings or under the machine write lock.
7145 * @param fl
7146 */
7147void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7148{
7149 mData->flModifications |= fl;
7150 if (fAllowStateModification && i_isStateModificationAllowed())
7151 mData->mCurrentStateModified = true;
7152}
7153
7154/**
7155 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7156 * care of the write locking.
7157 *
7158 * @param fModifications The flag to add.
7159 */
7160void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7161{
7162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7163 i_setModified(fModification, fAllowStateModification);
7164}
7165
7166/**
7167 * Saves the registry entry of this machine to the given configuration node.
7168 *
7169 * @param aEntryNode Node to save the registry entry to.
7170 *
7171 * @note locks this object for reading.
7172 */
7173HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7174{
7175 AutoLimitedCaller autoCaller(this);
7176 AssertComRCReturnRC(autoCaller.rc());
7177
7178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7179
7180 data.uuid = mData->mUuid;
7181 data.strSettingsFile = mData->m_strConfigFile;
7182
7183 return S_OK;
7184}
7185
7186/**
7187 * Calculates the absolute path of the given path taking the directory of the
7188 * machine settings file as the current directory.
7189 *
7190 * @param aPath Path to calculate the absolute path for.
7191 * @param aResult Where to put the result (used only on success, can be the
7192 * same Utf8Str instance as passed in @a aPath).
7193 * @return IPRT result.
7194 *
7195 * @note Locks this object for reading.
7196 */
7197int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7198{
7199 AutoCaller autoCaller(this);
7200 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7201
7202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7203
7204 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7205
7206 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7207
7208 strSettingsDir.stripFilename();
7209 char folder[RTPATH_MAX];
7210 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7211 if (RT_SUCCESS(vrc))
7212 aResult = folder;
7213
7214 return vrc;
7215}
7216
7217/**
7218 * Copies strSource to strTarget, making it relative to the machine folder
7219 * if it is a subdirectory thereof, or simply copying it otherwise.
7220 *
7221 * @param strSource Path to evaluate and copy.
7222 * @param strTarget Buffer to receive target path.
7223 *
7224 * @note Locks this object for reading.
7225 */
7226void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7227 Utf8Str &strTarget)
7228{
7229 AutoCaller autoCaller(this);
7230 AssertComRCReturn(autoCaller.rc(), (void)0);
7231
7232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7233
7234 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7235 // use strTarget as a temporary buffer to hold the machine settings dir
7236 strTarget = mData->m_strConfigFileFull;
7237 strTarget.stripFilename();
7238 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7239 {
7240 // is relative: then append what's left
7241 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7242 // for empty paths (only possible for subdirs) use "." to avoid
7243 // triggering default settings for not present config attributes.
7244 if (strTarget.isEmpty())
7245 strTarget = ".";
7246 }
7247 else
7248 // is not relative: then overwrite
7249 strTarget = strSource;
7250}
7251
7252/**
7253 * Returns the full path to the machine's log folder in the
7254 * \a aLogFolder argument.
7255 */
7256void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7257{
7258 AutoCaller autoCaller(this);
7259 AssertComRCReturnVoid(autoCaller.rc());
7260
7261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7262
7263 char szTmp[RTPATH_MAX];
7264 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7265 if (RT_SUCCESS(vrc))
7266 {
7267 if (szTmp[0] && !mUserData.isNull())
7268 {
7269 char szTmp2[RTPATH_MAX];
7270 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7271 if (RT_SUCCESS(vrc))
7272 aLogFolder = BstrFmt("%s%c%s",
7273 szTmp2,
7274 RTPATH_DELIMITER,
7275 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7276 }
7277 else
7278 vrc = VERR_PATH_IS_RELATIVE;
7279 }
7280
7281 if (RT_FAILURE(vrc))
7282 {
7283 // fallback if VBOX_USER_LOGHOME is not set or invalid
7284 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7285 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7286 aLogFolder.append(RTPATH_DELIMITER);
7287 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7288 }
7289}
7290
7291/**
7292 * Returns the full path to the machine's log file for an given index.
7293 */
7294Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7295 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7296{
7297 Utf8Str logFolder;
7298 getLogFolder(logFolder);
7299 Assert(logFolder.length());
7300 Utf8Str log;
7301 if (idx == 0)
7302 log = Utf8StrFmt("%s%cVBox.log",
7303 logFolder.c_str(), RTPATH_DELIMITER);
7304 else
7305 log = Utf8StrFmt("%s%cVBox.log.%d",
7306 logFolder.c_str(), RTPATH_DELIMITER, idx);
7307 return log;
7308}
7309
7310/**
7311 * Returns the full path to the machine's (hardened) startup log file.
7312 */
7313Utf8Str Machine::i_getStartupLogFilename(void)
7314{
7315 Utf8Str strFilename;
7316 getLogFolder(strFilename);
7317 Assert(strFilename.length());
7318 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7319 return strFilename;
7320}
7321
7322
7323/**
7324 * Composes a unique saved state filename based on the current system time. The filename is
7325 * granular to the second so this will work so long as no more than one snapshot is taken on
7326 * a machine per second.
7327 *
7328 * Before version 4.1, we used this formula for saved state files:
7329 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7330 * which no longer works because saved state files can now be shared between the saved state of the
7331 * "saved" machine and an online snapshot, and the following would cause problems:
7332 * 1) save machine
7333 * 2) create online snapshot from that machine state --> reusing saved state file
7334 * 3) save machine again --> filename would be reused, breaking the online snapshot
7335 *
7336 * So instead we now use a timestamp.
7337 *
7338 * @param str
7339 */
7340
7341void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7342{
7343 AutoCaller autoCaller(this);
7344 AssertComRCReturnVoid(autoCaller.rc());
7345
7346 {
7347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7348 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7349 }
7350
7351 RTTIMESPEC ts;
7352 RTTimeNow(&ts);
7353 RTTIME time;
7354 RTTimeExplode(&time, &ts);
7355
7356 strStateFilePath += RTPATH_DELIMITER;
7357 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7358 time.i32Year, time.u8Month, time.u8MonthDay,
7359 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7360}
7361
7362/**
7363 * Returns the full path to the default video capture file.
7364 */
7365void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7366{
7367 AutoCaller autoCaller(this);
7368 AssertComRCReturnVoid(autoCaller.rc());
7369
7370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7371
7372 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7373 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7374 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7375}
7376
7377/**
7378 * Returns whether at least one USB controller is present for the VM.
7379 */
7380bool Machine::i_isUSBControllerPresent()
7381{
7382 AutoCaller autoCaller(this);
7383 AssertComRCReturn(autoCaller.rc(), false);
7384
7385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7386
7387 return (mUSBControllers->size() > 0);
7388}
7389
7390/**
7391 * @note Locks this object for writing, calls the client process
7392 * (inside the lock).
7393 */
7394HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7395 const Utf8Str &strFrontend,
7396 const Utf8Str &strEnvironment,
7397 ProgressProxy *aProgress)
7398{
7399 LogFlowThisFuncEnter();
7400
7401 AssertReturn(aControl, E_FAIL);
7402 AssertReturn(aProgress, E_FAIL);
7403 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7404
7405 AutoCaller autoCaller(this);
7406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7407
7408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7409
7410 if (!mData->mRegistered)
7411 return setError(E_UNEXPECTED,
7412 tr("The machine '%s' is not registered"),
7413 mUserData->s.strName.c_str());
7414
7415 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7416
7417 /* The process started when launching a VM with separate UI/VM processes is always
7418 * the UI process, i.e. needs special handling as it won't claim the session. */
7419 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7420
7421 if (fSeparate)
7422 {
7423 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mType.compare("headless", Utf8Str::CaseInsensitive))
7424 return setError(VBOX_E_INVALID_OBJECT_STATE,
7425 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7426 mUserData->s.strName.c_str());
7427 }
7428 else
7429 {
7430 if ( mData->mSession.mState == SessionState_Locked
7431 || mData->mSession.mState == SessionState_Spawning
7432 || mData->mSession.mState == SessionState_Unlocking)
7433 return setError(VBOX_E_INVALID_OBJECT_STATE,
7434 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7435 mUserData->s.strName.c_str());
7436
7437 /* may not be busy */
7438 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7439 }
7440
7441 /* get the path to the executable */
7442 char szPath[RTPATH_MAX];
7443 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7444 size_t cchBufLeft = strlen(szPath);
7445 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7446 szPath[cchBufLeft] = 0;
7447 char *pszNamePart = szPath + cchBufLeft;
7448 cchBufLeft = sizeof(szPath) - cchBufLeft;
7449
7450 int vrc = VINF_SUCCESS;
7451 RTPROCESS pid = NIL_RTPROCESS;
7452
7453 RTENV env = RTENV_DEFAULT;
7454
7455 if (!strEnvironment.isEmpty())
7456 {
7457 char *newEnvStr = NULL;
7458
7459 do
7460 {
7461 /* clone the current environment */
7462 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7463 AssertRCBreakStmt(vrc2, vrc = vrc2);
7464
7465 newEnvStr = RTStrDup(strEnvironment.c_str());
7466 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7467
7468 /* put new variables to the environment
7469 * (ignore empty variable names here since RTEnv API
7470 * intentionally doesn't do that) */
7471 char *var = newEnvStr;
7472 for (char *p = newEnvStr; *p; ++p)
7473 {
7474 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7475 {
7476 *p = '\0';
7477 if (*var)
7478 {
7479 char *val = strchr(var, '=');
7480 if (val)
7481 {
7482 *val++ = '\0';
7483 vrc2 = RTEnvSetEx(env, var, val);
7484 }
7485 else
7486 vrc2 = RTEnvUnsetEx(env, var);
7487 if (RT_FAILURE(vrc2))
7488 break;
7489 }
7490 var = p + 1;
7491 }
7492 }
7493 if (RT_SUCCESS(vrc2) && *var)
7494 vrc2 = RTEnvPutEx(env, var);
7495
7496 AssertRCBreakStmt(vrc2, vrc = vrc2);
7497 }
7498 while (0);
7499
7500 if (newEnvStr != NULL)
7501 RTStrFree(newEnvStr);
7502 }
7503
7504 /* Hardened startup logging */
7505#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7506 Utf8Str strSupStartLogArg("--sup-startup-log=");
7507 {
7508 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7509 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7510 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7511 {
7512 Utf8Str strStartupLogDir = strStartupLogFile;
7513 strStartupLogDir.stripFilename();
7514 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7515 file without stripping the file. */
7516 }
7517 strSupStartLogArg.append(strStartupLogFile);
7518 }
7519 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7520#else
7521 const char *pszSupStartupLogArg = NULL;
7522#endif
7523
7524
7525#ifdef VBOX_WITH_QTGUI
7526 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7527 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7528 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7529 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7530 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7531 {
7532# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7533 /* Modify the base path so that we don't need to use ".." below. */
7534 RTPathStripTrailingSlash(szPath);
7535 RTPathStripFilename(szPath);
7536 cchBufLeft = strlen(szPath);
7537 pszNamePart = szPath + cchBufLeft;
7538 cchBufLeft = sizeof(szPath) - cchBufLeft;
7539
7540# define OSX_APP_NAME "VirtualBoxVM"
7541# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7542
7543 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7544 if ( strAppOverride.contains(".")
7545 || strAppOverride.contains("/")
7546 || strAppOverride.contains("\\")
7547 || strAppOverride.contains(":"))
7548 strAppOverride.setNull();
7549 Utf8Str strAppPath;
7550 if (!strAppOverride.isEmpty())
7551 {
7552 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7553 Utf8Str strFullPath(szPath);
7554 strFullPath.append(strAppPath);
7555 /* there is a race, but people using this deserve the failure */
7556 if (!RTFileExists(strFullPath.c_str()))
7557 strAppOverride.setNull();
7558 }
7559 if (strAppOverride.isEmpty())
7560 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7561 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7562 strcpy(pszNamePart, strAppPath.c_str());
7563# else
7564 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7565 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7566 strcpy(pszNamePart, s_szVirtualBox_exe);
7567# endif
7568
7569 Utf8Str idStr = mData->mUuid.toString();
7570 const char *apszArgs[] =
7571 {
7572 szPath,
7573 "--comment", mUserData->s.strName.c_str(),
7574 "--startvm", idStr.c_str(),
7575 "--no-startvm-errormsgbox",
7576 NULL, /* For "--separate". */
7577 NULL, /* For "--sup-startup-log". */
7578 NULL
7579 };
7580 unsigned iArg = 6;
7581 if (fSeparate)
7582 apszArgs[iArg++] = "--separate";
7583 apszArgs[iArg++] = pszSupStartupLogArg;
7584
7585 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7586 }
7587#else /* !VBOX_WITH_QTGUI */
7588 if (0)
7589 ;
7590#endif /* VBOX_WITH_QTGUI */
7591
7592 else
7593
7594#ifdef VBOX_WITH_VBOXSDL
7595 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7596 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7597 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7598 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7599 {
7600 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7601 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7602 strcpy(pszNamePart, s_szVBoxSDL_exe);
7603
7604 Utf8Str idStr = mData->mUuid.toString();
7605 const char *apszArgs[] =
7606 {
7607 szPath,
7608 "--comment", mUserData->s.strName.c_str(),
7609 "--startvm", idStr.c_str(),
7610 NULL, /* For "--separate". */
7611 NULL, /* For "--sup-startup-log". */
7612 NULL
7613 };
7614 unsigned iArg = 5;
7615 if (fSeparate)
7616 apszArgs[iArg++] = "--separate";
7617 apszArgs[iArg++] = pszSupStartupLogArg;
7618
7619 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7620 }
7621#else /* !VBOX_WITH_VBOXSDL */
7622 if (0)
7623 ;
7624#endif /* !VBOX_WITH_VBOXSDL */
7625
7626 else
7627
7628#ifdef VBOX_WITH_HEADLESS
7629 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7630 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7631 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7632 )
7633 {
7634 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7635 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7636 * and a VM works even if the server has not been installed.
7637 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7638 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7639 * differently in 4.0 and 3.x.
7640 */
7641 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7642 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7643 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7644
7645 Utf8Str idStr = mData->mUuid.toString();
7646 const char *apszArgs[] =
7647 {
7648 szPath,
7649 "--comment", mUserData->s.strName.c_str(),
7650 "--startvm", idStr.c_str(),
7651 "--vrde", "config",
7652 NULL, /* For "--capture". */
7653 NULL, /* For "--sup-startup-log". */
7654 NULL
7655 };
7656 unsigned iArg = 7;
7657 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7658 apszArgs[iArg++] = "--capture";
7659 apszArgs[iArg++] = pszSupStartupLogArg;
7660
7661# ifdef RT_OS_WINDOWS
7662 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7663# else
7664 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7665# endif
7666 }
7667#else /* !VBOX_WITH_HEADLESS */
7668 if (0)
7669 ;
7670#endif /* !VBOX_WITH_HEADLESS */
7671 else
7672 {
7673 RTEnvDestroy(env);
7674 return setError(E_INVALIDARG,
7675 tr("Invalid frontend name: '%s'"),
7676 strFrontend.c_str());
7677 }
7678
7679 RTEnvDestroy(env);
7680
7681 if (RT_FAILURE(vrc))
7682 return setError(VBOX_E_IPRT_ERROR,
7683 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7684 mUserData->s.strName.c_str(), vrc);
7685
7686 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7687
7688 if (!fSeparate)
7689 {
7690 /*
7691 * Note that we don't release the lock here before calling the client,
7692 * because it doesn't need to call us back if called with a NULL argument.
7693 * Releasing the lock here is dangerous because we didn't prepare the
7694 * launch data yet, but the client we've just started may happen to be
7695 * too fast and call LockMachine() that will fail (because of PID, etc.),
7696 * so that the Machine will never get out of the Spawning session state.
7697 */
7698
7699 /* inform the session that it will be a remote one */
7700 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7701#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7702 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7703#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7704 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7705#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7706 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7707
7708 if (FAILED(rc))
7709 {
7710 /* restore the session state */
7711 mData->mSession.mState = SessionState_Unlocked;
7712 alock.release();
7713 mParent->i_addProcessToReap(pid);
7714 /* The failure may occur w/o any error info (from RPC), so provide one */
7715 return setError(VBOX_E_VM_ERROR,
7716 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7717 }
7718
7719 /* attach launch data to the machine */
7720 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7721 mData->mSession.mRemoteControls.push_back(aControl);
7722 mData->mSession.mProgress = aProgress;
7723 mData->mSession.mPID = pid;
7724 mData->mSession.mState = SessionState_Spawning;
7725 mData->mSession.mType = strFrontend;
7726 }
7727 else
7728 {
7729 /* For separate UI process we declare the launch as completed instantly, as the
7730 * actual headless VM start may or may not come. No point in remembering anything
7731 * yet, as what matters for us is when the headless VM gets started. */
7732 aProgress->i_notifyComplete(S_OK);
7733 }
7734
7735 alock.release();
7736 mParent->i_addProcessToReap(pid);
7737
7738 LogFlowThisFuncLeave();
7739 return S_OK;
7740}
7741
7742/**
7743 * Returns @c true if the given session machine instance has an open direct
7744 * session (and optionally also for direct sessions which are closing) and
7745 * returns the session control machine instance if so.
7746 *
7747 * Note that when the method returns @c false, the arguments remain unchanged.
7748 *
7749 * @param aMachine Session machine object.
7750 * @param aControl Direct session control object (optional).
7751 * @param aAllowClosing If true then additionally a session which is currently
7752 * being closed will also be allowed.
7753 *
7754 * @note locks this object for reading.
7755 */
7756bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7757 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7758 bool aAllowClosing /*= false*/)
7759{
7760 AutoLimitedCaller autoCaller(this);
7761 AssertComRCReturn(autoCaller.rc(), false);
7762
7763 /* just return false for inaccessible machines */
7764 if (getObjectState().getState() != ObjectState::Ready)
7765 return false;
7766
7767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7768
7769 if ( ( mData->mSession.mState == SessionState_Locked
7770 && mData->mSession.mLockType == LockType_VM)
7771 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7772 )
7773 {
7774 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7775
7776 aMachine = mData->mSession.mMachine;
7777
7778 if (aControl != NULL)
7779 *aControl = mData->mSession.mDirectControl;
7780
7781 return true;
7782 }
7783
7784 return false;
7785}
7786
7787/**
7788 * Returns @c true if the given machine has an spawning direct session.
7789 *
7790 * @note locks this object for reading.
7791 */
7792bool Machine::i_isSessionSpawning()
7793{
7794 AutoLimitedCaller autoCaller(this);
7795 AssertComRCReturn(autoCaller.rc(), false);
7796
7797 /* just return false for inaccessible machines */
7798 if (getObjectState().getState() != ObjectState::Ready)
7799 return false;
7800
7801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7802
7803 if (mData->mSession.mState == SessionState_Spawning)
7804 return true;
7805
7806 return false;
7807}
7808
7809/**
7810 * Called from the client watcher thread to check for unexpected client process
7811 * death during Session_Spawning state (e.g. before it successfully opened a
7812 * direct session).
7813 *
7814 * On Win32 and on OS/2, this method is called only when we've got the
7815 * direct client's process termination notification, so it always returns @c
7816 * true.
7817 *
7818 * On other platforms, this method returns @c true if the client process is
7819 * terminated and @c false if it's still alive.
7820 *
7821 * @note Locks this object for writing.
7822 */
7823bool Machine::i_checkForSpawnFailure()
7824{
7825 AutoCaller autoCaller(this);
7826 if (!autoCaller.isOk())
7827 {
7828 /* nothing to do */
7829 LogFlowThisFunc(("Already uninitialized!\n"));
7830 return true;
7831 }
7832
7833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7834
7835 if (mData->mSession.mState != SessionState_Spawning)
7836 {
7837 /* nothing to do */
7838 LogFlowThisFunc(("Not spawning any more!\n"));
7839 return true;
7840 }
7841
7842 HRESULT rc = S_OK;
7843
7844 /* PID not yet initialized, skip check. */
7845 if (mData->mSession.mPID == NIL_RTPROCESS)
7846 return false;
7847
7848 RTPROCSTATUS status;
7849 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7850
7851 if (vrc != VERR_PROCESS_RUNNING)
7852 {
7853 Utf8Str strExtraInfo;
7854
7855#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7856 /* If the startup logfile exists and is of non-zero length, tell the
7857 user to look there for more details to encourage them to attach it
7858 when reporting startup issues. */
7859 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7860 uint64_t cbStartupLogFile = 0;
7861 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7862 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7863 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7864#endif
7865
7866 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7867 rc = setError(E_FAIL,
7868 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7869 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7870 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7871 rc = setError(E_FAIL,
7872 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7873 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7874 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7875 rc = setError(E_FAIL,
7876 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7877 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7878 else
7879 rc = setError(E_FAIL,
7880 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7881 i_getName().c_str(), vrc, strExtraInfo.c_str());
7882 }
7883
7884 if (FAILED(rc))
7885 {
7886 /* Close the remote session, remove the remote control from the list
7887 * and reset session state to Closed (@note keep the code in sync with
7888 * the relevant part in LockMachine()). */
7889
7890 Assert(mData->mSession.mRemoteControls.size() == 1);
7891 if (mData->mSession.mRemoteControls.size() == 1)
7892 {
7893 ErrorInfoKeeper eik;
7894 mData->mSession.mRemoteControls.front()->Uninitialize();
7895 }
7896
7897 mData->mSession.mRemoteControls.clear();
7898 mData->mSession.mState = SessionState_Unlocked;
7899
7900 /* finalize the progress after setting the state */
7901 if (!mData->mSession.mProgress.isNull())
7902 {
7903 mData->mSession.mProgress->notifyComplete(rc);
7904 mData->mSession.mProgress.setNull();
7905 }
7906
7907 mData->mSession.mPID = NIL_RTPROCESS;
7908
7909 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7910 return true;
7911 }
7912
7913 return false;
7914}
7915
7916/**
7917 * Checks whether the machine can be registered. If so, commits and saves
7918 * all settings.
7919 *
7920 * @note Must be called from mParent's write lock. Locks this object and
7921 * children for writing.
7922 */
7923HRESULT Machine::i_prepareRegister()
7924{
7925 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7926
7927 AutoLimitedCaller autoCaller(this);
7928 AssertComRCReturnRC(autoCaller.rc());
7929
7930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7931
7932 /* wait for state dependents to drop to zero */
7933 i_ensureNoStateDependencies();
7934
7935 if (!mData->mAccessible)
7936 return setError(VBOX_E_INVALID_OBJECT_STATE,
7937 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7938 mUserData->s.strName.c_str(),
7939 mData->mUuid.toString().c_str());
7940
7941 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7942
7943 if (mData->mRegistered)
7944 return setError(VBOX_E_INVALID_OBJECT_STATE,
7945 tr("The machine '%s' with UUID {%s} is already registered"),
7946 mUserData->s.strName.c_str(),
7947 mData->mUuid.toString().c_str());
7948
7949 HRESULT rc = S_OK;
7950
7951 // Ensure the settings are saved. If we are going to be registered and
7952 // no config file exists yet, create it by calling i_saveSettings() too.
7953 if ( (mData->flModifications)
7954 || (!mData->pMachineConfigFile->fileExists())
7955 )
7956 {
7957 rc = i_saveSettings(NULL);
7958 // no need to check whether VirtualBox.xml needs saving too since
7959 // we can't have a machine XML file rename pending
7960 if (FAILED(rc)) return rc;
7961 }
7962
7963 /* more config checking goes here */
7964
7965 if (SUCCEEDED(rc))
7966 {
7967 /* we may have had implicit modifications we want to fix on success */
7968 i_commit();
7969
7970 mData->mRegistered = true;
7971 }
7972 else
7973 {
7974 /* we may have had implicit modifications we want to cancel on failure*/
7975 i_rollback(false /* aNotify */);
7976 }
7977
7978 return rc;
7979}
7980
7981/**
7982 * Increases the number of objects dependent on the machine state or on the
7983 * registered state. Guarantees that these two states will not change at least
7984 * until #releaseStateDependency() is called.
7985 *
7986 * Depending on the @a aDepType value, additional state checks may be made.
7987 * These checks will set extended error info on failure. See
7988 * #checkStateDependency() for more info.
7989 *
7990 * If this method returns a failure, the dependency is not added and the caller
7991 * is not allowed to rely on any particular machine state or registration state
7992 * value and may return the failed result code to the upper level.
7993 *
7994 * @param aDepType Dependency type to add.
7995 * @param aState Current machine state (NULL if not interested).
7996 * @param aRegistered Current registered state (NULL if not interested).
7997 *
7998 * @note Locks this object for writing.
7999 */
8000HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8001 MachineState_T *aState /* = NULL */,
8002 BOOL *aRegistered /* = NULL */)
8003{
8004 AutoCaller autoCaller(this);
8005 AssertComRCReturnRC(autoCaller.rc());
8006
8007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8008
8009 HRESULT rc = i_checkStateDependency(aDepType);
8010 if (FAILED(rc)) return rc;
8011
8012 {
8013 if (mData->mMachineStateChangePending != 0)
8014 {
8015 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8016 * drop to zero so don't add more. It may make sense to wait a bit
8017 * and retry before reporting an error (since the pending state
8018 * transition should be really quick) but let's just assert for
8019 * now to see if it ever happens on practice. */
8020
8021 AssertFailed();
8022
8023 return setError(E_ACCESSDENIED,
8024 tr("Machine state change is in progress. Please retry the operation later."));
8025 }
8026
8027 ++mData->mMachineStateDeps;
8028 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8029 }
8030
8031 if (aState)
8032 *aState = mData->mMachineState;
8033 if (aRegistered)
8034 *aRegistered = mData->mRegistered;
8035
8036 return S_OK;
8037}
8038
8039/**
8040 * Decreases the number of objects dependent on the machine state.
8041 * Must always complete the #addStateDependency() call after the state
8042 * dependency is no more necessary.
8043 */
8044void Machine::i_releaseStateDependency()
8045{
8046 AutoCaller autoCaller(this);
8047 AssertComRCReturnVoid(autoCaller.rc());
8048
8049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8050
8051 /* releaseStateDependency() w/o addStateDependency()? */
8052 AssertReturnVoid(mData->mMachineStateDeps != 0);
8053 -- mData->mMachineStateDeps;
8054
8055 if (mData->mMachineStateDeps == 0)
8056 {
8057 /* inform i_ensureNoStateDependencies() that there are no more deps */
8058 if (mData->mMachineStateChangePending != 0)
8059 {
8060 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8061 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8062 }
8063 }
8064}
8065
8066Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8067{
8068 /* start with nothing found */
8069 Utf8Str strResult("");
8070
8071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8072
8073 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8074 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8075 // found:
8076 strResult = it->second; // source is a Utf8Str
8077
8078 return strResult;
8079}
8080
8081// protected methods
8082/////////////////////////////////////////////////////////////////////////////
8083
8084/**
8085 * Performs machine state checks based on the @a aDepType value. If a check
8086 * fails, this method will set extended error info, otherwise it will return
8087 * S_OK. It is supposed, that on failure, the caller will immediately return
8088 * the return value of this method to the upper level.
8089 *
8090 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8091 *
8092 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8093 * current state of this machine object allows to change settings of the
8094 * machine (i.e. the machine is not registered, or registered but not running
8095 * and not saved). It is useful to call this method from Machine setters
8096 * before performing any change.
8097 *
8098 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8099 * as for MutableStateDep except that if the machine is saved, S_OK is also
8100 * returned. This is useful in setters which allow changing machine
8101 * properties when it is in the saved state.
8102 *
8103 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8104 * if the current state of this machine object allows to change runtime
8105 * changeable settings of the machine (i.e. the machine is not registered, or
8106 * registered but either running or not running and not saved). It is useful
8107 * to call this method from Machine setters before performing any changes to
8108 * runtime changeable settings.
8109 *
8110 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8111 * the same as for MutableOrRunningStateDep except that if the machine is
8112 * saved, S_OK is also returned. This is useful in setters which allow
8113 * changing runtime and saved state changeable machine properties.
8114 *
8115 * @param aDepType Dependency type to check.
8116 *
8117 * @note Non Machine based classes should use #addStateDependency() and
8118 * #releaseStateDependency() methods or the smart AutoStateDependency
8119 * template.
8120 *
8121 * @note This method must be called from under this object's read or write
8122 * lock.
8123 */
8124HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8125{
8126 switch (aDepType)
8127 {
8128 case AnyStateDep:
8129 {
8130 break;
8131 }
8132 case MutableStateDep:
8133 {
8134 if ( mData->mRegistered
8135 && ( !i_isSessionMachine()
8136 || ( mData->mMachineState != MachineState_Aborted
8137 && mData->mMachineState != MachineState_Teleported
8138 && mData->mMachineState != MachineState_PoweredOff
8139 )
8140 )
8141 )
8142 return setError(VBOX_E_INVALID_VM_STATE,
8143 tr("The machine is not mutable (state is %s)"),
8144 Global::stringifyMachineState(mData->mMachineState));
8145 break;
8146 }
8147 case MutableOrSavedStateDep:
8148 {
8149 if ( mData->mRegistered
8150 && ( !i_isSessionMachine()
8151 || ( mData->mMachineState != MachineState_Aborted
8152 && mData->mMachineState != MachineState_Teleported
8153 && mData->mMachineState != MachineState_Saved
8154 && mData->mMachineState != MachineState_PoweredOff
8155 )
8156 )
8157 )
8158 return setError(VBOX_E_INVALID_VM_STATE,
8159 tr("The machine is not mutable (state is %s)"),
8160 Global::stringifyMachineState(mData->mMachineState));
8161 break;
8162 }
8163 case MutableOrRunningStateDep:
8164 {
8165 if ( mData->mRegistered
8166 && ( !i_isSessionMachine()
8167 || ( mData->mMachineState != MachineState_Aborted
8168 && mData->mMachineState != MachineState_Teleported
8169 && mData->mMachineState != MachineState_PoweredOff
8170 && !Global::IsOnline(mData->mMachineState)
8171 )
8172 )
8173 )
8174 return setError(VBOX_E_INVALID_VM_STATE,
8175 tr("The machine is not mutable (state is %s)"),
8176 Global::stringifyMachineState(mData->mMachineState));
8177 break;
8178 }
8179 case MutableOrSavedOrRunningStateDep:
8180 {
8181 if ( mData->mRegistered
8182 && ( !i_isSessionMachine()
8183 || ( mData->mMachineState != MachineState_Aborted
8184 && mData->mMachineState != MachineState_Teleported
8185 && mData->mMachineState != MachineState_Saved
8186 && mData->mMachineState != MachineState_PoweredOff
8187 && !Global::IsOnline(mData->mMachineState)
8188 )
8189 )
8190 )
8191 return setError(VBOX_E_INVALID_VM_STATE,
8192 tr("The machine is not mutable (state is %s)"),
8193 Global::stringifyMachineState(mData->mMachineState));
8194 break;
8195 }
8196 }
8197
8198 return S_OK;
8199}
8200
8201/**
8202 * Helper to initialize all associated child objects and allocate data
8203 * structures.
8204 *
8205 * This method must be called as a part of the object's initialization procedure
8206 * (usually done in the #init() method).
8207 *
8208 * @note Must be called only from #init() or from #registeredInit().
8209 */
8210HRESULT Machine::initDataAndChildObjects()
8211{
8212 AutoCaller autoCaller(this);
8213 AssertComRCReturnRC(autoCaller.rc());
8214 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8215 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8216
8217 AssertReturn(!mData->mAccessible, E_FAIL);
8218
8219 /* allocate data structures */
8220 mSSData.allocate();
8221 mUserData.allocate();
8222 mHWData.allocate();
8223 mMediaData.allocate();
8224 mStorageControllers.allocate();
8225 mUSBControllers.allocate();
8226
8227 /* initialize mOSTypeId */
8228 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8229
8230 /* create associated BIOS settings object */
8231 unconst(mBIOSSettings).createObject();
8232 mBIOSSettings->init(this);
8233
8234 /* create an associated VRDE object (default is disabled) */
8235 unconst(mVRDEServer).createObject();
8236 mVRDEServer->init(this);
8237
8238 /* create associated serial port objects */
8239 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8240 {
8241 unconst(mSerialPorts[slot]).createObject();
8242 mSerialPorts[slot]->init(this, slot);
8243 }
8244
8245 /* create associated parallel port objects */
8246 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8247 {
8248 unconst(mParallelPorts[slot]).createObject();
8249 mParallelPorts[slot]->init(this, slot);
8250 }
8251
8252 /* create the audio adapter object (always present, default is disabled) */
8253 unconst(mAudioAdapter).createObject();
8254 mAudioAdapter->init(this);
8255
8256 /* create the USB device filters object (always present) */
8257 unconst(mUSBDeviceFilters).createObject();
8258 mUSBDeviceFilters->init(this);
8259
8260 /* create associated network adapter objects */
8261 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8262 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8263 {
8264 unconst(mNetworkAdapters[slot]).createObject();
8265 mNetworkAdapters[slot]->init(this, slot);
8266 }
8267
8268 /* create the bandwidth control */
8269 unconst(mBandwidthControl).createObject();
8270 mBandwidthControl->init(this);
8271
8272 return S_OK;
8273}
8274
8275/**
8276 * Helper to uninitialize all associated child objects and to free all data
8277 * structures.
8278 *
8279 * This method must be called as a part of the object's uninitialization
8280 * procedure (usually done in the #uninit() method).
8281 *
8282 * @note Must be called only from #uninit() or from #registeredInit().
8283 */
8284void Machine::uninitDataAndChildObjects()
8285{
8286 AutoCaller autoCaller(this);
8287 AssertComRCReturnVoid(autoCaller.rc());
8288 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8289 || getObjectState().getState() == ObjectState::Limited);
8290
8291 /* tell all our other child objects we've been uninitialized */
8292 if (mBandwidthControl)
8293 {
8294 mBandwidthControl->uninit();
8295 unconst(mBandwidthControl).setNull();
8296 }
8297
8298 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8299 {
8300 if (mNetworkAdapters[slot])
8301 {
8302 mNetworkAdapters[slot]->uninit();
8303 unconst(mNetworkAdapters[slot]).setNull();
8304 }
8305 }
8306
8307 if (mUSBDeviceFilters)
8308 {
8309 mUSBDeviceFilters->uninit();
8310 unconst(mUSBDeviceFilters).setNull();
8311 }
8312
8313 if (mAudioAdapter)
8314 {
8315 mAudioAdapter->uninit();
8316 unconst(mAudioAdapter).setNull();
8317 }
8318
8319 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8320 {
8321 if (mParallelPorts[slot])
8322 {
8323 mParallelPorts[slot]->uninit();
8324 unconst(mParallelPorts[slot]).setNull();
8325 }
8326 }
8327
8328 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8329 {
8330 if (mSerialPorts[slot])
8331 {
8332 mSerialPorts[slot]->uninit();
8333 unconst(mSerialPorts[slot]).setNull();
8334 }
8335 }
8336
8337 if (mVRDEServer)
8338 {
8339 mVRDEServer->uninit();
8340 unconst(mVRDEServer).setNull();
8341 }
8342
8343 if (mBIOSSettings)
8344 {
8345 mBIOSSettings->uninit();
8346 unconst(mBIOSSettings).setNull();
8347 }
8348
8349 /* Deassociate media (only when a real Machine or a SnapshotMachine
8350 * instance is uninitialized; SessionMachine instances refer to real
8351 * Machine media). This is necessary for a clean re-initialization of
8352 * the VM after successfully re-checking the accessibility state. Note
8353 * that in case of normal Machine or SnapshotMachine uninitialization (as
8354 * a result of unregistering or deleting the snapshot), outdated media
8355 * attachments will already be uninitialized and deleted, so this
8356 * code will not affect them. */
8357 if ( !!mMediaData
8358 && (!i_isSessionMachine())
8359 )
8360 {
8361 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8362 it != mMediaData->mAttachments.end();
8363 ++it)
8364 {
8365 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8366 if (pMedium.isNull())
8367 continue;
8368 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8369 AssertComRC(rc);
8370 }
8371 }
8372
8373 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8374 {
8375 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8376 if (mData->mFirstSnapshot)
8377 {
8378 // snapshots tree is protected by machine write lock; strictly
8379 // this isn't necessary here since we're deleting the entire
8380 // machine, but otherwise we assert in Snapshot::uninit()
8381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8382 mData->mFirstSnapshot->uninit();
8383 mData->mFirstSnapshot.setNull();
8384 }
8385
8386 mData->mCurrentSnapshot.setNull();
8387 }
8388
8389 /* free data structures (the essential mData structure is not freed here
8390 * since it may be still in use) */
8391 mMediaData.free();
8392 mStorageControllers.free();
8393 mUSBControllers.free();
8394 mHWData.free();
8395 mUserData.free();
8396 mSSData.free();
8397}
8398
8399/**
8400 * Returns a pointer to the Machine object for this machine that acts like a
8401 * parent for complex machine data objects such as shared folders, etc.
8402 *
8403 * For primary Machine objects and for SnapshotMachine objects, returns this
8404 * object's pointer itself. For SessionMachine objects, returns the peer
8405 * (primary) machine pointer.
8406 */
8407Machine* Machine::i_getMachine()
8408{
8409 if (i_isSessionMachine())
8410 return (Machine*)mPeer;
8411 return this;
8412}
8413
8414/**
8415 * Makes sure that there are no machine state dependents. If necessary, waits
8416 * for the number of dependents to drop to zero.
8417 *
8418 * Make sure this method is called from under this object's write lock to
8419 * guarantee that no new dependents may be added when this method returns
8420 * control to the caller.
8421 *
8422 * @note Locks this object for writing. The lock will be released while waiting
8423 * (if necessary).
8424 *
8425 * @warning To be used only in methods that change the machine state!
8426 */
8427void Machine::i_ensureNoStateDependencies()
8428{
8429 AssertReturnVoid(isWriteLockOnCurrentThread());
8430
8431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8432
8433 /* Wait for all state dependents if necessary */
8434 if (mData->mMachineStateDeps != 0)
8435 {
8436 /* lazy semaphore creation */
8437 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8438 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8439
8440 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8441 mData->mMachineStateDeps));
8442
8443 ++mData->mMachineStateChangePending;
8444
8445 /* reset the semaphore before waiting, the last dependent will signal
8446 * it */
8447 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8448
8449 alock.release();
8450
8451 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8452
8453 alock.acquire();
8454
8455 -- mData->mMachineStateChangePending;
8456 }
8457}
8458
8459/**
8460 * Changes the machine state and informs callbacks.
8461 *
8462 * This method is not intended to fail so it either returns S_OK or asserts (and
8463 * returns a failure).
8464 *
8465 * @note Locks this object for writing.
8466 */
8467HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8468{
8469 LogFlowThisFuncEnter();
8470 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8471 Assert(aMachineState != MachineState_Null);
8472
8473 AutoCaller autoCaller(this);
8474 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8475
8476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8477
8478 /* wait for state dependents to drop to zero */
8479 i_ensureNoStateDependencies();
8480
8481 MachineState_T const enmOldState = mData->mMachineState;
8482 if (enmOldState != aMachineState)
8483 {
8484 mData->mMachineState = aMachineState;
8485 RTTimeNow(&mData->mLastStateChange);
8486
8487#ifdef VBOX_WITH_DTRACE_R3_MAIN
8488 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8489#endif
8490 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8491 }
8492
8493 LogFlowThisFuncLeave();
8494 return S_OK;
8495}
8496
8497/**
8498 * Searches for a shared folder with the given logical name
8499 * in the collection of shared folders.
8500 *
8501 * @param aName logical name of the shared folder
8502 * @param aSharedFolder where to return the found object
8503 * @param aSetError whether to set the error info if the folder is
8504 * not found
8505 * @return
8506 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8507 *
8508 * @note
8509 * must be called from under the object's lock!
8510 */
8511HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8512 ComObjPtr<SharedFolder> &aSharedFolder,
8513 bool aSetError /* = false */)
8514{
8515 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8516 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8517 it != mHWData->mSharedFolders.end();
8518 ++it)
8519 {
8520 SharedFolder *pSF = *it;
8521 AutoCaller autoCaller(pSF);
8522 if (pSF->i_getName() == aName)
8523 {
8524 aSharedFolder = pSF;
8525 rc = S_OK;
8526 break;
8527 }
8528 }
8529
8530 if (aSetError && FAILED(rc))
8531 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8532
8533 return rc;
8534}
8535
8536/**
8537 * Initializes all machine instance data from the given settings structures
8538 * from XML. The exception is the machine UUID which needs special handling
8539 * depending on the caller's use case, so the caller needs to set that herself.
8540 *
8541 * This gets called in several contexts during machine initialization:
8542 *
8543 * -- When machine XML exists on disk already and needs to be loaded into memory,
8544 * for example, from registeredInit() to load all registered machines on
8545 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8546 * attached to the machine should be part of some media registry already.
8547 *
8548 * -- During OVF import, when a machine config has been constructed from an
8549 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8550 * ensure that the media listed as attachments in the config (which have
8551 * been imported from the OVF) receive the correct registry ID.
8552 *
8553 * -- During VM cloning.
8554 *
8555 * @param config Machine settings from XML.
8556 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8557 * for each attached medium in the config.
8558 * @return
8559 */
8560HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8561 const Guid *puuidRegistry)
8562{
8563 // copy name, description, OS type, teleporter, UTC etc.
8564 mUserData->s = config.machineUserData;
8565
8566 // Decode the Icon overide data from config userdata and set onto Machine.
8567 #define DECODE_STR_MAX _1M
8568 const char* pszStr = config.machineUserData.ovIcon.c_str();
8569 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8570 if (cbOut > DECODE_STR_MAX)
8571 return setError(E_FAIL,
8572 tr("Icon Data too long.'%d' > '%d'"),
8573 cbOut,
8574 DECODE_STR_MAX);
8575 mUserData->mIcon.resize(cbOut);
8576 int vrc = VINF_SUCCESS;
8577 if (cbOut)
8578 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8579 if (RT_FAILURE(vrc))
8580 {
8581 mUserData->mIcon.resize(0);
8582 return setError(E_FAIL,
8583 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8584 pszStr,
8585 vrc);
8586 }
8587
8588 // look up the object by Id to check it is valid
8589 ComPtr<IGuestOSType> guestOSType;
8590 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8591 guestOSType.asOutParam());
8592 if (FAILED(rc)) return rc;
8593
8594 // stateFile (optional)
8595 if (config.strStateFile.isEmpty())
8596 mSSData->strStateFilePath.setNull();
8597 else
8598 {
8599 Utf8Str stateFilePathFull(config.strStateFile);
8600 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8601 if (RT_FAILURE(vrc))
8602 return setError(E_FAIL,
8603 tr("Invalid saved state file path '%s' (%Rrc)"),
8604 config.strStateFile.c_str(),
8605 vrc);
8606 mSSData->strStateFilePath = stateFilePathFull;
8607 }
8608
8609 // snapshot folder needs special processing so set it again
8610 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8611 if (FAILED(rc)) return rc;
8612
8613 /* Copy the extra data items (Not in any case config is already the same as
8614 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8615 * make sure the extra data map is copied). */
8616 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8617
8618 /* currentStateModified (optional, default is true) */
8619 mData->mCurrentStateModified = config.fCurrentStateModified;
8620
8621 mData->mLastStateChange = config.timeLastStateChange;
8622
8623 /*
8624 * note: all mUserData members must be assigned prior this point because
8625 * we need to commit changes in order to let mUserData be shared by all
8626 * snapshot machine instances.
8627 */
8628 mUserData.commitCopy();
8629
8630 // machine registry, if present (must be loaded before snapshots)
8631 if (config.canHaveOwnMediaRegistry())
8632 {
8633 // determine machine folder
8634 Utf8Str strMachineFolder = i_getSettingsFileFull();
8635 strMachineFolder.stripFilename();
8636 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8637 config.mediaRegistry,
8638 strMachineFolder);
8639 if (FAILED(rc)) return rc;
8640 }
8641
8642 /* Snapshot node (optional) */
8643 size_t cRootSnapshots;
8644 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8645 {
8646 // there must be only one root snapshot
8647 Assert(cRootSnapshots == 1);
8648
8649 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8650
8651 rc = i_loadSnapshot(snap,
8652 config.uuidCurrentSnapshot,
8653 NULL); // no parent == first snapshot
8654 if (FAILED(rc)) return rc;
8655 }
8656
8657 // hardware data
8658 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8659 if (FAILED(rc)) return rc;
8660
8661 // load storage controllers
8662 rc = i_loadStorageControllers(config.storageMachine,
8663 puuidRegistry,
8664 NULL /* puuidSnapshot */);
8665 if (FAILED(rc)) return rc;
8666
8667 /*
8668 * NOTE: the assignment below must be the last thing to do,
8669 * otherwise it will be not possible to change the settings
8670 * somewhere in the code above because all setters will be
8671 * blocked by i_checkStateDependency(MutableStateDep).
8672 */
8673
8674 /* set the machine state to Aborted or Saved when appropriate */
8675 if (config.fAborted)
8676 {
8677 mSSData->strStateFilePath.setNull();
8678
8679 /* no need to use i_setMachineState() during init() */
8680 mData->mMachineState = MachineState_Aborted;
8681 }
8682 else if (!mSSData->strStateFilePath.isEmpty())
8683 {
8684 /* no need to use i_setMachineState() during init() */
8685 mData->mMachineState = MachineState_Saved;
8686 }
8687
8688 // after loading settings, we are no longer different from the XML on disk
8689 mData->flModifications = 0;
8690
8691 return S_OK;
8692}
8693
8694/**
8695 * Recursively loads all snapshots starting from the given.
8696 *
8697 * @param aNode <Snapshot> node.
8698 * @param aCurSnapshotId Current snapshot ID from the settings file.
8699 * @param aParentSnapshot Parent snapshot.
8700 */
8701HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8702 const Guid &aCurSnapshotId,
8703 Snapshot *aParentSnapshot)
8704{
8705 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8706 AssertReturn(!i_isSessionMachine(), E_FAIL);
8707
8708 HRESULT rc = S_OK;
8709
8710 Utf8Str strStateFile;
8711 if (!data.strStateFile.isEmpty())
8712 {
8713 /* optional */
8714 strStateFile = data.strStateFile;
8715 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8716 if (RT_FAILURE(vrc))
8717 return setError(E_FAIL,
8718 tr("Invalid saved state file path '%s' (%Rrc)"),
8719 strStateFile.c_str(),
8720 vrc);
8721 }
8722
8723 /* create a snapshot machine object */
8724 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8725 pSnapshotMachine.createObject();
8726 rc = pSnapshotMachine->initFromSettings(this,
8727 data.hardware,
8728 &data.debugging,
8729 &data.autostart,
8730 data.storage,
8731 data.uuid.ref(),
8732 strStateFile);
8733 if (FAILED(rc)) return rc;
8734
8735 /* create a snapshot object */
8736 ComObjPtr<Snapshot> pSnapshot;
8737 pSnapshot.createObject();
8738 /* initialize the snapshot */
8739 rc = pSnapshot->init(mParent, // VirtualBox object
8740 data.uuid,
8741 data.strName,
8742 data.strDescription,
8743 data.timestamp,
8744 pSnapshotMachine,
8745 aParentSnapshot);
8746 if (FAILED(rc)) return rc;
8747
8748 /* memorize the first snapshot if necessary */
8749 if (!mData->mFirstSnapshot)
8750 mData->mFirstSnapshot = pSnapshot;
8751
8752 /* memorize the current snapshot when appropriate */
8753 if ( !mData->mCurrentSnapshot
8754 && pSnapshot->i_getId() == aCurSnapshotId
8755 )
8756 mData->mCurrentSnapshot = pSnapshot;
8757
8758 // now create the children
8759 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8760 it != data.llChildSnapshots.end();
8761 ++it)
8762 {
8763 const settings::Snapshot &childData = *it;
8764 // recurse
8765 rc = i_loadSnapshot(childData,
8766 aCurSnapshotId,
8767 pSnapshot); // parent = the one we created above
8768 if (FAILED(rc)) return rc;
8769 }
8770
8771 return rc;
8772}
8773
8774/**
8775 * Loads settings into mHWData.
8776 *
8777 * @param data Reference to the hardware settings.
8778 * @param pDbg Pointer to the debugging settings.
8779 * @param pAutostart Pointer to the autostart settings.
8780 */
8781HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8782 const settings::Autostart *pAutostart)
8783{
8784 AssertReturn(!i_isSessionMachine(), E_FAIL);
8785
8786 HRESULT rc = S_OK;
8787
8788 try
8789 {
8790 /* The hardware version attribute (optional). */
8791 mHWData->mHWVersion = data.strVersion;
8792 mHWData->mHardwareUUID = data.uuid;
8793
8794 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8795 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8796 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8797 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8798 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8799 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8800 mHWData->mPAEEnabled = data.fPAE;
8801 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8802 mHWData->mLongMode = data.enmLongMode;
8803 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8804 mHWData->mCPUCount = data.cCPUs;
8805 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8806 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8807
8808 // cpu
8809 if (mHWData->mCPUHotPlugEnabled)
8810 {
8811 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8812 it != data.llCpus.end();
8813 ++it)
8814 {
8815 const settings::Cpu &cpu = *it;
8816
8817 mHWData->mCPUAttached[cpu.ulId] = true;
8818 }
8819 }
8820
8821 // cpuid leafs
8822 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8823 it != data.llCpuIdLeafs.end();
8824 ++it)
8825 {
8826 const settings::CpuIdLeaf &leaf = *it;
8827
8828 switch (leaf.ulId)
8829 {
8830 case 0x0:
8831 case 0x1:
8832 case 0x2:
8833 case 0x3:
8834 case 0x4:
8835 case 0x5:
8836 case 0x6:
8837 case 0x7:
8838 case 0x8:
8839 case 0x9:
8840 case 0xA:
8841 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8842 break;
8843
8844 case 0x80000000:
8845 case 0x80000001:
8846 case 0x80000002:
8847 case 0x80000003:
8848 case 0x80000004:
8849 case 0x80000005:
8850 case 0x80000006:
8851 case 0x80000007:
8852 case 0x80000008:
8853 case 0x80000009:
8854 case 0x8000000A:
8855 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8856 break;
8857
8858 default:
8859 /* just ignore */
8860 break;
8861 }
8862 }
8863
8864 mHWData->mMemorySize = data.ulMemorySizeMB;
8865 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8866
8867 // boot order
8868 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8869 {
8870 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8871 if (it == data.mapBootOrder.end())
8872 mHWData->mBootOrder[i] = DeviceType_Null;
8873 else
8874 mHWData->mBootOrder[i] = it->second;
8875 }
8876
8877 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8878 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8879 mHWData->mMonitorCount = data.cMonitors;
8880 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8881 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8882 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8883 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8884 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8885 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8886 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8887 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8888 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8889 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8890 if (!data.strVideoCaptureFile.isEmpty())
8891 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8892 else
8893 mHWData->mVideoCaptureFile.setNull();
8894 mHWData->mFirmwareType = data.firmwareType;
8895 mHWData->mPointingHIDType = data.pointingHIDType;
8896 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8897 mHWData->mChipsetType = data.chipsetType;
8898 mHWData->mParavirtProvider = data.paravirtProvider;
8899 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8900 mHWData->mHPETEnabled = data.fHPETEnabled;
8901
8902 /* VRDEServer */
8903 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8904 if (FAILED(rc)) return rc;
8905
8906 /* BIOS */
8907 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8908 if (FAILED(rc)) return rc;
8909
8910 // Bandwidth control (must come before network adapters)
8911 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8912 if (FAILED(rc)) return rc;
8913
8914 /* Shared folders */
8915 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8916 it != data.usbSettings.llUSBControllers.end();
8917 ++it)
8918 {
8919 const settings::USBController &settingsCtrl = *it;
8920 ComObjPtr<USBController> newCtrl;
8921
8922 newCtrl.createObject();
8923 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8924 mUSBControllers->push_back(newCtrl);
8925 }
8926
8927 /* USB device filters */
8928 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8929 if (FAILED(rc)) return rc;
8930
8931 // network adapters
8932 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8933 size_t oldCount = mNetworkAdapters.size();
8934 if (newCount > oldCount)
8935 {
8936 mNetworkAdapters.resize(newCount);
8937 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8938 {
8939 unconst(mNetworkAdapters[slot]).createObject();
8940 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8941 }
8942 }
8943 else if (newCount < oldCount)
8944 mNetworkAdapters.resize(newCount);
8945 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8946 it != data.llNetworkAdapters.end();
8947 ++it)
8948 {
8949 const settings::NetworkAdapter &nic = *it;
8950
8951 /* slot unicity is guaranteed by XML Schema */
8952 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8953 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8954 if (FAILED(rc)) return rc;
8955 }
8956
8957 // serial ports
8958 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8959 it != data.llSerialPorts.end();
8960 ++it)
8961 {
8962 const settings::SerialPort &s = *it;
8963
8964 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8965 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8966 if (FAILED(rc)) return rc;
8967 }
8968
8969 // parallel ports (optional)
8970 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8971 it != data.llParallelPorts.end();
8972 ++it)
8973 {
8974 const settings::ParallelPort &p = *it;
8975
8976 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8977 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8978 if (FAILED(rc)) return rc;
8979 }
8980
8981 /* AudioAdapter */
8982 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8983 if (FAILED(rc)) return rc;
8984
8985 /* Shared folders */
8986 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8987 it != data.llSharedFolders.end();
8988 ++it)
8989 {
8990 const settings::SharedFolder &sf = *it;
8991
8992 ComObjPtr<SharedFolder> sharedFolder;
8993 /* Check for double entries. Not allowed! */
8994 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8995 if (SUCCEEDED(rc))
8996 return setError(VBOX_E_OBJECT_IN_USE,
8997 tr("Shared folder named '%s' already exists"),
8998 sf.strName.c_str());
8999
9000 /* Create the new shared folder. Don't break on error. This will be
9001 * reported when the machine starts. */
9002 sharedFolder.createObject();
9003 rc = sharedFolder->init(i_getMachine(),
9004 sf.strName,
9005 sf.strHostPath,
9006 RT_BOOL(sf.fWritable),
9007 RT_BOOL(sf.fAutoMount),
9008 false /* fFailOnError */);
9009 if (FAILED(rc)) return rc;
9010 mHWData->mSharedFolders.push_back(sharedFolder);
9011 }
9012
9013 // Clipboard
9014 mHWData->mClipboardMode = data.clipboardMode;
9015
9016 // drag'n'drop
9017 mHWData->mDnDMode = data.dndMode;
9018
9019 // guest settings
9020 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9021
9022 // IO settings
9023 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9024 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9025
9026 // Host PCI devices
9027 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9028 it != data.pciAttachments.end();
9029 ++it)
9030 {
9031 const settings::HostPCIDeviceAttachment &hpda = *it;
9032 ComObjPtr<PCIDeviceAttachment> pda;
9033
9034 pda.createObject();
9035 pda->i_loadSettings(this, hpda);
9036 mHWData->mPCIDeviceAssignments.push_back(pda);
9037 }
9038
9039 /*
9040 * (The following isn't really real hardware, but it lives in HWData
9041 * for reasons of convenience.)
9042 */
9043
9044#ifdef VBOX_WITH_GUEST_PROPS
9045 /* Guest properties (optional) */
9046 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9047 it != data.llGuestProperties.end();
9048 ++it)
9049 {
9050 const settings::GuestProperty &prop = *it;
9051 uint32_t fFlags = guestProp::NILFLAG;
9052 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9053 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9054 mHWData->mGuestProperties[prop.strName] = property;
9055 }
9056
9057 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9058#endif /* VBOX_WITH_GUEST_PROPS defined */
9059
9060 rc = i_loadDebugging(pDbg);
9061 if (FAILED(rc))
9062 return rc;
9063
9064 mHWData->mAutostart = *pAutostart;
9065
9066 /* default frontend */
9067 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9068 }
9069 catch(std::bad_alloc &)
9070 {
9071 return E_OUTOFMEMORY;
9072 }
9073
9074 AssertComRC(rc);
9075 return rc;
9076}
9077
9078/**
9079 * Called from Machine::loadHardware() to load the debugging settings of the
9080 * machine.
9081 *
9082 * @param pDbg Pointer to the settings.
9083 */
9084HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9085{
9086 mHWData->mDebugging = *pDbg;
9087 /* no more processing currently required, this will probably change. */
9088 return S_OK;
9089}
9090
9091/**
9092 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9093 *
9094 * @param data
9095 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9096 * @param puuidSnapshot
9097 * @return
9098 */
9099HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9100 const Guid *puuidRegistry,
9101 const Guid *puuidSnapshot)
9102{
9103 AssertReturn(!i_isSessionMachine(), E_FAIL);
9104
9105 HRESULT rc = S_OK;
9106
9107 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9108 it != data.llStorageControllers.end();
9109 ++it)
9110 {
9111 const settings::StorageController &ctlData = *it;
9112
9113 ComObjPtr<StorageController> pCtl;
9114 /* Try to find one with the name first. */
9115 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9116 if (SUCCEEDED(rc))
9117 return setError(VBOX_E_OBJECT_IN_USE,
9118 tr("Storage controller named '%s' already exists"),
9119 ctlData.strName.c_str());
9120
9121 pCtl.createObject();
9122 rc = pCtl->init(this,
9123 ctlData.strName,
9124 ctlData.storageBus,
9125 ctlData.ulInstance,
9126 ctlData.fBootable);
9127 if (FAILED(rc)) return rc;
9128
9129 mStorageControllers->push_back(pCtl);
9130
9131 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9132 if (FAILED(rc)) return rc;
9133
9134 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9135 if (FAILED(rc)) return rc;
9136
9137 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9138 if (FAILED(rc)) return rc;
9139
9140 /* Set IDE emulation settings (only for AHCI controller). */
9141 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9142 {
9143 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9144 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9145 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9146 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9147 )
9148 return rc;
9149 }
9150
9151 /* Load the attached devices now. */
9152 rc = i_loadStorageDevices(pCtl,
9153 ctlData,
9154 puuidRegistry,
9155 puuidSnapshot);
9156 if (FAILED(rc)) return rc;
9157 }
9158
9159 return S_OK;
9160}
9161
9162/**
9163 * Called from i_loadStorageControllers for a controller's devices.
9164 *
9165 * @param aStorageController
9166 * @param data
9167 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9168 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9169 * @return
9170 */
9171HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9172 const settings::StorageController &data,
9173 const Guid *puuidRegistry,
9174 const Guid *puuidSnapshot)
9175{
9176 HRESULT rc = S_OK;
9177
9178 /* paranoia: detect duplicate attachments */
9179 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9180 it != data.llAttachedDevices.end();
9181 ++it)
9182 {
9183 const settings::AttachedDevice &ad = *it;
9184
9185 for (settings::AttachedDevicesList::const_iterator it2 = it;
9186 it2 != data.llAttachedDevices.end();
9187 ++it2)
9188 {
9189 if (it == it2)
9190 continue;
9191
9192 const settings::AttachedDevice &ad2 = *it2;
9193
9194 if ( ad.lPort == ad2.lPort
9195 && ad.lDevice == ad2.lDevice)
9196 {
9197 return setError(E_FAIL,
9198 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9199 aStorageController->i_getName().c_str(),
9200 ad.lPort,
9201 ad.lDevice,
9202 mUserData->s.strName.c_str());
9203 }
9204 }
9205 }
9206
9207 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9208 it != data.llAttachedDevices.end();
9209 ++it)
9210 {
9211 const settings::AttachedDevice &dev = *it;
9212 ComObjPtr<Medium> medium;
9213
9214 switch (dev.deviceType)
9215 {
9216 case DeviceType_Floppy:
9217 case DeviceType_DVD:
9218 if (dev.strHostDriveSrc.isNotEmpty())
9219 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9220 false /* fRefresh */, medium);
9221 else
9222 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9223 dev.uuid,
9224 false /* fRefresh */,
9225 false /* aSetError */,
9226 medium);
9227 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9228 // This is not an error. The host drive or UUID might have vanished, so just go
9229 // ahead without this removeable medium attachment
9230 rc = S_OK;
9231 break;
9232
9233 case DeviceType_HardDisk:
9234 {
9235 /* find a hard disk by UUID */
9236 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9237 if (FAILED(rc))
9238 {
9239 if (i_isSnapshotMachine())
9240 {
9241 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9242 // so the user knows that the bad disk is in a snapshot somewhere
9243 com::ErrorInfo info;
9244 return setError(E_FAIL,
9245 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9246 puuidSnapshot->raw(),
9247 info.getText().raw());
9248 }
9249 else
9250 return rc;
9251 }
9252
9253 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9254
9255 if (medium->i_getType() == MediumType_Immutable)
9256 {
9257 if (i_isSnapshotMachine())
9258 return setError(E_FAIL,
9259 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9260 "of the virtual machine '%s' ('%s')"),
9261 medium->i_getLocationFull().c_str(),
9262 dev.uuid.raw(),
9263 puuidSnapshot->raw(),
9264 mUserData->s.strName.c_str(),
9265 mData->m_strConfigFileFull.c_str());
9266
9267 return setError(E_FAIL,
9268 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9269 medium->i_getLocationFull().c_str(),
9270 dev.uuid.raw(),
9271 mUserData->s.strName.c_str(),
9272 mData->m_strConfigFileFull.c_str());
9273 }
9274
9275 if (medium->i_getType() == MediumType_MultiAttach)
9276 {
9277 if (i_isSnapshotMachine())
9278 return setError(E_FAIL,
9279 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9280 "of the virtual machine '%s' ('%s')"),
9281 medium->i_getLocationFull().c_str(),
9282 dev.uuid.raw(),
9283 puuidSnapshot->raw(),
9284 mUserData->s.strName.c_str(),
9285 mData->m_strConfigFileFull.c_str());
9286
9287 return setError(E_FAIL,
9288 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9289 medium->i_getLocationFull().c_str(),
9290 dev.uuid.raw(),
9291 mUserData->s.strName.c_str(),
9292 mData->m_strConfigFileFull.c_str());
9293 }
9294
9295 if ( !i_isSnapshotMachine()
9296 && medium->i_getChildren().size() != 0
9297 )
9298 return setError(E_FAIL,
9299 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9300 "because it has %d differencing child hard disks"),
9301 medium->i_getLocationFull().c_str(),
9302 dev.uuid.raw(),
9303 mUserData->s.strName.c_str(),
9304 mData->m_strConfigFileFull.c_str(),
9305 medium->i_getChildren().size());
9306
9307 if (i_findAttachment(mMediaData->mAttachments,
9308 medium))
9309 return setError(E_FAIL,
9310 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9311 medium->i_getLocationFull().c_str(),
9312 dev.uuid.raw(),
9313 mUserData->s.strName.c_str(),
9314 mData->m_strConfigFileFull.c_str());
9315
9316 break;
9317 }
9318
9319 default:
9320 return setError(E_FAIL,
9321 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9322 medium->i_getLocationFull().c_str(),
9323 mUserData->s.strName.c_str(),
9324 mData->m_strConfigFileFull.c_str());
9325 }
9326
9327 if (FAILED(rc))
9328 break;
9329
9330 /* Bandwidth groups are loaded at this point. */
9331 ComObjPtr<BandwidthGroup> pBwGroup;
9332
9333 if (!dev.strBwGroup.isEmpty())
9334 {
9335 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9336 if (FAILED(rc))
9337 return setError(E_FAIL,
9338 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9339 medium->i_getLocationFull().c_str(),
9340 dev.strBwGroup.c_str(),
9341 mUserData->s.strName.c_str(),
9342 mData->m_strConfigFileFull.c_str());
9343 pBwGroup->i_reference();
9344 }
9345
9346 const Bstr controllerName = aStorageController->i_getName();
9347 ComObjPtr<MediumAttachment> pAttachment;
9348 pAttachment.createObject();
9349 rc = pAttachment->init(this,
9350 medium,
9351 controllerName,
9352 dev.lPort,
9353 dev.lDevice,
9354 dev.deviceType,
9355 false,
9356 dev.fPassThrough,
9357 dev.fTempEject,
9358 dev.fNonRotational,
9359 dev.fDiscard,
9360 dev.fHotPluggable,
9361 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9362 if (FAILED(rc)) break;
9363
9364 /* associate the medium with this machine and snapshot */
9365 if (!medium.isNull())
9366 {
9367 AutoCaller medCaller(medium);
9368 if (FAILED(medCaller.rc())) return medCaller.rc();
9369 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9370
9371 if (i_isSnapshotMachine())
9372 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9373 else
9374 rc = medium->i_addBackReference(mData->mUuid);
9375 /* If the medium->addBackReference fails it sets an appropriate
9376 * error message, so no need to do any guesswork here. */
9377
9378 if (puuidRegistry)
9379 // caller wants registry ID to be set on all attached media (OVF import case)
9380 medium->i_addRegistry(*puuidRegistry);
9381 }
9382
9383 if (FAILED(rc))
9384 break;
9385
9386 /* back up mMediaData to let registeredInit() properly rollback on failure
9387 * (= limited accessibility) */
9388 i_setModified(IsModified_Storage);
9389 mMediaData.backup();
9390 mMediaData->mAttachments.push_back(pAttachment);
9391 }
9392
9393 return rc;
9394}
9395
9396/**
9397 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9398 *
9399 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9400 * @param aSnapshot where to return the found snapshot
9401 * @param aSetError true to set extended error info on failure
9402 */
9403HRESULT Machine::i_findSnapshotById(const Guid &aId,
9404 ComObjPtr<Snapshot> &aSnapshot,
9405 bool aSetError /* = false */)
9406{
9407 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9408
9409 if (!mData->mFirstSnapshot)
9410 {
9411 if (aSetError)
9412 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9413 return E_FAIL;
9414 }
9415
9416 if (aId.isZero())
9417 aSnapshot = mData->mFirstSnapshot;
9418 else
9419 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9420
9421 if (!aSnapshot)
9422 {
9423 if (aSetError)
9424 return setError(E_FAIL,
9425 tr("Could not find a snapshot with UUID {%s}"),
9426 aId.toString().c_str());
9427 return E_FAIL;
9428 }
9429
9430 return S_OK;
9431}
9432
9433/**
9434 * Returns the snapshot with the given name or fails of no such snapshot.
9435 *
9436 * @param aName snapshot name to find
9437 * @param aSnapshot where to return the found snapshot
9438 * @param aSetError true to set extended error info on failure
9439 */
9440HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9441 ComObjPtr<Snapshot> &aSnapshot,
9442 bool aSetError /* = false */)
9443{
9444 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9445
9446 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9447
9448 if (!mData->mFirstSnapshot)
9449 {
9450 if (aSetError)
9451 return setError(VBOX_E_OBJECT_NOT_FOUND,
9452 tr("This machine does not have any snapshots"));
9453 return VBOX_E_OBJECT_NOT_FOUND;
9454 }
9455
9456 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9457
9458 if (!aSnapshot)
9459 {
9460 if (aSetError)
9461 return setError(VBOX_E_OBJECT_NOT_FOUND,
9462 tr("Could not find a snapshot named '%s'"), strName.c_str());
9463 return VBOX_E_OBJECT_NOT_FOUND;
9464 }
9465
9466 return S_OK;
9467}
9468
9469/**
9470 * Returns a storage controller object with the given name.
9471 *
9472 * @param aName storage controller name to find
9473 * @param aStorageController where to return the found storage controller
9474 * @param aSetError true to set extended error info on failure
9475 */
9476HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9477 ComObjPtr<StorageController> &aStorageController,
9478 bool aSetError /* = false */)
9479{
9480 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9481
9482 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9483 it != mStorageControllers->end();
9484 ++it)
9485 {
9486 if ((*it)->i_getName() == aName)
9487 {
9488 aStorageController = (*it);
9489 return S_OK;
9490 }
9491 }
9492
9493 if (aSetError)
9494 return setError(VBOX_E_OBJECT_NOT_FOUND,
9495 tr("Could not find a storage controller named '%s'"),
9496 aName.c_str());
9497 return VBOX_E_OBJECT_NOT_FOUND;
9498}
9499
9500/**
9501 * Returns a USB controller object with the given name.
9502 *
9503 * @param aName USB controller name to find
9504 * @param aUSBController where to return the found USB controller
9505 * @param aSetError true to set extended error info on failure
9506 */
9507HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9508 ComObjPtr<USBController> &aUSBController,
9509 bool aSetError /* = false */)
9510{
9511 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9512
9513 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9514 it != mUSBControllers->end();
9515 ++it)
9516 {
9517 if ((*it)->i_getName() == aName)
9518 {
9519 aUSBController = (*it);
9520 return S_OK;
9521 }
9522 }
9523
9524 if (aSetError)
9525 return setError(VBOX_E_OBJECT_NOT_FOUND,
9526 tr("Could not find a storage controller named '%s'"),
9527 aName.c_str());
9528 return VBOX_E_OBJECT_NOT_FOUND;
9529}
9530
9531/**
9532 * Returns the number of USB controller instance of the given type.
9533 *
9534 * @param enmType USB controller type.
9535 */
9536ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9537{
9538 ULONG cCtrls = 0;
9539
9540 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9541 it != mUSBControllers->end();
9542 ++it)
9543 {
9544 if ((*it)->i_getControllerType() == enmType)
9545 cCtrls++;
9546 }
9547
9548 return cCtrls;
9549}
9550
9551HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9552 MediaData::AttachmentList &atts)
9553{
9554 AutoCaller autoCaller(this);
9555 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9556
9557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9558
9559 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9560 it != mMediaData->mAttachments.end();
9561 ++it)
9562 {
9563 const ComObjPtr<MediumAttachment> &pAtt = *it;
9564 // should never happen, but deal with NULL pointers in the list.
9565 AssertStmt(!pAtt.isNull(), continue);
9566
9567 // getControllerName() needs caller+read lock
9568 AutoCaller autoAttCaller(pAtt);
9569 if (FAILED(autoAttCaller.rc()))
9570 {
9571 atts.clear();
9572 return autoAttCaller.rc();
9573 }
9574 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9575
9576 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9577 atts.push_back(pAtt);
9578 }
9579
9580 return S_OK;
9581}
9582
9583
9584/**
9585 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9586 * file if the machine name was changed and about creating a new settings file
9587 * if this is a new machine.
9588 *
9589 * @note Must be never called directly but only from #saveSettings().
9590 */
9591HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9592{
9593 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9594
9595 HRESULT rc = S_OK;
9596
9597 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9598
9599 /// @todo need to handle primary group change, too
9600
9601 /* attempt to rename the settings file if machine name is changed */
9602 if ( mUserData->s.fNameSync
9603 && mUserData.isBackedUp()
9604 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9605 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9606 )
9607 {
9608 bool dirRenamed = false;
9609 bool fileRenamed = false;
9610
9611 Utf8Str configFile, newConfigFile;
9612 Utf8Str configFilePrev, newConfigFilePrev;
9613 Utf8Str configDir, newConfigDir;
9614
9615 do
9616 {
9617 int vrc = VINF_SUCCESS;
9618
9619 Utf8Str name = mUserData.backedUpData()->s.strName;
9620 Utf8Str newName = mUserData->s.strName;
9621 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9622 if (group == "/")
9623 group.setNull();
9624 Utf8Str newGroup = mUserData->s.llGroups.front();
9625 if (newGroup == "/")
9626 newGroup.setNull();
9627
9628 configFile = mData->m_strConfigFileFull;
9629
9630 /* first, rename the directory if it matches the group and machine name */
9631 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9632 group.c_str(), RTPATH_DELIMITER, name.c_str());
9633 /** @todo hack, make somehow use of ComposeMachineFilename */
9634 if (mUserData->s.fDirectoryIncludesUUID)
9635 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9636 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9637 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9638 /** @todo hack, make somehow use of ComposeMachineFilename */
9639 if (mUserData->s.fDirectoryIncludesUUID)
9640 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9641 configDir = configFile;
9642 configDir.stripFilename();
9643 newConfigDir = configDir;
9644 if ( configDir.length() >= groupPlusName.length()
9645 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9646 groupPlusName.c_str()))
9647 {
9648 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9649 Utf8Str newConfigBaseDir(newConfigDir);
9650 newConfigDir.append(newGroupPlusName);
9651 /* consistency: use \ if appropriate on the platform */
9652 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9653 /* new dir and old dir cannot be equal here because of 'if'
9654 * above and because name != newName */
9655 Assert(configDir != newConfigDir);
9656 if (!fSettingsFileIsNew)
9657 {
9658 /* perform real rename only if the machine is not new */
9659 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9660 if ( vrc == VERR_FILE_NOT_FOUND
9661 || vrc == VERR_PATH_NOT_FOUND)
9662 {
9663 /* create the parent directory, then retry renaming */
9664 Utf8Str parent(newConfigDir);
9665 parent.stripFilename();
9666 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9667 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9668 }
9669 if (RT_FAILURE(vrc))
9670 {
9671 rc = setError(E_FAIL,
9672 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9673 configDir.c_str(),
9674 newConfigDir.c_str(),
9675 vrc);
9676 break;
9677 }
9678 /* delete subdirectories which are no longer needed */
9679 Utf8Str dir(configDir);
9680 dir.stripFilename();
9681 while (dir != newConfigBaseDir && dir != ".")
9682 {
9683 vrc = RTDirRemove(dir.c_str());
9684 if (RT_FAILURE(vrc))
9685 break;
9686 dir.stripFilename();
9687 }
9688 dirRenamed = true;
9689 }
9690 }
9691
9692 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9693 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9694
9695 /* then try to rename the settings file itself */
9696 if (newConfigFile != configFile)
9697 {
9698 /* get the path to old settings file in renamed directory */
9699 configFile = Utf8StrFmt("%s%c%s",
9700 newConfigDir.c_str(),
9701 RTPATH_DELIMITER,
9702 RTPathFilename(configFile.c_str()));
9703 if (!fSettingsFileIsNew)
9704 {
9705 /* perform real rename only if the machine is not new */
9706 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9707 if (RT_FAILURE(vrc))
9708 {
9709 rc = setError(E_FAIL,
9710 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9711 configFile.c_str(),
9712 newConfigFile.c_str(),
9713 vrc);
9714 break;
9715 }
9716 fileRenamed = true;
9717 configFilePrev = configFile;
9718 configFilePrev += "-prev";
9719 newConfigFilePrev = newConfigFile;
9720 newConfigFilePrev += "-prev";
9721 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9722 }
9723 }
9724
9725 // update m_strConfigFileFull amd mConfigFile
9726 mData->m_strConfigFileFull = newConfigFile;
9727 // compute the relative path too
9728 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9729
9730 // store the old and new so that VirtualBox::i_saveSettings() can update
9731 // the media registry
9732 if ( mData->mRegistered
9733 && (configDir != newConfigDir || configFile != newConfigFile))
9734 {
9735 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9736
9737 if (pfNeedsGlobalSaveSettings)
9738 *pfNeedsGlobalSaveSettings = true;
9739 }
9740
9741 // in the saved state file path, replace the old directory with the new directory
9742 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9743 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9744
9745 // and do the same thing for the saved state file paths of all the online snapshots
9746 if (mData->mFirstSnapshot)
9747 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9748 newConfigDir.c_str());
9749 }
9750 while (0);
9751
9752 if (FAILED(rc))
9753 {
9754 /* silently try to rename everything back */
9755 if (fileRenamed)
9756 {
9757 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9758 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9759 }
9760 if (dirRenamed)
9761 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9762 }
9763
9764 if (FAILED(rc)) return rc;
9765 }
9766
9767 if (fSettingsFileIsNew)
9768 {
9769 /* create a virgin config file */
9770 int vrc = VINF_SUCCESS;
9771
9772 /* ensure the settings directory exists */
9773 Utf8Str path(mData->m_strConfigFileFull);
9774 path.stripFilename();
9775 if (!RTDirExists(path.c_str()))
9776 {
9777 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9778 if (RT_FAILURE(vrc))
9779 {
9780 return setError(E_FAIL,
9781 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9782 path.c_str(),
9783 vrc);
9784 }
9785 }
9786
9787 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9788 path = Utf8Str(mData->m_strConfigFileFull);
9789 RTFILE f = NIL_RTFILE;
9790 vrc = RTFileOpen(&f, path.c_str(),
9791 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9792 if (RT_FAILURE(vrc))
9793 return setError(E_FAIL,
9794 tr("Could not create the settings file '%s' (%Rrc)"),
9795 path.c_str(),
9796 vrc);
9797 RTFileClose(f);
9798 }
9799
9800 return rc;
9801}
9802
9803/**
9804 * Saves and commits machine data, user data and hardware data.
9805 *
9806 * Note that on failure, the data remains uncommitted.
9807 *
9808 * @a aFlags may combine the following flags:
9809 *
9810 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9811 * Used when saving settings after an operation that makes them 100%
9812 * correspond to the settings from the current snapshot.
9813 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9814 * #isReallyModified() returns false. This is necessary for cases when we
9815 * change machine data directly, not through the backup()/commit() mechanism.
9816 * - SaveS_Force: settings will be saved without doing a deep compare of the
9817 * settings structures. This is used when this is called because snapshots
9818 * have changed to avoid the overhead of the deep compare.
9819 *
9820 * @note Must be called from under this object's write lock. Locks children for
9821 * writing.
9822 *
9823 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9824 * initialized to false and that will be set to true by this function if
9825 * the caller must invoke VirtualBox::i_saveSettings() because the global
9826 * settings have changed. This will happen if a machine rename has been
9827 * saved and the global machine and media registries will therefore need
9828 * updating.
9829 */
9830HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9831 int aFlags /*= 0*/)
9832{
9833 LogFlowThisFuncEnter();
9834
9835 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9836
9837 /* make sure child objects are unable to modify the settings while we are
9838 * saving them */
9839 i_ensureNoStateDependencies();
9840
9841 AssertReturn(!i_isSnapshotMachine(),
9842 E_FAIL);
9843
9844 HRESULT rc = S_OK;
9845 bool fNeedsWrite = false;
9846
9847 /* First, prepare to save settings. It will care about renaming the
9848 * settings directory and file if the machine name was changed and about
9849 * creating a new settings file if this is a new machine. */
9850 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9851 if (FAILED(rc)) return rc;
9852
9853 // keep a pointer to the current settings structures
9854 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9855 settings::MachineConfigFile *pNewConfig = NULL;
9856
9857 try
9858 {
9859 // make a fresh one to have everyone write stuff into
9860 pNewConfig = new settings::MachineConfigFile(NULL);
9861 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9862
9863 // now go and copy all the settings data from COM to the settings structures
9864 // (this calles i_saveSettings() on all the COM objects in the machine)
9865 i_copyMachineDataToSettings(*pNewConfig);
9866
9867 if (aFlags & SaveS_ResetCurStateModified)
9868 {
9869 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9870 mData->mCurrentStateModified = FALSE;
9871 fNeedsWrite = true; // always, no need to compare
9872 }
9873 else if (aFlags & SaveS_Force)
9874 {
9875 fNeedsWrite = true; // always, no need to compare
9876 }
9877 else
9878 {
9879 if (!mData->mCurrentStateModified)
9880 {
9881 // do a deep compare of the settings that we just saved with the settings
9882 // previously stored in the config file; this invokes MachineConfigFile::operator==
9883 // which does a deep compare of all the settings, which is expensive but less expensive
9884 // than writing out XML in vain
9885 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9886
9887 // could still be modified if any settings changed
9888 mData->mCurrentStateModified = fAnySettingsChanged;
9889
9890 fNeedsWrite = fAnySettingsChanged;
9891 }
9892 else
9893 fNeedsWrite = true;
9894 }
9895
9896 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9897
9898 if (fNeedsWrite)
9899 // now spit it all out!
9900 pNewConfig->write(mData->m_strConfigFileFull);
9901
9902 mData->pMachineConfigFile = pNewConfig;
9903 delete pOldConfig;
9904 i_commit();
9905
9906 // after saving settings, we are no longer different from the XML on disk
9907 mData->flModifications = 0;
9908 }
9909 catch (HRESULT err)
9910 {
9911 // we assume that error info is set by the thrower
9912 rc = err;
9913
9914 // restore old config
9915 delete pNewConfig;
9916 mData->pMachineConfigFile = pOldConfig;
9917 }
9918 catch (...)
9919 {
9920 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9921 }
9922
9923 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9924 {
9925 /* Fire the data change event, even on failure (since we've already
9926 * committed all data). This is done only for SessionMachines because
9927 * mutable Machine instances are always not registered (i.e. private
9928 * to the client process that creates them) and thus don't need to
9929 * inform callbacks. */
9930 if (i_isSessionMachine())
9931 mParent->i_onMachineDataChange(mData->mUuid);
9932 }
9933
9934 LogFlowThisFunc(("rc=%08X\n", rc));
9935 LogFlowThisFuncLeave();
9936 return rc;
9937}
9938
9939/**
9940 * Implementation for saving the machine settings into the given
9941 * settings::MachineConfigFile instance. This copies machine extradata
9942 * from the previous machine config file in the instance data, if any.
9943 *
9944 * This gets called from two locations:
9945 *
9946 * -- Machine::i_saveSettings(), during the regular XML writing;
9947 *
9948 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9949 * exported to OVF and we write the VirtualBox proprietary XML
9950 * into a <vbox:Machine> tag.
9951 *
9952 * This routine fills all the fields in there, including snapshots, *except*
9953 * for the following:
9954 *
9955 * -- fCurrentStateModified. There is some special logic associated with that.
9956 *
9957 * The caller can then call MachineConfigFile::write() or do something else
9958 * with it.
9959 *
9960 * Caller must hold the machine lock!
9961 *
9962 * This throws XML errors and HRESULT, so the caller must have a catch block!
9963 */
9964void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9965{
9966 // deep copy extradata
9967 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9968
9969 config.uuid = mData->mUuid;
9970
9971 // copy name, description, OS type, teleport, UTC etc.
9972 config.machineUserData = mUserData->s;
9973
9974 // Encode the Icon Override data from Machine and store on config userdata.
9975 std::vector<BYTE> iconByte;
9976 getIcon(iconByte);
9977 ssize_t cbData = iconByte.size();
9978 if (cbData > 0)
9979 {
9980 ssize_t cchOut = RTBase64EncodedLength(cbData);
9981 Utf8Str strIconData;
9982 strIconData.reserve(cchOut+1);
9983 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9984 strIconData.mutableRaw(), strIconData.capacity(),
9985 NULL);
9986 if (RT_FAILURE(vrc))
9987 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9988 strIconData.jolt();
9989 config.machineUserData.ovIcon = strIconData;
9990 }
9991 else
9992 config.machineUserData.ovIcon.setNull();
9993
9994 if ( mData->mMachineState == MachineState_Saved
9995 || mData->mMachineState == MachineState_Restoring
9996 // when doing certain snapshot operations we may or may not have
9997 // a saved state in the current state, so keep everything as is
9998 || ( ( mData->mMachineState == MachineState_Snapshotting
9999 || mData->mMachineState == MachineState_DeletingSnapshot)
10000 && (!mSSData->strStateFilePath.isEmpty())
10001 )
10002 )
10003 {
10004 Assert(!mSSData->strStateFilePath.isEmpty());
10005 /* try to make the file name relative to the settings file dir */
10006 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10007 }
10008 else
10009 {
10010 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10011 config.strStateFile.setNull();
10012 }
10013
10014 if (mData->mCurrentSnapshot)
10015 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10016 else
10017 config.uuidCurrentSnapshot.clear();
10018
10019 config.timeLastStateChange = mData->mLastStateChange;
10020 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10021 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10022
10023 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10024 if (FAILED(rc)) throw rc;
10025
10026 rc = i_saveStorageControllers(config.storageMachine);
10027 if (FAILED(rc)) throw rc;
10028
10029 // save machine's media registry if this is VirtualBox 4.0 or later
10030 if (config.canHaveOwnMediaRegistry())
10031 {
10032 // determine machine folder
10033 Utf8Str strMachineFolder = i_getSettingsFileFull();
10034 strMachineFolder.stripFilename();
10035 mParent->i_saveMediaRegistry(config.mediaRegistry,
10036 i_getId(), // only media with registry ID == machine UUID
10037 strMachineFolder);
10038 // this throws HRESULT
10039 }
10040
10041 // save snapshots
10042 rc = i_saveAllSnapshots(config);
10043 if (FAILED(rc)) throw rc;
10044}
10045
10046/**
10047 * Saves all snapshots of the machine into the given machine config file. Called
10048 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10049 * @param config
10050 * @return
10051 */
10052HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10053{
10054 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10055
10056 HRESULT rc = S_OK;
10057
10058 try
10059 {
10060 config.llFirstSnapshot.clear();
10061
10062 if (mData->mFirstSnapshot)
10063 {
10064 // the settings use a list for "the first snapshot"
10065 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10066
10067 // get reference to the snapshot on the list and work on that
10068 // element straight in the list to avoid excessive copying later
10069 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10070 if (FAILED(rc)) throw rc;
10071 }
10072
10073// if (mType == IsSessionMachine)
10074// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10075
10076 }
10077 catch (HRESULT err)
10078 {
10079 /* we assume that error info is set by the thrower */
10080 rc = err;
10081 }
10082 catch (...)
10083 {
10084 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10085 }
10086
10087 return rc;
10088}
10089
10090/**
10091 * Saves the VM hardware configuration. It is assumed that the
10092 * given node is empty.
10093 *
10094 * @param data Reference to the settings object for the hardware config.
10095 * @param pDbg Pointer to the settings object for the debugging config
10096 * which happens to live in mHWData.
10097 * @param pAutostart Pointer to the settings object for the autostart config
10098 * which happens to live in mHWData.
10099 */
10100HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10101 settings::Autostart *pAutostart)
10102{
10103 HRESULT rc = S_OK;
10104
10105 try
10106 {
10107 /* The hardware version attribute (optional).
10108 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10109 if ( mHWData->mHWVersion == "1"
10110 && mSSData->strStateFilePath.isEmpty()
10111 )
10112 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10113 other point needs to be found where this can be done. */
10114
10115 data.strVersion = mHWData->mHWVersion;
10116 data.uuid = mHWData->mHardwareUUID;
10117
10118 // CPU
10119 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10120 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10121 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10122 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10123 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10124 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10125 data.fPAE = !!mHWData->mPAEEnabled;
10126 data.enmLongMode = mHWData->mLongMode;
10127 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10128 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10129
10130 /* Standard and Extended CPUID leafs. */
10131 data.llCpuIdLeafs.clear();
10132 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10133 {
10134 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10135 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10136 }
10137 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10138 {
10139 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10140 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10141 }
10142
10143 data.cCPUs = mHWData->mCPUCount;
10144 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10145 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10146
10147 data.llCpus.clear();
10148 if (data.fCpuHotPlug)
10149 {
10150 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10151 {
10152 if (mHWData->mCPUAttached[idx])
10153 {
10154 settings::Cpu cpu;
10155 cpu.ulId = idx;
10156 data.llCpus.push_back(cpu);
10157 }
10158 }
10159 }
10160
10161 // memory
10162 data.ulMemorySizeMB = mHWData->mMemorySize;
10163 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10164
10165 // firmware
10166 data.firmwareType = mHWData->mFirmwareType;
10167
10168 // HID
10169 data.pointingHIDType = mHWData->mPointingHIDType;
10170 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10171
10172 // chipset
10173 data.chipsetType = mHWData->mChipsetType;
10174
10175 // paravirt
10176 data.paravirtProvider = mHWData->mParavirtProvider;
10177
10178
10179 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10180
10181 // HPET
10182 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10183
10184 // boot order
10185 data.mapBootOrder.clear();
10186 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10187 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10188
10189 // display
10190 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10191 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10192 data.cMonitors = mHWData->mMonitorCount;
10193 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10194 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10195 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10196 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10197 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10198 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10199 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10200 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10201 {
10202 if (mHWData->maVideoCaptureScreens[i])
10203 ASMBitSet(&data.u64VideoCaptureScreens, i);
10204 else
10205 ASMBitClear(&data.u64VideoCaptureScreens, i);
10206 }
10207 /* store relative video capture file if possible */
10208 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10209
10210 /* VRDEServer settings (optional) */
10211 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10212 if (FAILED(rc)) throw rc;
10213
10214 /* BIOS (required) */
10215 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10216 if (FAILED(rc)) throw rc;
10217
10218 /* USB Controller (required) */
10219 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10220 {
10221 ComObjPtr<USBController> ctrl = *it;
10222 settings::USBController settingsCtrl;
10223
10224 settingsCtrl.strName = ctrl->i_getName();
10225 settingsCtrl.enmType = ctrl->i_getControllerType();
10226
10227 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10228 }
10229
10230 /* USB device filters (required) */
10231 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10232 if (FAILED(rc)) throw rc;
10233
10234 /* Network adapters (required) */
10235 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10236 data.llNetworkAdapters.clear();
10237 /* Write out only the nominal number of network adapters for this
10238 * chipset type. Since Machine::commit() hasn't been called there
10239 * may be extra NIC settings in the vector. */
10240 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10241 {
10242 settings::NetworkAdapter nic;
10243 nic.ulSlot = (uint32_t)slot;
10244 /* paranoia check... must not be NULL, but must not crash either. */
10245 if (mNetworkAdapters[slot])
10246 {
10247 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10248 if (FAILED(rc)) throw rc;
10249
10250 data.llNetworkAdapters.push_back(nic);
10251 }
10252 }
10253
10254 /* Serial ports */
10255 data.llSerialPorts.clear();
10256 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10257 {
10258 settings::SerialPort s;
10259 s.ulSlot = slot;
10260 rc = mSerialPorts[slot]->i_saveSettings(s);
10261 if (FAILED(rc)) return rc;
10262
10263 data.llSerialPorts.push_back(s);
10264 }
10265
10266 /* Parallel ports */
10267 data.llParallelPorts.clear();
10268 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10269 {
10270 settings::ParallelPort p;
10271 p.ulSlot = slot;
10272 rc = mParallelPorts[slot]->i_saveSettings(p);
10273 if (FAILED(rc)) return rc;
10274
10275 data.llParallelPorts.push_back(p);
10276 }
10277
10278 /* Audio adapter */
10279 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10280 if (FAILED(rc)) return rc;
10281
10282 /* Shared folders */
10283 data.llSharedFolders.clear();
10284 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10285 it != mHWData->mSharedFolders.end();
10286 ++it)
10287 {
10288 SharedFolder *pSF = *it;
10289 AutoCaller sfCaller(pSF);
10290 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10291 settings::SharedFolder sf;
10292 sf.strName = pSF->i_getName();
10293 sf.strHostPath = pSF->i_getHostPath();
10294 sf.fWritable = !!pSF->i_isWritable();
10295 sf.fAutoMount = !!pSF->i_isAutoMounted();
10296
10297 data.llSharedFolders.push_back(sf);
10298 }
10299
10300 // clipboard
10301 data.clipboardMode = mHWData->mClipboardMode;
10302
10303 // drag'n'drop
10304 data.dndMode = mHWData->mDnDMode;
10305
10306 /* Guest */
10307 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10308
10309 // IO settings
10310 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10311 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10312
10313 /* BandwidthControl (required) */
10314 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10315 if (FAILED(rc)) throw rc;
10316
10317 /* Host PCI devices */
10318 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10319 it != mHWData->mPCIDeviceAssignments.end();
10320 ++it)
10321 {
10322 ComObjPtr<PCIDeviceAttachment> pda = *it;
10323 settings::HostPCIDeviceAttachment hpda;
10324
10325 rc = pda->i_saveSettings(hpda);
10326 if (FAILED(rc)) throw rc;
10327
10328 data.pciAttachments.push_back(hpda);
10329 }
10330
10331
10332 // guest properties
10333 data.llGuestProperties.clear();
10334#ifdef VBOX_WITH_GUEST_PROPS
10335 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10336 it != mHWData->mGuestProperties.end();
10337 ++it)
10338 {
10339 HWData::GuestProperty property = it->second;
10340
10341 /* Remove transient guest properties at shutdown unless we
10342 * are saving state */
10343 if ( ( mData->mMachineState == MachineState_PoweredOff
10344 || mData->mMachineState == MachineState_Aborted
10345 || mData->mMachineState == MachineState_Teleported)
10346 && ( property.mFlags & guestProp::TRANSIENT
10347 || property.mFlags & guestProp::TRANSRESET))
10348 continue;
10349 settings::GuestProperty prop;
10350 prop.strName = it->first;
10351 prop.strValue = property.strValue;
10352 prop.timestamp = property.mTimestamp;
10353 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10354 guestProp::writeFlags(property.mFlags, szFlags);
10355 prop.strFlags = szFlags;
10356
10357 data.llGuestProperties.push_back(prop);
10358 }
10359
10360 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10361 /* I presume this doesn't require a backup(). */
10362 mData->mGuestPropertiesModified = FALSE;
10363#endif /* VBOX_WITH_GUEST_PROPS defined */
10364
10365 *pDbg = mHWData->mDebugging;
10366 *pAutostart = mHWData->mAutostart;
10367
10368 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10369 }
10370 catch(std::bad_alloc &)
10371 {
10372 return E_OUTOFMEMORY;
10373 }
10374
10375 AssertComRC(rc);
10376 return rc;
10377}
10378
10379/**
10380 * Saves the storage controller configuration.
10381 *
10382 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10383 */
10384HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10385{
10386 data.llStorageControllers.clear();
10387
10388 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10389 it != mStorageControllers->end();
10390 ++it)
10391 {
10392 HRESULT rc;
10393 ComObjPtr<StorageController> pCtl = *it;
10394
10395 settings::StorageController ctl;
10396 ctl.strName = pCtl->i_getName();
10397 ctl.controllerType = pCtl->i_getControllerType();
10398 ctl.storageBus = pCtl->i_getStorageBus();
10399 ctl.ulInstance = pCtl->i_getInstance();
10400 ctl.fBootable = pCtl->i_getBootable();
10401
10402 /* Save the port count. */
10403 ULONG portCount;
10404 rc = pCtl->COMGETTER(PortCount)(&portCount);
10405 ComAssertComRCRet(rc, rc);
10406 ctl.ulPortCount = portCount;
10407
10408 /* Save fUseHostIOCache */
10409 BOOL fUseHostIOCache;
10410 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10411 ComAssertComRCRet(rc, rc);
10412 ctl.fUseHostIOCache = !!fUseHostIOCache;
10413
10414 /* Save IDE emulation settings. */
10415 if (ctl.controllerType == StorageControllerType_IntelAhci)
10416 {
10417 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10418 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10419 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10420 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10421 )
10422 ComAssertComRCRet(rc, rc);
10423 }
10424
10425 /* save the devices now. */
10426 rc = i_saveStorageDevices(pCtl, ctl);
10427 ComAssertComRCRet(rc, rc);
10428
10429 data.llStorageControllers.push_back(ctl);
10430 }
10431
10432 return S_OK;
10433}
10434
10435/**
10436 * Saves the hard disk configuration.
10437 */
10438HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10439 settings::StorageController &data)
10440{
10441 MediaData::AttachmentList atts;
10442
10443 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10444 if (FAILED(rc)) return rc;
10445
10446 data.llAttachedDevices.clear();
10447 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10448 it != atts.end();
10449 ++it)
10450 {
10451 settings::AttachedDevice dev;
10452 IMediumAttachment *iA = *it;
10453 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10454 Medium *pMedium = pAttach->i_getMedium();
10455
10456 dev.deviceType = pAttach->i_getType();
10457 dev.lPort = pAttach->i_getPort();
10458 dev.lDevice = pAttach->i_getDevice();
10459 dev.fPassThrough = pAttach->i_getPassthrough();
10460 dev.fHotPluggable = pAttach->i_getHotPluggable();
10461 if (pMedium)
10462 {
10463 if (pMedium->i_isHostDrive())
10464 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10465 else
10466 dev.uuid = pMedium->i_getId();
10467 dev.fTempEject = pAttach->i_getTempEject();
10468 dev.fNonRotational = pAttach->i_getNonRotational();
10469 dev.fDiscard = pAttach->i_getDiscard();
10470 }
10471
10472 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10473
10474 data.llAttachedDevices.push_back(dev);
10475 }
10476
10477 return S_OK;
10478}
10479
10480/**
10481 * Saves machine state settings as defined by aFlags
10482 * (SaveSTS_* values).
10483 *
10484 * @param aFlags Combination of SaveSTS_* flags.
10485 *
10486 * @note Locks objects for writing.
10487 */
10488HRESULT Machine::i_saveStateSettings(int aFlags)
10489{
10490 if (aFlags == 0)
10491 return S_OK;
10492
10493 AutoCaller autoCaller(this);
10494 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10495
10496 /* This object's write lock is also necessary to serialize file access
10497 * (prevent concurrent reads and writes) */
10498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10499
10500 HRESULT rc = S_OK;
10501
10502 Assert(mData->pMachineConfigFile);
10503
10504 try
10505 {
10506 if (aFlags & SaveSTS_CurStateModified)
10507 mData->pMachineConfigFile->fCurrentStateModified = true;
10508
10509 if (aFlags & SaveSTS_StateFilePath)
10510 {
10511 if (!mSSData->strStateFilePath.isEmpty())
10512 /* try to make the file name relative to the settings file dir */
10513 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10514 else
10515 mData->pMachineConfigFile->strStateFile.setNull();
10516 }
10517
10518 if (aFlags & SaveSTS_StateTimeStamp)
10519 {
10520 Assert( mData->mMachineState != MachineState_Aborted
10521 || mSSData->strStateFilePath.isEmpty());
10522
10523 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10524
10525 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10526//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10527 }
10528
10529 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10530 }
10531 catch (...)
10532 {
10533 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10534 }
10535
10536 return rc;
10537}
10538
10539/**
10540 * Ensures that the given medium is added to a media registry. If this machine
10541 * was created with 4.0 or later, then the machine registry is used. Otherwise
10542 * the global VirtualBox media registry is used.
10543 *
10544 * Caller must NOT hold machine lock, media tree or any medium locks!
10545 *
10546 * @param pMedium
10547 */
10548void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10549{
10550 /* Paranoia checks: do not hold machine or media tree locks. */
10551 AssertReturnVoid(!isWriteLockOnCurrentThread());
10552 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10553
10554 ComObjPtr<Medium> pBase;
10555 {
10556 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10557 pBase = pMedium->i_getBase();
10558 }
10559
10560 /* Paranoia checks: do not hold medium locks. */
10561 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10562 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10563
10564 // decide which medium registry to use now that the medium is attached:
10565 Guid uuid;
10566 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10567 // machine XML is VirtualBox 4.0 or higher:
10568 uuid = i_getId(); // machine UUID
10569 else
10570 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10571
10572 if (pMedium->i_addRegistry(uuid))
10573 mParent->i_markRegistryModified(uuid);
10574
10575 /* For more complex hard disk structures it can happen that the base
10576 * medium isn't yet associated with any medium registry. Do that now. */
10577 if (pMedium != pBase)
10578 {
10579 /* Tree lock needed by Medium::addRegistry when recursing. */
10580 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10581 if (pBase->i_addRegistryRecursive(uuid))
10582 {
10583 treeLock.release();
10584 mParent->i_markRegistryModified(uuid);
10585 }
10586 }
10587}
10588
10589/**
10590 * Creates differencing hard disks for all normal hard disks attached to this
10591 * machine and a new set of attachments to refer to created disks.
10592 *
10593 * Used when taking a snapshot or when deleting the current state. Gets called
10594 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10595 *
10596 * This method assumes that mMediaData contains the original hard disk attachments
10597 * it needs to create diffs for. On success, these attachments will be replaced
10598 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10599 * called to delete created diffs which will also rollback mMediaData and restore
10600 * whatever was backed up before calling this method.
10601 *
10602 * Attachments with non-normal hard disks are left as is.
10603 *
10604 * If @a aOnline is @c false then the original hard disks that require implicit
10605 * diffs will be locked for reading. Otherwise it is assumed that they are
10606 * already locked for writing (when the VM was started). Note that in the latter
10607 * case it is responsibility of the caller to lock the newly created diffs for
10608 * writing if this method succeeds.
10609 *
10610 * @param aProgress Progress object to run (must contain at least as
10611 * many operations left as the number of hard disks
10612 * attached).
10613 * @param aOnline Whether the VM was online prior to this operation.
10614 *
10615 * @note The progress object is not marked as completed, neither on success nor
10616 * on failure. This is a responsibility of the caller.
10617 *
10618 * @note Locks this object and the media tree for writing.
10619 */
10620HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10621 ULONG aWeight,
10622 bool aOnline)
10623{
10624 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10625
10626 AutoCaller autoCaller(this);
10627 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10628
10629 AutoMultiWriteLock2 alock(this->lockHandle(),
10630 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10631
10632 /* must be in a protective state because we release the lock below */
10633 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10634 || mData->mMachineState == MachineState_OnlineSnapshotting
10635 || mData->mMachineState == MachineState_LiveSnapshotting
10636 || mData->mMachineState == MachineState_RestoringSnapshot
10637 || mData->mMachineState == MachineState_DeletingSnapshot
10638 , E_FAIL);
10639
10640 HRESULT rc = S_OK;
10641
10642 // use appropriate locked media map (online or offline)
10643 MediumLockListMap lockedMediaOffline;
10644 MediumLockListMap *lockedMediaMap;
10645 if (aOnline)
10646 lockedMediaMap = &mData->mSession.mLockedMedia;
10647 else
10648 lockedMediaMap = &lockedMediaOffline;
10649
10650 try
10651 {
10652 if (!aOnline)
10653 {
10654 /* lock all attached hard disks early to detect "in use"
10655 * situations before creating actual diffs */
10656 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10657 it != mMediaData->mAttachments.end();
10658 ++it)
10659 {
10660 MediumAttachment* pAtt = *it;
10661 if (pAtt->i_getType() == DeviceType_HardDisk)
10662 {
10663 Medium* pMedium = pAtt->i_getMedium();
10664 Assert(pMedium);
10665
10666 MediumLockList *pMediumLockList(new MediumLockList());
10667 alock.release();
10668 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10669 false /* fMediumLockWrite */,
10670 false /* fMediumLockWriteAll */,
10671 NULL,
10672 *pMediumLockList);
10673 alock.acquire();
10674 if (FAILED(rc))
10675 {
10676 delete pMediumLockList;
10677 throw rc;
10678 }
10679 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10680 if (FAILED(rc))
10681 {
10682 throw setError(rc,
10683 tr("Collecting locking information for all attached media failed"));
10684 }
10685 }
10686 }
10687
10688 /* Now lock all media. If this fails, nothing is locked. */
10689 alock.release();
10690 rc = lockedMediaMap->Lock();
10691 alock.acquire();
10692 if (FAILED(rc))
10693 {
10694 throw setError(rc,
10695 tr("Locking of attached media failed"));
10696 }
10697 }
10698
10699 /* remember the current list (note that we don't use backup() since
10700 * mMediaData may be already backed up) */
10701 MediaData::AttachmentList atts = mMediaData->mAttachments;
10702
10703 /* start from scratch */
10704 mMediaData->mAttachments.clear();
10705
10706 /* go through remembered attachments and create diffs for normal hard
10707 * disks and attach them */
10708 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10709 it != atts.end();
10710 ++it)
10711 {
10712 MediumAttachment* pAtt = *it;
10713
10714 DeviceType_T devType = pAtt->i_getType();
10715 Medium* pMedium = pAtt->i_getMedium();
10716
10717 if ( devType != DeviceType_HardDisk
10718 || pMedium == NULL
10719 || pMedium->i_getType() != MediumType_Normal)
10720 {
10721 /* copy the attachment as is */
10722
10723 /** @todo the progress object created in SessionMachine::TakeSnaphot
10724 * only expects operations for hard disks. Later other
10725 * device types need to show up in the progress as well. */
10726 if (devType == DeviceType_HardDisk)
10727 {
10728 if (pMedium == NULL)
10729 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10730 aWeight); // weight
10731 else
10732 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10733 pMedium->i_getBase()->i_getName().c_str()).raw(),
10734 aWeight); // weight
10735 }
10736
10737 mMediaData->mAttachments.push_back(pAtt);
10738 continue;
10739 }
10740
10741 /* need a diff */
10742 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10743 pMedium->i_getBase()->i_getName().c_str()).raw(),
10744 aWeight); // weight
10745
10746 Utf8Str strFullSnapshotFolder;
10747 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10748
10749 ComObjPtr<Medium> diff;
10750 diff.createObject();
10751 // store the diff in the same registry as the parent
10752 // (this cannot fail here because we can't create implicit diffs for
10753 // unregistered images)
10754 Guid uuidRegistryParent;
10755 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10756 Assert(fInRegistry); NOREF(fInRegistry);
10757 rc = diff->init(mParent,
10758 pMedium->i_getPreferredDiffFormat(),
10759 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10760 uuidRegistryParent,
10761 DeviceType_HardDisk);
10762 if (FAILED(rc)) throw rc;
10763
10764 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10765 * the push_back? Looks like we're going to release medium with the
10766 * wrong kind of lock (general issue with if we fail anywhere at all)
10767 * and an orphaned VDI in the snapshots folder. */
10768
10769 /* update the appropriate lock list */
10770 MediumLockList *pMediumLockList;
10771 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10772 AssertComRCThrowRC(rc);
10773 if (aOnline)
10774 {
10775 alock.release();
10776 /* The currently attached medium will be read-only, change
10777 * the lock type to read. */
10778 rc = pMediumLockList->Update(pMedium, false);
10779 alock.acquire();
10780 AssertComRCThrowRC(rc);
10781 }
10782
10783 /* release the locks before the potentially lengthy operation */
10784 alock.release();
10785 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10786 pMediumLockList,
10787 NULL /* aProgress */,
10788 true /* aWait */);
10789 alock.acquire();
10790 if (FAILED(rc)) throw rc;
10791
10792 /* actual lock list update is done in Medium::commitMedia */
10793
10794 rc = diff->i_addBackReference(mData->mUuid);
10795 AssertComRCThrowRC(rc);
10796
10797 /* add a new attachment */
10798 ComObjPtr<MediumAttachment> attachment;
10799 attachment.createObject();
10800 rc = attachment->init(this,
10801 diff,
10802 pAtt->i_getControllerName(),
10803 pAtt->i_getPort(),
10804 pAtt->i_getDevice(),
10805 DeviceType_HardDisk,
10806 true /* aImplicit */,
10807 false /* aPassthrough */,
10808 false /* aTempEject */,
10809 pAtt->i_getNonRotational(),
10810 pAtt->i_getDiscard(),
10811 pAtt->i_getHotPluggable(),
10812 pAtt->i_getBandwidthGroup());
10813 if (FAILED(rc)) throw rc;
10814
10815 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10816 AssertComRCThrowRC(rc);
10817 mMediaData->mAttachments.push_back(attachment);
10818 }
10819 }
10820 catch (HRESULT aRC) { rc = aRC; }
10821
10822 /* unlock all hard disks we locked when there is no VM */
10823 if (!aOnline)
10824 {
10825 ErrorInfoKeeper eik;
10826
10827 HRESULT rc1 = lockedMediaMap->Clear();
10828 AssertComRC(rc1);
10829 }
10830
10831 return rc;
10832}
10833
10834/**
10835 * Deletes implicit differencing hard disks created either by
10836 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10837 *
10838 * Note that to delete hard disks created by #AttachDevice() this method is
10839 * called from #fixupMedia() when the changes are rolled back.
10840 *
10841 * @note Locks this object and the media tree for writing.
10842 */
10843HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10844{
10845 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10846
10847 AutoCaller autoCaller(this);
10848 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10849
10850 AutoMultiWriteLock2 alock(this->lockHandle(),
10851 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10852
10853 /* We absolutely must have backed up state. */
10854 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10855
10856 /* Check if there are any implicitly created diff images. */
10857 bool fImplicitDiffs = false;
10858 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10859 it != mMediaData->mAttachments.end();
10860 ++it)
10861 {
10862 const ComObjPtr<MediumAttachment> &pAtt = *it;
10863 if (pAtt->i_isImplicit())
10864 {
10865 fImplicitDiffs = true;
10866 break;
10867 }
10868 }
10869 /* If there is nothing to do, leave early. This saves lots of image locking
10870 * effort. It also avoids a MachineStateChanged event without real reason.
10871 * This is important e.g. when loading a VM config, because there should be
10872 * no events. Otherwise API clients can become thoroughly confused for
10873 * inaccessible VMs (the code for loading VM configs uses this method for
10874 * cleanup if the config makes no sense), as they take such events as an
10875 * indication that the VM is alive, and they would force the VM config to
10876 * be reread, leading to an endless loop. */
10877 if (!fImplicitDiffs)
10878 return S_OK;
10879
10880 HRESULT rc = S_OK;
10881 MachineState_T oldState = mData->mMachineState;
10882
10883 /* will release the lock before the potentially lengthy operation,
10884 * so protect with the special state (unless already protected) */
10885 if ( oldState != MachineState_Snapshotting
10886 && oldState != MachineState_OnlineSnapshotting
10887 && oldState != MachineState_LiveSnapshotting
10888 && oldState != MachineState_RestoringSnapshot
10889 && oldState != MachineState_DeletingSnapshot
10890 && oldState != MachineState_DeletingSnapshotOnline
10891 && oldState != MachineState_DeletingSnapshotPaused
10892 )
10893 i_setMachineState(MachineState_SettingUp);
10894
10895 // use appropriate locked media map (online or offline)
10896 MediumLockListMap lockedMediaOffline;
10897 MediumLockListMap *lockedMediaMap;
10898 if (aOnline)
10899 lockedMediaMap = &mData->mSession.mLockedMedia;
10900 else
10901 lockedMediaMap = &lockedMediaOffline;
10902
10903 try
10904 {
10905 if (!aOnline)
10906 {
10907 /* lock all attached hard disks early to detect "in use"
10908 * situations before deleting actual diffs */
10909 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10910 it != mMediaData->mAttachments.end();
10911 ++it)
10912 {
10913 MediumAttachment* pAtt = *it;
10914 if (pAtt->i_getType() == DeviceType_HardDisk)
10915 {
10916 Medium* pMedium = pAtt->i_getMedium();
10917 Assert(pMedium);
10918
10919 MediumLockList *pMediumLockList(new MediumLockList());
10920 alock.release();
10921 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10922 false /* fMediumLockWrite */,
10923 false /* fMediumLockWriteAll */,
10924 NULL,
10925 *pMediumLockList);
10926 alock.acquire();
10927
10928 if (FAILED(rc))
10929 {
10930 delete pMediumLockList;
10931 throw rc;
10932 }
10933
10934 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10935 if (FAILED(rc))
10936 throw rc;
10937 }
10938 }
10939
10940 if (FAILED(rc))
10941 throw rc;
10942 } // end of offline
10943
10944 /* Lock lists are now up to date and include implicitly created media */
10945
10946 /* Go through remembered attachments and delete all implicitly created
10947 * diffs and fix up the attachment information */
10948 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10949 MediaData::AttachmentList implicitAtts;
10950 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10951 it != mMediaData->mAttachments.end();
10952 ++it)
10953 {
10954 ComObjPtr<MediumAttachment> pAtt = *it;
10955 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10956 if (pMedium.isNull())
10957 continue;
10958
10959 // Implicit attachments go on the list for deletion and back references are removed.
10960 if (pAtt->i_isImplicit())
10961 {
10962 /* Deassociate and mark for deletion */
10963 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10964 rc = pMedium->i_removeBackReference(mData->mUuid);
10965 if (FAILED(rc))
10966 throw rc;
10967 implicitAtts.push_back(pAtt);
10968 continue;
10969 }
10970
10971 /* Was this medium attached before? */
10972 if (!i_findAttachment(oldAtts, pMedium))
10973 {
10974 /* no: de-associate */
10975 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10976 rc = pMedium->i_removeBackReference(mData->mUuid);
10977 if (FAILED(rc))
10978 throw rc;
10979 continue;
10980 }
10981 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10982 }
10983
10984 /* If there are implicit attachments to delete, throw away the lock
10985 * map contents (which will unlock all media) since the medium
10986 * attachments will be rolled back. Below we need to completely
10987 * recreate the lock map anyway since it is infinitely complex to
10988 * do this incrementally (would need reconstructing each attachment
10989 * change, which would be extremely hairy). */
10990 if (implicitAtts.size() != 0)
10991 {
10992 ErrorInfoKeeper eik;
10993
10994 HRESULT rc1 = lockedMediaMap->Clear();
10995 AssertComRC(rc1);
10996 }
10997
10998 /* rollback hard disk changes */
10999 mMediaData.rollback();
11000
11001 MultiResult mrc(S_OK);
11002
11003 // Delete unused implicit diffs.
11004 if (implicitAtts.size() != 0)
11005 {
11006 alock.release();
11007
11008 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11009 {
11010 // Remove medium associated with this attachment.
11011 ComObjPtr<MediumAttachment> pAtt = *it;
11012 Assert(pAtt);
11013 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11014 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11015 Assert(pMedium);
11016
11017 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11018 // continue on delete failure, just collect error messages
11019 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11020 pMedium->i_getLocationFull().c_str() ));
11021 mrc = rc;
11022 }
11023 // Clear the list of deleted implicit attachments now, while not
11024 // holding the lock, as it will ultimately trigger Medium::uninit()
11025 // calls which assume that the media tree lock isn't held.
11026 implicitAtts.clear();
11027
11028 alock.acquire();
11029
11030 /* if there is a VM recreate media lock map as mentioned above,
11031 * otherwise it is a waste of time and we leave things unlocked */
11032 if (aOnline)
11033 {
11034 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11035 /* must never be NULL, but better safe than sorry */
11036 if (!pMachine.isNull())
11037 {
11038 alock.release();
11039 rc = mData->mSession.mMachine->i_lockMedia();
11040 alock.acquire();
11041 if (FAILED(rc))
11042 throw rc;
11043 }
11044 }
11045 }
11046 }
11047 catch (HRESULT aRC) {rc = aRC;}
11048
11049 if (mData->mMachineState == MachineState_SettingUp)
11050 i_setMachineState(oldState);
11051
11052 /* unlock all hard disks we locked when there is no VM */
11053 if (!aOnline)
11054 {
11055 ErrorInfoKeeper eik;
11056
11057 HRESULT rc1 = lockedMediaMap->Clear();
11058 AssertComRC(rc1);
11059 }
11060
11061 return rc;
11062}
11063
11064
11065/**
11066 * Looks through the given list of media attachments for one with the given parameters
11067 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11068 * can be searched as well if needed.
11069 *
11070 * @param list
11071 * @param aControllerName
11072 * @param aControllerPort
11073 * @param aDevice
11074 * @return
11075 */
11076MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11077 IN_BSTR aControllerName,
11078 LONG aControllerPort,
11079 LONG aDevice)
11080{
11081 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11082 {
11083 MediumAttachment *pAttach = *it;
11084 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11085 return pAttach;
11086 }
11087
11088 return NULL;
11089}
11090
11091/**
11092 * Looks through the given list of media attachments for one with the given parameters
11093 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11094 * can be searched as well if needed.
11095 *
11096 * @param list
11097 * @param aControllerName
11098 * @param aControllerPort
11099 * @param aDevice
11100 * @return
11101 */
11102MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11103 ComObjPtr<Medium> pMedium)
11104{
11105 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11106 {
11107 MediumAttachment *pAttach = *it;
11108 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11109 if (pMediumThis == pMedium)
11110 return pAttach;
11111 }
11112
11113 return NULL;
11114}
11115
11116/**
11117 * Looks through the given list of media attachments for one with the given parameters
11118 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11119 * can be searched as well if needed.
11120 *
11121 * @param list
11122 * @param aControllerName
11123 * @param aControllerPort
11124 * @param aDevice
11125 * @return
11126 */
11127MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11128 Guid &id)
11129{
11130 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11131 {
11132 MediumAttachment *pAttach = *it;
11133 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11134 if (pMediumThis->i_getId() == id)
11135 return pAttach;
11136 }
11137
11138 return NULL;
11139}
11140
11141/**
11142 * Main implementation for Machine::DetachDevice. This also gets called
11143 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11144 *
11145 * @param pAttach Medium attachment to detach.
11146 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11147 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11148 * SnapshotMachine, and this must be its snapshot.
11149 * @return
11150 */
11151HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11152 AutoWriteLock &writeLock,
11153 Snapshot *pSnapshot)
11154{
11155 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11156 DeviceType_T mediumType = pAttach->i_getType();
11157
11158 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11159
11160 if (pAttach->i_isImplicit())
11161 {
11162 /* attempt to implicitly delete the implicitly created diff */
11163
11164 /// @todo move the implicit flag from MediumAttachment to Medium
11165 /// and forbid any hard disk operation when it is implicit. Or maybe
11166 /// a special media state for it to make it even more simple.
11167
11168 Assert(mMediaData.isBackedUp());
11169
11170 /* will release the lock before the potentially lengthy operation, so
11171 * protect with the special state */
11172 MachineState_T oldState = mData->mMachineState;
11173 i_setMachineState(MachineState_SettingUp);
11174
11175 writeLock.release();
11176
11177 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11178 true /*aWait*/);
11179
11180 writeLock.acquire();
11181
11182 i_setMachineState(oldState);
11183
11184 if (FAILED(rc)) return rc;
11185 }
11186
11187 i_setModified(IsModified_Storage);
11188 mMediaData.backup();
11189 mMediaData->mAttachments.remove(pAttach);
11190
11191 if (!oldmedium.isNull())
11192 {
11193 // if this is from a snapshot, do not defer detachment to commitMedia()
11194 if (pSnapshot)
11195 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11196 // else if non-hard disk media, do not defer detachment to commitMedia() either
11197 else if (mediumType != DeviceType_HardDisk)
11198 oldmedium->i_removeBackReference(mData->mUuid);
11199 }
11200
11201 return S_OK;
11202}
11203
11204/**
11205 * Goes thru all media of the given list and
11206 *
11207 * 1) calls i_detachDevice() on each of them for this machine and
11208 * 2) adds all Medium objects found in the process to the given list,
11209 * depending on cleanupMode.
11210 *
11211 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11212 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11213 * media to the list.
11214 *
11215 * This gets called from Machine::Unregister, both for the actual Machine and
11216 * the SnapshotMachine objects that might be found in the snapshots.
11217 *
11218 * Requires caller and locking. The machine lock must be passed in because it
11219 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11220 *
11221 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11222 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11223 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11224 * Full, then all media get added;
11225 * otherwise no media get added.
11226 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11227 * @return
11228 */
11229HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11230 Snapshot *pSnapshot,
11231 CleanupMode_T cleanupMode,
11232 MediaList &llMedia)
11233{
11234 Assert(isWriteLockOnCurrentThread());
11235
11236 HRESULT rc;
11237
11238 // make a temporary list because i_detachDevice invalidates iterators into
11239 // mMediaData->mAttachments
11240 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11241
11242 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11243 {
11244 ComObjPtr<MediumAttachment> &pAttach = *it;
11245 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11246
11247 if (!pMedium.isNull())
11248 {
11249 AutoCaller mac(pMedium);
11250 if (FAILED(mac.rc())) return mac.rc();
11251 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11252 DeviceType_T devType = pMedium->i_getDeviceType();
11253 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11254 && devType == DeviceType_HardDisk)
11255 || (cleanupMode == CleanupMode_Full)
11256 )
11257 {
11258 llMedia.push_back(pMedium);
11259 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11260 /* Not allowed to keep this lock as below we need the parent
11261 * medium lock, and the lock order is parent to child. */
11262 lock.release();
11263 /*
11264 * Search for medias which are not attached to any machine, but
11265 * in the chain to an attached disk. Mediums are only consided
11266 * if they are:
11267 * - have only one child
11268 * - no references to any machines
11269 * - are of normal medium type
11270 */
11271 while (!pParent.isNull())
11272 {
11273 AutoCaller mac1(pParent);
11274 if (FAILED(mac1.rc())) return mac1.rc();
11275 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11276 if (pParent->i_getChildren().size() == 1)
11277 {
11278 if ( pParent->i_getMachineBackRefCount() == 0
11279 && pParent->i_getType() == MediumType_Normal
11280 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11281 llMedia.push_back(pParent);
11282 }
11283 else
11284 break;
11285 pParent = pParent->i_getParent();
11286 }
11287 }
11288 }
11289
11290 // real machine: then we need to use the proper method
11291 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11292
11293 if (FAILED(rc))
11294 return rc;
11295 }
11296
11297 return S_OK;
11298}
11299
11300/**
11301 * Perform deferred hard disk detachments.
11302 *
11303 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11304 * backed up).
11305 *
11306 * If @a aOnline is @c true then this method will also unlock the old hard disks
11307 * for which the new implicit diffs were created and will lock these new diffs for
11308 * writing.
11309 *
11310 * @param aOnline Whether the VM was online prior to this operation.
11311 *
11312 * @note Locks this object for writing!
11313 */
11314void Machine::i_commitMedia(bool aOnline /*= false*/)
11315{
11316 AutoCaller autoCaller(this);
11317 AssertComRCReturnVoid(autoCaller.rc());
11318
11319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11320
11321 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11322
11323 HRESULT rc = S_OK;
11324
11325 /* no attach/detach operations -- nothing to do */
11326 if (!mMediaData.isBackedUp())
11327 return;
11328
11329 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11330 bool fMediaNeedsLocking = false;
11331
11332 /* enumerate new attachments */
11333 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11334 it != mMediaData->mAttachments.end();
11335 ++it)
11336 {
11337 MediumAttachment *pAttach = *it;
11338
11339 pAttach->i_commit();
11340
11341 Medium* pMedium = pAttach->i_getMedium();
11342 bool fImplicit = pAttach->i_isImplicit();
11343
11344 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11345 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11346 fImplicit));
11347
11348 /** @todo convert all this Machine-based voodoo to MediumAttachment
11349 * based commit logic. */
11350 if (fImplicit)
11351 {
11352 /* convert implicit attachment to normal */
11353 pAttach->i_setImplicit(false);
11354
11355 if ( aOnline
11356 && pMedium
11357 && pAttach->i_getType() == DeviceType_HardDisk
11358 )
11359 {
11360 /* update the appropriate lock list */
11361 MediumLockList *pMediumLockList;
11362 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11363 AssertComRC(rc);
11364 if (pMediumLockList)
11365 {
11366 /* unlock if there's a need to change the locking */
11367 if (!fMediaNeedsLocking)
11368 {
11369 rc = mData->mSession.mLockedMedia.Unlock();
11370 AssertComRC(rc);
11371 fMediaNeedsLocking = true;
11372 }
11373 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11374 AssertComRC(rc);
11375 rc = pMediumLockList->Append(pMedium, true);
11376 AssertComRC(rc);
11377 }
11378 }
11379
11380 continue;
11381 }
11382
11383 if (pMedium)
11384 {
11385 /* was this medium attached before? */
11386 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11387 {
11388 MediumAttachment *pOldAttach = *oldIt;
11389 if (pOldAttach->i_getMedium() == pMedium)
11390 {
11391 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11392
11393 /* yes: remove from old to avoid de-association */
11394 oldAtts.erase(oldIt);
11395 break;
11396 }
11397 }
11398 }
11399 }
11400
11401 /* enumerate remaining old attachments and de-associate from the
11402 * current machine state */
11403 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11404 {
11405 MediumAttachment *pAttach = *it;
11406 Medium* pMedium = pAttach->i_getMedium();
11407
11408 /* Detach only hard disks, since DVD/floppy media is detached
11409 * instantly in MountMedium. */
11410 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11411 {
11412 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11413
11414 /* now de-associate from the current machine state */
11415 rc = pMedium->i_removeBackReference(mData->mUuid);
11416 AssertComRC(rc);
11417
11418 if (aOnline)
11419 {
11420 /* unlock since medium is not used anymore */
11421 MediumLockList *pMediumLockList;
11422 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11423 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11424 {
11425 /* this happens for online snapshots, there the attachment
11426 * is changing, but only to a diff image created under
11427 * the old one, so there is no separate lock list */
11428 Assert(!pMediumLockList);
11429 }
11430 else
11431 {
11432 AssertComRC(rc);
11433 if (pMediumLockList)
11434 {
11435 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11436 AssertComRC(rc);
11437 }
11438 }
11439 }
11440 }
11441 }
11442
11443 /* take media locks again so that the locking state is consistent */
11444 if (fMediaNeedsLocking)
11445 {
11446 Assert(aOnline);
11447 rc = mData->mSession.mLockedMedia.Lock();
11448 AssertComRC(rc);
11449 }
11450
11451 /* commit the hard disk changes */
11452 mMediaData.commit();
11453
11454 if (i_isSessionMachine())
11455 {
11456 /*
11457 * Update the parent machine to point to the new owner.
11458 * This is necessary because the stored parent will point to the
11459 * session machine otherwise and cause crashes or errors later
11460 * when the session machine gets invalid.
11461 */
11462 /** @todo Change the MediumAttachment class to behave like any other
11463 * class in this regard by creating peer MediumAttachment
11464 * objects for session machines and share the data with the peer
11465 * machine.
11466 */
11467 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11468 it != mMediaData->mAttachments.end();
11469 ++it)
11470 (*it)->i_updateParentMachine(mPeer);
11471
11472 /* attach new data to the primary machine and reshare it */
11473 mPeer->mMediaData.attach(mMediaData);
11474 }
11475
11476 return;
11477}
11478
11479/**
11480 * Perform deferred deletion of implicitly created diffs.
11481 *
11482 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11483 * backed up).
11484 *
11485 * @note Locks this object for writing!
11486 */
11487void Machine::i_rollbackMedia()
11488{
11489 AutoCaller autoCaller(this);
11490 AssertComRCReturnVoid(autoCaller.rc());
11491
11492 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11493 LogFlowThisFunc(("Entering rollbackMedia\n"));
11494
11495 HRESULT rc = S_OK;
11496
11497 /* no attach/detach operations -- nothing to do */
11498 if (!mMediaData.isBackedUp())
11499 return;
11500
11501 /* enumerate new attachments */
11502 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11503 it != mMediaData->mAttachments.end();
11504 ++it)
11505 {
11506 MediumAttachment *pAttach = *it;
11507 /* Fix up the backrefs for DVD/floppy media. */
11508 if (pAttach->i_getType() != DeviceType_HardDisk)
11509 {
11510 Medium* pMedium = pAttach->i_getMedium();
11511 if (pMedium)
11512 {
11513 rc = pMedium->i_removeBackReference(mData->mUuid);
11514 AssertComRC(rc);
11515 }
11516 }
11517
11518 (*it)->i_rollback();
11519
11520 pAttach = *it;
11521 /* Fix up the backrefs for DVD/floppy media. */
11522 if (pAttach->i_getType() != DeviceType_HardDisk)
11523 {
11524 Medium* pMedium = pAttach->i_getMedium();
11525 if (pMedium)
11526 {
11527 rc = pMedium->i_addBackReference(mData->mUuid);
11528 AssertComRC(rc);
11529 }
11530 }
11531 }
11532
11533 /** @todo convert all this Machine-based voodoo to MediumAttachment
11534 * based rollback logic. */
11535 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11536
11537 return;
11538}
11539
11540/**
11541 * Returns true if the settings file is located in the directory named exactly
11542 * as the machine; this means, among other things, that the machine directory
11543 * should be auto-renamed.
11544 *
11545 * @param aSettingsDir if not NULL, the full machine settings file directory
11546 * name will be assigned there.
11547 *
11548 * @note Doesn't lock anything.
11549 * @note Not thread safe (must be called from this object's lock).
11550 */
11551bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11552{
11553 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11554 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11555 if (aSettingsDir)
11556 *aSettingsDir = strMachineDirName;
11557 strMachineDirName.stripPath(); // vmname
11558 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11559 strConfigFileOnly.stripPath() // vmname.vbox
11560 .stripSuffix(); // vmname
11561 /** @todo hack, make somehow use of ComposeMachineFilename */
11562 if (mUserData->s.fDirectoryIncludesUUID)
11563 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11564
11565 AssertReturn(!strMachineDirName.isEmpty(), false);
11566 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11567
11568 return strMachineDirName == strConfigFileOnly;
11569}
11570
11571/**
11572 * Discards all changes to machine settings.
11573 *
11574 * @param aNotify Whether to notify the direct session about changes or not.
11575 *
11576 * @note Locks objects for writing!
11577 */
11578void Machine::i_rollback(bool aNotify)
11579{
11580 AutoCaller autoCaller(this);
11581 AssertComRCReturn(autoCaller.rc(), (void)0);
11582
11583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11584
11585 if (!mStorageControllers.isNull())
11586 {
11587 if (mStorageControllers.isBackedUp())
11588 {
11589 /* unitialize all new devices (absent in the backed up list). */
11590 StorageControllerList::const_iterator it = mStorageControllers->begin();
11591 StorageControllerList *backedList = mStorageControllers.backedUpData();
11592 while (it != mStorageControllers->end())
11593 {
11594 if ( std::find(backedList->begin(), backedList->end(), *it)
11595 == backedList->end()
11596 )
11597 {
11598 (*it)->uninit();
11599 }
11600 ++it;
11601 }
11602
11603 /* restore the list */
11604 mStorageControllers.rollback();
11605 }
11606
11607 /* rollback any changes to devices after restoring the list */
11608 if (mData->flModifications & IsModified_Storage)
11609 {
11610 StorageControllerList::const_iterator it = mStorageControllers->begin();
11611 while (it != mStorageControllers->end())
11612 {
11613 (*it)->i_rollback();
11614 ++it;
11615 }
11616 }
11617 }
11618
11619 if (!mUSBControllers.isNull())
11620 {
11621 if (mUSBControllers.isBackedUp())
11622 {
11623 /* unitialize all new devices (absent in the backed up list). */
11624 USBControllerList::const_iterator it = mUSBControllers->begin();
11625 USBControllerList *backedList = mUSBControllers.backedUpData();
11626 while (it != mUSBControllers->end())
11627 {
11628 if ( std::find(backedList->begin(), backedList->end(), *it)
11629 == backedList->end()
11630 )
11631 {
11632 (*it)->uninit();
11633 }
11634 ++it;
11635 }
11636
11637 /* restore the list */
11638 mUSBControllers.rollback();
11639 }
11640
11641 /* rollback any changes to devices after restoring the list */
11642 if (mData->flModifications & IsModified_USB)
11643 {
11644 USBControllerList::const_iterator it = mUSBControllers->begin();
11645 while (it != mUSBControllers->end())
11646 {
11647 (*it)->i_rollback();
11648 ++it;
11649 }
11650 }
11651 }
11652
11653 mUserData.rollback();
11654
11655 mHWData.rollback();
11656
11657 if (mData->flModifications & IsModified_Storage)
11658 i_rollbackMedia();
11659
11660 if (mBIOSSettings)
11661 mBIOSSettings->i_rollback();
11662
11663 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11664 mVRDEServer->i_rollback();
11665
11666 if (mAudioAdapter)
11667 mAudioAdapter->i_rollback();
11668
11669 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11670 mUSBDeviceFilters->i_rollback();
11671
11672 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11673 mBandwidthControl->i_rollback();
11674
11675 if (!mHWData.isNull())
11676 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11677 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11678 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11679 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11680
11681 if (mData->flModifications & IsModified_NetworkAdapters)
11682 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11683 if ( mNetworkAdapters[slot]
11684 && mNetworkAdapters[slot]->i_isModified())
11685 {
11686 mNetworkAdapters[slot]->i_rollback();
11687 networkAdapters[slot] = mNetworkAdapters[slot];
11688 }
11689
11690 if (mData->flModifications & IsModified_SerialPorts)
11691 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11692 if ( mSerialPorts[slot]
11693 && mSerialPorts[slot]->i_isModified())
11694 {
11695 mSerialPorts[slot]->i_rollback();
11696 serialPorts[slot] = mSerialPorts[slot];
11697 }
11698
11699 if (mData->flModifications & IsModified_ParallelPorts)
11700 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11701 if ( mParallelPorts[slot]
11702 && mParallelPorts[slot]->i_isModified())
11703 {
11704 mParallelPorts[slot]->i_rollback();
11705 parallelPorts[slot] = mParallelPorts[slot];
11706 }
11707
11708 if (aNotify)
11709 {
11710 /* inform the direct session about changes */
11711
11712 ComObjPtr<Machine> that = this;
11713 uint32_t flModifications = mData->flModifications;
11714 alock.release();
11715
11716 if (flModifications & IsModified_SharedFolders)
11717 that->i_onSharedFolderChange();
11718
11719 if (flModifications & IsModified_VRDEServer)
11720 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11721 if (flModifications & IsModified_USB)
11722 that->i_onUSBControllerChange();
11723
11724 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11725 if (networkAdapters[slot])
11726 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11727 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11728 if (serialPorts[slot])
11729 that->i_onSerialPortChange(serialPorts[slot]);
11730 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11731 if (parallelPorts[slot])
11732 that->i_onParallelPortChange(parallelPorts[slot]);
11733
11734 if (flModifications & IsModified_Storage)
11735 that->i_onStorageControllerChange();
11736
11737#if 0
11738 if (flModifications & IsModified_BandwidthControl)
11739 that->onBandwidthControlChange();
11740#endif
11741 }
11742}
11743
11744/**
11745 * Commits all the changes to machine settings.
11746 *
11747 * Note that this operation is supposed to never fail.
11748 *
11749 * @note Locks this object and children for writing.
11750 */
11751void Machine::i_commit()
11752{
11753 AutoCaller autoCaller(this);
11754 AssertComRCReturnVoid(autoCaller.rc());
11755
11756 AutoCaller peerCaller(mPeer);
11757 AssertComRCReturnVoid(peerCaller.rc());
11758
11759 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11760
11761 /*
11762 * use safe commit to ensure Snapshot machines (that share mUserData)
11763 * will still refer to a valid memory location
11764 */
11765 mUserData.commitCopy();
11766
11767 mHWData.commit();
11768
11769 if (mMediaData.isBackedUp())
11770 i_commitMedia(Global::IsOnline(mData->mMachineState));
11771
11772 mBIOSSettings->i_commit();
11773 mVRDEServer->i_commit();
11774 mAudioAdapter->i_commit();
11775 mUSBDeviceFilters->i_commit();
11776 mBandwidthControl->i_commit();
11777
11778 /* Since mNetworkAdapters is a list which might have been changed (resized)
11779 * without using the Backupable<> template we need to handle the copying
11780 * of the list entries manually, including the creation of peers for the
11781 * new objects. */
11782 bool commitNetworkAdapters = false;
11783 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11784 if (mPeer)
11785 {
11786 /* commit everything, even the ones which will go away */
11787 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11788 mNetworkAdapters[slot]->i_commit();
11789 /* copy over the new entries, creating a peer and uninit the original */
11790 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11791 for (size_t slot = 0; slot < newSize; slot++)
11792 {
11793 /* look if this adapter has a peer device */
11794 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11795 if (!peer)
11796 {
11797 /* no peer means the adapter is a newly created one;
11798 * create a peer owning data this data share it with */
11799 peer.createObject();
11800 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11801 }
11802 mPeer->mNetworkAdapters[slot] = peer;
11803 }
11804 /* uninit any no longer needed network adapters */
11805 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11806 mNetworkAdapters[slot]->uninit();
11807 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11808 {
11809 if (mPeer->mNetworkAdapters[slot])
11810 mPeer->mNetworkAdapters[slot]->uninit();
11811 }
11812 /* Keep the original network adapter count until this point, so that
11813 * discarding a chipset type change will not lose settings. */
11814 mNetworkAdapters.resize(newSize);
11815 mPeer->mNetworkAdapters.resize(newSize);
11816 }
11817 else
11818 {
11819 /* we have no peer (our parent is the newly created machine);
11820 * just commit changes to the network adapters */
11821 commitNetworkAdapters = true;
11822 }
11823 if (commitNetworkAdapters)
11824 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11825 mNetworkAdapters[slot]->i_commit();
11826
11827 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11828 mSerialPorts[slot]->i_commit();
11829 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11830 mParallelPorts[slot]->i_commit();
11831
11832 bool commitStorageControllers = false;
11833
11834 if (mStorageControllers.isBackedUp())
11835 {
11836 mStorageControllers.commit();
11837
11838 if (mPeer)
11839 {
11840 /* Commit all changes to new controllers (this will reshare data with
11841 * peers for those who have peers) */
11842 StorageControllerList *newList = new StorageControllerList();
11843 StorageControllerList::const_iterator it = mStorageControllers->begin();
11844 while (it != mStorageControllers->end())
11845 {
11846 (*it)->i_commit();
11847
11848 /* look if this controller has a peer device */
11849 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11850 if (!peer)
11851 {
11852 /* no peer means the device is a newly created one;
11853 * create a peer owning data this device share it with */
11854 peer.createObject();
11855 peer->init(mPeer, *it, true /* aReshare */);
11856 }
11857 else
11858 {
11859 /* remove peer from the old list */
11860 mPeer->mStorageControllers->remove(peer);
11861 }
11862 /* and add it to the new list */
11863 newList->push_back(peer);
11864
11865 ++it;
11866 }
11867
11868 /* uninit old peer's controllers that are left */
11869 it = mPeer->mStorageControllers->begin();
11870 while (it != mPeer->mStorageControllers->end())
11871 {
11872 (*it)->uninit();
11873 ++it;
11874 }
11875
11876 /* attach new list of controllers to our peer */
11877 mPeer->mStorageControllers.attach(newList);
11878 }
11879 else
11880 {
11881 /* we have no peer (our parent is the newly created machine);
11882 * just commit changes to devices */
11883 commitStorageControllers = true;
11884 }
11885 }
11886 else
11887 {
11888 /* the list of controllers itself is not changed,
11889 * just commit changes to controllers themselves */
11890 commitStorageControllers = true;
11891 }
11892
11893 if (commitStorageControllers)
11894 {
11895 StorageControllerList::const_iterator it = mStorageControllers->begin();
11896 while (it != mStorageControllers->end())
11897 {
11898 (*it)->i_commit();
11899 ++it;
11900 }
11901 }
11902
11903 bool commitUSBControllers = false;
11904
11905 if (mUSBControllers.isBackedUp())
11906 {
11907 mUSBControllers.commit();
11908
11909 if (mPeer)
11910 {
11911 /* Commit all changes to new controllers (this will reshare data with
11912 * peers for those who have peers) */
11913 USBControllerList *newList = new USBControllerList();
11914 USBControllerList::const_iterator it = mUSBControllers->begin();
11915 while (it != mUSBControllers->end())
11916 {
11917 (*it)->i_commit();
11918
11919 /* look if this controller has a peer device */
11920 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11921 if (!peer)
11922 {
11923 /* no peer means the device is a newly created one;
11924 * create a peer owning data this device share it with */
11925 peer.createObject();
11926 peer->init(mPeer, *it, true /* aReshare */);
11927 }
11928 else
11929 {
11930 /* remove peer from the old list */
11931 mPeer->mUSBControllers->remove(peer);
11932 }
11933 /* and add it to the new list */
11934 newList->push_back(peer);
11935
11936 ++it;
11937 }
11938
11939 /* uninit old peer's controllers that are left */
11940 it = mPeer->mUSBControllers->begin();
11941 while (it != mPeer->mUSBControllers->end())
11942 {
11943 (*it)->uninit();
11944 ++it;
11945 }
11946
11947 /* attach new list of controllers to our peer */
11948 mPeer->mUSBControllers.attach(newList);
11949 }
11950 else
11951 {
11952 /* we have no peer (our parent is the newly created machine);
11953 * just commit changes to devices */
11954 commitUSBControllers = true;
11955 }
11956 }
11957 else
11958 {
11959 /* the list of controllers itself is not changed,
11960 * just commit changes to controllers themselves */
11961 commitUSBControllers = true;
11962 }
11963
11964 if (commitUSBControllers)
11965 {
11966 USBControllerList::const_iterator it = mUSBControllers->begin();
11967 while (it != mUSBControllers->end())
11968 {
11969 (*it)->i_commit();
11970 ++it;
11971 }
11972 }
11973
11974 if (i_isSessionMachine())
11975 {
11976 /* attach new data to the primary machine and reshare it */
11977 mPeer->mUserData.attach(mUserData);
11978 mPeer->mHWData.attach(mHWData);
11979 /* mMediaData is reshared by fixupMedia */
11980 // mPeer->mMediaData.attach(mMediaData);
11981 Assert(mPeer->mMediaData.data() == mMediaData.data());
11982 }
11983}
11984
11985/**
11986 * Copies all the hardware data from the given machine.
11987 *
11988 * Currently, only called when the VM is being restored from a snapshot. In
11989 * particular, this implies that the VM is not running during this method's
11990 * call.
11991 *
11992 * @note This method must be called from under this object's lock.
11993 *
11994 * @note This method doesn't call #commit(), so all data remains backed up and
11995 * unsaved.
11996 */
11997void Machine::i_copyFrom(Machine *aThat)
11998{
11999 AssertReturnVoid(!i_isSnapshotMachine());
12000 AssertReturnVoid(aThat->i_isSnapshotMachine());
12001
12002 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12003
12004 mHWData.assignCopy(aThat->mHWData);
12005
12006 // create copies of all shared folders (mHWData after attaching a copy
12007 // contains just references to original objects)
12008 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12009 it != mHWData->mSharedFolders.end();
12010 ++it)
12011 {
12012 ComObjPtr<SharedFolder> folder;
12013 folder.createObject();
12014 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12015 AssertComRC(rc);
12016 *it = folder;
12017 }
12018
12019 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12020 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12021 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12022 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12023 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12024
12025 /* create private copies of all controllers */
12026 mStorageControllers.backup();
12027 mStorageControllers->clear();
12028 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12029 it != aThat->mStorageControllers->end();
12030 ++it)
12031 {
12032 ComObjPtr<StorageController> ctrl;
12033 ctrl.createObject();
12034 ctrl->initCopy(this, *it);
12035 mStorageControllers->push_back(ctrl);
12036 }
12037
12038 /* create private copies of all USB controllers */
12039 mUSBControllers.backup();
12040 mUSBControllers->clear();
12041 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12042 it != aThat->mUSBControllers->end();
12043 ++it)
12044 {
12045 ComObjPtr<USBController> ctrl;
12046 ctrl.createObject();
12047 ctrl->initCopy(this, *it);
12048 mUSBControllers->push_back(ctrl);
12049 }
12050
12051 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12052 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12053 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12054 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12055 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12056 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12057 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12058}
12059
12060/**
12061 * Returns whether the given storage controller is hotplug capable.
12062 *
12063 * @returns true if the controller supports hotplugging
12064 * false otherwise.
12065 * @param enmCtrlType The controller type to check for.
12066 */
12067bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12068{
12069 ComPtr<ISystemProperties> systemProperties;
12070 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12071 if (FAILED(rc))
12072 return false;
12073
12074 BOOL aHotplugCapable = FALSE;
12075 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12076
12077 return RT_BOOL(aHotplugCapable);
12078}
12079
12080#ifdef VBOX_WITH_RESOURCE_USAGE_API
12081
12082void Machine::i_getDiskList(MediaList &list)
12083{
12084 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12085 it != mMediaData->mAttachments.end();
12086 ++it)
12087 {
12088 MediumAttachment* pAttach = *it;
12089 /* just in case */
12090 AssertStmt(pAttach, continue);
12091
12092 AutoCaller localAutoCallerA(pAttach);
12093 if (FAILED(localAutoCallerA.rc())) continue;
12094
12095 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12096
12097 if (pAttach->i_getType() == DeviceType_HardDisk)
12098 list.push_back(pAttach->i_getMedium());
12099 }
12100}
12101
12102void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12103{
12104 AssertReturnVoid(isWriteLockOnCurrentThread());
12105 AssertPtrReturnVoid(aCollector);
12106
12107 pm::CollectorHAL *hal = aCollector->getHAL();
12108 /* Create sub metrics */
12109 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12110 "Percentage of processor time spent in user mode by the VM process.");
12111 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12112 "Percentage of processor time spent in kernel mode by the VM process.");
12113 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12114 "Size of resident portion of VM process in memory.");
12115 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12116 "Actual size of all VM disks combined.");
12117 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12118 "Network receive rate.");
12119 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12120 "Network transmit rate.");
12121 /* Create and register base metrics */
12122 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12123 cpuLoadUser, cpuLoadKernel);
12124 aCollector->registerBaseMetric(cpuLoad);
12125 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12126 ramUsageUsed);
12127 aCollector->registerBaseMetric(ramUsage);
12128 MediaList disks;
12129 i_getDiskList(disks);
12130 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12131 diskUsageUsed);
12132 aCollector->registerBaseMetric(diskUsage);
12133
12134 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12135 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12136 new pm::AggregateAvg()));
12137 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12138 new pm::AggregateMin()));
12139 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12140 new pm::AggregateMax()));
12141 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12142 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12143 new pm::AggregateAvg()));
12144 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12145 new pm::AggregateMin()));
12146 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12147 new pm::AggregateMax()));
12148
12149 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12150 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12151 new pm::AggregateAvg()));
12152 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12153 new pm::AggregateMin()));
12154 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12155 new pm::AggregateMax()));
12156
12157 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12158 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12159 new pm::AggregateAvg()));
12160 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12161 new pm::AggregateMin()));
12162 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12163 new pm::AggregateMax()));
12164
12165
12166 /* Guest metrics collector */
12167 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12168 aCollector->registerGuest(mCollectorGuest);
12169 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12170 this, __PRETTY_FUNCTION__, mCollectorGuest));
12171
12172 /* Create sub metrics */
12173 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12174 "Percentage of processor time spent in user mode as seen by the guest.");
12175 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12176 "Percentage of processor time spent in kernel mode as seen by the guest.");
12177 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12178 "Percentage of processor time spent idling as seen by the guest.");
12179
12180 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12181 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12182 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12183 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12184 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12185 pm::SubMetric *guestMemCache = new pm::SubMetric(
12186 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12187
12188 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12189 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12190
12191 /* Create and register base metrics */
12192 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12193 machineNetRx, machineNetTx);
12194 aCollector->registerBaseMetric(machineNetRate);
12195
12196 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12197 guestLoadUser, guestLoadKernel, guestLoadIdle);
12198 aCollector->registerBaseMetric(guestCpuLoad);
12199
12200 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12201 guestMemTotal, guestMemFree,
12202 guestMemBalloon, guestMemShared,
12203 guestMemCache, guestPagedTotal);
12204 aCollector->registerBaseMetric(guestCpuMem);
12205
12206 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12207 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12208 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12209 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12210
12211 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12212 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12213 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12214 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12215
12216 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12217 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12218 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12219 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12220
12221 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12222 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12223 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12225
12226 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12227 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12228 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12229 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12230
12231 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12233 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12235
12236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12240
12241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12245
12246 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12250
12251 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12255
12256 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12260}
12261
12262void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12263{
12264 AssertReturnVoid(isWriteLockOnCurrentThread());
12265
12266 if (aCollector)
12267 {
12268 aCollector->unregisterMetricsFor(aMachine);
12269 aCollector->unregisterBaseMetricsFor(aMachine);
12270 }
12271}
12272
12273#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12274
12275
12276////////////////////////////////////////////////////////////////////////////////
12277
12278DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12279
12280HRESULT SessionMachine::FinalConstruct()
12281{
12282 LogFlowThisFunc(("\n"));
12283
12284 mClientToken = NULL;
12285
12286 return BaseFinalConstruct();
12287}
12288
12289void SessionMachine::FinalRelease()
12290{
12291 LogFlowThisFunc(("\n"));
12292
12293 Assert(!mClientToken);
12294 /* paranoia, should not hang around any more */
12295 if (mClientToken)
12296 {
12297 delete mClientToken;
12298 mClientToken = NULL;
12299 }
12300
12301 uninit(Uninit::Unexpected);
12302
12303 BaseFinalRelease();
12304}
12305
12306/**
12307 * @note Must be called only by Machine::LockMachine() from its own write lock.
12308 */
12309HRESULT SessionMachine::init(Machine *aMachine)
12310{
12311 LogFlowThisFuncEnter();
12312 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12313
12314 AssertReturn(aMachine, E_INVALIDARG);
12315
12316 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12317
12318 /* Enclose the state transition NotReady->InInit->Ready */
12319 AutoInitSpan autoInitSpan(this);
12320 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12321
12322 HRESULT rc = S_OK;
12323
12324 /* create the machine client token */
12325 try
12326 {
12327 mClientToken = new ClientToken(aMachine, this);
12328 if (!mClientToken->isReady())
12329 {
12330 delete mClientToken;
12331 mClientToken = NULL;
12332 rc = E_FAIL;
12333 }
12334 }
12335 catch (std::bad_alloc &)
12336 {
12337 rc = E_OUTOFMEMORY;
12338 }
12339 if (FAILED(rc))
12340 return rc;
12341
12342 /* memorize the peer Machine */
12343 unconst(mPeer) = aMachine;
12344 /* share the parent pointer */
12345 unconst(mParent) = aMachine->mParent;
12346
12347 /* take the pointers to data to share */
12348 mData.share(aMachine->mData);
12349 mSSData.share(aMachine->mSSData);
12350
12351 mUserData.share(aMachine->mUserData);
12352 mHWData.share(aMachine->mHWData);
12353 mMediaData.share(aMachine->mMediaData);
12354
12355 mStorageControllers.allocate();
12356 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12357 it != aMachine->mStorageControllers->end();
12358 ++it)
12359 {
12360 ComObjPtr<StorageController> ctl;
12361 ctl.createObject();
12362 ctl->init(this, *it);
12363 mStorageControllers->push_back(ctl);
12364 }
12365
12366 mUSBControllers.allocate();
12367 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12368 it != aMachine->mUSBControllers->end();
12369 ++it)
12370 {
12371 ComObjPtr<USBController> ctl;
12372 ctl.createObject();
12373 ctl->init(this, *it);
12374 mUSBControllers->push_back(ctl);
12375 }
12376
12377 unconst(mBIOSSettings).createObject();
12378 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12379 /* create another VRDEServer object that will be mutable */
12380 unconst(mVRDEServer).createObject();
12381 mVRDEServer->init(this, aMachine->mVRDEServer);
12382 /* create another audio adapter object that will be mutable */
12383 unconst(mAudioAdapter).createObject();
12384 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12385 /* create a list of serial ports that will be mutable */
12386 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12387 {
12388 unconst(mSerialPorts[slot]).createObject();
12389 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12390 }
12391 /* create a list of parallel ports that will be mutable */
12392 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12393 {
12394 unconst(mParallelPorts[slot]).createObject();
12395 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12396 }
12397
12398 /* create another USB device filters object that will be mutable */
12399 unconst(mUSBDeviceFilters).createObject();
12400 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12401
12402 /* create a list of network adapters that will be mutable */
12403 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12404 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12405 {
12406 unconst(mNetworkAdapters[slot]).createObject();
12407 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12408 }
12409
12410 /* create another bandwidth control object that will be mutable */
12411 unconst(mBandwidthControl).createObject();
12412 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12413
12414 /* default is to delete saved state on Saved -> PoweredOff transition */
12415 mRemoveSavedState = true;
12416
12417 /* Confirm a successful initialization when it's the case */
12418 autoInitSpan.setSucceeded();
12419
12420 miNATNetworksStarted = 0;
12421
12422 LogFlowThisFuncLeave();
12423 return rc;
12424}
12425
12426/**
12427 * Uninitializes this session object. If the reason is other than
12428 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12429 * or the client watcher code.
12430 *
12431 * @param aReason uninitialization reason
12432 *
12433 * @note Locks mParent + this object for writing.
12434 */
12435void SessionMachine::uninit(Uninit::Reason aReason)
12436{
12437 LogFlowThisFuncEnter();
12438 LogFlowThisFunc(("reason=%d\n", aReason));
12439
12440 /*
12441 * Strongly reference ourselves to prevent this object deletion after
12442 * mData->mSession.mMachine.setNull() below (which can release the last
12443 * reference and call the destructor). Important: this must be done before
12444 * accessing any members (and before AutoUninitSpan that does it as well).
12445 * This self reference will be released as the very last step on return.
12446 */
12447 ComObjPtr<SessionMachine> selfRef = this;
12448
12449 /* Enclose the state transition Ready->InUninit->NotReady */
12450 AutoUninitSpan autoUninitSpan(this);
12451 if (autoUninitSpan.uninitDone())
12452 {
12453 LogFlowThisFunc(("Already uninitialized\n"));
12454 LogFlowThisFuncLeave();
12455 return;
12456 }
12457
12458 if (autoUninitSpan.initFailed())
12459 {
12460 /* We've been called by init() because it's failed. It's not really
12461 * necessary (nor it's safe) to perform the regular uninit sequence
12462 * below, the following is enough.
12463 */
12464 LogFlowThisFunc(("Initialization failed.\n"));
12465 /* destroy the machine client token */
12466 if (mClientToken)
12467 {
12468 delete mClientToken;
12469 mClientToken = NULL;
12470 }
12471 uninitDataAndChildObjects();
12472 mData.free();
12473 unconst(mParent) = NULL;
12474 unconst(mPeer) = NULL;
12475 LogFlowThisFuncLeave();
12476 return;
12477 }
12478
12479 MachineState_T lastState;
12480 {
12481 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12482 lastState = mData->mMachineState;
12483 }
12484 NOREF(lastState);
12485
12486#ifdef VBOX_WITH_USB
12487 // release all captured USB devices, but do this before requesting the locks below
12488 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12489 {
12490 /* Console::captureUSBDevices() is called in the VM process only after
12491 * setting the machine state to Starting or Restoring.
12492 * Console::detachAllUSBDevices() will be called upon successful
12493 * termination. So, we need to release USB devices only if there was
12494 * an abnormal termination of a running VM.
12495 *
12496 * This is identical to SessionMachine::DetachAllUSBDevices except
12497 * for the aAbnormal argument. */
12498 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12499 AssertComRC(rc);
12500 NOREF(rc);
12501
12502 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12503 if (service)
12504 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12505 }
12506#endif /* VBOX_WITH_USB */
12507
12508 // we need to lock this object in uninit() because the lock is shared
12509 // with mPeer (as well as data we modify below). mParent lock is needed
12510 // by several calls to it, and USB needs host lock.
12511 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12512
12513#ifdef VBOX_WITH_RESOURCE_USAGE_API
12514 /*
12515 * It is safe to call Machine::i_unregisterMetrics() here because
12516 * PerformanceCollector::samplerCallback no longer accesses guest methods
12517 * holding the lock.
12518 */
12519 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12520 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12521 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12522 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 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12534 Global::IsOnlineOrTransient(lastState)));
12535
12536 /* reset the state to Aborted */
12537 if (mData->mMachineState != MachineState_Aborted)
12538 i_setMachineState(MachineState_Aborted);
12539 }
12540
12541 // any machine settings modified?
12542 if (mData->flModifications)
12543 {
12544 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12545 i_rollback(false /* aNotify */);
12546 }
12547
12548 mData->mSession.mPID = NIL_RTPROCESS;
12549
12550 if (aReason == Uninit::Unexpected)
12551 {
12552 /* Uninitialization didn't come from #checkForDeath(), so tell the
12553 * client watcher thread to update the set of machines that have open
12554 * sessions. */
12555 mParent->i_updateClientWatcher();
12556 }
12557
12558 /* uninitialize all remote controls */
12559 if (mData->mSession.mRemoteControls.size())
12560 {
12561 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12562 mData->mSession.mRemoteControls.size()));
12563
12564 Data::Session::RemoteControlList::iterator it =
12565 mData->mSession.mRemoteControls.begin();
12566 while (it != mData->mSession.mRemoteControls.end())
12567 {
12568 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12569 HRESULT rc = (*it)->Uninitialize();
12570 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12571 if (FAILED(rc))
12572 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12573 ++it;
12574 }
12575 mData->mSession.mRemoteControls.clear();
12576 }
12577
12578 /* Remove all references to the NAT network service. The service will stop
12579 * if all references (also from other VMs) are removed. */
12580 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12581 {
12582 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12583 {
12584 NetworkAttachmentType_T type;
12585 HRESULT hrc;
12586
12587 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12588 if ( SUCCEEDED(hrc)
12589 && type == NetworkAttachmentType_NATNetwork)
12590 {
12591 Bstr name;
12592 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12593 if (SUCCEEDED(hrc))
12594 {
12595 multilock.release();
12596 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12597 mUserData->s.strName.c_str(), name.raw()));
12598 mParent->i_natNetworkRefDec(name.raw());
12599 multilock.acquire();
12600 }
12601 }
12602 }
12603 }
12604
12605 /*
12606 * An expected uninitialization can come only from #checkForDeath().
12607 * Otherwise it means that something's gone really wrong (for example,
12608 * the Session implementation has released the VirtualBox reference
12609 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12610 * etc). However, it's also possible, that the client releases the IPC
12611 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12612 * but the VirtualBox release event comes first to the server process.
12613 * This case is practically possible, so we should not assert on an
12614 * unexpected uninit, just log a warning.
12615 */
12616
12617 if ((aReason == Uninit::Unexpected))
12618 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12619
12620 if (aReason != Uninit::Normal)
12621 {
12622 mData->mSession.mDirectControl.setNull();
12623 }
12624 else
12625 {
12626 /* this must be null here (see #OnSessionEnd()) */
12627 Assert(mData->mSession.mDirectControl.isNull());
12628 Assert(mData->mSession.mState == SessionState_Unlocking);
12629 Assert(!mData->mSession.mProgress.isNull());
12630 }
12631 if (mData->mSession.mProgress)
12632 {
12633 if (aReason == Uninit::Normal)
12634 mData->mSession.mProgress->i_notifyComplete(S_OK);
12635 else
12636 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12637 COM_IIDOF(ISession),
12638 getComponentName(),
12639 tr("The VM session was aborted"));
12640 mData->mSession.mProgress.setNull();
12641 }
12642
12643 /* remove the association between the peer machine and this session machine */
12644 Assert( (SessionMachine*)mData->mSession.mMachine == this
12645 || aReason == Uninit::Unexpected);
12646
12647 /* reset the rest of session data */
12648 mData->mSession.mLockType = LockType_Null;
12649 mData->mSession.mMachine.setNull();
12650 mData->mSession.mState = SessionState_Unlocked;
12651 mData->mSession.mType.setNull();
12652
12653 /* destroy the machine client token before leaving the exclusive lock */
12654 if (mClientToken)
12655 {
12656 delete mClientToken;
12657 mClientToken = NULL;
12658 }
12659
12660 /* fire an event */
12661 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12662
12663 uninitDataAndChildObjects();
12664
12665 /* free the essential data structure last */
12666 mData.free();
12667
12668 /* release the exclusive lock before setting the below two to NULL */
12669 multilock.release();
12670
12671 unconst(mParent) = NULL;
12672 unconst(mPeer) = NULL;
12673
12674 LogFlowThisFuncLeave();
12675}
12676
12677// util::Lockable interface
12678////////////////////////////////////////////////////////////////////////////////
12679
12680/**
12681 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12682 * with the primary Machine instance (mPeer).
12683 */
12684RWLockHandle *SessionMachine::lockHandle() const
12685{
12686 AssertReturn(mPeer != NULL, NULL);
12687 return mPeer->lockHandle();
12688}
12689
12690// IInternalMachineControl methods
12691////////////////////////////////////////////////////////////////////////////////
12692
12693/**
12694 * Passes collected guest statistics to performance collector object
12695 */
12696HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12697 ULONG aCpuKernel, ULONG aCpuIdle,
12698 ULONG aMemTotal, ULONG aMemFree,
12699 ULONG aMemBalloon, ULONG aMemShared,
12700 ULONG aMemCache, ULONG aPageTotal,
12701 ULONG aAllocVMM, ULONG aFreeVMM,
12702 ULONG aBalloonedVMM, ULONG aSharedVMM,
12703 ULONG aVmNetRx, ULONG aVmNetTx)
12704{
12705#ifdef VBOX_WITH_RESOURCE_USAGE_API
12706 if (mCollectorGuest)
12707 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12708 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12709 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12710 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12711
12712 return S_OK;
12713#else
12714 NOREF(aValidStats);
12715 NOREF(aCpuUser);
12716 NOREF(aCpuKernel);
12717 NOREF(aCpuIdle);
12718 NOREF(aMemTotal);
12719 NOREF(aMemFree);
12720 NOREF(aMemBalloon);
12721 NOREF(aMemShared);
12722 NOREF(aMemCache);
12723 NOREF(aPageTotal);
12724 NOREF(aAllocVMM);
12725 NOREF(aFreeVMM);
12726 NOREF(aBalloonedVMM);
12727 NOREF(aSharedVMM);
12728 NOREF(aVmNetRx);
12729 NOREF(aVmNetTx);
12730 return E_NOTIMPL;
12731#endif
12732}
12733
12734////////////////////////////////////////////////////////////////////////////////
12735//
12736// SessionMachine task records
12737//
12738////////////////////////////////////////////////////////////////////////////////
12739
12740/**
12741 * Task record for saving the machine state.
12742 */
12743struct SessionMachine::SaveStateTask
12744 : public Machine::Task
12745{
12746 SaveStateTask(SessionMachine *m,
12747 Progress *p,
12748 const Utf8Str &t,
12749 Reason_T enmReason,
12750 const Utf8Str &strStateFilePath)
12751 : Task(m, p, t),
12752 m_enmReason(enmReason),
12753 m_strStateFilePath(strStateFilePath)
12754 {}
12755
12756 void handler()
12757 {
12758 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12759 }
12760
12761 Reason_T m_enmReason;
12762 Utf8Str m_strStateFilePath;
12763};
12764
12765/**
12766 * Task thread implementation for SessionMachine::SaveState(), called from
12767 * SessionMachine::taskHandler().
12768 *
12769 * @note Locks this object for writing.
12770 *
12771 * @param task
12772 * @return
12773 */
12774void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12775{
12776 LogFlowThisFuncEnter();
12777
12778 AutoCaller autoCaller(this);
12779 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12780 if (FAILED(autoCaller.rc()))
12781 {
12782 /* we might have been uninitialized because the session was accidentally
12783 * closed by the client, so don't assert */
12784 HRESULT rc = setError(E_FAIL,
12785 tr("The session has been accidentally closed"));
12786 task.m_pProgress->i_notifyComplete(rc);
12787 LogFlowThisFuncLeave();
12788 return;
12789 }
12790
12791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12792
12793 HRESULT rc = S_OK;
12794
12795 try
12796 {
12797 ComPtr<IInternalSessionControl> directControl;
12798 if (mData->mSession.mLockType == LockType_VM)
12799 directControl = mData->mSession.mDirectControl;
12800 if (directControl.isNull())
12801 throw setError(VBOX_E_INVALID_VM_STATE,
12802 tr("Trying to save state without a running VM"));
12803 alock.release();
12804 BOOL fSuspendedBySave;
12805 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12806 Assert(!fSuspendedBySave);
12807 alock.acquire();
12808
12809 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12810 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12811 throw E_FAIL);
12812
12813 if (SUCCEEDED(rc))
12814 {
12815 mSSData->strStateFilePath = task.m_strStateFilePath;
12816
12817 /* save all VM settings */
12818 rc = i_saveSettings(NULL);
12819 // no need to check whether VirtualBox.xml needs saving also since
12820 // we can't have a name change pending at this point
12821 }
12822 else
12823 {
12824 // On failure, set the state to the state we had at the beginning.
12825 i_setMachineState(task.m_machineStateBackup);
12826 i_updateMachineStateOnClient();
12827
12828 // Delete the saved state file (might have been already created).
12829 // No need to check whether this is shared with a snapshot here
12830 // because we certainly created a fresh saved state file here.
12831 RTFileDelete(task.m_strStateFilePath.c_str());
12832 }
12833 }
12834 catch (HRESULT aRC) { rc = aRC; }
12835
12836 task.m_pProgress->i_notifyComplete(rc);
12837
12838 LogFlowThisFuncLeave();
12839}
12840
12841/**
12842 * @note Locks this object for writing.
12843 */
12844HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12845{
12846 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12847}
12848
12849HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12850{
12851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12852
12853 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12854 if (FAILED(rc)) return rc;
12855
12856 if ( mData->mMachineState != MachineState_Running
12857 && mData->mMachineState != MachineState_Paused
12858 )
12859 return setError(VBOX_E_INVALID_VM_STATE,
12860 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12861 Global::stringifyMachineState(mData->mMachineState));
12862
12863 ComObjPtr<Progress> pProgress;
12864 pProgress.createObject();
12865 rc = pProgress->init(i_getVirtualBox(),
12866 static_cast<IMachine *>(this) /* aInitiator */,
12867 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12868 FALSE /* aCancelable */);
12869 if (FAILED(rc))
12870 return rc;
12871
12872 Utf8Str strStateFilePath;
12873 i_composeSavedStateFilename(strStateFilePath);
12874
12875 /* create and start the task on a separate thread (note that it will not
12876 * start working until we release alock) */
12877 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12878 rc = pTask->createThread();
12879 if (FAILED(rc))
12880 return rc;
12881
12882 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12883 i_setMachineState(MachineState_Saving);
12884 i_updateMachineStateOnClient();
12885
12886 pProgress.queryInterfaceTo(aProgress.asOutParam());
12887
12888 return S_OK;
12889}
12890
12891/**
12892 * @note Locks this object for writing.
12893 */
12894HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12895{
12896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12897
12898 HRESULT rc = i_checkStateDependency(MutableStateDep);
12899 if (FAILED(rc)) return rc;
12900
12901 if ( mData->mMachineState != MachineState_PoweredOff
12902 && mData->mMachineState != MachineState_Teleported
12903 && mData->mMachineState != MachineState_Aborted
12904 )
12905 return setError(VBOX_E_INVALID_VM_STATE,
12906 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12907 Global::stringifyMachineState(mData->mMachineState));
12908
12909 com::Utf8Str stateFilePathFull;
12910 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12911 if (RT_FAILURE(vrc))
12912 return setError(VBOX_E_FILE_ERROR,
12913 tr("Invalid saved state file path '%s' (%Rrc)"),
12914 aSavedStateFile.c_str(),
12915 vrc);
12916
12917 mSSData->strStateFilePath = stateFilePathFull;
12918
12919 /* The below i_setMachineState() will detect the state transition and will
12920 * update the settings file */
12921
12922 return i_setMachineState(MachineState_Saved);
12923}
12924
12925/**
12926 * @note Locks this object for writing.
12927 */
12928HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12929{
12930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12931
12932 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12933 if (FAILED(rc)) return rc;
12934
12935 if (mData->mMachineState != MachineState_Saved)
12936 return setError(VBOX_E_INVALID_VM_STATE,
12937 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12938 Global::stringifyMachineState(mData->mMachineState));
12939
12940 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12941
12942 /*
12943 * Saved -> PoweredOff transition will be detected in the SessionMachine
12944 * and properly handled.
12945 */
12946 rc = i_setMachineState(MachineState_PoweredOff);
12947 return rc;
12948}
12949
12950
12951/**
12952 * @note Locks the same as #i_setMachineState() does.
12953 */
12954HRESULT SessionMachine::updateState(MachineState_T aState)
12955{
12956 return i_setMachineState(aState);
12957}
12958
12959/**
12960 * @note Locks this object for writing.
12961 */
12962HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12963{
12964 IProgress* pProgress(aProgress);
12965
12966 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12967
12968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12969
12970 if (mData->mSession.mState != SessionState_Locked)
12971 return VBOX_E_INVALID_OBJECT_STATE;
12972
12973 if (!mData->mSession.mProgress.isNull())
12974 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12975
12976 /* If we didn't reference the NAT network service yet, add a reference to
12977 * force a start */
12978 if (miNATNetworksStarted < 1)
12979 {
12980 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12981 {
12982 NetworkAttachmentType_T type;
12983 HRESULT hrc;
12984 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12985 if ( SUCCEEDED(hrc)
12986 && type == NetworkAttachmentType_NATNetwork)
12987 {
12988 Bstr name;
12989 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12990 if (SUCCEEDED(hrc))
12991 {
12992 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12993 mUserData->s.strName.c_str(), name.raw()));
12994 mPeer->lockHandle()->unlockWrite();
12995 mParent->i_natNetworkRefInc(name.raw());
12996#ifdef RT_LOCK_STRICT
12997 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12998#else
12999 mPeer->lockHandle()->lockWrite();
13000#endif
13001 }
13002 }
13003 }
13004 miNATNetworksStarted++;
13005 }
13006
13007 LogFlowThisFunc(("returns S_OK.\n"));
13008 return S_OK;
13009}
13010
13011/**
13012 * @note Locks this object for writing.
13013 */
13014HRESULT SessionMachine::endPowerUp(LONG aResult)
13015{
13016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13017
13018 if (mData->mSession.mState != SessionState_Locked)
13019 return VBOX_E_INVALID_OBJECT_STATE;
13020
13021 /* Finalize the LaunchVMProcess progress object. */
13022 if (mData->mSession.mProgress)
13023 {
13024 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13025 mData->mSession.mProgress.setNull();
13026 }
13027
13028 if (SUCCEEDED((HRESULT)aResult))
13029 {
13030#ifdef VBOX_WITH_RESOURCE_USAGE_API
13031 /* The VM has been powered up successfully, so it makes sense
13032 * now to offer the performance metrics for a running machine
13033 * object. Doing it earlier wouldn't be safe. */
13034 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13035 mData->mSession.mPID);
13036#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13037 }
13038
13039 return S_OK;
13040}
13041
13042/**
13043 * @note Locks this object for writing.
13044 */
13045HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13046{
13047 LogFlowThisFuncEnter();
13048
13049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13050
13051 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13052 E_FAIL);
13053
13054 /* create a progress object to track operation completion */
13055 ComObjPtr<Progress> pProgress;
13056 pProgress.createObject();
13057 pProgress->init(i_getVirtualBox(),
13058 static_cast<IMachine *>(this) /* aInitiator */,
13059 Bstr(tr("Stopping the virtual machine")).raw(),
13060 FALSE /* aCancelable */);
13061
13062 /* fill in the console task data */
13063 mConsoleTaskData.mLastState = mData->mMachineState;
13064 mConsoleTaskData.mProgress = pProgress;
13065
13066 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13067 i_setMachineState(MachineState_Stopping);
13068
13069 pProgress.queryInterfaceTo(aProgress.asOutParam());
13070
13071 return S_OK;
13072}
13073
13074/**
13075 * @note Locks this object for writing.
13076 */
13077HRESULT SessionMachine::endPoweringDown(LONG aResult,
13078 const com::Utf8Str &aErrMsg)
13079{
13080 LogFlowThisFuncEnter();
13081
13082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13083
13084 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13085 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13086 && mConsoleTaskData.mLastState != MachineState_Null,
13087 E_FAIL);
13088
13089 /*
13090 * On failure, set the state to the state we had when BeginPoweringDown()
13091 * was called (this is expected by Console::PowerDown() and the associated
13092 * task). On success the VM process already changed the state to
13093 * MachineState_PoweredOff, so no need to do anything.
13094 */
13095 if (FAILED(aResult))
13096 i_setMachineState(mConsoleTaskData.mLastState);
13097
13098 /* notify the progress object about operation completion */
13099 Assert(mConsoleTaskData.mProgress);
13100 if (SUCCEEDED(aResult))
13101 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13102 else
13103 {
13104 if (aErrMsg.length())
13105 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13106 COM_IIDOF(ISession),
13107 getComponentName(),
13108 aErrMsg.c_str());
13109 else
13110 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13111 }
13112
13113 /* clear out the temporary saved state data */
13114 mConsoleTaskData.mLastState = MachineState_Null;
13115 mConsoleTaskData.mProgress.setNull();
13116
13117 LogFlowThisFuncLeave();
13118 return S_OK;
13119}
13120
13121
13122/**
13123 * Goes through the USB filters of the given machine to see if the given
13124 * device matches any filter or not.
13125 *
13126 * @note Locks the same as USBController::hasMatchingFilter() does.
13127 */
13128HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13129 BOOL *aMatched,
13130 ULONG *aMaskedInterfaces)
13131{
13132 LogFlowThisFunc(("\n"));
13133
13134#ifdef VBOX_WITH_USB
13135 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13136#else
13137 NOREF(aDevice);
13138 NOREF(aMaskedInterfaces);
13139 *aMatched = FALSE;
13140#endif
13141
13142 return S_OK;
13143}
13144
13145/**
13146 * @note Locks the same as Host::captureUSBDevice() does.
13147 */
13148HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13149{
13150 LogFlowThisFunc(("\n"));
13151
13152#ifdef VBOX_WITH_USB
13153 /* if captureDeviceForVM() fails, it must have set extended error info */
13154 clearError();
13155 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13156 if (FAILED(rc)) return rc;
13157
13158 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13159 AssertReturn(service, E_FAIL);
13160 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13161#else
13162 NOREF(aId);
13163 return E_NOTIMPL;
13164#endif
13165}
13166
13167/**
13168 * @note Locks the same as Host::detachUSBDevice() does.
13169 */
13170HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13171 BOOL aDone)
13172{
13173 LogFlowThisFunc(("\n"));
13174
13175#ifdef VBOX_WITH_USB
13176 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13177 AssertReturn(service, E_FAIL);
13178 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13179#else
13180 NOREF(aId);
13181 NOREF(aDone);
13182 return E_NOTIMPL;
13183#endif
13184}
13185
13186/**
13187 * Inserts all machine filters to the USB proxy service and then calls
13188 * Host::autoCaptureUSBDevices().
13189 *
13190 * Called by Console from the VM process upon VM startup.
13191 *
13192 * @note Locks what called methods lock.
13193 */
13194HRESULT SessionMachine::autoCaptureUSBDevices()
13195{
13196 LogFlowThisFunc(("\n"));
13197
13198#ifdef VBOX_WITH_USB
13199 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13200 AssertComRC(rc);
13201 NOREF(rc);
13202
13203 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13204 AssertReturn(service, E_FAIL);
13205 return service->autoCaptureDevicesForVM(this);
13206#else
13207 return S_OK;
13208#endif
13209}
13210
13211/**
13212 * Removes all machine filters from the USB proxy service and then calls
13213 * Host::detachAllUSBDevices().
13214 *
13215 * Called by Console from the VM process upon normal VM termination or by
13216 * SessionMachine::uninit() upon abnormal VM termination (from under the
13217 * Machine/SessionMachine lock).
13218 *
13219 * @note Locks what called methods lock.
13220 */
13221HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13222{
13223 LogFlowThisFunc(("\n"));
13224
13225#ifdef VBOX_WITH_USB
13226 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13227 AssertComRC(rc);
13228 NOREF(rc);
13229
13230 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13231 AssertReturn(service, E_FAIL);
13232 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13233#else
13234 NOREF(aDone);
13235 return S_OK;
13236#endif
13237}
13238
13239/**
13240 * @note Locks this object for writing.
13241 */
13242HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13243 ComPtr<IProgress> &aProgress)
13244{
13245 LogFlowThisFuncEnter();
13246
13247 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13248 /*
13249 * We don't assert below because it might happen that a non-direct session
13250 * informs us it is closed right after we've been uninitialized -- it's ok.
13251 */
13252
13253 /* get IInternalSessionControl interface */
13254 ComPtr<IInternalSessionControl> control(aSession);
13255
13256 ComAssertRet(!control.isNull(), E_INVALIDARG);
13257
13258 /* Creating a Progress object requires the VirtualBox lock, and
13259 * thus locking it here is required by the lock order rules. */
13260 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13261
13262 if (control == mData->mSession.mDirectControl)
13263 {
13264 /* The direct session is being normally closed by the client process
13265 * ----------------------------------------------------------------- */
13266
13267 /* go to the closing state (essential for all open*Session() calls and
13268 * for #checkForDeath()) */
13269 Assert(mData->mSession.mState == SessionState_Locked);
13270 mData->mSession.mState = SessionState_Unlocking;
13271
13272 /* set direct control to NULL to release the remote instance */
13273 mData->mSession.mDirectControl.setNull();
13274 LogFlowThisFunc(("Direct control is set to NULL\n"));
13275
13276 if (mData->mSession.mProgress)
13277 {
13278 /* finalize the progress, someone might wait if a frontend
13279 * closes the session before powering on the VM. */
13280 mData->mSession.mProgress->notifyComplete(E_FAIL,
13281 COM_IIDOF(ISession),
13282 getComponentName(),
13283 tr("The VM session was closed before any attempt to power it on"));
13284 mData->mSession.mProgress.setNull();
13285 }
13286
13287 /* Create the progress object the client will use to wait until
13288 * #checkForDeath() is called to uninitialize this session object after
13289 * it releases the IPC semaphore.
13290 * Note! Because we're "reusing" mProgress here, this must be a proxy
13291 * object just like for LaunchVMProcess. */
13292 Assert(mData->mSession.mProgress.isNull());
13293 ComObjPtr<ProgressProxy> progress;
13294 progress.createObject();
13295 ComPtr<IUnknown> pPeer(mPeer);
13296 progress->init(mParent, pPeer,
13297 Bstr(tr("Closing session")).raw(),
13298 FALSE /* aCancelable */);
13299 progress.queryInterfaceTo(aProgress.asOutParam());
13300 mData->mSession.mProgress = progress;
13301 }
13302 else
13303 {
13304 /* the remote session is being normally closed */
13305 Data::Session::RemoteControlList::iterator it =
13306 mData->mSession.mRemoteControls.begin();
13307 while (it != mData->mSession.mRemoteControls.end())
13308 {
13309 if (control == *it)
13310 break;
13311 ++it;
13312 }
13313 BOOL found = it != mData->mSession.mRemoteControls.end();
13314 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13315 E_INVALIDARG);
13316 // This MUST be erase(it), not remove(*it) as the latter triggers a
13317 // very nasty use after free due to the place where the value "lives".
13318 mData->mSession.mRemoteControls.erase(it);
13319 }
13320
13321 /* signal the client watcher thread, because the client is going away */
13322 mParent->i_updateClientWatcher();
13323
13324 LogFlowThisFuncLeave();
13325 return S_OK;
13326}
13327
13328HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13329 std::vector<com::Utf8Str> &aValues,
13330 std::vector<LONG64> &aTimestamps,
13331 std::vector<com::Utf8Str> &aFlags)
13332{
13333 LogFlowThisFunc(("\n"));
13334
13335#ifdef VBOX_WITH_GUEST_PROPS
13336 using namespace guestProp;
13337
13338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13339
13340 size_t cEntries = mHWData->mGuestProperties.size();
13341 aNames.resize(cEntries);
13342 aValues.resize(cEntries);
13343 aTimestamps.resize(cEntries);
13344 aFlags.resize(cEntries);
13345
13346 size_t i = 0;
13347 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13348 it != mHWData->mGuestProperties.end();
13349 ++it, ++i)
13350 {
13351 char szFlags[MAX_FLAGS_LEN + 1];
13352 aNames[i] = it->first;
13353 aValues[i] = it->second.strValue;
13354 aTimestamps[i] = it->second.mTimestamp;
13355
13356 /* If it is NULL, keep it NULL. */
13357 if (it->second.mFlags)
13358 {
13359 writeFlags(it->second.mFlags, szFlags);
13360 aFlags[i] = szFlags;
13361 }
13362 else
13363 aFlags[i] = "";
13364 }
13365 return S_OK;
13366#else
13367 ReturnComNotImplemented();
13368#endif
13369}
13370
13371HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13372 const com::Utf8Str &aValue,
13373 LONG64 aTimestamp,
13374 const com::Utf8Str &aFlags,
13375 BOOL *aNotify)
13376{
13377 LogFlowThisFunc(("\n"));
13378
13379#ifdef VBOX_WITH_GUEST_PROPS
13380 using namespace guestProp;
13381
13382 *aNotify = FALSE;
13383
13384 try
13385 {
13386 /*
13387 * Convert input up front.
13388 */
13389 uint32_t fFlags = NILFLAG;
13390 if (aFlags.length())
13391 {
13392 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13393 AssertRCReturn(vrc, E_INVALIDARG);
13394 }
13395
13396 /*
13397 * Now grab the object lock, validate the state and do the update.
13398 */
13399
13400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13401
13402 switch (mData->mMachineState)
13403 {
13404 case MachineState_Paused:
13405 case MachineState_Running:
13406 case MachineState_Teleporting:
13407 case MachineState_TeleportingPausedVM:
13408 case MachineState_OnlineSnapshotting:
13409 case MachineState_LiveSnapshotting:
13410 case MachineState_DeletingSnapshotOnline:
13411 case MachineState_DeletingSnapshotPaused:
13412 case MachineState_Saving:
13413 case MachineState_Stopping:
13414 break;
13415
13416 default:
13417 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13418 VBOX_E_INVALID_VM_STATE);
13419 }
13420
13421 i_setModified(IsModified_MachineData);
13422 mHWData.backup();
13423
13424 bool fDelete = !aValue.length();
13425 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13426 if (it != mHWData->mGuestProperties.end())
13427 {
13428 if (!fDelete)
13429 {
13430 it->second.strValue = aValue;
13431 it->second.mTimestamp = aTimestamp;
13432 it->second.mFlags = fFlags;
13433 }
13434 else
13435 mHWData->mGuestProperties.erase(it);
13436
13437 mData->mGuestPropertiesModified = TRUE;
13438 }
13439 else if (!fDelete)
13440 {
13441 HWData::GuestProperty prop;
13442 prop.strValue = aValue;
13443 prop.mTimestamp = aTimestamp;
13444 prop.mFlags = fFlags;
13445
13446 mHWData->mGuestProperties[aName] = prop;
13447 mData->mGuestPropertiesModified = TRUE;
13448 }
13449
13450 /*
13451 * Send a callback notification if appropriate
13452 */
13453 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13454 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13455 RTSTR_MAX,
13456 aName.c_str(),
13457 RTSTR_MAX, NULL)
13458 )
13459 {
13460 alock.release();
13461
13462 mParent->i_onGuestPropertyChange(mData->mUuid,
13463 Bstr(aName).raw(),
13464 Bstr(aValue).raw(),
13465 Bstr(aFlags).raw());
13466 *aNotify = TRUE;
13467 }
13468 }
13469 catch (...)
13470 {
13471 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13472 }
13473 return S_OK;
13474#else
13475 ReturnComNotImplemented();
13476#endif
13477}
13478
13479
13480HRESULT SessionMachine::lockMedia()
13481{
13482 AutoMultiWriteLock2 alock(this->lockHandle(),
13483 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13484
13485 AssertReturn( mData->mMachineState == MachineState_Starting
13486 || mData->mMachineState == MachineState_Restoring
13487 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13488
13489 clearError();
13490 alock.release();
13491 return i_lockMedia();
13492}
13493
13494HRESULT SessionMachine::unlockMedia()
13495{
13496 HRESULT hrc = i_unlockMedia();
13497 return hrc;
13498}
13499
13500HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13501 ComPtr<IMediumAttachment> &aNewAttachment)
13502{
13503 // request the host lock first, since might be calling Host methods for getting host drives;
13504 // next, protect the media tree all the while we're in here, as well as our member variables
13505 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13506 this->lockHandle(),
13507 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13508
13509 IMediumAttachment *iAttach = aAttachment;
13510 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13511
13512 Bstr ctrlName;
13513 LONG lPort;
13514 LONG lDevice;
13515 bool fTempEject;
13516 {
13517 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13518
13519 /* Need to query the details first, as the IMediumAttachment reference
13520 * might be to the original settings, which we are going to change. */
13521 ctrlName = pAttach->i_getControllerName();
13522 lPort = pAttach->i_getPort();
13523 lDevice = pAttach->i_getDevice();
13524 fTempEject = pAttach->i_getTempEject();
13525 }
13526
13527 if (!fTempEject)
13528 {
13529 /* Remember previously mounted medium. The medium before taking the
13530 * backup is not necessarily the same thing. */
13531 ComObjPtr<Medium> oldmedium;
13532 oldmedium = pAttach->i_getMedium();
13533
13534 i_setModified(IsModified_Storage);
13535 mMediaData.backup();
13536
13537 // The backup operation makes the pAttach reference point to the
13538 // old settings. Re-get the correct reference.
13539 pAttach = i_findAttachment(mMediaData->mAttachments,
13540 ctrlName.raw(),
13541 lPort,
13542 lDevice);
13543
13544 {
13545 AutoCaller autoAttachCaller(this);
13546 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13547
13548 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13549 if (!oldmedium.isNull())
13550 oldmedium->i_removeBackReference(mData->mUuid);
13551
13552 pAttach->i_updateMedium(NULL);
13553 pAttach->i_updateEjected();
13554 }
13555
13556 i_setModified(IsModified_Storage);
13557 }
13558 else
13559 {
13560 {
13561 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13562 pAttach->i_updateEjected();
13563 }
13564 }
13565
13566 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13567
13568 return S_OK;
13569}
13570
13571// public methods only for internal purposes
13572/////////////////////////////////////////////////////////////////////////////
13573
13574#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13575/**
13576 * Called from the client watcher thread to check for expected or unexpected
13577 * death of the client process that has a direct session to this machine.
13578 *
13579 * On Win32 and on OS/2, this method is called only when we've got the
13580 * mutex (i.e. the client has either died or terminated normally) so it always
13581 * returns @c true (the client is terminated, the session machine is
13582 * uninitialized).
13583 *
13584 * On other platforms, the method returns @c true if the client process has
13585 * terminated normally or abnormally and the session machine was uninitialized,
13586 * and @c false if the client process is still alive.
13587 *
13588 * @note Locks this object for writing.
13589 */
13590bool SessionMachine::i_checkForDeath()
13591{
13592 Uninit::Reason reason;
13593 bool terminated = false;
13594
13595 /* Enclose autoCaller with a block because calling uninit() from under it
13596 * will deadlock. */
13597 {
13598 AutoCaller autoCaller(this);
13599 if (!autoCaller.isOk())
13600 {
13601 /* return true if not ready, to cause the client watcher to exclude
13602 * the corresponding session from watching */
13603 LogFlowThisFunc(("Already uninitialized!\n"));
13604 return true;
13605 }
13606
13607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13608
13609 /* Determine the reason of death: if the session state is Closing here,
13610 * everything is fine. Otherwise it means that the client did not call
13611 * OnSessionEnd() before it released the IPC semaphore. This may happen
13612 * either because the client process has abnormally terminated, or
13613 * because it simply forgot to call ISession::Close() before exiting. We
13614 * threat the latter also as an abnormal termination (see
13615 * Session::uninit() for details). */
13616 reason = mData->mSession.mState == SessionState_Unlocking ?
13617 Uninit::Normal :
13618 Uninit::Abnormal;
13619
13620 if (mClientToken)
13621 terminated = mClientToken->release();
13622 } /* AutoCaller block */
13623
13624 if (terminated)
13625 uninit(reason);
13626
13627 return terminated;
13628}
13629
13630void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13631{
13632 LogFlowThisFunc(("\n"));
13633
13634 strTokenId.setNull();
13635
13636 AutoCaller autoCaller(this);
13637 AssertComRCReturnVoid(autoCaller.rc());
13638
13639 Assert(mClientToken);
13640 if (mClientToken)
13641 mClientToken->getId(strTokenId);
13642}
13643#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13644IToken *SessionMachine::i_getToken()
13645{
13646 LogFlowThisFunc(("\n"));
13647
13648 AutoCaller autoCaller(this);
13649 AssertComRCReturn(autoCaller.rc(), NULL);
13650
13651 Assert(mClientToken);
13652 if (mClientToken)
13653 return mClientToken->getToken();
13654 else
13655 return NULL;
13656}
13657#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13658
13659Machine::ClientToken *SessionMachine::i_getClientToken()
13660{
13661 LogFlowThisFunc(("\n"));
13662
13663 AutoCaller autoCaller(this);
13664 AssertComRCReturn(autoCaller.rc(), NULL);
13665
13666 return mClientToken;
13667}
13668
13669
13670/**
13671 * @note Locks this object for reading.
13672 */
13673HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13674{
13675 LogFlowThisFunc(("\n"));
13676
13677 AutoCaller autoCaller(this);
13678 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13679
13680 ComPtr<IInternalSessionControl> directControl;
13681 {
13682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13683 if (mData->mSession.mLockType == LockType_VM)
13684 directControl = mData->mSession.mDirectControl;
13685 }
13686
13687 /* ignore notifications sent after #OnSessionEnd() is called */
13688 if (!directControl)
13689 return S_OK;
13690
13691 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13692}
13693
13694/**
13695 * @note Locks this object for reading.
13696 */
13697HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13698 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13699 IN_BSTR aGuestIp, LONG aGuestPort)
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 * instead acting like callback we ask IVirtualBox deliver corresponding event
13718 */
13719
13720 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13721 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13722 return S_OK;
13723}
13724
13725/**
13726 * @note Locks this object for reading.
13727 */
13728HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13729{
13730 LogFlowThisFunc(("\n"));
13731
13732 AutoCaller autoCaller(this);
13733 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13734
13735 ComPtr<IInternalSessionControl> directControl;
13736 {
13737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13738 if (mData->mSession.mLockType == LockType_VM)
13739 directControl = mData->mSession.mDirectControl;
13740 }
13741
13742 /* ignore notifications sent after #OnSessionEnd() is called */
13743 if (!directControl)
13744 return S_OK;
13745
13746 return directControl->OnSerialPortChange(serialPort);
13747}
13748
13749/**
13750 * @note Locks this object for reading.
13751 */
13752HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13753{
13754 LogFlowThisFunc(("\n"));
13755
13756 AutoCaller autoCaller(this);
13757 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13758
13759 ComPtr<IInternalSessionControl> directControl;
13760 {
13761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13762 if (mData->mSession.mLockType == LockType_VM)
13763 directControl = mData->mSession.mDirectControl;
13764 }
13765
13766 /* ignore notifications sent after #OnSessionEnd() is called */
13767 if (!directControl)
13768 return S_OK;
13769
13770 return directControl->OnParallelPortChange(parallelPort);
13771}
13772
13773/**
13774 * @note Locks this object for reading.
13775 */
13776HRESULT SessionMachine::i_onStorageControllerChange()
13777{
13778 LogFlowThisFunc(("\n"));
13779
13780 AutoCaller autoCaller(this);
13781 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13782
13783 ComPtr<IInternalSessionControl> directControl;
13784 {
13785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13786 if (mData->mSession.mLockType == LockType_VM)
13787 directControl = mData->mSession.mDirectControl;
13788 }
13789
13790 /* ignore notifications sent after #OnSessionEnd() is called */
13791 if (!directControl)
13792 return S_OK;
13793
13794 return directControl->OnStorageControllerChange();
13795}
13796
13797/**
13798 * @note Locks this object for reading.
13799 */
13800HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13801{
13802 LogFlowThisFunc(("\n"));
13803
13804 AutoCaller autoCaller(this);
13805 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13806
13807 ComPtr<IInternalSessionControl> directControl;
13808 {
13809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13810 if (mData->mSession.mLockType == LockType_VM)
13811 directControl = mData->mSession.mDirectControl;
13812 }
13813
13814 /* ignore notifications sent after #OnSessionEnd() is called */
13815 if (!directControl)
13816 return S_OK;
13817
13818 return directControl->OnMediumChange(aAttachment, aForce);
13819}
13820
13821/**
13822 * @note Locks this object for reading.
13823 */
13824HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13825{
13826 LogFlowThisFunc(("\n"));
13827
13828 AutoCaller autoCaller(this);
13829 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13830
13831 ComPtr<IInternalSessionControl> directControl;
13832 {
13833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13834 if (mData->mSession.mLockType == LockType_VM)
13835 directControl = mData->mSession.mDirectControl;
13836 }
13837
13838 /* ignore notifications sent after #OnSessionEnd() is called */
13839 if (!directControl)
13840 return S_OK;
13841
13842 return directControl->OnCPUChange(aCPU, aRemove);
13843}
13844
13845HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13846{
13847 LogFlowThisFunc(("\n"));
13848
13849 AutoCaller autoCaller(this);
13850 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13851
13852 ComPtr<IInternalSessionControl> directControl;
13853 {
13854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13855 if (mData->mSession.mLockType == LockType_VM)
13856 directControl = mData->mSession.mDirectControl;
13857 }
13858
13859 /* ignore notifications sent after #OnSessionEnd() is called */
13860 if (!directControl)
13861 return S_OK;
13862
13863 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13864}
13865
13866/**
13867 * @note Locks this object for reading.
13868 */
13869HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13870{
13871 LogFlowThisFunc(("\n"));
13872
13873 AutoCaller autoCaller(this);
13874 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13875
13876 ComPtr<IInternalSessionControl> directControl;
13877 {
13878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13879 if (mData->mSession.mLockType == LockType_VM)
13880 directControl = mData->mSession.mDirectControl;
13881 }
13882
13883 /* ignore notifications sent after #OnSessionEnd() is called */
13884 if (!directControl)
13885 return S_OK;
13886
13887 return directControl->OnVRDEServerChange(aRestart);
13888}
13889
13890/**
13891 * @note Locks this object for reading.
13892 */
13893HRESULT SessionMachine::i_onVideoCaptureChange()
13894{
13895 LogFlowThisFunc(("\n"));
13896
13897 AutoCaller autoCaller(this);
13898 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13899
13900 ComPtr<IInternalSessionControl> directControl;
13901 {
13902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13903 if (mData->mSession.mLockType == LockType_VM)
13904 directControl = mData->mSession.mDirectControl;
13905 }
13906
13907 /* ignore notifications sent after #OnSessionEnd() is called */
13908 if (!directControl)
13909 return S_OK;
13910
13911 return directControl->OnVideoCaptureChange();
13912}
13913
13914/**
13915 * @note Locks this object for reading.
13916 */
13917HRESULT SessionMachine::i_onUSBControllerChange()
13918{
13919 LogFlowThisFunc(("\n"));
13920
13921 AutoCaller autoCaller(this);
13922 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13923
13924 ComPtr<IInternalSessionControl> directControl;
13925 {
13926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13927 if (mData->mSession.mLockType == LockType_VM)
13928 directControl = mData->mSession.mDirectControl;
13929 }
13930
13931 /* ignore notifications sent after #OnSessionEnd() is called */
13932 if (!directControl)
13933 return S_OK;
13934
13935 return directControl->OnUSBControllerChange();
13936}
13937
13938/**
13939 * @note Locks this object for reading.
13940 */
13941HRESULT SessionMachine::i_onSharedFolderChange()
13942{
13943 LogFlowThisFunc(("\n"));
13944
13945 AutoCaller autoCaller(this);
13946 AssertComRCReturnRC(autoCaller.rc());
13947
13948 ComPtr<IInternalSessionControl> directControl;
13949 {
13950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13951 if (mData->mSession.mLockType == LockType_VM)
13952 directControl = mData->mSession.mDirectControl;
13953 }
13954
13955 /* ignore notifications sent after #OnSessionEnd() is called */
13956 if (!directControl)
13957 return S_OK;
13958
13959 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13960}
13961
13962/**
13963 * @note Locks this object for reading.
13964 */
13965HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13966{
13967 LogFlowThisFunc(("\n"));
13968
13969 AutoCaller autoCaller(this);
13970 AssertComRCReturnRC(autoCaller.rc());
13971
13972 ComPtr<IInternalSessionControl> directControl;
13973 {
13974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13975 if (mData->mSession.mLockType == LockType_VM)
13976 directControl = mData->mSession.mDirectControl;
13977 }
13978
13979 /* ignore notifications sent after #OnSessionEnd() is called */
13980 if (!directControl)
13981 return S_OK;
13982
13983 return directControl->OnClipboardModeChange(aClipboardMode);
13984}
13985
13986/**
13987 * @note Locks this object for reading.
13988 */
13989HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13990{
13991 LogFlowThisFunc(("\n"));
13992
13993 AutoCaller autoCaller(this);
13994 AssertComRCReturnRC(autoCaller.rc());
13995
13996 ComPtr<IInternalSessionControl> directControl;
13997 {
13998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13999 if (mData->mSession.mLockType == LockType_VM)
14000 directControl = mData->mSession.mDirectControl;
14001 }
14002
14003 /* ignore notifications sent after #OnSessionEnd() is called */
14004 if (!directControl)
14005 return S_OK;
14006
14007 return directControl->OnDnDModeChange(aDnDMode);
14008}
14009
14010/**
14011 * @note Locks this object for reading.
14012 */
14013HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14014{
14015 LogFlowThisFunc(("\n"));
14016
14017 AutoCaller autoCaller(this);
14018 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14019
14020 ComPtr<IInternalSessionControl> directControl;
14021 {
14022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14023 if (mData->mSession.mLockType == LockType_VM)
14024 directControl = mData->mSession.mDirectControl;
14025 }
14026
14027 /* ignore notifications sent after #OnSessionEnd() is called */
14028 if (!directControl)
14029 return S_OK;
14030
14031 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14032}
14033
14034/**
14035 * @note Locks this object for reading.
14036 */
14037HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14038{
14039 LogFlowThisFunc(("\n"));
14040
14041 AutoCaller autoCaller(this);
14042 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14043
14044 ComPtr<IInternalSessionControl> directControl;
14045 {
14046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14047 if (mData->mSession.mLockType == LockType_VM)
14048 directControl = mData->mSession.mDirectControl;
14049 }
14050
14051 /* ignore notifications sent after #OnSessionEnd() is called */
14052 if (!directControl)
14053 return S_OK;
14054
14055 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14056}
14057
14058/**
14059 * Returns @c true if this machine's USB controller reports it has a matching
14060 * filter for the given USB device and @c false otherwise.
14061 *
14062 * @note locks this object for reading.
14063 */
14064bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14065{
14066 AutoCaller autoCaller(this);
14067 /* silently return if not ready -- this method may be called after the
14068 * direct machine session has been called */
14069 if (!autoCaller.isOk())
14070 return false;
14071
14072#ifdef VBOX_WITH_USB
14073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14074
14075 switch (mData->mMachineState)
14076 {
14077 case MachineState_Starting:
14078 case MachineState_Restoring:
14079 case MachineState_TeleportingIn:
14080 case MachineState_Paused:
14081 case MachineState_Running:
14082 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14083 * elsewhere... */
14084 alock.release();
14085 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14086 default: break;
14087 }
14088#else
14089 NOREF(aDevice);
14090 NOREF(aMaskedIfs);
14091#endif
14092 return false;
14093}
14094
14095/**
14096 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14097 */
14098HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14099 IVirtualBoxErrorInfo *aError,
14100 ULONG aMaskedIfs,
14101 const com::Utf8Str &aCaptureFilename)
14102{
14103 LogFlowThisFunc(("\n"));
14104
14105 AutoCaller autoCaller(this);
14106
14107 /* This notification may happen after the machine object has been
14108 * uninitialized (the session was closed), so don't assert. */
14109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14110
14111 ComPtr<IInternalSessionControl> directControl;
14112 {
14113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14114 if (mData->mSession.mLockType == LockType_VM)
14115 directControl = mData->mSession.mDirectControl;
14116 }
14117
14118 /* fail on notifications sent after #OnSessionEnd() is called, it is
14119 * expected by the caller */
14120 if (!directControl)
14121 return E_FAIL;
14122
14123 /* No locks should be held at this point. */
14124 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14125 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14126
14127 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14128}
14129
14130/**
14131 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14132 */
14133HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14134 IVirtualBoxErrorInfo *aError)
14135{
14136 LogFlowThisFunc(("\n"));
14137
14138 AutoCaller autoCaller(this);
14139
14140 /* This notification may happen after the machine object has been
14141 * uninitialized (the session was closed), so don't assert. */
14142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14143
14144 ComPtr<IInternalSessionControl> directControl;
14145 {
14146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14147 if (mData->mSession.mLockType == LockType_VM)
14148 directControl = mData->mSession.mDirectControl;
14149 }
14150
14151 /* fail on notifications sent after #OnSessionEnd() is called, it is
14152 * expected by the caller */
14153 if (!directControl)
14154 return E_FAIL;
14155
14156 /* No locks should be held at this point. */
14157 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14158 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14159
14160 return directControl->OnUSBDeviceDetach(aId, aError);
14161}
14162
14163// protected methods
14164/////////////////////////////////////////////////////////////////////////////
14165
14166/**
14167 * Deletes the given file if it is no longer in use by either the current machine state
14168 * (if the machine is "saved") or any of the machine's snapshots.
14169 *
14170 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14171 * but is different for each SnapshotMachine. When calling this, the order of calling this
14172 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14173 * is therefore critical. I know, it's all rather messy.
14174 *
14175 * @param strStateFile
14176 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14177 * the test for whether the saved state file is in use.
14178 */
14179void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14180 Snapshot *pSnapshotToIgnore)
14181{
14182 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14183 if ( (strStateFile.isNotEmpty())
14184 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14185 )
14186 // ... and it must also not be shared with other snapshots
14187 if ( !mData->mFirstSnapshot
14188 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14189 // this checks the SnapshotMachine's state file paths
14190 )
14191 RTFileDelete(strStateFile.c_str());
14192}
14193
14194/**
14195 * Locks the attached media.
14196 *
14197 * All attached hard disks are locked for writing and DVD/floppy are locked for
14198 * reading. Parents of attached hard disks (if any) are locked for reading.
14199 *
14200 * This method also performs accessibility check of all media it locks: if some
14201 * media is inaccessible, the method will return a failure and a bunch of
14202 * extended error info objects per each inaccessible medium.
14203 *
14204 * Note that this method is atomic: if it returns a success, all media are
14205 * locked as described above; on failure no media is locked at all (all
14206 * succeeded individual locks will be undone).
14207 *
14208 * The caller is responsible for doing the necessary state sanity checks.
14209 *
14210 * The locks made by this method must be undone by calling #unlockMedia() when
14211 * no more needed.
14212 */
14213HRESULT SessionMachine::i_lockMedia()
14214{
14215 AutoCaller autoCaller(this);
14216 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14217
14218 AutoMultiWriteLock2 alock(this->lockHandle(),
14219 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14220
14221 /* bail out if trying to lock things with already set up locking */
14222 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14223
14224 MultiResult mrc(S_OK);
14225
14226 /* Collect locking information for all medium objects attached to the VM. */
14227 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14228 it != mMediaData->mAttachments.end();
14229 ++it)
14230 {
14231 MediumAttachment* pAtt = *it;
14232 DeviceType_T devType = pAtt->i_getType();
14233 Medium *pMedium = pAtt->i_getMedium();
14234
14235 MediumLockList *pMediumLockList(new MediumLockList());
14236 // There can be attachments without a medium (floppy/dvd), and thus
14237 // it's impossible to create a medium lock list. It still makes sense
14238 // to have the empty medium lock list in the map in case a medium is
14239 // attached later.
14240 if (pMedium != NULL)
14241 {
14242 MediumType_T mediumType = pMedium->i_getType();
14243 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14244 || mediumType == MediumType_Shareable;
14245 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14246
14247 alock.release();
14248 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14249 !fIsReadOnlyLock /* fMediumLockWrite */,
14250 false /* fMediumLockWriteAll */,
14251 NULL,
14252 *pMediumLockList);
14253 alock.acquire();
14254 if (FAILED(mrc))
14255 {
14256 delete pMediumLockList;
14257 mData->mSession.mLockedMedia.Clear();
14258 break;
14259 }
14260 }
14261
14262 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14263 if (FAILED(rc))
14264 {
14265 mData->mSession.mLockedMedia.Clear();
14266 mrc = setError(rc,
14267 tr("Collecting locking information for all attached media failed"));
14268 break;
14269 }
14270 }
14271
14272 if (SUCCEEDED(mrc))
14273 {
14274 /* Now lock all media. If this fails, nothing is locked. */
14275 alock.release();
14276 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14277 alock.acquire();
14278 if (FAILED(rc))
14279 {
14280 mrc = setError(rc,
14281 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14282 }
14283 }
14284
14285 return mrc;
14286}
14287
14288/**
14289 * Undoes the locks made by by #lockMedia().
14290 */
14291HRESULT SessionMachine::i_unlockMedia()
14292{
14293 AutoCaller autoCaller(this);
14294 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14295
14296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14297
14298 /* we may be holding important error info on the current thread;
14299 * preserve it */
14300 ErrorInfoKeeper eik;
14301
14302 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14303 AssertComRC(rc);
14304 return rc;
14305}
14306
14307/**
14308 * Helper to change the machine state (reimplementation).
14309 *
14310 * @note Locks this object for writing.
14311 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14312 * it can cause crashes in random places due to unexpectedly committing
14313 * the current settings. The caller is responsible for that. The call
14314 * to saveStateSettings is fine, because this method does not commit.
14315 */
14316HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14317{
14318 LogFlowThisFuncEnter();
14319 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14320
14321 AutoCaller autoCaller(this);
14322 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14323
14324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14325
14326 MachineState_T oldMachineState = mData->mMachineState;
14327
14328 AssertMsgReturn(oldMachineState != aMachineState,
14329 ("oldMachineState=%s, aMachineState=%s\n",
14330 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14331 E_FAIL);
14332
14333 HRESULT rc = S_OK;
14334
14335 int stsFlags = 0;
14336 bool deleteSavedState = false;
14337
14338 /* detect some state transitions */
14339
14340 if ( ( oldMachineState == MachineState_Saved
14341 && aMachineState == MachineState_Restoring)
14342 || ( ( oldMachineState == MachineState_PoweredOff
14343 || oldMachineState == MachineState_Teleported
14344 || oldMachineState == MachineState_Aborted
14345 )
14346 && ( aMachineState == MachineState_TeleportingIn
14347 || aMachineState == MachineState_Starting
14348 )
14349 )
14350 )
14351 {
14352 /* The EMT thread is about to start */
14353
14354 /* Nothing to do here for now... */
14355
14356 /// @todo NEWMEDIA don't let mDVDDrive and other children
14357 /// change anything when in the Starting/Restoring state
14358 }
14359 else if ( ( oldMachineState == MachineState_Running
14360 || oldMachineState == MachineState_Paused
14361 || oldMachineState == MachineState_Teleporting
14362 || oldMachineState == MachineState_OnlineSnapshotting
14363 || oldMachineState == MachineState_LiveSnapshotting
14364 || oldMachineState == MachineState_Stuck
14365 || oldMachineState == MachineState_Starting
14366 || oldMachineState == MachineState_Stopping
14367 || oldMachineState == MachineState_Saving
14368 || oldMachineState == MachineState_Restoring
14369 || oldMachineState == MachineState_TeleportingPausedVM
14370 || oldMachineState == MachineState_TeleportingIn
14371 )
14372 && ( aMachineState == MachineState_PoweredOff
14373 || aMachineState == MachineState_Saved
14374 || aMachineState == MachineState_Teleported
14375 || aMachineState == MachineState_Aborted
14376 )
14377 )
14378 {
14379 /* The EMT thread has just stopped, unlock attached media. Note that as
14380 * opposed to locking that is done from Console, we do unlocking here
14381 * because the VM process may have aborted before having a chance to
14382 * properly unlock all media it locked. */
14383
14384 unlockMedia();
14385 }
14386
14387 if (oldMachineState == MachineState_Restoring)
14388 {
14389 if (aMachineState != MachineState_Saved)
14390 {
14391 /*
14392 * delete the saved state file once the machine has finished
14393 * restoring from it (note that Console sets the state from
14394 * Restoring to Saved if the VM couldn't restore successfully,
14395 * to give the user an ability to fix an error and retry --
14396 * we keep the saved state file in this case)
14397 */
14398 deleteSavedState = true;
14399 }
14400 }
14401 else if ( oldMachineState == MachineState_Saved
14402 && ( aMachineState == MachineState_PoweredOff
14403 || aMachineState == MachineState_Aborted
14404 || aMachineState == MachineState_Teleported
14405 )
14406 )
14407 {
14408 /*
14409 * delete the saved state after SessionMachine::ForgetSavedState() is called
14410 * or if the VM process (owning a direct VM session) crashed while the
14411 * VM was Saved
14412 */
14413
14414 /// @todo (dmik)
14415 // Not sure that deleting the saved state file just because of the
14416 // client death before it attempted to restore the VM is a good
14417 // thing. But when it crashes we need to go to the Aborted state
14418 // which cannot have the saved state file associated... The only
14419 // way to fix this is to make the Aborted condition not a VM state
14420 // but a bool flag: i.e., when a crash occurs, set it to true and
14421 // change the state to PoweredOff or Saved depending on the
14422 // saved state presence.
14423
14424 deleteSavedState = true;
14425 mData->mCurrentStateModified = TRUE;
14426 stsFlags |= SaveSTS_CurStateModified;
14427 }
14428
14429 if ( aMachineState == MachineState_Starting
14430 || aMachineState == MachineState_Restoring
14431 || aMachineState == MachineState_TeleportingIn
14432 )
14433 {
14434 /* set the current state modified flag to indicate that the current
14435 * state is no more identical to the state in the
14436 * current snapshot */
14437 if (!mData->mCurrentSnapshot.isNull())
14438 {
14439 mData->mCurrentStateModified = TRUE;
14440 stsFlags |= SaveSTS_CurStateModified;
14441 }
14442 }
14443
14444 if (deleteSavedState)
14445 {
14446 if (mRemoveSavedState)
14447 {
14448 Assert(!mSSData->strStateFilePath.isEmpty());
14449
14450 // it is safe to delete the saved state file if ...
14451 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14452 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14453 // ... none of the snapshots share the saved state file
14454 )
14455 RTFileDelete(mSSData->strStateFilePath.c_str());
14456 }
14457
14458 mSSData->strStateFilePath.setNull();
14459 stsFlags |= SaveSTS_StateFilePath;
14460 }
14461
14462 /* redirect to the underlying peer machine */
14463 mPeer->i_setMachineState(aMachineState);
14464
14465 if ( aMachineState == MachineState_PoweredOff
14466 || aMachineState == MachineState_Teleported
14467 || aMachineState == MachineState_Aborted
14468 || aMachineState == MachineState_Saved)
14469 {
14470 /* the machine has stopped execution
14471 * (or the saved state file was adopted) */
14472 stsFlags |= SaveSTS_StateTimeStamp;
14473 }
14474
14475 if ( ( oldMachineState == MachineState_PoweredOff
14476 || oldMachineState == MachineState_Aborted
14477 || oldMachineState == MachineState_Teleported
14478 )
14479 && aMachineState == MachineState_Saved)
14480 {
14481 /* the saved state file was adopted */
14482 Assert(!mSSData->strStateFilePath.isEmpty());
14483 stsFlags |= SaveSTS_StateFilePath;
14484 }
14485
14486#ifdef VBOX_WITH_GUEST_PROPS
14487 if ( aMachineState == MachineState_PoweredOff
14488 || aMachineState == MachineState_Aborted
14489 || aMachineState == MachineState_Teleported)
14490 {
14491 /* Make sure any transient guest properties get removed from the
14492 * property store on shutdown. */
14493 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14494
14495 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14496 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14497 while (it != llGuestProperties.end())
14498 {
14499 const settings::GuestProperty &prop = *it;
14500 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14501 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14502 {
14503 it = llGuestProperties.erase(it);
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 BOOL *aNotify)
14711{
14712 NOREF(aName);
14713 NOREF(aValue);
14714 NOREF(aTimestamp);
14715 NOREF(aFlags);
14716 NOREF(aNotify);
14717 ReturnComNotImplemented();
14718}
14719
14720HRESULT Machine::lockMedia()
14721{
14722 ReturnComNotImplemented();
14723}
14724
14725HRESULT Machine::unlockMedia()
14726{
14727 ReturnComNotImplemented();
14728}
14729
14730HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14731 ComPtr<IMediumAttachment> &aNewAttachment)
14732{
14733 NOREF(aAttachment);
14734 NOREF(aNewAttachment);
14735 ReturnComNotImplemented();
14736}
14737
14738HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14739 ULONG aCpuUser,
14740 ULONG aCpuKernel,
14741 ULONG aCpuIdle,
14742 ULONG aMemTotal,
14743 ULONG aMemFree,
14744 ULONG aMemBalloon,
14745 ULONG aMemShared,
14746 ULONG aMemCache,
14747 ULONG aPagedTotal,
14748 ULONG aMemAllocTotal,
14749 ULONG aMemFreeTotal,
14750 ULONG aMemBalloonTotal,
14751 ULONG aMemSharedTotal,
14752 ULONG aVmNetRx,
14753 ULONG aVmNetTx)
14754{
14755 NOREF(aValidStats);
14756 NOREF(aCpuUser);
14757 NOREF(aCpuKernel);
14758 NOREF(aCpuIdle);
14759 NOREF(aMemTotal);
14760 NOREF(aMemFree);
14761 NOREF(aMemBalloon);
14762 NOREF(aMemShared);
14763 NOREF(aMemCache);
14764 NOREF(aPagedTotal);
14765 NOREF(aMemAllocTotal);
14766 NOREF(aMemFreeTotal);
14767 NOREF(aMemBalloonTotal);
14768 NOREF(aMemSharedTotal);
14769 NOREF(aVmNetRx);
14770 NOREF(aVmNetTx);
14771 ReturnComNotImplemented();
14772}
Note: See TracBrowser for help on using the repository browser.

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