VirtualBox

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

Last change on this file since 52325 was 52312, checked in by vboxsync, 11 years ago

6219: New parameters related to file size / recording time limitation for VM Video Capture have been added (vcpmaxtime, vcpmaxsize and vcpoptions - special codec options in key=value format). EbmlWriter has been refactored. Removed some redundant code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 501.1 KB
Line 
1/* $Id: MachineImpl.cpp 52312 2014-08-07 12:54:38Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 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 "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureMaxTime = 0;
169 mVideoCaptureMaxFileSize = 0;
170 mVideoCaptureEnabled = false;
171 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
172 maVideoCaptureScreens[i] = true;
173
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExUXEnabled = true;
184 mHWVirtExForceEnabled = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mSyntheticCpu = false;
192 mTripleFaultReset = false;
193 mHPETEnabled = false;
194
195 /* default boot order: floppy - DVD - HDD */
196 mBootOrder[0] = DeviceType_Floppy;
197 mBootOrder[1] = DeviceType_DVD;
198 mBootOrder[2] = DeviceType_HardDisk;
199 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
200 mBootOrder[i] = DeviceType_Null;
201
202 mClipboardMode = ClipboardMode_Disabled;
203 mDnDMode = DnDMode_Disabled;
204 mGuestPropertyNotificationPatterns = "";
205
206 mFirmwareType = FirmwareType_BIOS;
207 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
208 mPointingHIDType = PointingHIDType_PS2Mouse;
209 mChipsetType = ChipsetType_PIIX3;
210 mParavirtProvider = ParavirtProvider_Default;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Apply BIOS defaults */
345 mBIOSSettings->i_applyDefaults(aOsType);
346
347 /* Apply network adapters defaults */
348 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
349 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
350
351 /* Apply serial port defaults */
352 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
353 mSerialPorts[slot]->i_applyDefaults(aOsType);
354
355 /* Let the OS type select 64-bit ness. */
356 mHWData->mLongMode = aOsType->i_is64Bit()
357 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
358 }
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param aConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param strName Name for the new machine; this overrides what is specified in config and is used
511 * for the settings file as well.
512 * @param config Machine configuration loaded and parsed from XML.
513 *
514 * @return Success indicator. if not S_OK, the machine object is invalid
515 */
516HRESULT Machine::init(VirtualBox *aParent,
517 const Utf8Str &strName,
518 const settings::MachineConfigFile &config)
519{
520 LogFlowThisFuncEnter();
521
522 /* Enclose the state transition NotReady->InInit->Ready */
523 AutoInitSpan autoInitSpan(this);
524 AssertReturn(autoInitSpan.isOk(), E_FAIL);
525
526 Utf8Str strConfigFile;
527 aParent->i_getDefaultMachineFolder(strConfigFile);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(".vbox");
533
534 HRESULT rc = initImpl(aParent, strConfigFile);
535 if (FAILED(rc)) return rc;
536
537 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
538 if (FAILED(rc)) return rc;
539
540 rc = initDataAndChildObjects();
541
542 if (SUCCEEDED(rc))
543 {
544 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
545 mData->mAccessible = TRUE;
546
547 // create empty machine config for instance data
548 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
549
550 // generate fresh UUID, ignore machine config
551 unconst(mData->mUuid).create();
552
553 rc = i_loadMachineDataFromSettings(config,
554 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
555
556 // override VM name as well, it may be different
557 mUserData->s.strName = strName;
558
559 if (SUCCEEDED(rc))
560 {
561 /* At this point the changing of the current state modification
562 * flag is allowed. */
563 i_allowStateModification();
564
565 /* commit all changes made during the initialization */
566 i_commit();
567 }
568 }
569
570 /* Confirm a successful initialization when it's the case */
571 if (SUCCEEDED(rc))
572 {
573 if (mData->mAccessible)
574 autoInitSpan.setSucceeded();
575 else
576 {
577 /* Ignore all errors from unregistering, they would destroy
578- * the more interesting error information we already have,
579- * pinpointing the issue with the VM config. */
580 ErrorInfoKeeper eik;
581
582 autoInitSpan.setLimited();
583
584 // uninit media from this machine's media registry, or else
585 // reloading the settings will fail
586 mParent->i_unregisterMachineMedia(i_getId());
587 }
588 }
589
590 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
591 "rc=%08X\n",
592 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
593 mData->mRegistered, mData->mAccessible, rc));
594
595 LogFlowThisFuncLeave();
596
597 return rc;
598}
599
600/**
601 * Shared code between the various init() implementations.
602 * @param aParent
603 * @return
604 */
605HRESULT Machine::initImpl(VirtualBox *aParent,
606 const Utf8Str &strConfigFile)
607{
608 LogFlowThisFuncEnter();
609
610 AssertReturn(aParent, E_INVALIDARG);
611 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
612
613 HRESULT rc = S_OK;
614
615 /* share the parent weakly */
616 unconst(mParent) = aParent;
617
618 /* allocate the essential machine data structure (the rest will be
619 * allocated later by initDataAndChildObjects() */
620 mData.allocate();
621
622 /* memorize the config file name (as provided) */
623 mData->m_strConfigFile = strConfigFile;
624
625 /* get the full file name */
626 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
627 if (RT_FAILURE(vrc1))
628 return setError(VBOX_E_FILE_ERROR,
629 tr("Invalid machine settings file name '%s' (%Rrc)"),
630 strConfigFile.c_str(),
631 vrc1);
632
633 LogFlowThisFuncLeave();
634
635 return rc;
636}
637
638/**
639 * Tries to create a machine settings file in the path stored in the machine
640 * instance data. Used when a new machine is created to fail gracefully if
641 * the settings file could not be written (e.g. because machine dir is read-only).
642 * @return
643 */
644HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
645{
646 HRESULT rc = S_OK;
647
648 // when we create a new machine, we must be able to create the settings file
649 RTFILE f = NIL_RTFILE;
650 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
651 if ( RT_SUCCESS(vrc)
652 || vrc == VERR_SHARING_VIOLATION
653 )
654 {
655 if (RT_SUCCESS(vrc))
656 RTFileClose(f);
657 if (!fForceOverwrite)
658 rc = setError(VBOX_E_FILE_ERROR,
659 tr("Machine settings file '%s' already exists"),
660 mData->m_strConfigFileFull.c_str());
661 else
662 {
663 /* try to delete the config file, as otherwise the creation
664 * of a new settings file will fail. */
665 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
666 if (RT_FAILURE(vrc2))
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Could not delete the existing settings file '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(), vrc2);
670 }
671 }
672 else if ( vrc != VERR_FILE_NOT_FOUND
673 && vrc != VERR_PATH_NOT_FOUND
674 )
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Invalid machine settings file name '%s' (%Rrc)"),
677 mData->m_strConfigFileFull.c_str(),
678 vrc);
679 return rc;
680}
681
682/**
683 * Initializes the registered machine by loading the settings file.
684 * This method is separated from #init() in order to make it possible to
685 * retry the operation after VirtualBox startup instead of refusing to
686 * startup the whole VirtualBox server in case if the settings file of some
687 * registered VM is invalid or inaccessible.
688 *
689 * @note Must be always called from this object's write lock
690 * (unless called from #init() that doesn't need any locking).
691 * @note Locks the mUSBController method for writing.
692 * @note Subclasses must not call this method.
693 */
694HRESULT Machine::i_registeredInit()
695{
696 AssertReturn(!i_isSessionMachine(), E_FAIL);
697 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
698 AssertReturn(mData->mUuid.isValid(), E_FAIL);
699 AssertReturn(!mData->mAccessible, E_FAIL);
700
701 HRESULT rc = initDataAndChildObjects();
702
703 if (SUCCEEDED(rc))
704 {
705 /* Temporarily reset the registered flag in order to let setters
706 * potentially called from loadSettings() succeed (isMutable() used in
707 * all setters will return FALSE for a Machine instance if mRegistered
708 * is TRUE). */
709 mData->mRegistered = FALSE;
710
711 try
712 {
713 // load and parse machine XML; this will throw on XML or logic errors
714 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
715
716 if (mData->mUuid != mData->pMachineConfigFile->uuid)
717 throw setError(E_FAIL,
718 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
719 mData->pMachineConfigFile->uuid.raw(),
720 mData->m_strConfigFileFull.c_str(),
721 mData->mUuid.toString().c_str(),
722 mParent->i_settingsFilePath().c_str());
723
724 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
725 NULL /* const Guid *puuidRegistry */);
726 if (FAILED(rc)) throw rc;
727 }
728 catch (HRESULT err)
729 {
730 /* we assume that error info is set by the thrower */
731 rc = err;
732 }
733 catch (...)
734 {
735 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
736 }
737
738 /* Restore the registered flag (even on failure) */
739 mData->mRegistered = TRUE;
740 }
741
742 if (SUCCEEDED(rc))
743 {
744 /* Set mAccessible to TRUE only if we successfully locked and loaded
745 * the settings file */
746 mData->mAccessible = TRUE;
747
748 /* commit all changes made during loading the settings file */
749 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
750 /// @todo r=klaus for some reason the settings loading logic backs up
751 // the settings, and therefore a commit is needed. Should probably be changed.
752 }
753 else
754 {
755 /* If the machine is registered, then, instead of returning a
756 * failure, we mark it as inaccessible and set the result to
757 * success to give it a try later */
758
759 /* fetch the current error info */
760 mData->mAccessError = com::ErrorInfo();
761 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
762 mData->mUuid.raw(),
763 mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it does
831 * VirtualBox::addCaller() for the duration of the
832 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 LogWarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 pVirtualBox.queryInterfaceTo(aParent.asOutParam());
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 rc = errorInfo.queryInterfaceTo(aAccessError.asOutParam());
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1048 it != mUserData->s.llGroups.end(); ++it, ++i)
1049 aGroups[i] = (*it);
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1055{
1056 StringsList llGroups;
1057 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1058 if (FAILED(rc))
1059 return rc;
1060
1061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 // changing machine groups is possible while the VM is offline
1064 rc = i_checkStateDependency(OfflineStateDep);
1065 if (FAILED(rc)) return rc;
1066
1067 i_setModified(IsModified_MachineData);
1068 mUserData.backup();
1069 mUserData->s.llGroups = llGroups;
1070
1071 return S_OK;
1072}
1073
1074HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1075{
1076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 aOSTypeId = mUserData->s.strOsType;
1079
1080 return S_OK;
1081}
1082
1083HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1084{
1085 /* look up the object by Id to check it is valid */
1086 ComPtr<IGuestOSType> guestOSType;
1087 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1088 if (FAILED(rc)) return rc;
1089
1090 /* when setting, always use the "etalon" value for consistency -- lookup
1091 * by ID is case-insensitive and the input value may have different case */
1092 Bstr osTypeId;
1093 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1094 if (FAILED(rc)) return rc;
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 *aParavirtProvider = mHWData->mParavirtProvider;
1223
1224 return S_OK;
1225}
1226
1227HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 HRESULT rc = i_checkStateDependency(MutableStateDep);
1232 if (FAILED(rc)) return rc;
1233
1234 if (aParavirtProvider != mHWData->mParavirtProvider)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtProvider = aParavirtProvider;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249 switch (mHWData->mParavirtProvider)
1250 {
1251 case ParavirtProvider_None:
1252 case ParavirtProvider_HyperV:
1253 case ParavirtProvider_Minimal:
1254 break;
1255
1256 /* Resolve dynamic provider types to the effective types. */
1257 default:
1258 {
1259 ComPtr<IGuestOSType> ptrGuestOSType;
1260 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1261 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1262
1263 Bstr guestTypeFamilyId;
1264 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1266 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1267
1268 switch (mHWData->mParavirtProvider)
1269 {
1270 case ParavirtProvider_Legacy:
1271 {
1272 if (fOsXGuest)
1273 *aParavirtProvider = ParavirtProvider_Minimal;
1274 else
1275 *aParavirtProvider = ParavirtProvider_None;
1276 break;
1277 }
1278
1279 case ParavirtProvider_Default:
1280 {
1281 if (fOsXGuest)
1282 *aParavirtProvider = ParavirtProvider_Minimal;
1283#if 0 /* Activate this soon. */
1284 else if ( mUserData->s.strOsType == "Windows81"
1285 || mUserData->s.strOsType == "Windows81_64"
1286 || mUserData->s.strOsType == "Windows8"
1287 || mUserData->s.strOsType == "Windows8_64"
1288 || mUserData->s.strOsType == "Windows7"
1289 || mUserData->s.strOsType == "Windows7_64"
1290 || mUserData->s.strOsType == "WindowsVista"
1291 || mUserData->s.strOsType == "WindowsVista_64"
1292 || mUserData->s.strOsType == "Windows2012"
1293 || mUserData->s.strOsType == "Windows2012_64"
1294 || mUserData->s.strOsType == "Windows2008"
1295 || mUserData->s.strOsType == "Windows2008_64")
1296 {
1297 *aParavirtProvider = ParavirtProvider_HyperV;
1298 }
1299#endif
1300 else
1301 *aParavirtProvider = ParavirtProvider_None;
1302 break;
1303 }
1304 }
1305 break;
1306 }
1307 }
1308
1309 Assert( *aParavirtProvider == ParavirtProvider_None
1310 || *aParavirtProvider == ParavirtProvider_Minimal
1311 || *aParavirtProvider == ParavirtProvider_HyperV);
1312 return S_OK;
1313}
1314
1315HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1316{
1317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1318
1319 aHardwareVersion = mHWData->mHWVersion;
1320
1321 return S_OK;
1322}
1323
1324HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1325{
1326 /* check known version */
1327 Utf8Str hwVersion = aHardwareVersion;
1328 if ( hwVersion.compare("1") != 0
1329 && hwVersion.compare("2") != 0)
1330 return setError(E_INVALIDARG,
1331 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1332
1333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 HRESULT rc = i_checkStateDependency(MutableStateDep);
1336 if (FAILED(rc)) return rc;
1337
1338 i_setModified(IsModified_MachineData);
1339 mHWData.backup();
1340 mHWData->mHWVersion = aHardwareVersion;
1341
1342 return S_OK;
1343}
1344
1345HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1346{
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 if (!mHWData->mHardwareUUID.isZero())
1350 aHardwareUUID = mHWData->mHardwareUUID;
1351 else
1352 aHardwareUUID = mData->mUuid;
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1358{
1359 if (!aHardwareUUID.isValid())
1360 return E_INVALIDARG;
1361
1362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1363
1364 HRESULT rc = i_checkStateDependency(MutableStateDep);
1365 if (FAILED(rc)) return rc;
1366
1367 i_setModified(IsModified_MachineData);
1368 mHWData.backup();
1369 if (aHardwareUUID == mData->mUuid)
1370 mHWData->mHardwareUUID.clear();
1371 else
1372 mHWData->mHardwareUUID = aHardwareUUID;
1373
1374 return S_OK;
1375}
1376
1377HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1378{
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 *aMemorySize = mHWData->mMemorySize;
1382
1383 return S_OK;
1384}
1385
1386HRESULT Machine::setMemorySize(ULONG aMemorySize)
1387{
1388 /* check RAM limits */
1389 if ( aMemorySize < MM_RAM_MIN_IN_MB
1390 || aMemorySize > MM_RAM_MAX_IN_MB
1391 )
1392 return setError(E_INVALIDARG,
1393 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1394 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
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 mHWData->mMemorySize = aMemorySize;
1404
1405 return S_OK;
1406}
1407
1408HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1409{
1410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1411
1412 *aCPUCount = mHWData->mCPUCount;
1413
1414 return S_OK;
1415}
1416
1417HRESULT Machine::setCPUCount(ULONG aCPUCount)
1418{
1419 /* check CPU limits */
1420 if ( aCPUCount < SchemaDefs::MinCPUCount
1421 || aCPUCount > SchemaDefs::MaxCPUCount
1422 )
1423 return setError(E_INVALIDARG,
1424 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1425 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1426
1427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1430 if (mHWData->mCPUHotPlugEnabled)
1431 {
1432 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1433 {
1434 if (mHWData->mCPUAttached[idx])
1435 return setError(E_INVALIDARG,
1436 tr("There is still a CPU attached to socket %lu."
1437 "Detach the CPU before removing the socket"),
1438 aCPUCount, idx+1);
1439 }
1440 }
1441
1442 HRESULT rc = i_checkStateDependency(MutableStateDep);
1443 if (FAILED(rc)) return rc;
1444
1445 i_setModified(IsModified_MachineData);
1446 mHWData.backup();
1447 mHWData->mCPUCount = aCPUCount;
1448
1449 return S_OK;
1450}
1451
1452HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1453{
1454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1457
1458 return S_OK;
1459}
1460
1461HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1462{
1463 HRESULT rc = S_OK;
1464
1465 /* check throttle limits */
1466 if ( aCPUExecutionCap < 1
1467 || aCPUExecutionCap > 100
1468 )
1469 return setError(E_INVALIDARG,
1470 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1471 aCPUExecutionCap, 1, 100);
1472
1473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1474
1475 alock.release();
1476 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1477 alock.acquire();
1478 if (FAILED(rc)) return rc;
1479
1480 i_setModified(IsModified_MachineData);
1481 mHWData.backup();
1482 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1483
1484 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1485 if (Global::IsOnline(mData->mMachineState))
1486 i_saveSettings(NULL);
1487
1488 return S_OK;
1489}
1490
1491HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1492{
1493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1494
1495 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1496
1497 return S_OK;
1498}
1499
1500HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1501{
1502 HRESULT rc = S_OK;
1503
1504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1505
1506 rc = i_checkStateDependency(MutableStateDep);
1507 if (FAILED(rc)) return rc;
1508
1509 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1510 {
1511 if (aCPUHotPlugEnabled)
1512 {
1513 i_setModified(IsModified_MachineData);
1514 mHWData.backup();
1515
1516 /* Add the amount of CPUs currently attached */
1517 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1518 mHWData->mCPUAttached[i] = true;
1519 }
1520 else
1521 {
1522 /*
1523 * We can disable hotplug only if the amount of maximum CPUs is equal
1524 * to the amount of attached CPUs
1525 */
1526 unsigned cCpusAttached = 0;
1527 unsigned iHighestId = 0;
1528
1529 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1530 {
1531 if (mHWData->mCPUAttached[i])
1532 {
1533 cCpusAttached++;
1534 iHighestId = i;
1535 }
1536 }
1537
1538 if ( (cCpusAttached != mHWData->mCPUCount)
1539 || (iHighestId >= mHWData->mCPUCount))
1540 return setError(E_INVALIDARG,
1541 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1542
1543 i_setModified(IsModified_MachineData);
1544 mHWData.backup();
1545 }
1546 }
1547
1548 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1549
1550 return rc;
1551}
1552
1553HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1554{
1555#ifdef VBOX_WITH_USB_CARDREADER
1556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1557
1558 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1559
1560 return S_OK;
1561#else
1562 NOREF(aEmulatedUSBCardReaderEnabled);
1563 return E_NOTIMPL;
1564#endif
1565}
1566
1567HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1568{
1569#ifdef VBOX_WITH_USB_CARDREADER
1570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1571
1572 HRESULT rc = i_checkStateDependency(MutableStateDep);
1573 if (FAILED(rc)) return rc;
1574
1575 i_setModified(IsModified_MachineData);
1576 mHWData.backup();
1577 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1578
1579 return S_OK;
1580#else
1581 NOREF(aEmulatedUSBCardReaderEnabled);
1582 return E_NOTIMPL;
1583#endif
1584}
1585
1586HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1587{
1588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1589
1590 *aHPETEnabled = mHWData->mHPETEnabled;
1591
1592 return S_OK;
1593}
1594
1595HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1596{
1597 HRESULT rc = S_OK;
1598
1599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1600
1601 rc = i_checkStateDependency(MutableStateDep);
1602 if (FAILED(rc)) return rc;
1603
1604 i_setModified(IsModified_MachineData);
1605 mHWData.backup();
1606
1607 mHWData->mHPETEnabled = aHPETEnabled;
1608
1609 return rc;
1610}
1611
1612HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1613{
1614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1615
1616 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1617 return S_OK;
1618}
1619
1620HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1621{
1622 HRESULT rc = S_OK;
1623
1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 i_setModified(IsModified_MachineData);
1627 mHWData.backup();
1628 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1629
1630 alock.release();
1631 rc = i_onVideoCaptureChange();
1632 alock.acquire();
1633 if (FAILED(rc))
1634 {
1635 /*
1636 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1637 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1638 * determine if it should start or stop capturing. Therefore we need to manually
1639 * undo change.
1640 */
1641 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1642 return rc;
1643 }
1644
1645 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1646 if (Global::IsOnline(mData->mMachineState))
1647 i_saveSettings(NULL);
1648
1649 return rc;
1650}
1651
1652HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1653{
1654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1655 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1656 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1657 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1658 return S_OK;
1659}
1660
1661HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1662{
1663 SafeArray<BOOL> screens(aVideoCaptureScreens);
1664 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1665 bool fChanged = false;
1666
1667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1668
1669 for (unsigned i = 0; i < screens.size(); ++i)
1670 {
1671 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1672 {
1673 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1674 fChanged = true;
1675 }
1676 }
1677 if (fChanged)
1678 {
1679 alock.release();
1680 HRESULT rc = i_onVideoCaptureChange();
1681 alock.acquire();
1682 if (FAILED(rc)) return rc;
1683 i_setModified(IsModified_MachineData);
1684
1685 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1686 if (Global::IsOnline(mData->mMachineState))
1687 i_saveSettings(NULL);
1688 }
1689
1690 return S_OK;
1691}
1692
1693HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1694{
1695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1696 if (mHWData->mVideoCaptureFile.isEmpty())
1697 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1698 else
1699 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1700 return S_OK;
1701}
1702
1703HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1704{
1705 Utf8Str strFile(aVideoCaptureFile);
1706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1707
1708 if ( Global::IsOnline(mData->mMachineState)
1709 && mHWData->mVideoCaptureEnabled)
1710 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1711
1712 if (!RTPathStartsWithRoot(strFile.c_str()))
1713 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1714
1715 if (!strFile.isEmpty())
1716 {
1717 Utf8Str defaultFile;
1718 i_getDefaultVideoCaptureFile(defaultFile);
1719 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1720 strFile.setNull();
1721 }
1722
1723 i_setModified(IsModified_MachineData);
1724 mHWData.backup();
1725 mHWData->mVideoCaptureFile = strFile;
1726
1727 return S_OK;
1728}
1729
1730HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1731{
1732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1733 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1734 return S_OK;
1735}
1736
1737HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1738{
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 i_setModified(IsModified_MachineData);
1746 mHWData.backup();
1747 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1748
1749 return S_OK;
1750}
1751
1752HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1753{
1754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1755 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1756 return S_OK;
1757}
1758
1759HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1760{
1761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1762
1763 if ( Global::IsOnline(mData->mMachineState)
1764 && mHWData->mVideoCaptureEnabled)
1765 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1766
1767 i_setModified(IsModified_MachineData);
1768 mHWData.backup();
1769 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1770
1771 return S_OK;
1772}
1773
1774HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1775{
1776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1777 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1778 return S_OK;
1779}
1780
1781HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1782{
1783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1784
1785 if ( Global::IsOnline(mData->mMachineState)
1786 && mHWData->mVideoCaptureEnabled)
1787 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1788
1789 i_setModified(IsModified_MachineData);
1790 mHWData.backup();
1791 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1792
1793 return S_OK;
1794}
1795
1796HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1797{
1798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1799 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1800 return S_OK;
1801}
1802
1803HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1804{
1805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1806
1807 if ( Global::IsOnline(mData->mMachineState)
1808 && mHWData->mVideoCaptureEnabled)
1809 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1810
1811 i_setModified(IsModified_MachineData);
1812 mHWData.backup();
1813 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1814
1815 return S_OK;
1816}
1817
1818HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1819{
1820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1821 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1822 return S_OK;
1823}
1824
1825HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1826{
1827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 if ( Global::IsOnline(mData->mMachineState)
1830 && mHWData->mVideoCaptureEnabled)
1831 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1832
1833 i_setModified(IsModified_MachineData);
1834 mHWData.backup();
1835 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1836
1837 return S_OK;
1838}
1839
1840HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1841{
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1844 return S_OK;
1845}
1846
1847HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1848{
1849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1850
1851 if ( Global::IsOnline(mData->mMachineState)
1852 && mHWData->mVideoCaptureEnabled)
1853 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1854
1855 i_setModified(IsModified_MachineData);
1856 mHWData.backup();
1857 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1858
1859 return S_OK;
1860}
1861
1862HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1863{
1864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1867 return S_OK;
1868}
1869
1870HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1871{
1872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1873
1874 if ( Global::IsOnline(mData->mMachineState)
1875 && mHWData->mVideoCaptureEnabled)
1876 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1877
1878 i_setModified(IsModified_MachineData);
1879 mHWData.backup();
1880 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1881
1882 return S_OK;
1883}
1884
1885HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1886{
1887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1890
1891 return S_OK;
1892}
1893
1894HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1895{
1896 switch (aGraphicsControllerType)
1897 {
1898 case GraphicsControllerType_Null:
1899 case GraphicsControllerType_VBoxVGA:
1900#ifdef VBOX_WITH_VMSVGA
1901 case GraphicsControllerType_VMSVGA:
1902#endif
1903 break;
1904 default:
1905 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1906 }
1907
1908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1909
1910 HRESULT rc = i_checkStateDependency(MutableStateDep);
1911 if (FAILED(rc)) return rc;
1912
1913 i_setModified(IsModified_MachineData);
1914 mHWData.backup();
1915 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1916
1917 return S_OK;
1918}
1919
1920HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1921{
1922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 *aVRAMSize = mHWData->mVRAMSize;
1925
1926 return S_OK;
1927}
1928
1929HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1930{
1931 /* check VRAM limits */
1932 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1933 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1934 return setError(E_INVALIDARG,
1935 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1936 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1937
1938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1939
1940 HRESULT rc = i_checkStateDependency(MutableStateDep);
1941 if (FAILED(rc)) return rc;
1942
1943 i_setModified(IsModified_MachineData);
1944 mHWData.backup();
1945 mHWData->mVRAMSize = aVRAMSize;
1946
1947 return S_OK;
1948}
1949
1950/** @todo this method should not be public */
1951HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1952{
1953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1954
1955 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1956
1957 return S_OK;
1958}
1959
1960/**
1961 * Set the memory balloon size.
1962 *
1963 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1964 * we have to make sure that we never call IGuest from here.
1965 */
1966HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1967{
1968 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1969#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1970 /* check limits */
1971 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1972 return setError(E_INVALIDARG,
1973 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1974 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1975
1976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 i_setModified(IsModified_MachineData);
1979 mHWData.backup();
1980 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1981
1982 return S_OK;
1983#else
1984 NOREF(aMemoryBalloonSize);
1985 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1986#endif
1987}
1988
1989HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1990{
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1994 return S_OK;
1995}
1996
1997HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1998{
1999#ifdef VBOX_WITH_PAGE_SHARING
2000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2001
2002 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2003 i_setModified(IsModified_MachineData);
2004 mHWData.backup();
2005 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2006 return S_OK;
2007#else
2008 NOREF(aPageFusionEnabled);
2009 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2010#endif
2011}
2012
2013HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2014{
2015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2016
2017 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2018
2019 return S_OK;
2020}
2021
2022HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2023{
2024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 HRESULT rc = i_checkStateDependency(MutableStateDep);
2027 if (FAILED(rc)) return rc;
2028
2029 /** @todo check validity! */
2030
2031 i_setModified(IsModified_MachineData);
2032 mHWData.backup();
2033 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2034
2035 return S_OK;
2036}
2037
2038
2039HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2040{
2041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2042
2043 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2044
2045 return S_OK;
2046}
2047
2048HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2049{
2050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2051
2052 HRESULT rc = i_checkStateDependency(MutableStateDep);
2053 if (FAILED(rc)) return rc;
2054
2055 /** @todo check validity! */
2056 i_setModified(IsModified_MachineData);
2057 mHWData.backup();
2058 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2059
2060 return S_OK;
2061}
2062
2063HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2064{
2065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2066
2067 *aMonitorCount = mHWData->mMonitorCount;
2068
2069 return S_OK;
2070}
2071
2072HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2073{
2074 /* make sure monitor count is a sensible number */
2075 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2076 return setError(E_INVALIDARG,
2077 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2078 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2079
2080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2081
2082 HRESULT rc = i_checkStateDependency(MutableStateDep);
2083 if (FAILED(rc)) return rc;
2084
2085 i_setModified(IsModified_MachineData);
2086 mHWData.backup();
2087 mHWData->mMonitorCount = aMonitorCount;
2088
2089 return S_OK;
2090}
2091
2092HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2093{
2094 /* mBIOSSettings is constant during life time, no need to lock */
2095 mBIOSSettings.queryInterfaceTo(aBIOSSettings.asOutParam());
2096
2097 return S_OK;
2098}
2099
2100HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2101{
2102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2103
2104 switch (aProperty)
2105 {
2106 case CPUPropertyType_PAE:
2107 *aValue = mHWData->mPAEEnabled;
2108 break;
2109
2110 case CPUPropertyType_Synthetic:
2111 *aValue = mHWData->mSyntheticCpu;
2112 break;
2113
2114 case CPUPropertyType_LongMode:
2115 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2116 *aValue = TRUE;
2117 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2118 *aValue = FALSE;
2119#if HC_ARCH_BITS == 64
2120 else
2121 *aValue = TRUE;
2122#else
2123 else
2124 {
2125 *aValue = FALSE;
2126
2127 ComPtr<IGuestOSType> ptrGuestOSType;
2128 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2129 if (SUCCEEDED(hrc2))
2130 {
2131 BOOL fIs64Bit = FALSE;
2132 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2133 if (SUCCEEDED(hrc2) && fIs64Bit)
2134 {
2135 ComObjPtr<Host> ptrHost = mParent->i_host();
2136 alock.release();
2137
2138 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2139 if (FAILED(hrc2))
2140 *aValue = FALSE;
2141 }
2142 }
2143 }
2144#endif
2145 break;
2146
2147 case CPUPropertyType_TripleFaultReset:
2148 *aValue = mHWData->mTripleFaultReset;
2149 break;
2150
2151 default:
2152 return E_INVALIDARG;
2153 }
2154 return S_OK;
2155}
2156
2157HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2158{
2159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2160
2161 HRESULT rc = i_checkStateDependency(MutableStateDep);
2162 if (FAILED(rc)) return rc;
2163
2164 switch (aProperty)
2165 {
2166 case CPUPropertyType_PAE:
2167 i_setModified(IsModified_MachineData);
2168 mHWData.backup();
2169 mHWData->mPAEEnabled = !!aValue;
2170 break;
2171
2172 case CPUPropertyType_Synthetic:
2173 i_setModified(IsModified_MachineData);
2174 mHWData.backup();
2175 mHWData->mSyntheticCpu = !!aValue;
2176 break;
2177
2178 case CPUPropertyType_LongMode:
2179 i_setModified(IsModified_MachineData);
2180 mHWData.backup();
2181 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2182 break;
2183
2184 case CPUPropertyType_TripleFaultReset:
2185 i_setModified(IsModified_MachineData);
2186 mHWData.backup();
2187 mHWData->mTripleFaultReset = !!aValue;
2188 break;
2189
2190 default:
2191 return E_INVALIDARG;
2192 }
2193 return S_OK;
2194}
2195
2196HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2197{
2198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2199
2200 switch(aId)
2201 {
2202 case 0x0:
2203 case 0x1:
2204 case 0x2:
2205 case 0x3:
2206 case 0x4:
2207 case 0x5:
2208 case 0x6:
2209 case 0x7:
2210 case 0x8:
2211 case 0x9:
2212 case 0xA:
2213 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2214 return E_INVALIDARG;
2215
2216 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2217 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2218 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2219 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2220 break;
2221
2222 case 0x80000000:
2223 case 0x80000001:
2224 case 0x80000002:
2225 case 0x80000003:
2226 case 0x80000004:
2227 case 0x80000005:
2228 case 0x80000006:
2229 case 0x80000007:
2230 case 0x80000008:
2231 case 0x80000009:
2232 case 0x8000000A:
2233 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2234 return E_INVALIDARG;
2235
2236 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2237 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2238 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2239 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2240 break;
2241
2242 default:
2243 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2244 }
2245 return S_OK;
2246}
2247
2248
2249HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2250{
2251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2252
2253 HRESULT rc = i_checkStateDependency(MutableStateDep);
2254 if (FAILED(rc)) return rc;
2255
2256 switch(aId)
2257 {
2258 case 0x0:
2259 case 0x1:
2260 case 0x2:
2261 case 0x3:
2262 case 0x4:
2263 case 0x5:
2264 case 0x6:
2265 case 0x7:
2266 case 0x8:
2267 case 0x9:
2268 case 0xA:
2269 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2270 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2271 i_setModified(IsModified_MachineData);
2272 mHWData.backup();
2273 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2274 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2275 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2276 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2277 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2278 break;
2279
2280 case 0x80000000:
2281 case 0x80000001:
2282 case 0x80000002:
2283 case 0x80000003:
2284 case 0x80000004:
2285 case 0x80000005:
2286 case 0x80000006:
2287 case 0x80000007:
2288 case 0x80000008:
2289 case 0x80000009:
2290 case 0x8000000A:
2291 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2292 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2293 i_setModified(IsModified_MachineData);
2294 mHWData.backup();
2295 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2296 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2297 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2298 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2299 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2300 break;
2301
2302 default:
2303 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2304 }
2305 return S_OK;
2306}
2307
2308HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2309{
2310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2311
2312 HRESULT rc = i_checkStateDependency(MutableStateDep);
2313 if (FAILED(rc)) return rc;
2314
2315 switch(aId)
2316 {
2317 case 0x0:
2318 case 0x1:
2319 case 0x2:
2320 case 0x3:
2321 case 0x4:
2322 case 0x5:
2323 case 0x6:
2324 case 0x7:
2325 case 0x8:
2326 case 0x9:
2327 case 0xA:
2328 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2329 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2330 i_setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 /* Invalidate leaf. */
2333 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2334 break;
2335
2336 case 0x80000000:
2337 case 0x80000001:
2338 case 0x80000002:
2339 case 0x80000003:
2340 case 0x80000004:
2341 case 0x80000005:
2342 case 0x80000006:
2343 case 0x80000007:
2344 case 0x80000008:
2345 case 0x80000009:
2346 case 0x8000000A:
2347 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2348 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2349 i_setModified(IsModified_MachineData);
2350 mHWData.backup();
2351 /* Invalidate leaf. */
2352 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2353 break;
2354
2355 default:
2356 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2357 }
2358 return S_OK;
2359}
2360
2361HRESULT Machine::removeAllCPUIDLeaves()
2362{
2363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2364
2365 HRESULT rc = i_checkStateDependency(MutableStateDep);
2366 if (FAILED(rc)) return rc;
2367
2368 i_setModified(IsModified_MachineData);
2369 mHWData.backup();
2370
2371 /* Invalidate all standard leafs. */
2372 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2373 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2374
2375 /* Invalidate all extended leafs. */
2376 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2377 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2378
2379 return S_OK;
2380}
2381HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2382{
2383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2384
2385 switch(aProperty)
2386 {
2387 case HWVirtExPropertyType_Enabled:
2388 *aValue = mHWData->mHWVirtExEnabled;
2389 break;
2390
2391 case HWVirtExPropertyType_VPID:
2392 *aValue = mHWData->mHWVirtExVPIDEnabled;
2393 break;
2394
2395 case HWVirtExPropertyType_NestedPaging:
2396 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2397 break;
2398
2399 case HWVirtExPropertyType_UnrestrictedExecution:
2400 *aValue = mHWData->mHWVirtExUXEnabled;
2401 break;
2402
2403 case HWVirtExPropertyType_LargePages:
2404 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2405#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2406 *aValue = FALSE;
2407#endif
2408 break;
2409
2410 case HWVirtExPropertyType_Force:
2411 *aValue = mHWData->mHWVirtExForceEnabled;
2412 break;
2413
2414 default:
2415 return E_INVALIDARG;
2416 }
2417 return S_OK;
2418}
2419
2420HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2421{
2422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2423
2424 HRESULT rc = i_checkStateDependency(MutableStateDep);
2425 if (FAILED(rc)) return rc;
2426
2427 switch(aProperty)
2428 {
2429 case HWVirtExPropertyType_Enabled:
2430 i_setModified(IsModified_MachineData);
2431 mHWData.backup();
2432 mHWData->mHWVirtExEnabled = !!aValue;
2433 break;
2434
2435 case HWVirtExPropertyType_VPID:
2436 i_setModified(IsModified_MachineData);
2437 mHWData.backup();
2438 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2439 break;
2440
2441 case HWVirtExPropertyType_NestedPaging:
2442 i_setModified(IsModified_MachineData);
2443 mHWData.backup();
2444 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2445 break;
2446
2447 case HWVirtExPropertyType_UnrestrictedExecution:
2448 i_setModified(IsModified_MachineData);
2449 mHWData.backup();
2450 mHWData->mHWVirtExUXEnabled = !!aValue;
2451 break;
2452
2453 case HWVirtExPropertyType_LargePages:
2454 i_setModified(IsModified_MachineData);
2455 mHWData.backup();
2456 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2457 break;
2458
2459 case HWVirtExPropertyType_Force:
2460 i_setModified(IsModified_MachineData);
2461 mHWData.backup();
2462 mHWData->mHWVirtExForceEnabled = !!aValue;
2463 break;
2464
2465 default:
2466 return E_INVALIDARG;
2467 }
2468
2469 return S_OK;
2470}
2471
2472HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2473{
2474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2475
2476 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2477
2478 return S_OK;
2479}
2480
2481HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2482{
2483 /* @todo (r=dmik):
2484 * 1. Allow to change the name of the snapshot folder containing snapshots
2485 * 2. Rename the folder on disk instead of just changing the property
2486 * value (to be smart and not to leave garbage). Note that it cannot be
2487 * done here because the change may be rolled back. Thus, the right
2488 * place is #saveSettings().
2489 */
2490
2491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2492
2493 HRESULT rc = i_checkStateDependency(MutableStateDep);
2494 if (FAILED(rc)) return rc;
2495
2496 if (!mData->mCurrentSnapshot.isNull())
2497 return setError(E_FAIL,
2498 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2499
2500 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2501
2502 if (strSnapshotFolder.isEmpty())
2503 strSnapshotFolder = "Snapshots";
2504 int vrc = i_calculateFullPath(strSnapshotFolder,
2505 strSnapshotFolder);
2506 if (RT_FAILURE(vrc))
2507 return setError(E_FAIL,
2508 tr("Invalid snapshot folder '%s' (%Rrc)"),
2509 strSnapshotFolder.c_str(), vrc);
2510
2511 i_setModified(IsModified_MachineData);
2512 mUserData.backup();
2513
2514 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2515
2516 return S_OK;
2517}
2518
2519HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2520{
2521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2522
2523 aMediumAttachments.resize(mMediaData->mAttachments.size());
2524 size_t i = 0;
2525 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2526 it != mMediaData->mAttachments.end(); ++it, ++i)
2527 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
2528
2529 return S_OK;
2530}
2531
2532HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2533{
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 Assert(!!mVRDEServer);
2537
2538 mVRDEServer.queryInterfaceTo(aVRDEServer.asOutParam());
2539
2540 return S_OK;
2541}
2542
2543HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2544{
2545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 mAudioAdapter.queryInterfaceTo(aAudioAdapter.asOutParam());
2548
2549 return S_OK;
2550}
2551
2552HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2553{
2554#ifdef VBOX_WITH_VUSB
2555 clearError();
2556 MultiResult rc(S_OK);
2557
2558# ifdef VBOX_WITH_USB
2559 rc = mParent->i_host()->i_checkUSBProxyService();
2560 if (FAILED(rc)) return rc;
2561# endif
2562
2563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 USBControllerList data = *mUSBControllers.data();
2566 aUSBControllers.resize(data.size());
2567 size_t i = 0;
2568 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2569 (*it).queryInterfaceTo(aUSBControllers[i].asOutParam());
2570
2571 return S_OK;
2572#else
2573 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2574 * extended error info to indicate that USB is simply not available
2575 * (w/o treating it as a failure), for example, as in OSE */
2576 NOREF(aUSBControllers);
2577 ReturnComNotImplemented();
2578#endif /* VBOX_WITH_VUSB */
2579}
2580
2581HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2582{
2583#ifdef VBOX_WITH_VUSB
2584 clearError();
2585 MultiResult rc(S_OK);
2586
2587# ifdef VBOX_WITH_USB
2588 rc = mParent->i_host()->i_checkUSBProxyService();
2589 if (FAILED(rc)) return rc;
2590# endif
2591
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters.asOutParam());
2595#else
2596 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2597 * extended error info to indicate that USB is simply not available
2598 * (w/o treating it as a failure), for example, as in OSE */
2599 NOREF(aUSBDeviceFilters);
2600 ReturnComNotImplemented();
2601#endif /* VBOX_WITH_VUSB */
2602}
2603
2604HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2605{
2606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2607
2608 aSettingsFilePath = mData->m_strConfigFileFull;
2609
2610 return S_OK;
2611}
2612
2613HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2614{
2615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2616
2617 HRESULT rc = i_checkStateDependency(MutableStateDep);
2618 if (FAILED(rc)) return rc;
2619
2620 if (!mData->pMachineConfigFile->fileExists())
2621 // this is a new machine, and no config file exists yet:
2622 *aSettingsModified = TRUE;
2623 else
2624 *aSettingsModified = (mData->flModifications != 0);
2625
2626 return S_OK;
2627}
2628
2629HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2630{
2631
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 *aSessionState = mData->mSession.mState;
2635
2636 return S_OK;
2637}
2638
2639HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2640{
2641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 aSessionType = mData->mSession.mType;
2644
2645 return S_OK;
2646}
2647
2648HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2649{
2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 *aSessionPID = mData->mSession.mPID;
2653
2654 return S_OK;
2655}
2656
2657HRESULT Machine::getState(MachineState_T *aState)
2658{
2659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2660
2661 *aState = mData->mMachineState;
2662
2663 return S_OK;
2664}
2665
2666HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2667{
2668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2669
2670 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 aStateFilePath = mSSData->strStateFilePath;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2685{
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 i_getLogFolder(aLogFolder);
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot.asOutParam());
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2703{
2704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2707 ? 0
2708 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2709
2710 return S_OK;
2711}
2712
2713HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2714{
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 /* Note: for machines with no snapshots, we always return FALSE
2718 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2719 * reasons :) */
2720
2721 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2722 ? FALSE
2723 : mData->mCurrentStateModified;
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aSharedFolders.resize(mHWData->mSharedFolders.size());
2733 size_t i = 0;
2734 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2735 it != mHWData->mSharedFolders.end(); ++i, ++it)
2736 (*it).queryInterfaceTo(aSharedFolders[i].asOutParam());
2737
2738 return S_OK;
2739}
2740
2741HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2742{
2743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 *aClipboardMode = mHWData->mClipboardMode;
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2751{
2752 HRESULT rc = S_OK;
2753
2754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2755
2756 alock.release();
2757 rc = i_onClipboardModeChange(aClipboardMode);
2758 alock.acquire();
2759 if (FAILED(rc)) return rc;
2760
2761 i_setModified(IsModified_MachineData);
2762 mHWData.backup();
2763 mHWData->mClipboardMode = aClipboardMode;
2764
2765 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2766 if (Global::IsOnline(mData->mMachineState))
2767 i_saveSettings(NULL);
2768
2769 return S_OK;
2770}
2771
2772HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2773{
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 *aDnDMode = mHWData->mDnDMode;
2777
2778 return S_OK;
2779}
2780
2781HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2782{
2783 HRESULT rc = S_OK;
2784
2785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 alock.release();
2788 rc = i_onDnDModeChange(aDnDMode);
2789
2790 alock.acquire();
2791 if (FAILED(rc)) return rc;
2792
2793 i_setModified(IsModified_MachineData);
2794 mHWData.backup();
2795 mHWData->mDnDMode = aDnDMode;
2796
2797 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2798 if (Global::IsOnline(mData->mMachineState))
2799 i_saveSettings(NULL);
2800
2801 return S_OK;
2802}
2803
2804HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 try
2809 {
2810 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2811 }
2812 catch (...)
2813 {
2814 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2815 }
2816
2817 return S_OK;
2818}
2819
2820HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2821{
2822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 HRESULT rc = i_checkStateDependency(MutableStateDep);
2825 if (FAILED(rc)) return rc;
2826
2827 i_setModified(IsModified_MachineData);
2828 mHWData.backup();
2829 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2830 return rc;
2831}
2832
2833HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2834{
2835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2836 StorageControllerList data = *mStorageControllers.data();
2837 size_t i = 0;
2838 aStorageControllers.resize(data.size());
2839 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2840 (*it).queryInterfaceTo(aStorageControllers[i].asOutParam());
2841 return S_OK;
2842}
2843
2844HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 *aEnabled = mUserData->s.fTeleporterEnabled;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2854{
2855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 /* Only allow it to be set to true when PoweredOff or Aborted.
2858 (Clearing it is always permitted.) */
2859 if ( aTeleporterEnabled
2860 && mData->mRegistered
2861 && ( !i_isSessionMachine()
2862 || ( mData->mMachineState != MachineState_PoweredOff
2863 && mData->mMachineState != MachineState_Teleported
2864 && mData->mMachineState != MachineState_Aborted
2865 )
2866 )
2867 )
2868 return setError(VBOX_E_INVALID_VM_STATE,
2869 tr("The machine is not powered off (state is %s)"),
2870 Global::stringifyMachineState(mData->mMachineState));
2871
2872 i_setModified(IsModified_MachineData);
2873 mUserData.backup();
2874 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2875
2876 return S_OK;
2877}
2878
2879HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2880{
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882
2883 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2884
2885 return S_OK;
2886}
2887
2888HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2889{
2890 if (aTeleporterPort >= _64K)
2891 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2892
2893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 HRESULT rc = i_checkStateDependency(MutableStateDep);
2896 if (FAILED(rc)) return rc;
2897
2898 i_setModified(IsModified_MachineData);
2899 mUserData.backup();
2900 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2901
2902 return S_OK;
2903}
2904
2905HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2906{
2907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2910
2911 return S_OK;
2912}
2913
2914HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2915{
2916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 HRESULT rc = i_checkStateDependency(MutableStateDep);
2919 if (FAILED(rc)) return rc;
2920
2921 i_setModified(IsModified_MachineData);
2922 mUserData.backup();
2923 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2924
2925 return S_OK;
2926}
2927
2928HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2929{
2930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2931 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2932
2933 return S_OK;
2934}
2935
2936HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2937{
2938 /*
2939 * Hash the password first.
2940 */
2941 com::Utf8Str aT = aTeleporterPassword;
2942
2943 if (!aT.isEmpty())
2944 {
2945 if (VBoxIsPasswordHashed(&aT))
2946 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2947 VBoxHashPassword(&aT);
2948 }
2949
2950 /*
2951 * Do the update.
2952 */
2953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2954 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2955 if (SUCCEEDED(hrc))
2956 {
2957 i_setModified(IsModified_MachineData);
2958 mUserData.backup();
2959 mUserData->s.strTeleporterPassword = aT;
2960 }
2961
2962 return hrc;
2963}
2964
2965HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2966{
2967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2970 return S_OK;
2971}
2972
2973HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2974{
2975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 /* @todo deal with running state change. */
2978 HRESULT rc = i_checkStateDependency(MutableStateDep);
2979 if (FAILED(rc)) return rc;
2980
2981 i_setModified(IsModified_MachineData);
2982 mUserData.backup();
2983 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2984 return S_OK;
2985}
2986
2987HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2988{
2989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2990
2991 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2992 return S_OK;
2993}
2994
2995HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2996{
2997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2998
2999 /* @todo deal with running state change. */
3000 HRESULT rc = i_checkStateDependency(MutableStateDep);
3001 if (FAILED(rc)) return rc;
3002
3003 i_setModified(IsModified_MachineData);
3004 mUserData.backup();
3005 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3006 return S_OK;
3007}
3008
3009HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3010{
3011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3012
3013 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3014 return S_OK;
3015}
3016
3017HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3018{
3019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3020
3021 /* @todo deal with running state change. */
3022 HRESULT rc = i_checkStateDependency(MutableStateDep);
3023 if (FAILED(rc)) return rc;
3024
3025 i_setModified(IsModified_MachineData);
3026 mUserData.backup();
3027 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3028 return S_OK;
3029}
3030
3031HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3032{
3033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3034
3035 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3036
3037 return S_OK;
3038}
3039
3040HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3041{
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043
3044 /* @todo deal with running state change. */
3045 HRESULT rc = i_checkStateDependency(MutableStateDep);
3046 if (FAILED(rc)) return rc;
3047
3048 i_setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3051
3052 return S_OK;
3053}
3054
3055HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3056{
3057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3060 return S_OK;
3061}
3062
3063HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3064{
3065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3066
3067 /* @todo deal with running state change. */
3068 HRESULT rc = i_checkStateDependency(MutableStateDep);
3069 if (FAILED(rc)) return rc;
3070
3071 i_setModified(IsModified_MachineData);
3072 mUserData.backup();
3073 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3074 return S_OK;
3075}
3076
3077HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3078{
3079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3082
3083 return S_OK;
3084}
3085
3086HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3087{
3088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3089
3090 /* Only allow it to be set to true when PoweredOff or Aborted.
3091 (Clearing it is always permitted.) */
3092 if ( aRTCUseUTC
3093 && mData->mRegistered
3094 && ( !i_isSessionMachine()
3095 || ( mData->mMachineState != MachineState_PoweredOff
3096 && mData->mMachineState != MachineState_Teleported
3097 && mData->mMachineState != MachineState_Aborted
3098 )
3099 )
3100 )
3101 return setError(VBOX_E_INVALID_VM_STATE,
3102 tr("The machine is not powered off (state is %s)"),
3103 Global::stringifyMachineState(mData->mMachineState));
3104
3105 i_setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3108
3109 return S_OK;
3110}
3111
3112HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3113{
3114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3117
3118 return S_OK;
3119}
3120
3121HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3122{
3123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 HRESULT rc = i_checkStateDependency(MutableStateDep);
3126 if (FAILED(rc)) return rc;
3127
3128 i_setModified(IsModified_MachineData);
3129 mHWData.backup();
3130 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3131
3132 return S_OK;
3133}
3134
3135HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3136{
3137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 *aIOCacheSize = mHWData->mIOCacheSize;
3140
3141 return S_OK;
3142}
3143
3144HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3145{
3146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3147
3148 HRESULT rc = i_checkStateDependency(MutableStateDep);
3149 if (FAILED(rc)) return rc;
3150
3151 i_setModified(IsModified_MachineData);
3152 mHWData.backup();
3153 mHWData->mIOCacheSize = aIOCacheSize;
3154
3155 return S_OK;
3156}
3157
3158
3159/**
3160 * @note Locks objects!
3161 */
3162HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3163 LockType_T aLockType)
3164
3165{
3166 /* check the session state */
3167 SessionState_T state;
3168 HRESULT rc = aSession->COMGETTER(State)(&state);
3169 if (FAILED(rc)) return rc;
3170
3171 if (state != SessionState_Unlocked)
3172 return setError(VBOX_E_INVALID_OBJECT_STATE,
3173 tr("The given session is busy"));
3174
3175 // get the client's IInternalSessionControl interface
3176 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3177 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3178 E_INVALIDARG);
3179
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 if (!mData->mRegistered)
3183 return setError(E_UNEXPECTED,
3184 tr("The machine '%s' is not registered"),
3185 mUserData->s.strName.c_str());
3186
3187 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3188
3189 SessionState_T oldState = mData->mSession.mState;
3190 /* Hack: in case the session is closing and there is a progress object
3191 * which allows waiting for the session to be closed, take the opportunity
3192 * and do a limited wait (max. 1 second). This helps a lot when the system
3193 * is busy and thus session closing can take a little while. */
3194 if ( mData->mSession.mState == SessionState_Unlocking
3195 && mData->mSession.mProgress)
3196 {
3197 alock.release();
3198 mData->mSession.mProgress->WaitForCompletion(1000);
3199 alock.acquire();
3200 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3201 }
3202
3203 // try again now
3204 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3205 // (i.e. session machine exists)
3206 && (aLockType == LockType_Shared) // caller wants a shared link to the
3207 // existing session that holds the write lock:
3208 )
3209 {
3210 // OK, share the session... we are now dealing with three processes:
3211 // 1) VBoxSVC (where this code runs);
3212 // 2) process C: the caller's client process (who wants a shared session);
3213 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3214
3215 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3216 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3217 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3218 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3219 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3220
3221 /*
3222 * Release the lock before calling the client process. It's safe here
3223 * since the only thing to do after we get the lock again is to add
3224 * the remote control to the list (which doesn't directly influence
3225 * anything).
3226 */
3227 alock.release();
3228
3229 // get the console of the session holding the write lock (this is a remote call)
3230 ComPtr<IConsole> pConsoleW;
3231 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3232 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3233 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3234 if (FAILED(rc))
3235 // the failure may occur w/o any error info (from RPC), so provide one
3236 return setError(VBOX_E_VM_ERROR,
3237 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3238
3239 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3240
3241 // share the session machine and W's console with the caller's session
3242 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3243 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3244 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3245
3246 if (FAILED(rc))
3247 // the failure may occur w/o any error info (from RPC), so provide one
3248 return setError(VBOX_E_VM_ERROR,
3249 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3250 alock.acquire();
3251
3252 // need to revalidate the state after acquiring the lock again
3253 if (mData->mSession.mState != SessionState_Locked)
3254 {
3255 pSessionControl->Uninitialize();
3256 return setError(VBOX_E_INVALID_SESSION_STATE,
3257 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3258 mUserData->s.strName.c_str());
3259 }
3260
3261 // add the caller's session to the list
3262 mData->mSession.mRemoteControls.push_back(pSessionControl);
3263 }
3264 else if ( mData->mSession.mState == SessionState_Locked
3265 || mData->mSession.mState == SessionState_Unlocking
3266 )
3267 {
3268 // sharing not permitted, or machine still unlocking:
3269 return setError(VBOX_E_INVALID_OBJECT_STATE,
3270 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3271 mUserData->s.strName.c_str());
3272 }
3273 else
3274 {
3275 // machine is not locked: then write-lock the machine (create the session machine)
3276
3277 // must not be busy
3278 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3279
3280 // get the caller's session PID
3281 RTPROCESS pid = NIL_RTPROCESS;
3282 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3283 pSessionControl->GetPID((ULONG*)&pid);
3284 Assert(pid != NIL_RTPROCESS);
3285
3286 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3287
3288 if (fLaunchingVMProcess)
3289 {
3290 if (mData->mSession.mPID == NIL_RTPROCESS)
3291 {
3292 // two or more clients racing for a lock, the one which set the
3293 // session state to Spawning will win, the others will get an
3294 // error as we can't decide here if waiting a little would help
3295 // (only for shared locks this would avoid an error)
3296 return setError(VBOX_E_INVALID_OBJECT_STATE,
3297 tr("The machine '%s' already has a lock request pending"),
3298 mUserData->s.strName.c_str());
3299 }
3300
3301 // this machine is awaiting for a spawning session to be opened:
3302 // then the calling process must be the one that got started by
3303 // LaunchVMProcess()
3304
3305 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3306 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3307
3308#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3309 /* Hardened windows builds spawns three processes when a VM is
3310 launched, the 3rd one is the one that will end up here. */
3311 RTPROCESS ppid;
3312 int rc = RTProcQueryParent(pid, &ppid);
3313 if (RT_SUCCESS(rc))
3314 rc = RTProcQueryParent(ppid, &ppid);
3315 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3316 || rc == VERR_ACCESS_DENIED)
3317 {
3318 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3319 mData->mSession.mPID = pid;
3320 }
3321#endif
3322
3323 if (mData->mSession.mPID != pid)
3324 return setError(E_ACCESSDENIED,
3325 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3326 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3327 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3328 }
3329
3330 // create the mutable SessionMachine from the current machine
3331 ComObjPtr<SessionMachine> sessionMachine;
3332 sessionMachine.createObject();
3333 rc = sessionMachine->init(this);
3334 AssertComRC(rc);
3335
3336 /* NOTE: doing return from this function after this point but
3337 * before the end is forbidden since it may call SessionMachine::uninit()
3338 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3339 * lock while still holding the Machine lock in alock so that a deadlock
3340 * is possible due to the wrong lock order. */
3341
3342 if (SUCCEEDED(rc))
3343 {
3344 /*
3345 * Set the session state to Spawning to protect against subsequent
3346 * attempts to open a session and to unregister the machine after
3347 * we release the lock.
3348 */
3349 SessionState_T origState = mData->mSession.mState;
3350 mData->mSession.mState = SessionState_Spawning;
3351
3352#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3353 /* Get the client token ID to be passed to the client process */
3354 Utf8Str strTokenId;
3355 sessionMachine->i_getTokenId(strTokenId);
3356 Assert(!strTokenId.isEmpty());
3357#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3358 /* Get the client token to be passed to the client process */
3359 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3360 /* The token is now "owned" by pToken, fix refcount */
3361 if (!pToken.isNull())
3362 pToken->Release();
3363#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3364
3365 /*
3366 * Release the lock before calling the client process -- it will call
3367 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3368 * because the state is Spawning, so that LaunchVMProcess() and
3369 * LockMachine() calls will fail. This method, called before we
3370 * acquire the lock again, will fail because of the wrong PID.
3371 *
3372 * Note that mData->mSession.mRemoteControls accessed outside
3373 * the lock may not be modified when state is Spawning, so it's safe.
3374 */
3375 alock.release();
3376
3377 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3378#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3379 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3380#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3381 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3382 /* Now the token is owned by the client process. */
3383 pToken.setNull();
3384#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3385 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3386
3387 /* The failure may occur w/o any error info (from RPC), so provide one */
3388 if (FAILED(rc))
3389 setError(VBOX_E_VM_ERROR,
3390 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3391
3392 if ( SUCCEEDED(rc)
3393 && fLaunchingVMProcess
3394 )
3395 {
3396 /* complete the remote session initialization */
3397
3398 /* get the console from the direct session */
3399 ComPtr<IConsole> console;
3400 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3401 ComAssertComRC(rc);
3402
3403 if (SUCCEEDED(rc) && !console)
3404 {
3405 ComAssert(!!console);
3406 rc = E_FAIL;
3407 }
3408
3409 /* assign machine & console to the remote session */
3410 if (SUCCEEDED(rc))
3411 {
3412 /*
3413 * after LaunchVMProcess(), the first and the only
3414 * entry in remoteControls is that remote session
3415 */
3416 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3417 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3418 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3419
3420 /* The failure may occur w/o any error info (from RPC), so provide one */
3421 if (FAILED(rc))
3422 setError(VBOX_E_VM_ERROR,
3423 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3424 }
3425
3426 if (FAILED(rc))
3427 pSessionControl->Uninitialize();
3428 }
3429
3430 /* acquire the lock again */
3431 alock.acquire();
3432
3433 /* Restore the session state */
3434 mData->mSession.mState = origState;
3435 }
3436
3437 // finalize spawning anyway (this is why we don't return on errors above)
3438 if (fLaunchingVMProcess)
3439 {
3440 /* Note that the progress object is finalized later */
3441 /** @todo Consider checking mData->mSession.mProgress for cancellation
3442 * around here. */
3443
3444 /* We don't reset mSession.mPID here because it is necessary for
3445 * SessionMachine::uninit() to reap the child process later. */
3446
3447 if (FAILED(rc))
3448 {
3449 /* Close the remote session, remove the remote control from the list
3450 * and reset session state to Closed (@note keep the code in sync
3451 * with the relevant part in checkForSpawnFailure()). */
3452
3453 Assert(mData->mSession.mRemoteControls.size() == 1);
3454 if (mData->mSession.mRemoteControls.size() == 1)
3455 {
3456 ErrorInfoKeeper eik;
3457 mData->mSession.mRemoteControls.front()->Uninitialize();
3458 }
3459
3460 mData->mSession.mRemoteControls.clear();
3461 mData->mSession.mState = SessionState_Unlocked;
3462 }
3463 }
3464 else
3465 {
3466 /* memorize PID of the directly opened session */
3467 if (SUCCEEDED(rc))
3468 mData->mSession.mPID = pid;
3469 }
3470
3471 if (SUCCEEDED(rc))
3472 {
3473 /* memorize the direct session control and cache IUnknown for it */
3474 mData->mSession.mDirectControl = pSessionControl;
3475 mData->mSession.mState = SessionState_Locked;
3476 /* associate the SessionMachine with this Machine */
3477 mData->mSession.mMachine = sessionMachine;
3478
3479 /* request an IUnknown pointer early from the remote party for later
3480 * identity checks (it will be internally cached within mDirectControl
3481 * at least on XPCOM) */
3482 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3483 NOREF(unk);
3484 }
3485
3486 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3487 * would break the lock order */
3488 alock.release();
3489
3490 /* uninitialize the created session machine on failure */
3491 if (FAILED(rc))
3492 sessionMachine->uninit();
3493
3494 }
3495
3496 if (SUCCEEDED(rc))
3497 {
3498 /*
3499 * tell the client watcher thread to update the set of
3500 * machines that have open sessions
3501 */
3502 mParent->i_updateClientWatcher();
3503
3504 if (oldState != SessionState_Locked)
3505 /* fire an event */
3506 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3507 }
3508
3509 return rc;
3510}
3511
3512/**
3513 * @note Locks objects!
3514 */
3515HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3516 const com::Utf8Str &aType,
3517 const com::Utf8Str &aEnvironment,
3518 ComPtr<IProgress> &aProgress)
3519{
3520 Utf8Str strFrontend(aType);
3521 Utf8Str strEnvironment(aEnvironment);
3522 /* "emergencystop" doesn't need the session, so skip the checks/interface
3523 * retrieval. This code doesn't quite fit in here, but introducing a
3524 * special API method would be even more effort, and would require explicit
3525 * support by every API client. It's better to hide the feature a bit. */
3526 if (strFrontend != "emergencystop")
3527 CheckComArgNotNull(aSession);
3528
3529 HRESULT rc = S_OK;
3530 if (strFrontend.isEmpty())
3531 {
3532 Bstr bstrFrontend;
3533 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3534 if (FAILED(rc))
3535 return rc;
3536 strFrontend = bstrFrontend;
3537 if (strFrontend.isEmpty())
3538 {
3539 ComPtr<ISystemProperties> systemProperties;
3540 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3541 if (FAILED(rc))
3542 return rc;
3543 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3544 if (FAILED(rc))
3545 return rc;
3546 strFrontend = bstrFrontend;
3547 }
3548 /* paranoia - emergencystop is not a valid default */
3549 if (strFrontend == "emergencystop")
3550 strFrontend = Utf8Str::Empty;
3551 }
3552 /* default frontend: Qt GUI */
3553 if (strFrontend.isEmpty())
3554 strFrontend = "GUI/Qt";
3555
3556 if (strFrontend != "emergencystop")
3557 {
3558 /* check the session state */
3559 SessionState_T state;
3560 rc = aSession->COMGETTER(State)(&state);
3561 if (FAILED(rc))
3562 return rc;
3563
3564 if (state != SessionState_Unlocked)
3565 return setError(VBOX_E_INVALID_OBJECT_STATE,
3566 tr("The given session is busy"));
3567
3568 /* get the IInternalSessionControl interface */
3569 ComPtr<IInternalSessionControl> control(aSession);
3570 ComAssertMsgRet(!control.isNull(),
3571 ("No IInternalSessionControl interface"),
3572 E_INVALIDARG);
3573
3574 /* get the teleporter enable state for the progress object init. */
3575 BOOL fTeleporterEnabled;
3576 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3577 if (FAILED(rc))
3578 return rc;
3579
3580 /* create a progress object */
3581 ComObjPtr<ProgressProxy> progress;
3582 progress.createObject();
3583 rc = progress->init(mParent,
3584 static_cast<IMachine*>(this),
3585 Bstr(tr("Starting VM")).raw(),
3586 TRUE /* aCancelable */,
3587 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3588 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3589 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3590 2 /* uFirstOperationWeight */,
3591 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3592
3593 if (SUCCEEDED(rc))
3594 {
3595 rc = i_launchVMProcess(control, strFrontend, strEnvironment, progress);
3596 if (SUCCEEDED(rc))
3597 {
3598 progress.queryInterfaceTo(aProgress.asOutParam());
3599
3600 /* signal the client watcher thread */
3601 mParent->i_updateClientWatcher();
3602
3603 /* fire an event */
3604 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3605 }
3606 }
3607 }
3608 else
3609 {
3610 /* no progress object - either instant success or failure */
3611 aProgress = NULL;
3612
3613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3614
3615 if (mData->mSession.mState != SessionState_Locked)
3616 return setError(VBOX_E_INVALID_OBJECT_STATE,
3617 tr("The machine '%s' is not locked by a session"),
3618 mUserData->s.strName.c_str());
3619
3620 /* must have a VM process associated - do not kill normal API clients
3621 * with an open session */
3622 if (!Global::IsOnline(mData->mMachineState))
3623 return setError(VBOX_E_INVALID_OBJECT_STATE,
3624 tr("The machine '%s' does not have a VM process"),
3625 mUserData->s.strName.c_str());
3626
3627 /* forcibly terminate the VM process */
3628 if (mData->mSession.mPID != NIL_RTPROCESS)
3629 RTProcTerminate(mData->mSession.mPID);
3630
3631 /* signal the client watcher thread, as most likely the client has
3632 * been terminated */
3633 mParent->i_updateClientWatcher();
3634 }
3635
3636 return rc;
3637}
3638
3639HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3640{
3641 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3642 return setError(E_INVALIDARG,
3643 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3644 aPosition, SchemaDefs::MaxBootPosition);
3645
3646 if (aDevice == DeviceType_USB)
3647 return setError(E_NOTIMPL,
3648 tr("Booting from USB device is currently not supported"));
3649
3650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3651
3652 HRESULT rc = i_checkStateDependency(MutableStateDep);
3653 if (FAILED(rc)) return rc;
3654
3655 i_setModified(IsModified_MachineData);
3656 mHWData.backup();
3657 mHWData->mBootOrder[aPosition - 1] = aDevice;
3658
3659 return S_OK;
3660}
3661
3662HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3663{
3664 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3665 return setError(E_INVALIDARG,
3666 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3667 aPosition, SchemaDefs::MaxBootPosition);
3668
3669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3670
3671 *aDevice = mHWData->mBootOrder[aPosition - 1];
3672
3673 return S_OK;
3674}
3675
3676HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3677 LONG aControllerPort,
3678 LONG aDevice,
3679 DeviceType_T aType,
3680 const ComPtr<IMedium> &aMedium)
3681{
3682 IMedium *aM = aMedium;
3683 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3684 aName.c_str(), aControllerPort, aDevice, aType, aM));
3685
3686 // request the host lock first, since might be calling Host methods for getting host drives;
3687 // next, protect the media tree all the while we're in here, as well as our member variables
3688 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3689 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3690
3691 HRESULT rc = i_checkStateDependency(MutableStateDep);
3692 if (FAILED(rc)) return rc;
3693
3694 /// @todo NEWMEDIA implicit machine registration
3695 if (!mData->mRegistered)
3696 return setError(VBOX_E_INVALID_OBJECT_STATE,
3697 tr("Cannot attach storage devices to an unregistered machine"));
3698
3699 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3700
3701 /* Check for an existing controller. */
3702 ComObjPtr<StorageController> ctl;
3703 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3704 if (FAILED(rc)) return rc;
3705
3706 StorageControllerType_T ctrlType;
3707 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3708 if (FAILED(rc))
3709 return setError(E_FAIL,
3710 tr("Could not get type of controller '%s'"),
3711 aName.c_str());
3712
3713 bool fSilent = false;
3714 Utf8Str strReconfig;
3715
3716 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3717 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3718 if ( mData->mMachineState == MachineState_Paused
3719 && strReconfig == "1")
3720 fSilent = true;
3721
3722 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3723 bool fHotplug = false;
3724 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3725 fHotplug = true;
3726
3727 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3728 return setError(VBOX_E_INVALID_VM_STATE,
3729 tr("Controller '%s' does not support hotplugging"),
3730 aName.c_str());
3731
3732 // check that the port and device are not out of range
3733 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3734 if (FAILED(rc)) return rc;
3735
3736 /* check if the device slot is already busy */
3737 MediumAttachment *pAttachTemp;
3738 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3739 Bstr(aName).raw(),
3740 aControllerPort,
3741 aDevice)))
3742 {
3743 Medium *pMedium = pAttachTemp->i_getMedium();
3744 if (pMedium)
3745 {
3746 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3747 return setError(VBOX_E_OBJECT_IN_USE,
3748 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3749 pMedium->i_getLocationFull().c_str(),
3750 aControllerPort,
3751 aDevice,
3752 aName.c_str());
3753 }
3754 else
3755 return setError(VBOX_E_OBJECT_IN_USE,
3756 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3757 aControllerPort, aDevice, aName.c_str());
3758 }
3759
3760 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3761 if (aMedium && medium.isNull())
3762 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3763
3764 AutoCaller mediumCaller(medium);
3765 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3766
3767 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3768
3769 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3770 && !medium.isNull()
3771 )
3772 return setError(VBOX_E_OBJECT_IN_USE,
3773 tr("Medium '%s' is already attached to this virtual machine"),
3774 medium->i_getLocationFull().c_str());
3775
3776 if (!medium.isNull())
3777 {
3778 MediumType_T mtype = medium->i_getType();
3779 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3780 // For DVDs it's not written to the config file, so needs no global config
3781 // version bump. For floppies it's a new attribute "type", which is ignored
3782 // by older VirtualBox version, so needs no global config version bump either.
3783 // For hard disks this type is not accepted.
3784 if (mtype == MediumType_MultiAttach)
3785 {
3786 // This type is new with VirtualBox 4.0 and therefore requires settings
3787 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3788 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3789 // two reasons: The medium type is a property of the media registry tree, which
3790 // can reside in the global config file (for pre-4.0 media); we would therefore
3791 // possibly need to bump the global config version. We don't want to do that though
3792 // because that might make downgrading to pre-4.0 impossible.
3793 // As a result, we can only use these two new types if the medium is NOT in the
3794 // global registry:
3795 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3796 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3797 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3798 )
3799 return setError(VBOX_E_INVALID_OBJECT_STATE,
3800 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3801 "to machines that were created with VirtualBox 4.0 or later"),
3802 medium->i_getLocationFull().c_str());
3803 }
3804 }
3805
3806 bool fIndirect = false;
3807 if (!medium.isNull())
3808 fIndirect = medium->i_isReadOnly();
3809 bool associate = true;
3810
3811 do
3812 {
3813 if ( aType == DeviceType_HardDisk
3814 && mMediaData.isBackedUp())
3815 {
3816 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3817
3818 /* check if the medium was attached to the VM before we started
3819 * changing attachments in which case the attachment just needs to
3820 * be restored */
3821 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3822 {
3823 AssertReturn(!fIndirect, E_FAIL);
3824
3825 /* see if it's the same bus/channel/device */
3826 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3827 {
3828 /* the simplest case: restore the whole attachment
3829 * and return, nothing else to do */
3830 mMediaData->mAttachments.push_back(pAttachTemp);
3831
3832 /* Reattach the medium to the VM. */
3833 if (fHotplug || fSilent)
3834 {
3835 mediumLock.release();
3836 treeLock.release();
3837 alock.release();
3838
3839 MediumLockList *pMediumLockList(new MediumLockList());
3840
3841 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3842 true /* fMediumLockWrite */,
3843 NULL,
3844 *pMediumLockList);
3845 alock.acquire();
3846 if (FAILED(rc))
3847 delete pMediumLockList;
3848 else
3849 {
3850 mData->mSession.mLockedMedia.Unlock();
3851 alock.release();
3852 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3853 mData->mSession.mLockedMedia.Lock();
3854 alock.acquire();
3855 }
3856 alock.release();
3857
3858 if (SUCCEEDED(rc))
3859 {
3860 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3861 /* Remove lock list in case of error. */
3862 if (FAILED(rc))
3863 {
3864 mData->mSession.mLockedMedia.Unlock();
3865 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3866 mData->mSession.mLockedMedia.Lock();
3867 }
3868 }
3869 }
3870
3871 return S_OK;
3872 }
3873
3874 /* bus/channel/device differ; we need a new attachment object,
3875 * but don't try to associate it again */
3876 associate = false;
3877 break;
3878 }
3879 }
3880
3881 /* go further only if the attachment is to be indirect */
3882 if (!fIndirect)
3883 break;
3884
3885 /* perform the so called smart attachment logic for indirect
3886 * attachments. Note that smart attachment is only applicable to base
3887 * hard disks. */
3888
3889 if (medium->i_getParent().isNull())
3890 {
3891 /* first, investigate the backup copy of the current hard disk
3892 * attachments to make it possible to re-attach existing diffs to
3893 * another device slot w/o losing their contents */
3894 if (mMediaData.isBackedUp())
3895 {
3896 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3897
3898 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3899 uint32_t foundLevel = 0;
3900
3901 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3902 {
3903 uint32_t level = 0;
3904 MediumAttachment *pAttach = *it;
3905 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3906 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3907 if (pMedium.isNull())
3908 continue;
3909
3910 if (pMedium->i_getBase(&level) == medium)
3911 {
3912 /* skip the hard disk if its currently attached (we
3913 * cannot attach the same hard disk twice) */
3914 if (i_findAttachment(mMediaData->mAttachments,
3915 pMedium))
3916 continue;
3917
3918 /* matched device, channel and bus (i.e. attached to the
3919 * same place) will win and immediately stop the search;
3920 * otherwise the attachment that has the youngest
3921 * descendant of medium will be used
3922 */
3923 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3924 {
3925 /* the simplest case: restore the whole attachment
3926 * and return, nothing else to do */
3927 mMediaData->mAttachments.push_back(*it);
3928
3929 /* Reattach the medium to the VM. */
3930 if (fHotplug || fSilent)
3931 {
3932 mediumLock.release();
3933 treeLock.release();
3934 alock.release();
3935
3936 MediumLockList *pMediumLockList(new MediumLockList());
3937
3938 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3939 true /* fMediumLockWrite */,
3940 NULL,
3941 *pMediumLockList);
3942 alock.acquire();
3943 if (FAILED(rc))
3944 delete pMediumLockList;
3945 else
3946 {
3947 mData->mSession.mLockedMedia.Unlock();
3948 alock.release();
3949 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3950 mData->mSession.mLockedMedia.Lock();
3951 alock.acquire();
3952 }
3953 alock.release();
3954
3955 if (SUCCEEDED(rc))
3956 {
3957 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3958 /* Remove lock list in case of error. */
3959 if (FAILED(rc))
3960 {
3961 mData->mSession.mLockedMedia.Unlock();
3962 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3963 mData->mSession.mLockedMedia.Lock();
3964 }
3965 }
3966 }
3967
3968 return S_OK;
3969 }
3970 else if ( foundIt == oldAtts.end()
3971 || level > foundLevel /* prefer younger */
3972 )
3973 {
3974 foundIt = it;
3975 foundLevel = level;
3976 }
3977 }
3978 }
3979
3980 if (foundIt != oldAtts.end())
3981 {
3982 /* use the previously attached hard disk */
3983 medium = (*foundIt)->i_getMedium();
3984 mediumCaller.attach(medium);
3985 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3986 mediumLock.attach(medium);
3987 /* not implicit, doesn't require association with this VM */
3988 fIndirect = false;
3989 associate = false;
3990 /* go right to the MediumAttachment creation */
3991 break;
3992 }
3993 }
3994
3995 /* must give up the medium lock and medium tree lock as below we
3996 * go over snapshots, which needs a lock with higher lock order. */
3997 mediumLock.release();
3998 treeLock.release();
3999
4000 /* then, search through snapshots for the best diff in the given
4001 * hard disk's chain to base the new diff on */
4002
4003 ComObjPtr<Medium> base;
4004 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4005 while (snap)
4006 {
4007 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4008
4009 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4010
4011 MediumAttachment *pAttachFound = NULL;
4012 uint32_t foundLevel = 0;
4013
4014 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4015 {
4016 MediumAttachment *pAttach = *it;
4017 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4018 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4019 if (pMedium.isNull())
4020 continue;
4021
4022 uint32_t level = 0;
4023 if (pMedium->i_getBase(&level) == medium)
4024 {
4025 /* matched device, channel and bus (i.e. attached to the
4026 * same place) will win and immediately stop the search;
4027 * otherwise the attachment that has the youngest
4028 * descendant of medium will be used
4029 */
4030 if ( pAttach->i_getDevice() == aDevice
4031 && pAttach->i_getPort() == aControllerPort
4032 && pAttach->i_getControllerName() == aName
4033 )
4034 {
4035 pAttachFound = pAttach;
4036 break;
4037 }
4038 else if ( !pAttachFound
4039 || level > foundLevel /* prefer younger */
4040 )
4041 {
4042 pAttachFound = pAttach;
4043 foundLevel = level;
4044 }
4045 }
4046 }
4047
4048 if (pAttachFound)
4049 {
4050 base = pAttachFound->i_getMedium();
4051 break;
4052 }
4053
4054 snap = snap->i_getParent();
4055 }
4056
4057 /* re-lock medium tree and the medium, as we need it below */
4058 treeLock.acquire();
4059 mediumLock.acquire();
4060
4061 /* found a suitable diff, use it as a base */
4062 if (!base.isNull())
4063 {
4064 medium = base;
4065 mediumCaller.attach(medium);
4066 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4067 mediumLock.attach(medium);
4068 }
4069 }
4070
4071 Utf8Str strFullSnapshotFolder;
4072 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4073
4074 ComObjPtr<Medium> diff;
4075 diff.createObject();
4076 // store this diff in the same registry as the parent
4077 Guid uuidRegistryParent;
4078 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4079 {
4080 // parent image has no registry: this can happen if we're attaching a new immutable
4081 // image that has not yet been attached (medium then points to the base and we're
4082 // creating the diff image for the immutable, and the parent is not yet registered);
4083 // put the parent in the machine registry then
4084 mediumLock.release();
4085 treeLock.release();
4086 alock.release();
4087 i_addMediumToRegistry(medium);
4088 alock.acquire();
4089 treeLock.acquire();
4090 mediumLock.acquire();
4091 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4092 }
4093 rc = diff->init(mParent,
4094 medium->i_getPreferredDiffFormat(),
4095 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4096 uuidRegistryParent);
4097 if (FAILED(rc)) return rc;
4098
4099 /* Apply the normal locking logic to the entire chain. */
4100 MediumLockList *pMediumLockList(new MediumLockList());
4101 mediumLock.release();
4102 treeLock.release();
4103 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4104 true /* fMediumLockWrite */,
4105 medium,
4106 *pMediumLockList);
4107 treeLock.acquire();
4108 mediumLock.acquire();
4109 if (SUCCEEDED(rc))
4110 {
4111 mediumLock.release();
4112 treeLock.release();
4113 rc = pMediumLockList->Lock();
4114 treeLock.acquire();
4115 mediumLock.acquire();
4116 if (FAILED(rc))
4117 setError(rc,
4118 tr("Could not lock medium when creating diff '%s'"),
4119 diff->i_getLocationFull().c_str());
4120 else
4121 {
4122 /* will release the lock before the potentially lengthy
4123 * operation, so protect with the special state */
4124 MachineState_T oldState = mData->mMachineState;
4125 i_setMachineState(MachineState_SettingUp);
4126
4127 mediumLock.release();
4128 treeLock.release();
4129 alock.release();
4130
4131 rc = medium->i_createDiffStorage(diff,
4132 MediumVariant_Standard,
4133 pMediumLockList,
4134 NULL /* aProgress */,
4135 true /* aWait */);
4136
4137 alock.acquire();
4138 treeLock.acquire();
4139 mediumLock.acquire();
4140
4141 i_setMachineState(oldState);
4142 }
4143 }
4144
4145 /* Unlock the media and free the associated memory. */
4146 delete pMediumLockList;
4147
4148 if (FAILED(rc)) return rc;
4149
4150 /* use the created diff for the actual attachment */
4151 medium = diff;
4152 mediumCaller.attach(medium);
4153 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4154 mediumLock.attach(medium);
4155 }
4156 while (0);
4157
4158 ComObjPtr<MediumAttachment> attachment;
4159 attachment.createObject();
4160 rc = attachment->init(this,
4161 medium,
4162 aName,
4163 aControllerPort,
4164 aDevice,
4165 aType,
4166 fIndirect,
4167 false /* fPassthrough */,
4168 false /* fTempEject */,
4169 false /* fNonRotational */,
4170 false /* fDiscard */,
4171 fHotplug /* fHotPluggable */,
4172 Utf8Str::Empty);
4173 if (FAILED(rc)) return rc;
4174
4175 if (associate && !medium.isNull())
4176 {
4177 // as the last step, associate the medium to the VM
4178 rc = medium->i_addBackReference(mData->mUuid);
4179 // here we can fail because of Deleting, or being in process of creating a Diff
4180 if (FAILED(rc)) return rc;
4181
4182 mediumLock.release();
4183 treeLock.release();
4184 alock.release();
4185 i_addMediumToRegistry(medium);
4186 alock.acquire();
4187 treeLock.acquire();
4188 mediumLock.acquire();
4189 }
4190
4191 /* success: finally remember the attachment */
4192 i_setModified(IsModified_Storage);
4193 mMediaData.backup();
4194 mMediaData->mAttachments.push_back(attachment);
4195
4196 mediumLock.release();
4197 treeLock.release();
4198 alock.release();
4199
4200 if (fHotplug || fSilent)
4201 {
4202 if (!medium.isNull())
4203 {
4204 MediumLockList *pMediumLockList(new MediumLockList());
4205
4206 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4207 true /* fMediumLockWrite */,
4208 NULL,
4209 *pMediumLockList);
4210 alock.acquire();
4211 if (FAILED(rc))
4212 delete pMediumLockList;
4213 else
4214 {
4215 mData->mSession.mLockedMedia.Unlock();
4216 alock.release();
4217 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4218 mData->mSession.mLockedMedia.Lock();
4219 alock.acquire();
4220 }
4221 alock.release();
4222 }
4223
4224 if (SUCCEEDED(rc))
4225 {
4226 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4227 /* Remove lock list in case of error. */
4228 if (FAILED(rc))
4229 {
4230 mData->mSession.mLockedMedia.Unlock();
4231 mData->mSession.mLockedMedia.Remove(attachment);
4232 mData->mSession.mLockedMedia.Lock();
4233 }
4234 }
4235 }
4236
4237 mParent->i_saveModifiedRegistries();
4238
4239 return rc;
4240}
4241
4242HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4243 LONG aDevice)
4244{
4245 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4246 aName.c_str(), aControllerPort, aDevice));
4247
4248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4249
4250 HRESULT rc = i_checkStateDependency(MutableStateDep);
4251 if (FAILED(rc)) return rc;
4252
4253 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4254
4255 /* Check for an existing controller. */
4256 ComObjPtr<StorageController> ctl;
4257 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4258 if (FAILED(rc)) return rc;
4259
4260 StorageControllerType_T ctrlType;
4261 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4262 if (FAILED(rc))
4263 return setError(E_FAIL,
4264 tr("Could not get type of controller '%s'"),
4265 aName.c_str());
4266
4267 bool fSilent = false;
4268 Utf8Str strReconfig;
4269
4270 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4271 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4272 if ( mData->mMachineState == MachineState_Paused
4273 && strReconfig == "1")
4274 fSilent = true;
4275
4276 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4277 bool fHotplug = false;
4278 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4279 fHotplug = true;
4280
4281 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4282 return setError(VBOX_E_INVALID_VM_STATE,
4283 tr("Controller '%s' does not support hotplugging"),
4284 aName.c_str());
4285
4286 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4287 Bstr(aName).raw(),
4288 aControllerPort,
4289 aDevice);
4290 if (!pAttach)
4291 return setError(VBOX_E_OBJECT_NOT_FOUND,
4292 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4293 aDevice, aControllerPort, aName.c_str());
4294
4295 if (fHotplug && !pAttach->i_getHotPluggable())
4296 return setError(VBOX_E_NOT_SUPPORTED,
4297 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4298 aDevice, aControllerPort, aName.c_str());
4299
4300 /*
4301 * The VM has to detach the device before we delete any implicit diffs.
4302 * If this fails we can roll back without loosing data.
4303 */
4304 if (fHotplug || fSilent)
4305 {
4306 alock.release();
4307 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4308 alock.acquire();
4309 }
4310 if (FAILED(rc)) return rc;
4311
4312 /* If we are here everything went well and we can delete the implicit now. */
4313 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4314
4315 alock.release();
4316
4317 mParent->i_saveModifiedRegistries();
4318
4319 return rc;
4320}
4321
4322HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4323 LONG aDevice, BOOL aPassthrough)
4324{
4325 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4326 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4327
4328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4329
4330 HRESULT rc = i_checkStateDependency(MutableStateDep);
4331 if (FAILED(rc)) return rc;
4332
4333 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4334
4335 if (Global::IsOnlineOrTransient(mData->mMachineState))
4336 return setError(VBOX_E_INVALID_VM_STATE,
4337 tr("Invalid machine state: %s"),
4338 Global::stringifyMachineState(mData->mMachineState));
4339
4340 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4341 Bstr(aName).raw(),
4342 aControllerPort,
4343 aDevice);
4344 if (!pAttach)
4345 return setError(VBOX_E_OBJECT_NOT_FOUND,
4346 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4347 aDevice, aControllerPort, aName.c_str());
4348
4349
4350 i_setModified(IsModified_Storage);
4351 mMediaData.backup();
4352
4353 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4354
4355 if (pAttach->i_getType() != DeviceType_DVD)
4356 return setError(E_INVALIDARG,
4357 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4358 aDevice, aControllerPort, aName.c_str());
4359 pAttach->i_updatePassthrough(!!aPassthrough);
4360
4361 return S_OK;
4362}
4363
4364HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4365 LONG aDevice, BOOL aTemporaryEject)
4366{
4367
4368 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4369 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4370
4371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4372
4373 HRESULT rc = i_checkStateDependency(MutableStateDep);
4374 if (FAILED(rc)) return rc;
4375
4376 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4377 Bstr(aName).raw(),
4378 aControllerPort,
4379 aDevice);
4380 if (!pAttach)
4381 return setError(VBOX_E_OBJECT_NOT_FOUND,
4382 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4383 aDevice, aControllerPort, aName.c_str());
4384
4385
4386 i_setModified(IsModified_Storage);
4387 mMediaData.backup();
4388
4389 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4390
4391 if (pAttach->i_getType() != DeviceType_DVD)
4392 return setError(E_INVALIDARG,
4393 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4394 aDevice, aControllerPort, aName.c_str());
4395 pAttach->i_updateTempEject(!!aTemporaryEject);
4396
4397 return S_OK;
4398}
4399
4400HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4401 LONG aDevice, BOOL aNonRotational)
4402{
4403
4404 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4405 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4406
4407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4408
4409 HRESULT rc = i_checkStateDependency(MutableStateDep);
4410 if (FAILED(rc)) return rc;
4411
4412 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4413
4414 if (Global::IsOnlineOrTransient(mData->mMachineState))
4415 return setError(VBOX_E_INVALID_VM_STATE,
4416 tr("Invalid machine state: %s"),
4417 Global::stringifyMachineState(mData->mMachineState));
4418
4419 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4420 Bstr(aName).raw(),
4421 aControllerPort,
4422 aDevice);
4423 if (!pAttach)
4424 return setError(VBOX_E_OBJECT_NOT_FOUND,
4425 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4426 aDevice, aControllerPort, aName.c_str());
4427
4428
4429 i_setModified(IsModified_Storage);
4430 mMediaData.backup();
4431
4432 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4433
4434 if (pAttach->i_getType() != DeviceType_HardDisk)
4435 return setError(E_INVALIDARG,
4436 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"),
4437 aDevice, aControllerPort, aName.c_str());
4438 pAttach->i_updateNonRotational(!!aNonRotational);
4439
4440 return S_OK;
4441}
4442
4443HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4444 LONG aDevice, BOOL aDiscard)
4445{
4446
4447 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4448 aName.c_str(), aControllerPort, aDevice, aDiscard));
4449
4450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4451
4452 HRESULT rc = i_checkStateDependency(MutableStateDep);
4453 if (FAILED(rc)) return rc;
4454
4455 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4456
4457 if (Global::IsOnlineOrTransient(mData->mMachineState))
4458 return setError(VBOX_E_INVALID_VM_STATE,
4459 tr("Invalid machine state: %s"),
4460 Global::stringifyMachineState(mData->mMachineState));
4461
4462 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4463 Bstr(aName).raw(),
4464 aControllerPort,
4465 aDevice);
4466 if (!pAttach)
4467 return setError(VBOX_E_OBJECT_NOT_FOUND,
4468 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4469 aDevice, aControllerPort, aName.c_str());
4470
4471
4472 i_setModified(IsModified_Storage);
4473 mMediaData.backup();
4474
4475 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4476
4477 if (pAttach->i_getType() != DeviceType_HardDisk)
4478 return setError(E_INVALIDARG,
4479 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"),
4480 aDevice, aControllerPort, aName.c_str());
4481 pAttach->i_updateDiscard(!!aDiscard);
4482
4483 return S_OK;
4484}
4485
4486HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4487 LONG aDevice, BOOL aHotPluggable)
4488{
4489 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4490 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4491
4492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4493
4494 HRESULT rc = i_checkStateDependency(MutableStateDep);
4495 if (FAILED(rc)) return rc;
4496
4497 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4498
4499 if (Global::IsOnlineOrTransient(mData->mMachineState))
4500 return setError(VBOX_E_INVALID_VM_STATE,
4501 tr("Invalid machine state: %s"),
4502 Global::stringifyMachineState(mData->mMachineState));
4503
4504 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4505 Bstr(aName).raw(),
4506 aControllerPort,
4507 aDevice);
4508 if (!pAttach)
4509 return setError(VBOX_E_OBJECT_NOT_FOUND,
4510 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4511 aDevice, aControllerPort, aName.c_str());
4512
4513 /* Check for an existing controller. */
4514 ComObjPtr<StorageController> ctl;
4515 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4516 if (FAILED(rc)) return rc;
4517
4518 StorageControllerType_T ctrlType;
4519 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4520 if (FAILED(rc))
4521 return setError(E_FAIL,
4522 tr("Could not get type of controller '%s'"),
4523 aName.c_str());
4524
4525 if (!i_isControllerHotplugCapable(ctrlType))
4526 return setError(VBOX_E_NOT_SUPPORTED,
4527 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4528 aName.c_str());
4529
4530 i_setModified(IsModified_Storage);
4531 mMediaData.backup();
4532
4533 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4534
4535 if (pAttach->i_getType() == DeviceType_Floppy)
4536 return setError(E_INVALIDARG,
4537 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"),
4538 aDevice, aControllerPort, aName.c_str());
4539 pAttach->i_updateHotPluggable(!!aHotPluggable);
4540
4541 return S_OK;
4542}
4543
4544HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4545 LONG aDevice)
4546{
4547 int rc = S_OK;
4548 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4549 aName.c_str(), aControllerPort, aDevice));
4550
4551 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4552
4553 return rc;
4554}
4555
4556HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4557 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4558{
4559 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4560 aName.c_str(), aControllerPort, aDevice));
4561
4562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4563
4564 HRESULT rc = i_checkStateDependency(MutableStateDep);
4565 if (FAILED(rc)) return rc;
4566
4567 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4568
4569 if (Global::IsOnlineOrTransient(mData->mMachineState))
4570 return setError(VBOX_E_INVALID_VM_STATE,
4571 tr("Invalid machine state: %s"),
4572 Global::stringifyMachineState(mData->mMachineState));
4573
4574 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4575 Bstr(aName).raw(),
4576 aControllerPort,
4577 aDevice);
4578 if (!pAttach)
4579 return setError(VBOX_E_OBJECT_NOT_FOUND,
4580 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4581 aDevice, aControllerPort, aName.c_str());
4582
4583
4584 i_setModified(IsModified_Storage);
4585 mMediaData.backup();
4586
4587 IBandwidthGroup *iB = aBandwidthGroup;
4588 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4589 if (aBandwidthGroup && group.isNull())
4590 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4591
4592 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4593
4594 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4595 if (strBandwidthGroupOld.isNotEmpty())
4596 {
4597 /* Get the bandwidth group object and release it - this must not fail. */
4598 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4599 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4600 Assert(SUCCEEDED(rc));
4601
4602 pBandwidthGroupOld->i_release();
4603 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4604 }
4605
4606 if (!group.isNull())
4607 {
4608 group->i_reference();
4609 pAttach->i_updateBandwidthGroup(group->i_getName());
4610 }
4611
4612 return S_OK;
4613}
4614
4615HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4616 LONG aControllerPort,
4617 LONG aDevice,
4618 DeviceType_T aType)
4619{
4620 HRESULT rc = S_OK;
4621
4622 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4623 aName.c_str(), aControllerPort, aDevice, aType));
4624
4625 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4626
4627 return rc;
4628}
4629
4630
4631HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4632 LONG aControllerPort,
4633 LONG aDevice,
4634 BOOL aForce)
4635{
4636 int rc = S_OK;
4637 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4638 aName.c_str(), aControllerPort, aForce));
4639
4640 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4641
4642 return rc;
4643}
4644
4645HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4646 LONG aControllerPort,
4647 LONG aDevice,
4648 const ComPtr<IMedium> &aMedium,
4649 BOOL aForce)
4650{
4651 int rc = S_OK;
4652 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4653 aName.c_str(), aControllerPort, aDevice, aForce));
4654
4655 // request the host lock first, since might be calling Host methods for getting host drives;
4656 // next, protect the media tree all the while we're in here, as well as our member variables
4657 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4658 this->lockHandle(),
4659 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4660
4661 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4662 Bstr(aName).raw(),
4663 aControllerPort,
4664 aDevice);
4665 if (pAttach.isNull())
4666 return setError(VBOX_E_OBJECT_NOT_FOUND,
4667 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4668 aDevice, aControllerPort, aName.c_str());
4669
4670 /* Remember previously mounted medium. The medium before taking the
4671 * backup is not necessarily the same thing. */
4672 ComObjPtr<Medium> oldmedium;
4673 oldmedium = pAttach->i_getMedium();
4674
4675 IMedium *iM = aMedium;
4676 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4677 if (aMedium && pMedium.isNull())
4678 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4679
4680 AutoCaller mediumCaller(pMedium);
4681 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4682
4683 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4684 if (pMedium)
4685 {
4686 DeviceType_T mediumType = pAttach->i_getType();
4687 switch (mediumType)
4688 {
4689 case DeviceType_DVD:
4690 case DeviceType_Floppy:
4691 break;
4692
4693 default:
4694 return setError(VBOX_E_INVALID_OBJECT_STATE,
4695 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4696 aControllerPort,
4697 aDevice,
4698 aName.c_str());
4699 }
4700 }
4701
4702 i_setModified(IsModified_Storage);
4703 mMediaData.backup();
4704
4705 {
4706 // The backup operation makes the pAttach reference point to the
4707 // old settings. Re-get the correct reference.
4708 pAttach = i_findAttachment(mMediaData->mAttachments,
4709 Bstr(aName).raw(),
4710 aControllerPort,
4711 aDevice);
4712 if (!oldmedium.isNull())
4713 oldmedium->i_removeBackReference(mData->mUuid);
4714 if (!pMedium.isNull())
4715 {
4716 pMedium->i_addBackReference(mData->mUuid);
4717
4718 mediumLock.release();
4719 multiLock.release();
4720 i_addMediumToRegistry(pMedium);
4721 multiLock.acquire();
4722 mediumLock.acquire();
4723 }
4724
4725 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4726 pAttach->i_updateMedium(pMedium);
4727 }
4728
4729 i_setModified(IsModified_Storage);
4730
4731 mediumLock.release();
4732 multiLock.release();
4733 rc = i_onMediumChange(pAttach, aForce);
4734 multiLock.acquire();
4735 mediumLock.acquire();
4736
4737 /* On error roll back this change only. */
4738 if (FAILED(rc))
4739 {
4740 if (!pMedium.isNull())
4741 pMedium->i_removeBackReference(mData->mUuid);
4742 pAttach = i_findAttachment(mMediaData->mAttachments,
4743 Bstr(aName).raw(),
4744 aControllerPort,
4745 aDevice);
4746 /* If the attachment is gone in the meantime, bail out. */
4747 if (pAttach.isNull())
4748 return rc;
4749 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4750 if (!oldmedium.isNull())
4751 oldmedium->i_addBackReference(mData->mUuid);
4752 pAttach->i_updateMedium(oldmedium);
4753 }
4754
4755 mediumLock.release();
4756 multiLock.release();
4757
4758 mParent->i_saveModifiedRegistries();
4759
4760 return rc;
4761}
4762HRESULT Machine::getMedium(const com::Utf8Str &aName,
4763 LONG aControllerPort,
4764 LONG aDevice,
4765 ComPtr<IMedium> &aMedium)
4766{
4767 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4768 aName.c_str(), aControllerPort, aDevice));
4769
4770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4771
4772 aMedium = NULL;
4773
4774 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4775 Bstr(aName).raw(),
4776 aControllerPort,
4777 aDevice);
4778 if (pAttach.isNull())
4779 return setError(VBOX_E_OBJECT_NOT_FOUND,
4780 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4781 aDevice, aControllerPort, aName.c_str());
4782
4783 pAttach->i_getMedium().queryInterfaceTo(aMedium.asOutParam());
4784
4785 return S_OK;
4786}
4787
4788HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4789{
4790
4791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4792
4793 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4794
4795 return S_OK;
4796}
4797
4798HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4799{
4800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4801
4802 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4803
4804 return S_OK;
4805}
4806
4807HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4808{
4809 /* Do not assert if slot is out of range, just return the advertised
4810 status. testdriver/vbox.py triggers this in logVmInfo. */
4811 if (aSlot >= mNetworkAdapters.size())
4812 return setError(E_INVALIDARG,
4813 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4814 aSlot, mNetworkAdapters.size());
4815
4816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4817
4818 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4819
4820 return S_OK;
4821}
4822
4823HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4824{
4825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4826
4827 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4828 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4829 size_t i = 0;
4830 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4831 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4832 ++it, ++i)
4833 aKeys[i] = it->first;
4834
4835 return S_OK;
4836}
4837
4838 /**
4839 * @note Locks this object for reading.
4840 */
4841HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4842 com::Utf8Str &aValue)
4843{
4844 /* start with nothing found */
4845 aValue = "";
4846
4847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4848
4849 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4850 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4851 // found:
4852 aValue = it->second; // source is a Utf8Str
4853
4854 /* return the result to caller (may be empty) */
4855 return S_OK;
4856}
4857
4858 /**
4859 * @note Locks mParent for writing + this object for writing.
4860 */
4861HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4862{
4863 Utf8Str strOldValue; // empty
4864
4865 // locking note: we only hold the read lock briefly to look up the old value,
4866 // then release it and call the onExtraCanChange callbacks. There is a small
4867 // chance of a race insofar as the callback might be called twice if two callers
4868 // change the same key at the same time, but that's a much better solution
4869 // than the deadlock we had here before. The actual changing of the extradata
4870 // is then performed under the write lock and race-free.
4871
4872 // look up the old value first; if nothing has changed then we need not do anything
4873 {
4874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4875 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4876 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4877 strOldValue = it->second;
4878 }
4879
4880 bool fChanged;
4881 if ((fChanged = (strOldValue != aValue)))
4882 {
4883 // ask for permission from all listeners outside the locks;
4884 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4885 // lock to copy the list of callbacks to invoke
4886 Bstr error;
4887 Bstr bstrValue(aValue);
4888
4889 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4890 {
4891 const char *sep = error.isEmpty() ? "" : ": ";
4892 CBSTR err = error.raw();
4893 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4894 sep, err));
4895 return setError(E_ACCESSDENIED,
4896 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4897 aKey.c_str(),
4898 aValue.c_str(),
4899 sep,
4900 err);
4901 }
4902
4903 // data is changing and change not vetoed: then write it out under the lock
4904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4905
4906 if (i_isSnapshotMachine())
4907 {
4908 HRESULT rc = i_checkStateDependency(MutableStateDep);
4909 if (FAILED(rc)) return rc;
4910 }
4911
4912 if (aValue.isEmpty())
4913 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4914 else
4915 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4916 // creates a new key if needed
4917
4918 bool fNeedsGlobalSaveSettings = false;
4919 i_saveSettings(&fNeedsGlobalSaveSettings);
4920
4921 if (fNeedsGlobalSaveSettings)
4922 {
4923 // save the global settings; for that we should hold only the VirtualBox lock
4924 alock.release();
4925 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4926 mParent->i_saveSettings();
4927 }
4928 }
4929
4930 // fire notification outside the lock
4931 if (fChanged)
4932 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4933
4934 return S_OK;
4935}
4936
4937HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4938{
4939 aProgress = NULL;
4940 NOREF(aSettingsFilePath);
4941 ReturnComNotImplemented();
4942}
4943
4944HRESULT Machine::saveSettings()
4945{
4946 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4947
4948 /* when there was auto-conversion, we want to save the file even if
4949 * the VM is saved */
4950 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4951 if (FAILED(rc)) return rc;
4952
4953 /* the settings file path may never be null */
4954 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4955
4956 /* save all VM data excluding snapshots */
4957 bool fNeedsGlobalSaveSettings = false;
4958 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4959 mlock.release();
4960
4961 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4962 {
4963 // save the global settings; for that we should hold only the VirtualBox lock
4964 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4965 rc = mParent->i_saveSettings();
4966 }
4967
4968 return rc;
4969}
4970
4971
4972HRESULT Machine::discardSettings()
4973{
4974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4975
4976 HRESULT rc = i_checkStateDependency(MutableStateDep);
4977 if (FAILED(rc)) return rc;
4978
4979 /*
4980 * during this rollback, the session will be notified if data has
4981 * been actually changed
4982 */
4983 i_rollback(true /* aNotify */);
4984
4985 return S_OK;
4986}
4987
4988/** @note Locks objects! */
4989HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4990 std::vector<ComPtr<IMedium> > &aMedia)
4991{
4992 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4993 AutoLimitedCaller autoCaller(this);
4994 AssertComRCReturnRC(autoCaller.rc());
4995
4996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4997
4998 Guid id(i_getId());
4999
5000 if (mData->mSession.mState != SessionState_Unlocked)
5001 return setError(VBOX_E_INVALID_OBJECT_STATE,
5002 tr("Cannot unregister the machine '%s' while it is locked"),
5003 mUserData->s.strName.c_str());
5004
5005 // wait for state dependents to drop to zero
5006 i_ensureNoStateDependencies();
5007
5008 if (!mData->mAccessible)
5009 {
5010 // inaccessible maschines can only be unregistered; uninitialize ourselves
5011 // here because currently there may be no unregistered that are inaccessible
5012 // (this state combination is not supported). Note releasing the caller and
5013 // leaving the lock before calling uninit()
5014 alock.release();
5015 autoCaller.release();
5016
5017 uninit();
5018
5019 mParent->i_unregisterMachine(this, id);
5020 // calls VirtualBox::i_saveSettings()
5021
5022 return S_OK;
5023 }
5024
5025 HRESULT rc = S_OK;
5026
5027 // discard saved state
5028 if (mData->mMachineState == MachineState_Saved)
5029 {
5030 // add the saved state file to the list of files the caller should delete
5031 Assert(!mSSData->strStateFilePath.isEmpty());
5032 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5033
5034 mSSData->strStateFilePath.setNull();
5035
5036 // unconditionally set the machine state to powered off, we now
5037 // know no session has locked the machine
5038 mData->mMachineState = MachineState_PoweredOff;
5039 }
5040
5041 size_t cSnapshots = 0;
5042 if (mData->mFirstSnapshot)
5043 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5044 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5045 // fail now before we start detaching media
5046 return setError(VBOX_E_INVALID_OBJECT_STATE,
5047 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5048 mUserData->s.strName.c_str(), cSnapshots);
5049
5050 // This list collects the medium objects from all medium attachments
5051 // which we will detach from the machine and its snapshots, in a specific
5052 // order which allows for closing all media without getting "media in use"
5053 // errors, simply by going through the list from the front to the back:
5054 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5055 // and must be closed before the parent media from the snapshots, or closing the parents
5056 // will fail because they still have children);
5057 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5058 // the root ("first") snapshot of the machine.
5059 MediaList llMedia;
5060
5061 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5062 && mMediaData->mAttachments.size()
5063 )
5064 {
5065 // we have media attachments: detach them all and add the Medium objects to our list
5066 if (aCleanupMode != CleanupMode_UnregisterOnly)
5067 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5068 else
5069 return setError(VBOX_E_INVALID_OBJECT_STATE,
5070 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5071 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5072 }
5073
5074 if (cSnapshots)
5075 {
5076 // autoCleanup must be true here, or we would have failed above
5077
5078 // add the media from the medium attachments of the snapshots to llMedia
5079 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5080 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5081 // into the children first
5082
5083 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5084 MachineState_T oldState = mData->mMachineState;
5085 mData->mMachineState = MachineState_DeletingSnapshot;
5086
5087 // make a copy of the first snapshot so the refcount does not drop to 0
5088 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5089 // because of the AutoCaller voodoo)
5090 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5091
5092 // GO!
5093 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5094
5095 mData->mMachineState = oldState;
5096 }
5097
5098 if (FAILED(rc))
5099 {
5100 i_rollbackMedia();
5101 return rc;
5102 }
5103
5104 // commit all the media changes made above
5105 i_commitMedia();
5106
5107 mData->mRegistered = false;
5108
5109 // machine lock no longer needed
5110 alock.release();
5111
5112 // return media to caller
5113 size_t i = 0;
5114 aMedia.resize(llMedia.size());
5115 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5116 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5117
5118 mParent->i_unregisterMachine(this, id);
5119 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5120
5121 return S_OK;
5122}
5123
5124struct Machine::DeleteTask
5125{
5126 ComObjPtr<Machine> pMachine;
5127 RTCList<ComPtr<IMedium> > llMediums;
5128 StringsList llFilesToDelete;
5129 ComObjPtr<Progress> pProgress;
5130};
5131
5132HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5133{
5134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5135
5136 HRESULT rc = i_checkStateDependency(MutableStateDep);
5137 if (FAILED(rc)) return rc;
5138
5139 if (mData->mRegistered)
5140 return setError(VBOX_E_INVALID_VM_STATE,
5141 tr("Cannot delete settings of a registered machine"));
5142
5143 DeleteTask *pTask = new DeleteTask;
5144 pTask->pMachine = this;
5145
5146 // collect files to delete
5147 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5148
5149 for (size_t i = 0; i < aMedia.size(); ++i)
5150 {
5151 IMedium *pIMedium(aMedia[i]);
5152 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5153 if (pMedium.isNull())
5154 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5155 SafeArray<BSTR> ids;
5156 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5157 if (FAILED(rc)) return rc;
5158 /* At this point the medium should not have any back references
5159 * anymore. If it has it is attached to another VM and *must* not
5160 * deleted. */
5161 if (ids.size() < 1)
5162 pTask->llMediums.append(pMedium);
5163 }
5164 if (mData->pMachineConfigFile->fileExists())
5165 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5166
5167 pTask->pProgress.createObject();
5168 pTask->pProgress->init(i_getVirtualBox(),
5169 static_cast<IMachine*>(this) /* aInitiator */,
5170 Bstr(tr("Deleting files")).raw(),
5171 true /* fCancellable */,
5172 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5173 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5174
5175 int vrc = RTThreadCreate(NULL,
5176 Machine::deleteThread,
5177 (void*)pTask,
5178 0,
5179 RTTHREADTYPE_MAIN_WORKER,
5180 0,
5181 "MachineDelete");
5182
5183 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5184
5185 if (RT_FAILURE(vrc))
5186 {
5187 delete pTask;
5188 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5189 }
5190
5191 LogFlowFuncLeave();
5192
5193 return S_OK;
5194}
5195
5196/**
5197 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5198 * calls Machine::deleteTaskWorker() on the actual machine object.
5199 * @param Thread
5200 * @param pvUser
5201 * @return
5202 */
5203/*static*/
5204DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5205{
5206 LogFlowFuncEnter();
5207
5208 DeleteTask *pTask = (DeleteTask*)pvUser;
5209 Assert(pTask);
5210 Assert(pTask->pMachine);
5211 Assert(pTask->pProgress);
5212
5213 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5214 pTask->pProgress->i_notifyComplete(rc);
5215
5216 delete pTask;
5217
5218 LogFlowFuncLeave();
5219
5220 NOREF(Thread);
5221
5222 return VINF_SUCCESS;
5223}
5224
5225/**
5226 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5227 * @param task
5228 * @return
5229 */
5230HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5231{
5232 AutoCaller autoCaller(this);
5233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5234
5235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5236
5237 HRESULT rc = S_OK;
5238
5239 try
5240 {
5241 ULONG uLogHistoryCount = 3;
5242 ComPtr<ISystemProperties> systemProperties;
5243 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5244 if (FAILED(rc)) throw rc;
5245
5246 if (!systemProperties.isNull())
5247 {
5248 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5249 if (FAILED(rc)) throw rc;
5250 }
5251
5252 MachineState_T oldState = mData->mMachineState;
5253 i_setMachineState(MachineState_SettingUp);
5254 alock.release();
5255 for (size_t i = 0; i < task.llMediums.size(); ++i)
5256 {
5257 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5258 {
5259 AutoCaller mac(pMedium);
5260 if (FAILED(mac.rc())) throw mac.rc();
5261 Utf8Str strLocation = pMedium->i_getLocationFull();
5262 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5263 if (FAILED(rc)) throw rc;
5264 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5265 }
5266 ComPtr<IProgress> pProgress2;
5267 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5268 if (FAILED(rc)) throw rc;
5269 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5270 if (FAILED(rc)) throw rc;
5271 /* Check the result of the asynchronous process. */
5272 LONG iRc;
5273 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5274 if (FAILED(rc)) throw rc;
5275 /* If the thread of the progress object has an error, then
5276 * retrieve the error info from there, or it'll be lost. */
5277 if (FAILED(iRc))
5278 throw setError(ProgressErrorInfo(pProgress2));
5279
5280 /* Close the medium, deliberately without checking the return
5281- * code, and without leaving any trace in the error info, as
5282- * a failure here is a very minor issue, which shouldn't happen
5283- * as above we even managed to delete the medium. */
5284 {
5285 ErrorInfoKeeper eik;
5286 pMedium->Close();
5287 }
5288 }
5289 i_setMachineState(oldState);
5290 alock.acquire();
5291
5292 // delete the files pushed on the task list by Machine::Delete()
5293 // (this includes saved states of the machine and snapshots and
5294 // medium storage files from the IMedium list passed in, and the
5295 // machine XML file)
5296 StringsList::const_iterator it = task.llFilesToDelete.begin();
5297 while (it != task.llFilesToDelete.end())
5298 {
5299 const Utf8Str &strFile = *it;
5300 LogFunc(("Deleting file %s\n", strFile.c_str()));
5301 int vrc = RTFileDelete(strFile.c_str());
5302 if (RT_FAILURE(vrc))
5303 throw setError(VBOX_E_IPRT_ERROR,
5304 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5305
5306 ++it;
5307 if (it == task.llFilesToDelete.end())
5308 {
5309 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5310 if (FAILED(rc)) throw rc;
5311 break;
5312 }
5313
5314 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5315 if (FAILED(rc)) throw rc;
5316 }
5317
5318 /* delete the settings only when the file actually exists */
5319 if (mData->pMachineConfigFile->fileExists())
5320 {
5321 /* Delete any backup or uncommitted XML files. Ignore failures.
5322 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5323 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5324 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5325 RTFileDelete(otherXml.c_str());
5326 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5327 RTFileDelete(otherXml.c_str());
5328
5329 /* delete the Logs folder, nothing important should be left
5330 * there (we don't check for errors because the user might have
5331 * some private files there that we don't want to delete) */
5332 Utf8Str logFolder;
5333 getLogFolder(logFolder);
5334 Assert(logFolder.length());
5335 if (RTDirExists(logFolder.c_str()))
5336 {
5337 /* Delete all VBox.log[.N] files from the Logs folder
5338 * (this must be in sync with the rotation logic in
5339 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5340 * files that may have been created by the GUI. */
5341 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5342 logFolder.c_str(), RTPATH_DELIMITER);
5343 RTFileDelete(log.c_str());
5344 log = Utf8StrFmt("%s%cVBox.png",
5345 logFolder.c_str(), RTPATH_DELIMITER);
5346 RTFileDelete(log.c_str());
5347 for (int i = uLogHistoryCount; i > 0; i--)
5348 {
5349 log = Utf8StrFmt("%s%cVBox.log.%d",
5350 logFolder.c_str(), RTPATH_DELIMITER, i);
5351 RTFileDelete(log.c_str());
5352 log = Utf8StrFmt("%s%cVBox.png.%d",
5353 logFolder.c_str(), RTPATH_DELIMITER, i);
5354 RTFileDelete(log.c_str());
5355 }
5356
5357 RTDirRemove(logFolder.c_str());
5358 }
5359
5360 /* delete the Snapshots folder, nothing important should be left
5361 * there (we don't check for errors because the user might have
5362 * some private files there that we don't want to delete) */
5363 Utf8Str strFullSnapshotFolder;
5364 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5365 Assert(!strFullSnapshotFolder.isEmpty());
5366 if (RTDirExists(strFullSnapshotFolder.c_str()))
5367 RTDirRemove(strFullSnapshotFolder.c_str());
5368
5369 // delete the directory that contains the settings file, but only
5370 // if it matches the VM name
5371 Utf8Str settingsDir;
5372 if (i_isInOwnDir(&settingsDir))
5373 RTDirRemove(settingsDir.c_str());
5374 }
5375
5376 alock.release();
5377
5378 mParent->i_saveModifiedRegistries();
5379 }
5380 catch (HRESULT aRC) { rc = aRC; }
5381
5382 return rc;
5383}
5384
5385HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5386{
5387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5388
5389 ComObjPtr<Snapshot> pSnapshot;
5390 HRESULT rc;
5391
5392 if (aNameOrId.isEmpty())
5393 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5394 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5395 else
5396 {
5397 Guid uuid(aNameOrId);
5398 if (uuid.isValid())
5399 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5400 else
5401 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5402 }
5403 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5404
5405 return rc;
5406}
5407
5408HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5409{
5410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5411
5412 HRESULT rc = i_checkStateDependency(MutableStateDep);
5413 if (FAILED(rc)) return rc;
5414
5415 ComObjPtr<SharedFolder> sharedFolder;
5416 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5417 if (SUCCEEDED(rc))
5418 return setError(VBOX_E_OBJECT_IN_USE,
5419 tr("Shared folder named '%s' already exists"),
5420 aName.c_str());
5421
5422 sharedFolder.createObject();
5423 rc = sharedFolder->init(i_getMachine(),
5424 aName,
5425 aHostPath,
5426 !!aWritable,
5427 !!aAutomount,
5428 true /* fFailOnError */);
5429 if (FAILED(rc)) return rc;
5430
5431 i_setModified(IsModified_SharedFolders);
5432 mHWData.backup();
5433 mHWData->mSharedFolders.push_back(sharedFolder);
5434
5435 /* inform the direct session if any */
5436 alock.release();
5437 i_onSharedFolderChange();
5438
5439 return S_OK;
5440}
5441
5442HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5443{
5444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5445
5446 HRESULT rc = i_checkStateDependency(MutableStateDep);
5447 if (FAILED(rc)) return rc;
5448
5449 ComObjPtr<SharedFolder> sharedFolder;
5450 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5451 if (FAILED(rc)) return rc;
5452
5453 i_setModified(IsModified_SharedFolders);
5454 mHWData.backup();
5455 mHWData->mSharedFolders.remove(sharedFolder);
5456
5457 /* inform the direct session if any */
5458 alock.release();
5459 i_onSharedFolderChange();
5460
5461 return S_OK;
5462}
5463
5464HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5465{
5466 /* start with No */
5467 *aCanShow = FALSE;
5468
5469 ComPtr<IInternalSessionControl> directControl;
5470 {
5471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5472
5473 if (mData->mSession.mState != SessionState_Locked)
5474 return setError(VBOX_E_INVALID_VM_STATE,
5475 tr("Machine is not locked for session (session state: %s)"),
5476 Global::stringifySessionState(mData->mSession.mState));
5477
5478 directControl = mData->mSession.mDirectControl;
5479 }
5480
5481 /* ignore calls made after #OnSessionEnd() is called */
5482 if (!directControl)
5483 return S_OK;
5484
5485 LONG64 dummy;
5486 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5487}
5488
5489HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5490{
5491 ComPtr<IInternalSessionControl> directControl;
5492 {
5493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5494
5495 if (mData->mSession.mState != SessionState_Locked)
5496 return setError(E_FAIL,
5497 tr("Machine is not locked for session (session state: %s)"),
5498 Global::stringifySessionState(mData->mSession.mState));
5499
5500 directControl = mData->mSession.mDirectControl;
5501 }
5502
5503 /* ignore calls made after #OnSessionEnd() is called */
5504 if (!directControl)
5505 return S_OK;
5506
5507 BOOL dummy;
5508 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5509}
5510
5511#ifdef VBOX_WITH_GUEST_PROPS
5512/**
5513 * Look up a guest property in VBoxSVC's internal structures.
5514 */
5515HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5516 com::Utf8Str &aValue,
5517 LONG64 *aTimestamp,
5518 com::Utf8Str &aFlags) const
5519{
5520 using namespace guestProp;
5521
5522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5523 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5524
5525 if (it != mHWData->mGuestProperties.end())
5526 {
5527 char szFlags[MAX_FLAGS_LEN + 1];
5528 aValue = it->second.strValue;
5529 *aTimestamp = it->second.mTimestamp;
5530 writeFlags(it->second.mFlags, szFlags);
5531 aFlags = Utf8Str(szFlags);
5532 }
5533
5534 return S_OK;
5535}
5536
5537/**
5538 * Query the VM that a guest property belongs to for the property.
5539 * @returns E_ACCESSDENIED if the VM process is not available or not
5540 * currently handling queries and the lookup should then be done in
5541 * VBoxSVC.
5542 */
5543HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5544 com::Utf8Str &aValue,
5545 LONG64 *aTimestamp,
5546 com::Utf8Str &aFlags) const
5547{
5548 HRESULT rc = S_OK;
5549 BSTR bValue = NULL;
5550 BSTR bFlags = NULL;
5551
5552 ComPtr<IInternalSessionControl> directControl;
5553 directControl = mData->mSession.mDirectControl;
5554
5555 /* fail if we were called after #OnSessionEnd() is called. This is a
5556 * silly race condition. */
5557
5558 /** @todo This code is bothering API clients (like python script clients) with
5559 * the AccessGuestProperty call, creating unncessary IPC. Need to
5560 * have a way of figuring out which kind of direct session it is... */
5561 if (!directControl)
5562 rc = E_ACCESSDENIED;
5563 else
5564 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5565 0 /* accessMode */,
5566 &bValue, aTimestamp, &bFlags);
5567
5568 aValue = bValue;
5569 aFlags = bFlags;
5570
5571 return rc;
5572}
5573#endif // VBOX_WITH_GUEST_PROPS
5574
5575HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5576 com::Utf8Str &aValue,
5577 LONG64 *aTimestamp,
5578 com::Utf8Str &aFlags)
5579{
5580#ifndef VBOX_WITH_GUEST_PROPS
5581 ReturnComNotImplemented();
5582#else // VBOX_WITH_GUEST_PROPS
5583
5584 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5585
5586 if (rc == E_ACCESSDENIED)
5587 /* The VM is not running or the service is not (yet) accessible */
5588 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5589 return rc;
5590#endif // VBOX_WITH_GUEST_PROPS
5591}
5592
5593HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5594{
5595 LONG64 dummyTimestamp;
5596 com::Utf8Str dummyFlags;
5597 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5598 return rc;
5599
5600}
5601HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5602{
5603 com::Utf8Str dummyFlags;
5604 com::Utf8Str dummyValue;
5605 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5606 return rc;
5607}
5608
5609#ifdef VBOX_WITH_GUEST_PROPS
5610/**
5611 * Set a guest property in VBoxSVC's internal structures.
5612 */
5613HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5614 const com::Utf8Str &aFlags, bool fDelete)
5615{
5616 using namespace guestProp;
5617
5618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5619 HRESULT rc = S_OK;
5620
5621 rc = i_checkStateDependency(MutableStateDep);
5622 if (FAILED(rc)) return rc;
5623
5624 try
5625 {
5626 uint32_t fFlags = NILFLAG;
5627 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5628 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5629
5630 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5631 if (it == mHWData->mGuestProperties.end())
5632 {
5633 if (!fDelete)
5634 {
5635 i_setModified(IsModified_MachineData);
5636 mHWData.backupEx();
5637
5638 RTTIMESPEC time;
5639 HWData::GuestProperty prop;
5640 prop.strValue = Bstr(aValue).raw();
5641 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5642 prop.mFlags = fFlags;
5643 mHWData->mGuestProperties[aName] = prop;
5644 }
5645 }
5646 else
5647 {
5648 if (it->second.mFlags & (RDONLYHOST))
5649 {
5650 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5651 }
5652 else
5653 {
5654 i_setModified(IsModified_MachineData);
5655 mHWData.backupEx();
5656
5657 /* The backupEx() operation invalidates our iterator,
5658 * so get a new one. */
5659 it = mHWData->mGuestProperties.find(aName);
5660 Assert(it != mHWData->mGuestProperties.end());
5661
5662 if (!fDelete)
5663 {
5664 RTTIMESPEC time;
5665 it->second.strValue = aValue;
5666 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5667 it->second.mFlags = fFlags;
5668 }
5669 else
5670 mHWData->mGuestProperties.erase(it);
5671 }
5672 }
5673
5674 if ( SUCCEEDED(rc)
5675 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5676 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5677 RTSTR_MAX,
5678 aName.c_str(),
5679 RTSTR_MAX,
5680 NULL)
5681 )
5682 )
5683 {
5684 alock.release();
5685
5686 mParent->i_onGuestPropertyChange(mData->mUuid,
5687 Bstr(aName).raw(),
5688 Bstr(aValue).raw(),
5689 Bstr(aFlags).raw());
5690 }
5691 }
5692 catch (std::bad_alloc &)
5693 {
5694 rc = E_OUTOFMEMORY;
5695 }
5696
5697 return rc;
5698}
5699
5700/**
5701 * Set a property on the VM that that property belongs to.
5702 * @returns E_ACCESSDENIED if the VM process is not available or not
5703 * currently handling queries and the setting should then be done in
5704 * VBoxSVC.
5705 */
5706HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5707 const com::Utf8Str &aFlags, bool fDelete)
5708{
5709 HRESULT rc;
5710
5711 try
5712 {
5713 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5714
5715 BSTR dummy = NULL; /* will not be changed (setter) */
5716 LONG64 dummy64;
5717 if (!directControl)
5718 rc = E_ACCESSDENIED;
5719 else
5720 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5721 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5722 fDelete? 2: 1 /* accessMode */,
5723 &dummy, &dummy64, &dummy);
5724 }
5725 catch (std::bad_alloc &)
5726 {
5727 rc = E_OUTOFMEMORY;
5728 }
5729
5730 return rc;
5731}
5732#endif // VBOX_WITH_GUEST_PROPS
5733
5734HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5735 const com::Utf8Str &aFlags)
5736{
5737#ifndef VBOX_WITH_GUEST_PROPS
5738 ReturnComNotImplemented();
5739#else // VBOX_WITH_GUEST_PROPS
5740 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5741 if (rc == E_ACCESSDENIED)
5742 /* The VM is not running or the service is not (yet) accessible */
5743 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5744 return rc;
5745#endif // VBOX_WITH_GUEST_PROPS
5746}
5747
5748HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5749{
5750 return setGuestProperty(aProperty, aValue, "");
5751}
5752
5753HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5754{
5755#ifndef VBOX_WITH_GUEST_PROPS
5756 ReturnComNotImplemented();
5757#else // VBOX_WITH_GUEST_PROPS
5758 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5759 if (rc == E_ACCESSDENIED)
5760 /* The VM is not running or the service is not (yet) accessible */
5761 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5762 return rc;
5763#endif // VBOX_WITH_GUEST_PROPS
5764}
5765
5766#ifdef VBOX_WITH_GUEST_PROPS
5767/**
5768 * Enumerate the guest properties in VBoxSVC's internal structures.
5769 */
5770HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5771 std::vector<com::Utf8Str> &aNames,
5772 std::vector<com::Utf8Str> &aValues,
5773 std::vector<LONG64> &aTimestamps,
5774 std::vector<com::Utf8Str> &aFlags)
5775{
5776 using namespace guestProp;
5777
5778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5779 Utf8Str strPatterns(aPatterns);
5780
5781 HWData::GuestPropertyMap propMap;
5782
5783 /*
5784 * Look for matching patterns and build up a list.
5785 */
5786 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5787 while (it != mHWData->mGuestProperties.end())
5788 {
5789 if ( strPatterns.isEmpty()
5790 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5791 RTSTR_MAX,
5792 it->first.c_str(),
5793 RTSTR_MAX,
5794 NULL)
5795 )
5796 propMap.insert(*it);
5797 it++;
5798 }
5799
5800 alock.release();
5801
5802 /*
5803 * And build up the arrays for returning the property information.
5804 */
5805 size_t cEntries = propMap.size();
5806
5807 aNames.resize(cEntries);
5808 aValues.resize(cEntries);
5809 aTimestamps.resize(cEntries);
5810 aFlags.resize(cEntries);
5811
5812 char szFlags[MAX_FLAGS_LEN + 1];
5813 size_t i= 0;
5814 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5815 {
5816 aNames[i] = it->first;
5817 aValues[i] = it->second.strValue;
5818 aTimestamps[i] = it->second.mTimestamp;
5819 writeFlags(it->second.mFlags, szFlags);
5820 aFlags[i] = Utf8Str(szFlags);
5821 }
5822
5823 return S_OK;
5824}
5825
5826/**
5827 * Enumerate the properties managed by a VM.
5828 * @returns E_ACCESSDENIED if the VM process is not available or not
5829 * currently handling queries and the setting should then be done in
5830 * VBoxSVC.
5831 */
5832HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5833 std::vector<com::Utf8Str> &aNames,
5834 std::vector<com::Utf8Str> &aValues,
5835 std::vector<LONG64> &aTimestamps,
5836 std::vector<com::Utf8Str> &aFlags)
5837{
5838 HRESULT rc;
5839 ComPtr<IInternalSessionControl> directControl;
5840 directControl = mData->mSession.mDirectControl;
5841
5842
5843 com::SafeArray<BSTR> bNames;
5844 com::SafeArray<BSTR> bValues;
5845 com::SafeArray<LONG64> bTimestamps;
5846 com::SafeArray<BSTR> bFlags;
5847
5848 if (!directControl)
5849 rc = E_ACCESSDENIED;
5850 else
5851 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5852 ComSafeArrayAsOutParam(bNames),
5853 ComSafeArrayAsOutParam(bValues),
5854 ComSafeArrayAsOutParam(bTimestamps),
5855 ComSafeArrayAsOutParam(bFlags));
5856 size_t i;
5857 aNames.resize(bNames.size());
5858 for (i = 0; i < bNames.size(); ++i)
5859 aNames[i] = Utf8Str(bNames[i]);
5860 aValues.resize(bValues.size());
5861 for (i = 0; i < bValues.size(); ++i)
5862 aValues[i] = Utf8Str(bValues[i]);
5863 aTimestamps.resize(bTimestamps.size());
5864 for (i = 0; i < bTimestamps.size(); ++i)
5865 aTimestamps[i] = bTimestamps[i];
5866 aFlags.resize(bFlags.size());
5867 for (i = 0; i < bFlags.size(); ++i)
5868 aFlags[i] = Utf8Str(bFlags[i]);
5869
5870 return rc;
5871}
5872#endif // VBOX_WITH_GUEST_PROPS
5873HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5874 std::vector<com::Utf8Str> &aNames,
5875 std::vector<com::Utf8Str> &aValues,
5876 std::vector<LONG64> &aTimestamps,
5877 std::vector<com::Utf8Str> &aFlags)
5878{
5879#ifndef VBOX_WITH_GUEST_PROPS
5880 ReturnComNotImplemented();
5881#else // VBOX_WITH_GUEST_PROPS
5882
5883 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5884
5885 if (rc == E_ACCESSDENIED)
5886 /* The VM is not running or the service is not (yet) accessible */
5887 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5888 return rc;
5889#endif // VBOX_WITH_GUEST_PROPS
5890}
5891
5892HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5893 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5894{
5895 MediaData::AttachmentList atts;
5896
5897 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5898 if (FAILED(rc)) return rc;
5899
5900 size_t i = 0;
5901 aMediumAttachments.resize(atts.size());
5902 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5903 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5904
5905 return S_OK;
5906}
5907
5908HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5909 LONG aControllerPort,
5910 LONG aDevice,
5911 ComPtr<IMediumAttachment> &aAttachment)
5912{
5913 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5914 aName.c_str(), aControllerPort, aDevice));
5915
5916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5917
5918 aAttachment = NULL;
5919
5920 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5921 Bstr(aName).raw(),
5922 aControllerPort,
5923 aDevice);
5924 if (pAttach.isNull())
5925 return setError(VBOX_E_OBJECT_NOT_FOUND,
5926 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5927 aDevice, aControllerPort, aName.c_str());
5928
5929 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5930
5931 return S_OK;
5932}
5933
5934
5935HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5936 StorageBus_T aConnectionType,
5937 ComPtr<IStorageController> &aController)
5938{
5939 if ( (aConnectionType <= StorageBus_Null)
5940 || (aConnectionType > StorageBus_USB))
5941 return setError(E_INVALIDARG,
5942 tr("Invalid connection type: %d"),
5943 aConnectionType);
5944
5945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5946
5947 HRESULT rc = i_checkStateDependency(MutableStateDep);
5948 if (FAILED(rc)) return rc;
5949
5950 /* try to find one with the name first. */
5951 ComObjPtr<StorageController> ctrl;
5952
5953 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5954 if (SUCCEEDED(rc))
5955 return setError(VBOX_E_OBJECT_IN_USE,
5956 tr("Storage controller named '%s' already exists"),
5957 aName.c_str());
5958
5959 ctrl.createObject();
5960
5961 /* get a new instance number for the storage controller */
5962 ULONG ulInstance = 0;
5963 bool fBootable = true;
5964 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5965 it != mStorageControllers->end();
5966 ++it)
5967 {
5968 if ((*it)->i_getStorageBus() == aConnectionType)
5969 {
5970 ULONG ulCurInst = (*it)->i_getInstance();
5971
5972 if (ulCurInst >= ulInstance)
5973 ulInstance = ulCurInst + 1;
5974
5975 /* Only one controller of each type can be marked as bootable. */
5976 if ((*it)->i_getBootable())
5977 fBootable = false;
5978 }
5979 }
5980
5981 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5982 if (FAILED(rc)) return rc;
5983
5984 i_setModified(IsModified_Storage);
5985 mStorageControllers.backup();
5986 mStorageControllers->push_back(ctrl);
5987
5988 ctrl.queryInterfaceTo(aController.asOutParam());
5989
5990 /* inform the direct session if any */
5991 alock.release();
5992 i_onStorageControllerChange();
5993
5994 return S_OK;
5995}
5996
5997HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5998 ComPtr<IStorageController> &aStorageController)
5999{
6000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6001
6002 ComObjPtr<StorageController> ctrl;
6003
6004 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6005 if (SUCCEEDED(rc))
6006 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6007
6008 return rc;
6009}
6010
6011HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6012 ComPtr<IStorageController> &aStorageController)
6013{
6014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6015
6016 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6017 it != mStorageControllers->end();
6018 ++it)
6019 {
6020 if ((*it)->i_getInstance() == aInstance)
6021 {
6022 (*it).queryInterfaceTo(aStorageController.asOutParam());
6023 return S_OK;
6024 }
6025 }
6026
6027 return setError(VBOX_E_OBJECT_NOT_FOUND,
6028 tr("Could not find a storage controller with instance number '%lu'"),
6029 aInstance);
6030}
6031
6032HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6033{
6034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6035
6036 HRESULT rc = i_checkStateDependency(MutableStateDep);
6037 if (FAILED(rc)) return rc;
6038
6039 ComObjPtr<StorageController> ctrl;
6040
6041 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6042 if (SUCCEEDED(rc))
6043 {
6044 /* Ensure that only one controller of each type is marked as bootable. */
6045 if (aBootable == TRUE)
6046 {
6047 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6048 it != mStorageControllers->end();
6049 ++it)
6050 {
6051 ComObjPtr<StorageController> aCtrl = (*it);
6052
6053 if ( (aCtrl->i_getName() != aName)
6054 && aCtrl->i_getBootable() == TRUE
6055 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6056 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6057 {
6058 aCtrl->i_setBootable(FALSE);
6059 break;
6060 }
6061 }
6062 }
6063
6064 if (SUCCEEDED(rc))
6065 {
6066 ctrl->i_setBootable(aBootable);
6067 i_setModified(IsModified_Storage);
6068 }
6069 }
6070
6071 if (SUCCEEDED(rc))
6072 {
6073 /* inform the direct session if any */
6074 alock.release();
6075 i_onStorageControllerChange();
6076 }
6077
6078 return rc;
6079}
6080
6081HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6082{
6083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6084
6085 HRESULT rc = i_checkStateDependency(MutableStateDep);
6086 if (FAILED(rc)) return rc;
6087
6088 ComObjPtr<StorageController> ctrl;
6089 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6090 if (FAILED(rc)) return rc;
6091
6092 {
6093 /* find all attached devices to the appropriate storage controller and detach them all */
6094 // make a temporary list because detachDevice invalidates iterators into
6095 // mMediaData->mAttachments
6096 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6097
6098 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6099 it != llAttachments2.end();
6100 ++it)
6101 {
6102 MediumAttachment *pAttachTemp = *it;
6103
6104 AutoCaller localAutoCaller(pAttachTemp);
6105 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6106
6107 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6108
6109 if (pAttachTemp->i_getControllerName() == aName)
6110 {
6111 rc = i_detachDevice(pAttachTemp, alock, NULL);
6112 if (FAILED(rc)) return rc;
6113 }
6114 }
6115 }
6116
6117 /* We can remove it now. */
6118 i_setModified(IsModified_Storage);
6119 mStorageControllers.backup();
6120
6121 ctrl->i_unshare();
6122
6123 mStorageControllers->remove(ctrl);
6124
6125 /* inform the direct session if any */
6126 alock.release();
6127 i_onStorageControllerChange();
6128
6129 return S_OK;
6130}
6131
6132HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6133 ComPtr<IUSBController> &aController)
6134{
6135 if ( (aType <= USBControllerType_Null)
6136 || (aType >= USBControllerType_Last))
6137 return setError(E_INVALIDARG,
6138 tr("Invalid USB controller type: %d"),
6139 aType);
6140
6141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6142
6143 HRESULT rc = i_checkStateDependency(MutableStateDep);
6144 if (FAILED(rc)) return rc;
6145
6146 /* try to find one with the same type first. */
6147 ComObjPtr<USBController> ctrl;
6148
6149 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6150 if (SUCCEEDED(rc))
6151 return setError(VBOX_E_OBJECT_IN_USE,
6152 tr("USB controller named '%s' already exists"),
6153 aName.c_str());
6154
6155 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6156 ULONG maxInstances;
6157 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6158 if (FAILED(rc))
6159 return rc;
6160
6161 ULONG cInstances = i_getUSBControllerCountByType(aType);
6162 if (cInstances >= maxInstances)
6163 return setError(E_INVALIDARG,
6164 tr("Too many USB controllers of this type"));
6165
6166 ctrl.createObject();
6167
6168 rc = ctrl->init(this, aName, aType);
6169 if (FAILED(rc)) return rc;
6170
6171 i_setModified(IsModified_USB);
6172 mUSBControllers.backup();
6173 mUSBControllers->push_back(ctrl);
6174
6175 ctrl.queryInterfaceTo(aController.asOutParam());
6176
6177 /* inform the direct session if any */
6178 alock.release();
6179 i_onUSBControllerChange();
6180
6181 return S_OK;
6182}
6183
6184HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6185{
6186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6187
6188 ComObjPtr<USBController> ctrl;
6189
6190 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6191 if (SUCCEEDED(rc))
6192 ctrl.queryInterfaceTo(aController.asOutParam());
6193
6194 return rc;
6195}
6196
6197HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6198 ULONG *aControllers)
6199{
6200 if ( (aType <= USBControllerType_Null)
6201 || (aType >= USBControllerType_Last))
6202 return setError(E_INVALIDARG,
6203 tr("Invalid USB controller type: %d"),
6204 aType);
6205
6206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6207
6208 ComObjPtr<USBController> ctrl;
6209
6210 *aControllers = i_getUSBControllerCountByType(aType);
6211
6212 return S_OK;
6213}
6214
6215HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6216{
6217
6218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6219
6220 HRESULT rc = i_checkStateDependency(MutableStateDep);
6221 if (FAILED(rc)) return rc;
6222
6223 ComObjPtr<USBController> ctrl;
6224 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6225 if (FAILED(rc)) return rc;
6226
6227 i_setModified(IsModified_USB);
6228 mUSBControllers.backup();
6229
6230 ctrl->i_unshare();
6231
6232 mUSBControllers->remove(ctrl);
6233
6234 /* inform the direct session if any */
6235 alock.release();
6236 i_onUSBControllerChange();
6237
6238 return S_OK;
6239}
6240
6241HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6242 ULONG *aOriginX,
6243 ULONG *aOriginY,
6244 ULONG *aWidth,
6245 ULONG *aHeight,
6246 BOOL *aEnabled)
6247{
6248 uint32_t u32OriginX= 0;
6249 uint32_t u32OriginY= 0;
6250 uint32_t u32Width = 0;
6251 uint32_t u32Height = 0;
6252 uint16_t u16Flags = 0;
6253
6254 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6255 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6256 if (RT_FAILURE(vrc))
6257 {
6258#ifdef RT_OS_WINDOWS
6259 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6260 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6261 * So just assign fEnable to TRUE again.
6262 * The right fix would be to change GUI API wrappers to make sure that parameters
6263 * are changed only if API succeeds.
6264 */
6265 *aEnabled = TRUE;
6266#endif
6267 return setError(VBOX_E_IPRT_ERROR,
6268 tr("Saved guest size is not available (%Rrc)"),
6269 vrc);
6270 }
6271
6272 *aOriginX = u32OriginX;
6273 *aOriginY = u32OriginY;
6274 *aWidth = u32Width;
6275 *aHeight = u32Height;
6276 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6277
6278 return S_OK;
6279}
6280
6281HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6282{
6283 if (aScreenId != 0)
6284 return E_NOTIMPL;
6285
6286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6287
6288 uint8_t *pu8Data = NULL;
6289 uint32_t cbData = 0;
6290 uint32_t u32Width = 0;
6291 uint32_t u32Height = 0;
6292
6293 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6294
6295 if (RT_FAILURE(vrc))
6296 return setError(VBOX_E_IPRT_ERROR,
6297 tr("Saved screenshot data is not available (%Rrc)"),
6298 vrc);
6299
6300 *aSize = cbData;
6301 *aWidth = u32Width;
6302 *aHeight = u32Height;
6303
6304 freeSavedDisplayScreenshot(pu8Data);
6305
6306 return S_OK;
6307}
6308
6309
6310HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6311{
6312 if (aScreenId != 0)
6313 return E_NOTIMPL;
6314
6315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6316
6317 uint8_t *pu8Data = NULL;
6318 uint32_t cbData = 0;
6319 uint32_t u32Width = 0;
6320 uint32_t u32Height = 0;
6321
6322 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6323
6324 if (RT_FAILURE(vrc))
6325 return setError(VBOX_E_IPRT_ERROR,
6326 tr("Saved screenshot data is not available (%Rrc)"),
6327 vrc);
6328
6329 *aWidth = u32Width;
6330 *aHeight = u32Height;
6331
6332 com::SafeArray<BYTE> bitmap(cbData);
6333 /* Convert pixels to format expected by the API caller. */
6334 if (aBGR)
6335 {
6336 /* [0] B, [1] G, [2] R, [3] A. */
6337 for (unsigned i = 0; i < cbData; i += 4)
6338 {
6339 bitmap[i] = pu8Data[i];
6340 bitmap[i + 1] = pu8Data[i + 1];
6341 bitmap[i + 2] = pu8Data[i + 2];
6342 bitmap[i + 3] = 0xff;
6343 }
6344 }
6345 else
6346 {
6347 /* [0] R, [1] G, [2] B, [3] A. */
6348 for (unsigned i = 0; i < cbData; i += 4)
6349 {
6350 bitmap[i] = pu8Data[i + 2];
6351 bitmap[i + 1] = pu8Data[i + 1];
6352 bitmap[i + 2] = pu8Data[i];
6353 bitmap[i + 3] = 0xff;
6354 }
6355 }
6356 aData.resize(bitmap.size());
6357 for (size_t i = 0; i < bitmap.size(); ++i)
6358 aData[i] = bitmap[i];
6359
6360 freeSavedDisplayScreenshot(pu8Data);
6361
6362 return S_OK;
6363}
6364
6365HRESULT Machine::readSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6366{
6367 if (aScreenId != 0)
6368 return E_NOTIMPL;
6369
6370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6371
6372 uint8_t *pu8Data = NULL;
6373 uint32_t cbData = 0;
6374 uint32_t u32Width = 0;
6375 uint32_t u32Height = 0;
6376
6377 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6378
6379 if (RT_FAILURE(vrc))
6380 return setError(VBOX_E_IPRT_ERROR,
6381 tr("Saved screenshot data is not available (%Rrc)"),
6382 vrc);
6383
6384 *aWidth = u32Width;
6385 *aHeight = u32Height;
6386
6387 HRESULT rc = S_OK;
6388 uint8_t *pu8PNG = NULL;
6389 uint32_t cbPNG = 0;
6390 uint32_t cxPNG = 0;
6391 uint32_t cyPNG = 0;
6392
6393 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6394
6395 if (RT_SUCCESS(vrc))
6396 {
6397 com::SafeArray<BYTE> screenData(cbPNG);
6398 screenData.initFrom(pu8PNG, cbPNG);
6399 if (pu8PNG)
6400 RTMemFree(pu8PNG);
6401 aData.resize(screenData.size());
6402 for (size_t i = 0; i < screenData.size(); ++i)
6403 aData[i] = screenData[i];
6404 }
6405 else
6406 {
6407 if (pu8PNG)
6408 RTMemFree(pu8PNG);
6409 return setError(VBOX_E_IPRT_ERROR,
6410 tr("Could not convert screenshot to PNG (%Rrc)"),
6411 vrc);
6412 }
6413
6414 freeSavedDisplayScreenshot(pu8Data);
6415
6416 return rc;
6417}
6418
6419HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6420{
6421 if (aScreenId != 0)
6422 return E_NOTIMPL;
6423
6424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6425
6426 uint8_t *pu8Data = NULL;
6427 uint32_t cbData = 0;
6428 uint32_t u32Width = 0;
6429 uint32_t u32Height = 0;
6430
6431 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6432
6433 if (RT_FAILURE(vrc))
6434 return setError(VBOX_E_IPRT_ERROR,
6435 tr("Saved screenshot data is not available (%Rrc)"),
6436 vrc);
6437
6438 *aSize = cbData;
6439 *aWidth = u32Width;
6440 *aHeight = u32Height;
6441
6442 freeSavedDisplayScreenshot(pu8Data);
6443
6444 return S_OK;
6445}
6446
6447HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6448{
6449 if (aScreenId != 0)
6450 return E_NOTIMPL;
6451
6452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6453
6454 uint8_t *pu8Data = NULL;
6455 uint32_t cbData = 0;
6456 uint32_t u32Width = 0;
6457 uint32_t u32Height = 0;
6458
6459 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6460
6461 if (RT_FAILURE(vrc))
6462 return setError(VBOX_E_IPRT_ERROR,
6463 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6464 vrc);
6465
6466 *aWidth = u32Width;
6467 *aHeight = u32Height;
6468
6469 com::SafeArray<BYTE> png(cbData);
6470 png.initFrom(pu8Data, cbData);
6471 aData.resize(png.size());
6472 for (size_t i = 0; i < png.size(); ++i)
6473 aData[i] = png[i];
6474
6475 freeSavedDisplayScreenshot(pu8Data);
6476
6477 return S_OK;
6478}
6479
6480HRESULT Machine::hotPlugCPU(ULONG aCpu)
6481{
6482 HRESULT rc = S_OK;
6483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6484
6485 if (!mHWData->mCPUHotPlugEnabled)
6486 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6487
6488 if (aCpu >= mHWData->mCPUCount)
6489 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6490
6491 if (mHWData->mCPUAttached[aCpu])
6492 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6493
6494 alock.release();
6495 rc = i_onCPUChange(aCpu, false);
6496 alock.acquire();
6497 if (FAILED(rc)) return rc;
6498
6499 i_setModified(IsModified_MachineData);
6500 mHWData.backup();
6501 mHWData->mCPUAttached[aCpu] = true;
6502
6503 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6504 if (Global::IsOnline(mData->mMachineState))
6505 i_saveSettings(NULL);
6506
6507 return S_OK;
6508}
6509
6510HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6511{
6512 HRESULT rc = S_OK;
6513
6514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6515
6516 if (!mHWData->mCPUHotPlugEnabled)
6517 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6518
6519 if (aCpu >= SchemaDefs::MaxCPUCount)
6520 return setError(E_INVALIDARG,
6521 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6522 SchemaDefs::MaxCPUCount);
6523
6524 if (!mHWData->mCPUAttached[aCpu])
6525 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6526
6527 /* CPU 0 can't be detached */
6528 if (aCpu == 0)
6529 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6530
6531 alock.release();
6532 rc = i_onCPUChange(aCpu, true);
6533 alock.acquire();
6534 if (FAILED(rc)) return rc;
6535
6536 i_setModified(IsModified_MachineData);
6537 mHWData.backup();
6538 mHWData->mCPUAttached[aCpu] = false;
6539
6540 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6541 if (Global::IsOnline(mData->mMachineState))
6542 i_saveSettings(NULL);
6543
6544 return S_OK;
6545}
6546
6547HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6548{
6549 *aAttached = false;
6550
6551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6552
6553 /* If hotplug is enabled the CPU is always enabled. */
6554 if (!mHWData->mCPUHotPlugEnabled)
6555 {
6556 if (aCpu < mHWData->mCPUCount)
6557 *aAttached = true;
6558 }
6559 else
6560 {
6561 if (aCpu < SchemaDefs::MaxCPUCount)
6562 *aAttached = mHWData->mCPUAttached[aCpu];
6563 }
6564
6565 return S_OK;
6566}
6567
6568HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6569{
6570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6571
6572 Utf8Str log = i_queryLogFilename(aIdx);
6573 if (!RTFileExists(log.c_str()))
6574 log.setNull();
6575 aFilename = log;
6576
6577 return S_OK;
6578}
6579
6580HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6581{
6582 if (aSize < 0)
6583 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6584
6585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6586
6587 HRESULT rc = S_OK;
6588 Utf8Str log = i_queryLogFilename(aIdx);
6589
6590 /* do not unnecessarily hold the lock while doing something which does
6591 * not need the lock and potentially takes a long time. */
6592 alock.release();
6593
6594 /* Limit the chunk size to 32K for now, as that gives better performance
6595 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6596 * One byte expands to approx. 25 bytes of breathtaking XML. */
6597 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6598 com::SafeArray<BYTE> logData(cbData);
6599
6600 RTFILE LogFile;
6601 int vrc = RTFileOpen(&LogFile, log.c_str(),
6602 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6603 if (RT_SUCCESS(vrc))
6604 {
6605 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6606 if (RT_SUCCESS(vrc))
6607 logData.resize(cbData);
6608 else
6609 rc = setError(VBOX_E_IPRT_ERROR,
6610 tr("Could not read log file '%s' (%Rrc)"),
6611 log.c_str(), vrc);
6612 RTFileClose(LogFile);
6613 }
6614 else
6615 rc = setError(VBOX_E_IPRT_ERROR,
6616 tr("Could not open log file '%s' (%Rrc)"),
6617 log.c_str(), vrc);
6618
6619 if (FAILED(rc))
6620 logData.resize(0);
6621
6622 aData.resize(logData.size());
6623 for (size_t i = 0; i < logData.size(); ++i)
6624 aData[i] = logData[i];
6625
6626 return rc;
6627}
6628
6629
6630/**
6631 * Currently this method doesn't attach device to the running VM,
6632 * just makes sure it's plugged on next VM start.
6633 */
6634HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6635{
6636 // lock scope
6637 {
6638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6639
6640 HRESULT rc = i_checkStateDependency(MutableStateDep);
6641 if (FAILED(rc)) return rc;
6642
6643 ChipsetType_T aChipset = ChipsetType_PIIX3;
6644 COMGETTER(ChipsetType)(&aChipset);
6645
6646 if (aChipset != ChipsetType_ICH9)
6647 {
6648 return setError(E_INVALIDARG,
6649 tr("Host PCI attachment only supported with ICH9 chipset"));
6650 }
6651
6652 // check if device with this host PCI address already attached
6653 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6654 it != mHWData->mPCIDeviceAssignments.end();
6655 ++it)
6656 {
6657 LONG iHostAddress = -1;
6658 ComPtr<PCIDeviceAttachment> pAttach;
6659 pAttach = *it;
6660 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6661 if (iHostAddress == aHostAddress)
6662 return setError(E_INVALIDARG,
6663 tr("Device with host PCI address already attached to this VM"));
6664 }
6665
6666 ComObjPtr<PCIDeviceAttachment> pda;
6667 char name[32];
6668
6669 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6670 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6671 Bstr bname(name);
6672 pda.createObject();
6673 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6674 i_setModified(IsModified_MachineData);
6675 mHWData.backup();
6676 mHWData->mPCIDeviceAssignments.push_back(pda);
6677 }
6678
6679 return S_OK;
6680}
6681
6682/**
6683 * Currently this method doesn't detach device from the running VM,
6684 * just makes sure it's not plugged on next VM start.
6685 */
6686HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6687{
6688 ComObjPtr<PCIDeviceAttachment> pAttach;
6689 bool fRemoved = false;
6690 HRESULT rc;
6691
6692 // lock scope
6693 {
6694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6695
6696 rc = i_checkStateDependency(MutableStateDep);
6697 if (FAILED(rc)) return rc;
6698
6699 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6700 it != mHWData->mPCIDeviceAssignments.end();
6701 ++it)
6702 {
6703 LONG iHostAddress = -1;
6704 pAttach = *it;
6705 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6706 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6707 {
6708 i_setModified(IsModified_MachineData);
6709 mHWData.backup();
6710 mHWData->mPCIDeviceAssignments.remove(pAttach);
6711 fRemoved = true;
6712 break;
6713 }
6714 }
6715 }
6716
6717
6718 /* Fire event outside of the lock */
6719 if (fRemoved)
6720 {
6721 Assert(!pAttach.isNull());
6722 ComPtr<IEventSource> es;
6723 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6724 Assert(SUCCEEDED(rc));
6725 Bstr mid;
6726 rc = this->COMGETTER(Id)(mid.asOutParam());
6727 Assert(SUCCEEDED(rc));
6728 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6729 }
6730
6731 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6732 tr("No host PCI device %08x attached"),
6733 aHostAddress
6734 );
6735}
6736
6737HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6738{
6739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6740
6741 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6742
6743 size_t i = 0;
6744 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6745 it != mHWData->mPCIDeviceAssignments.end();
6746 ++i, ++it)
6747 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6748
6749 return S_OK;
6750}
6751
6752HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6753{
6754 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6755
6756 return S_OK;
6757}
6758
6759HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6760{
6761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6762
6763 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6764
6765 return S_OK;
6766}
6767
6768HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6769{
6770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6771 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6772 if (SUCCEEDED(hrc))
6773 {
6774 hrc = mHWData.backupEx();
6775 if (SUCCEEDED(hrc))
6776 {
6777 i_setModified(IsModified_MachineData);
6778 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6779 }
6780 }
6781 return hrc;
6782}
6783
6784HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6785{
6786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6787 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6788 return S_OK;
6789}
6790
6791HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6792{
6793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6794 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6795 if (SUCCEEDED(hrc))
6796 {
6797 hrc = mHWData.backupEx();
6798 if (SUCCEEDED(hrc))
6799 {
6800 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6801 if (SUCCEEDED(hrc))
6802 i_setModified(IsModified_MachineData);
6803 }
6804 }
6805 return hrc;
6806}
6807
6808HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6809{
6810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6811
6812 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6813
6814 return S_OK;
6815}
6816
6817HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
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.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6828 }
6829 }
6830 return hrc;
6831}
6832
6833HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6834{
6835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6836
6837 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6838
6839 return S_OK;
6840}
6841
6842HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6843{
6844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6845
6846 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6847 if ( SUCCEEDED(hrc)
6848 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6849 {
6850 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6851 int vrc;
6852
6853 if (aAutostartEnabled)
6854 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6855 else
6856 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6857
6858 if (RT_SUCCESS(vrc))
6859 {
6860 hrc = mHWData.backupEx();
6861 if (SUCCEEDED(hrc))
6862 {
6863 i_setModified(IsModified_MachineData);
6864 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6865 }
6866 }
6867 else if (vrc == VERR_NOT_SUPPORTED)
6868 hrc = setError(VBOX_E_NOT_SUPPORTED,
6869 tr("The VM autostart feature is not supported on this platform"));
6870 else if (vrc == VERR_PATH_NOT_FOUND)
6871 hrc = setError(E_FAIL,
6872 tr("The path to the autostart database is not set"));
6873 else
6874 hrc = setError(E_UNEXPECTED,
6875 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6876 aAutostartEnabled ? "Adding" : "Removing",
6877 mUserData->s.strName.c_str(), vrc);
6878 }
6879 return hrc;
6880}
6881
6882HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6883{
6884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6885
6886 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6887
6888 return S_OK;
6889}
6890
6891HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6892{
6893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6894 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6895 if (SUCCEEDED(hrc))
6896 {
6897 hrc = mHWData.backupEx();
6898 if (SUCCEEDED(hrc))
6899 {
6900 i_setModified(IsModified_MachineData);
6901 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6902 }
6903 }
6904 return hrc;
6905}
6906
6907HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6908{
6909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6910
6911 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6912
6913 return S_OK;
6914}
6915
6916HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6917{
6918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6919 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6920 if ( SUCCEEDED(hrc)
6921 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6922 {
6923 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6924 int vrc;
6925
6926 if (aAutostopType != AutostopType_Disabled)
6927 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6928 else
6929 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6930
6931 if (RT_SUCCESS(vrc))
6932 {
6933 hrc = mHWData.backupEx();
6934 if (SUCCEEDED(hrc))
6935 {
6936 i_setModified(IsModified_MachineData);
6937 mHWData->mAutostart.enmAutostopType = aAutostopType;
6938 }
6939 }
6940 else if (vrc == VERR_NOT_SUPPORTED)
6941 hrc = setError(VBOX_E_NOT_SUPPORTED,
6942 tr("The VM autostop feature is not supported on this platform"));
6943 else if (vrc == VERR_PATH_NOT_FOUND)
6944 hrc = setError(E_FAIL,
6945 tr("The path to the autostart database is not set"));
6946 else
6947 hrc = setError(E_UNEXPECTED,
6948 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6949 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6950 mUserData->s.strName.c_str(), vrc);
6951 }
6952 return hrc;
6953}
6954
6955HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6956{
6957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6958
6959 aDefaultFrontend = mHWData->mDefaultFrontend;
6960
6961 return S_OK;
6962}
6963
6964HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6965{
6966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6967 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6968 if (SUCCEEDED(hrc))
6969 {
6970 hrc = mHWData.backupEx();
6971 if (SUCCEEDED(hrc))
6972 {
6973 i_setModified(IsModified_MachineData);
6974 mHWData->mDefaultFrontend = aDefaultFrontend;
6975 }
6976 }
6977 return hrc;
6978}
6979
6980HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6981{
6982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6983 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
6984 aIcon.resize(mUserData->mIcon.size());
6985 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
6986 aIcon.resize(icon.size());
6987 for (size_t i = 0; i < icon.size(); ++i)
6988 aIcon[i] = icon[i];
6989 return S_OK;
6990}
6991
6992HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6993{
6994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6995 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6996 if (SUCCEEDED(hrc))
6997 {
6998 i_setModified(IsModified_MachineData);
6999 mUserData.backup();
7000 com::SafeArray<BYTE> icon(aIcon);
7001 mUserData->mIcon.resize(aIcon.size());
7002 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7003 }
7004 return hrc;
7005}
7006
7007HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7008{
7009
7010#ifdef VBOX_WITH_USB
7011 *aUSBProxyAvailable = true;
7012#else
7013 *aUSBProxyAvailable = false;
7014#endif
7015 return S_OK;
7016}
7017
7018HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7019 ComPtr<IProgress> &aProgress)
7020{
7021 ComObjPtr<Progress> pP;
7022 Progress *ppP = pP;
7023 IProgress *iP = static_cast<IProgress *>(ppP);
7024 IProgress **pProgress = &iP;
7025
7026 IMachine *pTarget = aTarget;
7027
7028 /* Convert the options. */
7029 RTCList<CloneOptions_T> optList;
7030 if (aOptions.size())
7031 for (size_t i = 0; i < aOptions.size(); ++i)
7032 optList.append(aOptions[i]);
7033
7034 if (optList.contains(CloneOptions_Link))
7035 {
7036 if (!i_isSnapshotMachine())
7037 return setError(E_INVALIDARG,
7038 tr("Linked clone can only be created from a snapshot"));
7039 if (aMode != CloneMode_MachineState)
7040 return setError(E_INVALIDARG,
7041 tr("Linked clone can only be created for a single machine state"));
7042 }
7043 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7044
7045 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7046
7047 HRESULT rc = pWorker->start(pProgress);
7048
7049 pP = static_cast<Progress *>(*pProgress);
7050 pP.queryInterfaceTo(aProgress.asOutParam());
7051
7052 return rc;
7053
7054}
7055
7056// public methods for internal purposes
7057/////////////////////////////////////////////////////////////////////////////
7058
7059/**
7060 * Adds the given IsModified_* flag to the dirty flags of the machine.
7061 * This must be called either during i_loadSettings or under the machine write lock.
7062 * @param fl
7063 */
7064void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7065{
7066 mData->flModifications |= fl;
7067 if (fAllowStateModification && i_isStateModificationAllowed())
7068 mData->mCurrentStateModified = true;
7069}
7070
7071/**
7072 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7073 * care of the write locking.
7074 *
7075 * @param fModifications The flag to add.
7076 */
7077void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7078{
7079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7080 i_setModified(fModification, fAllowStateModification);
7081}
7082
7083/**
7084 * Saves the registry entry of this machine to the given configuration node.
7085 *
7086 * @param aEntryNode Node to save the registry entry to.
7087 *
7088 * @note locks this object for reading.
7089 */
7090HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7091{
7092 AutoLimitedCaller autoCaller(this);
7093 AssertComRCReturnRC(autoCaller.rc());
7094
7095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7096
7097 data.uuid = mData->mUuid;
7098 data.strSettingsFile = mData->m_strConfigFile;
7099
7100 return S_OK;
7101}
7102
7103/**
7104 * Calculates the absolute path of the given path taking the directory of the
7105 * machine settings file as the current directory.
7106 *
7107 * @param aPath Path to calculate the absolute path for.
7108 * @param aResult Where to put the result (used only on success, can be the
7109 * same Utf8Str instance as passed in @a aPath).
7110 * @return IPRT result.
7111 *
7112 * @note Locks this object for reading.
7113 */
7114int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7115{
7116 AutoCaller autoCaller(this);
7117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7118
7119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7120
7121 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7122
7123 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7124
7125 strSettingsDir.stripFilename();
7126 char folder[RTPATH_MAX];
7127 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7128 if (RT_SUCCESS(vrc))
7129 aResult = folder;
7130
7131 return vrc;
7132}
7133
7134/**
7135 * Copies strSource to strTarget, making it relative to the machine folder
7136 * if it is a subdirectory thereof, or simply copying it otherwise.
7137 *
7138 * @param strSource Path to evaluate and copy.
7139 * @param strTarget Buffer to receive target path.
7140 *
7141 * @note Locks this object for reading.
7142 */
7143void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7144 Utf8Str &strTarget)
7145{
7146 AutoCaller autoCaller(this);
7147 AssertComRCReturn(autoCaller.rc(), (void)0);
7148
7149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7150
7151 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7152 // use strTarget as a temporary buffer to hold the machine settings dir
7153 strTarget = mData->m_strConfigFileFull;
7154 strTarget.stripFilename();
7155 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7156 {
7157 // is relative: then append what's left
7158 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7159 // for empty paths (only possible for subdirs) use "." to avoid
7160 // triggering default settings for not present config attributes.
7161 if (strTarget.isEmpty())
7162 strTarget = ".";
7163 }
7164 else
7165 // is not relative: then overwrite
7166 strTarget = strSource;
7167}
7168
7169/**
7170 * Returns the full path to the machine's log folder in the
7171 * \a aLogFolder argument.
7172 */
7173void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7174{
7175 AutoCaller autoCaller(this);
7176 AssertComRCReturnVoid(autoCaller.rc());
7177
7178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7179
7180 char szTmp[RTPATH_MAX];
7181 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7182 if (RT_SUCCESS(vrc))
7183 {
7184 if (szTmp[0] && !mUserData.isNull())
7185 {
7186 char szTmp2[RTPATH_MAX];
7187 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7188 if (RT_SUCCESS(vrc))
7189 aLogFolder = BstrFmt("%s%c%s",
7190 szTmp2,
7191 RTPATH_DELIMITER,
7192 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7193 }
7194 else
7195 vrc = VERR_PATH_IS_RELATIVE;
7196 }
7197
7198 if (RT_FAILURE(vrc))
7199 {
7200 // fallback if VBOX_USER_LOGHOME is not set or invalid
7201 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7202 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7203 aLogFolder.append(RTPATH_DELIMITER);
7204 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7205 }
7206}
7207
7208/**
7209 * Returns the full path to the machine's log file for an given index.
7210 */
7211Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail. See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7212{
7213 Utf8Str logFolder;
7214 getLogFolder(logFolder);
7215 Assert(logFolder.length());
7216 Utf8Str log;
7217 if (idx == 0)
7218 log = Utf8StrFmt("%s%cVBox.log",
7219 logFolder.c_str(), RTPATH_DELIMITER);
7220 else
7221 log = Utf8StrFmt("%s%cVBox.log.%d",
7222 logFolder.c_str(), RTPATH_DELIMITER, idx);
7223 return log;
7224}
7225
7226/**
7227 * Returns the full path to the machine's (hardened) startup log file.
7228 */
7229Utf8Str Machine::i_getStartupLogFilename(void)
7230{
7231 Utf8Str strFilename;
7232 getLogFolder(strFilename);
7233 Assert(strFilename.length());
7234 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7235 return strFilename;
7236}
7237
7238
7239/**
7240 * Composes a unique saved state filename based on the current system time. The filename is
7241 * granular to the second so this will work so long as no more than one snapshot is taken on
7242 * a machine per second.
7243 *
7244 * Before version 4.1, we used this formula for saved state files:
7245 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7246 * which no longer works because saved state files can now be shared between the saved state of the
7247 * "saved" machine and an online snapshot, and the following would cause problems:
7248 * 1) save machine
7249 * 2) create online snapshot from that machine state --> reusing saved state file
7250 * 3) save machine again --> filename would be reused, breaking the online snapshot
7251 *
7252 * So instead we now use a timestamp.
7253 *
7254 * @param str
7255 */
7256
7257void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7258{
7259 AutoCaller autoCaller(this);
7260 AssertComRCReturnVoid(autoCaller.rc());
7261
7262 {
7263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7264 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7265 }
7266
7267 RTTIMESPEC ts;
7268 RTTimeNow(&ts);
7269 RTTIME time;
7270 RTTimeExplode(&time, &ts);
7271
7272 strStateFilePath += RTPATH_DELIMITER;
7273 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7274 time.i32Year, time.u8Month, time.u8MonthDay,
7275 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7276}
7277
7278/**
7279 * Returns the full path to the default video capture file.
7280 */
7281void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7282{
7283 AutoCaller autoCaller(this);
7284 AssertComRCReturnVoid(autoCaller.rc());
7285
7286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7287
7288 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7289 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7290 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7291}
7292
7293/**
7294 * Returns whether at least one USB controller is present for the VM.
7295 */
7296bool Machine::i_isUSBControllerPresent()
7297{
7298 AutoCaller autoCaller(this);
7299 AssertComRCReturn(autoCaller.rc(), false);
7300
7301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7302
7303 return (mUSBControllers->size() > 0);
7304}
7305
7306/**
7307 * @note Locks this object for writing, calls the client process
7308 * (inside the lock).
7309 */
7310HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7311 const Utf8Str &strFrontend,
7312 const Utf8Str &strEnvironment,
7313 ProgressProxy *aProgress)
7314{
7315 LogFlowThisFuncEnter();
7316
7317 AssertReturn(aControl, E_FAIL);
7318 AssertReturn(aProgress, E_FAIL);
7319 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7320
7321 AutoCaller autoCaller(this);
7322 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7323
7324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7325
7326 if (!mData->mRegistered)
7327 return setError(E_UNEXPECTED,
7328 tr("The machine '%s' is not registered"),
7329 mUserData->s.strName.c_str());
7330
7331 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7332
7333 if ( mData->mSession.mState == SessionState_Locked
7334 || mData->mSession.mState == SessionState_Spawning
7335 || mData->mSession.mState == SessionState_Unlocking)
7336 return setError(VBOX_E_INVALID_OBJECT_STATE,
7337 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7338 mUserData->s.strName.c_str());
7339
7340 /* may not be busy */
7341 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7342
7343 /* get the path to the executable */
7344 char szPath[RTPATH_MAX];
7345 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7346 size_t cchBufLeft = strlen(szPath);
7347 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7348 szPath[cchBufLeft] = 0;
7349 char *pszNamePart = szPath + cchBufLeft;
7350 cchBufLeft = sizeof(szPath) - cchBufLeft;
7351
7352 int vrc = VINF_SUCCESS;
7353 RTPROCESS pid = NIL_RTPROCESS;
7354
7355 RTENV env = RTENV_DEFAULT;
7356
7357 if (!strEnvironment.isEmpty())
7358 {
7359 char *newEnvStr = NULL;
7360
7361 do
7362 {
7363 /* clone the current environment */
7364 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7365 AssertRCBreakStmt(vrc2, vrc = vrc2);
7366
7367 newEnvStr = RTStrDup(strEnvironment.c_str());
7368 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7369
7370 /* put new variables to the environment
7371 * (ignore empty variable names here since RTEnv API
7372 * intentionally doesn't do that) */
7373 char *var = newEnvStr;
7374 for (char *p = newEnvStr; *p; ++p)
7375 {
7376 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7377 {
7378 *p = '\0';
7379 if (*var)
7380 {
7381 char *val = strchr(var, '=');
7382 if (val)
7383 {
7384 *val++ = '\0';
7385 vrc2 = RTEnvSetEx(env, var, val);
7386 }
7387 else
7388 vrc2 = RTEnvUnsetEx(env, var);
7389 if (RT_FAILURE(vrc2))
7390 break;
7391 }
7392 var = p + 1;
7393 }
7394 }
7395 if (RT_SUCCESS(vrc2) && *var)
7396 vrc2 = RTEnvPutEx(env, var);
7397
7398 AssertRCBreakStmt(vrc2, vrc = vrc2);
7399 }
7400 while (0);
7401
7402 if (newEnvStr != NULL)
7403 RTStrFree(newEnvStr);
7404 }
7405
7406 /* Hardened startup logging */
7407#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7408 Utf8Str strSupStartLogArg("--sup-startup-log=");
7409 {
7410 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7411 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7412 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7413 {
7414 Utf8Str strStartupLogDir = strStartupLogFile;
7415 strStartupLogDir.stripFilename();
7416 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a file without stripping the file. */
7417 }
7418 strSupStartLogArg.append(strStartupLogFile);
7419 }
7420 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7421#else
7422 const char *pszSupStartupLogArg = NULL;
7423#endif
7424
7425
7426#ifdef VBOX_WITH_QTGUI
7427 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7428 {
7429# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7430 /* Modify the base path so that we don't need to use ".." below. */
7431 RTPathStripTrailingSlash(szPath);
7432 RTPathStripFilename(szPath);
7433 cchBufLeft = strlen(szPath);
7434 pszNamePart = szPath + cchBufLeft;
7435 cchBufLeft = sizeof(szPath) - cchBufLeft;
7436
7437# define OSX_APP_NAME "VirtualBoxVM"
7438# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7439
7440 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7441 if ( strAppOverride.contains(".")
7442 || strAppOverride.contains("/")
7443 || strAppOverride.contains("\\")
7444 || strAppOverride.contains(":"))
7445 strAppOverride.setNull();
7446 Utf8Str strAppPath;
7447 if (!strAppOverride.isEmpty())
7448 {
7449 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7450 Utf8Str strFullPath(szPath);
7451 strFullPath.append(strAppPath);
7452 /* there is a race, but people using this deserve the failure */
7453 if (!RTFileExists(strFullPath.c_str()))
7454 strAppOverride.setNull();
7455 }
7456 if (strAppOverride.isEmpty())
7457 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7458 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7459 strcpy(pszNamePart, strAppPath.c_str());
7460# else
7461 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7462 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7463 strcpy(pszNamePart, s_szVirtualBox_exe);
7464# endif
7465
7466 Utf8Str idStr = mData->mUuid.toString();
7467 const char *apszArgs[] =
7468 {
7469 szPath,
7470 "--comment", mUserData->s.strName.c_str(),
7471 "--startvm", idStr.c_str(),
7472 "--no-startvm-errormsgbox",
7473 pszSupStartupLogArg,
7474 NULL
7475 };
7476 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7477 }
7478#else /* !VBOX_WITH_QTGUI */
7479 if (0)
7480 ;
7481#endif /* VBOX_WITH_QTGUI */
7482
7483 else
7484
7485#ifdef VBOX_WITH_VBOXSDL
7486 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7487 {
7488 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7489 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7490 strcpy(pszNamePart, s_szVBoxSDL_exe);
7491
7492 Utf8Str idStr = mData->mUuid.toString();
7493 const char *apszArgs[] =
7494 {
7495 szPath,
7496 "--comment", mUserData->s.strName.c_str(),
7497 "--startvm", idStr.c_str(),
7498 pszSupStartupLogArg,
7499 NULL
7500 };
7501 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7502 }
7503#else /* !VBOX_WITH_VBOXSDL */
7504 if (0)
7505 ;
7506#endif /* !VBOX_WITH_VBOXSDL */
7507
7508 else
7509
7510#ifdef VBOX_WITH_HEADLESS
7511 if ( strFrontend == "headless"
7512 || strFrontend == "capture"
7513 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7514 )
7515 {
7516 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7517 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7518 * and a VM works even if the server has not been installed.
7519 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7520 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7521 * differently in 4.0 and 3.x.
7522 */
7523 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7524 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7525 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7526
7527 Utf8Str idStr = mData->mUuid.toString();
7528 const char *apszArgs[] =
7529 {
7530 szPath,
7531 "--comment", mUserData->s.strName.c_str(),
7532 "--startvm", idStr.c_str(),
7533 "--vrde", "config",
7534 0, /* For "--capture". */
7535 0, /* For "--sup-startup-log". */
7536 0
7537 };
7538 unsigned iArg = 7;
7539 if (strFrontend == "capture")
7540 apszArgs[iArg++] = "--capture";
7541 apszArgs[iArg++] = pszSupStartupLogArg;
7542
7543# ifdef RT_OS_WINDOWS
7544 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7545# else
7546 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7547# endif
7548 }
7549#else /* !VBOX_WITH_HEADLESS */
7550 if (0)
7551 ;
7552#endif /* !VBOX_WITH_HEADLESS */
7553 else
7554 {
7555 RTEnvDestroy(env);
7556 return setError(E_INVALIDARG,
7557 tr("Invalid frontend name: '%s'"),
7558 strFrontend.c_str());
7559 }
7560
7561 RTEnvDestroy(env);
7562
7563 if (RT_FAILURE(vrc))
7564 return setError(VBOX_E_IPRT_ERROR,
7565 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7566 mUserData->s.strName.c_str(), vrc);
7567
7568 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7569
7570 /*
7571 * Note that we don't release the lock here before calling the client,
7572 * because it doesn't need to call us back if called with a NULL argument.
7573 * Releasing the lock here is dangerous because we didn't prepare the
7574 * launch data yet, but the client we've just started may happen to be
7575 * too fast and call LockMachine() that will fail (because of PID, etc.),
7576 * so that the Machine will never get out of the Spawning session state.
7577 */
7578
7579 /* inform the session that it will be a remote one */
7580 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7581#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7582 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7583#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7584 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7585#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7586 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7587
7588 if (FAILED(rc))
7589 {
7590 /* restore the session state */
7591 mData->mSession.mState = SessionState_Unlocked;
7592 alock.release();
7593 mParent->i_addProcessToReap(pid);
7594 /* The failure may occur w/o any error info (from RPC), so provide one */
7595 return setError(VBOX_E_VM_ERROR,
7596 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7597 }
7598
7599 /* attach launch data to the machine */
7600 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7601 mData->mSession.mRemoteControls.push_back(aControl);
7602 mData->mSession.mProgress = aProgress;
7603 mData->mSession.mPID = pid;
7604 mData->mSession.mState = SessionState_Spawning;
7605 mData->mSession.mType = strFrontend;
7606
7607 alock.release();
7608 mParent->i_addProcessToReap(pid);
7609
7610 LogFlowThisFuncLeave();
7611 return S_OK;
7612}
7613
7614/**
7615 * Returns @c true if the given session machine instance has an open direct
7616 * session (and optionally also for direct sessions which are closing) and
7617 * returns the session control machine instance if so.
7618 *
7619 * Note that when the method returns @c false, the arguments remain unchanged.
7620 *
7621 * @param aMachine Session machine object.
7622 * @param aControl Direct session control object (optional).
7623 * @param aAllowClosing If true then additionally a session which is currently
7624 * being closed will also be allowed.
7625 *
7626 * @note locks this object for reading.
7627 */
7628bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7629 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7630 bool aAllowClosing /*= false*/)
7631{
7632 AutoLimitedCaller autoCaller(this);
7633 AssertComRCReturn(autoCaller.rc(), false);
7634
7635 /* just return false for inaccessible machines */
7636 if (getObjectState().getState() != ObjectState::Ready)
7637 return false;
7638
7639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7640
7641 if ( mData->mSession.mState == SessionState_Locked
7642 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7643 )
7644 {
7645 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7646
7647 aMachine = mData->mSession.mMachine;
7648
7649 if (aControl != NULL)
7650 *aControl = mData->mSession.mDirectControl;
7651
7652 return true;
7653 }
7654
7655 return false;
7656}
7657
7658/**
7659 * Returns @c true if the given machine has an spawning direct session.
7660 *
7661 * @note locks this object for reading.
7662 */
7663bool Machine::i_isSessionSpawning()
7664{
7665 AutoLimitedCaller autoCaller(this);
7666 AssertComRCReturn(autoCaller.rc(), false);
7667
7668 /* just return false for inaccessible machines */
7669 if (getObjectState().getState() != ObjectState::Ready)
7670 return false;
7671
7672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7673
7674 if (mData->mSession.mState == SessionState_Spawning)
7675 return true;
7676
7677 return false;
7678}
7679
7680/**
7681 * Called from the client watcher thread to check for unexpected client process
7682 * death during Session_Spawning state (e.g. before it successfully opened a
7683 * direct session).
7684 *
7685 * On Win32 and on OS/2, this method is called only when we've got the
7686 * direct client's process termination notification, so it always returns @c
7687 * true.
7688 *
7689 * On other platforms, this method returns @c true if the client process is
7690 * terminated and @c false if it's still alive.
7691 *
7692 * @note Locks this object for writing.
7693 */
7694bool Machine::i_checkForSpawnFailure()
7695{
7696 AutoCaller autoCaller(this);
7697 if (!autoCaller.isOk())
7698 {
7699 /* nothing to do */
7700 LogFlowThisFunc(("Already uninitialized!\n"));
7701 return true;
7702 }
7703
7704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7705
7706 if (mData->mSession.mState != SessionState_Spawning)
7707 {
7708 /* nothing to do */
7709 LogFlowThisFunc(("Not spawning any more!\n"));
7710 return true;
7711 }
7712
7713 HRESULT rc = S_OK;
7714
7715 /* PID not yet initialized, skip check. */
7716 if (mData->mSession.mPID == NIL_RTPROCESS)
7717 return false;
7718
7719 RTPROCSTATUS status;
7720 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7721
7722 if (vrc != VERR_PROCESS_RUNNING)
7723 {
7724 Utf8Str strExtraInfo;
7725
7726#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7727 /* If the startup logfile exists and is of non-zero length, tell the
7728 user to look there for more details to encourage them to attach it
7729 when reporting startup issues. */
7730 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7731 uint64_t cbStartupLogFile = 0;
7732 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7733 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7734 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7735#endif
7736
7737 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7738 rc = setError(E_FAIL,
7739 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7740 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7741 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7742 rc = setError(E_FAIL,
7743 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7744 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7745 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7746 rc = setError(E_FAIL,
7747 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7748 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7749 else
7750 rc = setError(E_FAIL,
7751 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7752 i_getName().c_str(), vrc, strExtraInfo.c_str());
7753 }
7754
7755 if (FAILED(rc))
7756 {
7757 /* Close the remote session, remove the remote control from the list
7758 * and reset session state to Closed (@note keep the code in sync with
7759 * the relevant part in LockMachine()). */
7760
7761 Assert(mData->mSession.mRemoteControls.size() == 1);
7762 if (mData->mSession.mRemoteControls.size() == 1)
7763 {
7764 ErrorInfoKeeper eik;
7765 mData->mSession.mRemoteControls.front()->Uninitialize();
7766 }
7767
7768 mData->mSession.mRemoteControls.clear();
7769 mData->mSession.mState = SessionState_Unlocked;
7770
7771 /* finalize the progress after setting the state */
7772 if (!mData->mSession.mProgress.isNull())
7773 {
7774 mData->mSession.mProgress->notifyComplete(rc);
7775 mData->mSession.mProgress.setNull();
7776 }
7777
7778 mData->mSession.mPID = NIL_RTPROCESS;
7779
7780 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7781 return true;
7782 }
7783
7784 return false;
7785}
7786
7787/**
7788 * Checks whether the machine can be registered. If so, commits and saves
7789 * all settings.
7790 *
7791 * @note Must be called from mParent's write lock. Locks this object and
7792 * children for writing.
7793 */
7794HRESULT Machine::i_prepareRegister()
7795{
7796 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7797
7798 AutoLimitedCaller autoCaller(this);
7799 AssertComRCReturnRC(autoCaller.rc());
7800
7801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7802
7803 /* wait for state dependents to drop to zero */
7804 i_ensureNoStateDependencies();
7805
7806 if (!mData->mAccessible)
7807 return setError(VBOX_E_INVALID_OBJECT_STATE,
7808 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7809 mUserData->s.strName.c_str(),
7810 mData->mUuid.toString().c_str());
7811
7812 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7813
7814 if (mData->mRegistered)
7815 return setError(VBOX_E_INVALID_OBJECT_STATE,
7816 tr("The machine '%s' with UUID {%s} is already registered"),
7817 mUserData->s.strName.c_str(),
7818 mData->mUuid.toString().c_str());
7819
7820 HRESULT rc = S_OK;
7821
7822 // Ensure the settings are saved. If we are going to be registered and
7823 // no config file exists yet, create it by calling i_saveSettings() too.
7824 if ( (mData->flModifications)
7825 || (!mData->pMachineConfigFile->fileExists())
7826 )
7827 {
7828 rc = i_saveSettings(NULL);
7829 // no need to check whether VirtualBox.xml needs saving too since
7830 // we can't have a machine XML file rename pending
7831 if (FAILED(rc)) return rc;
7832 }
7833
7834 /* more config checking goes here */
7835
7836 if (SUCCEEDED(rc))
7837 {
7838 /* we may have had implicit modifications we want to fix on success */
7839 i_commit();
7840
7841 mData->mRegistered = true;
7842 }
7843 else
7844 {
7845 /* we may have had implicit modifications we want to cancel on failure*/
7846 i_rollback(false /* aNotify */);
7847 }
7848
7849 return rc;
7850}
7851
7852/**
7853 * Increases the number of objects dependent on the machine state or on the
7854 * registered state. Guarantees that these two states will not change at least
7855 * until #releaseStateDependency() is called.
7856 *
7857 * Depending on the @a aDepType value, additional state checks may be made.
7858 * These checks will set extended error info on failure. See
7859 * #checkStateDependency() for more info.
7860 *
7861 * If this method returns a failure, the dependency is not added and the caller
7862 * is not allowed to rely on any particular machine state or registration state
7863 * value and may return the failed result code to the upper level.
7864 *
7865 * @param aDepType Dependency type to add.
7866 * @param aState Current machine state (NULL if not interested).
7867 * @param aRegistered Current registered state (NULL if not interested).
7868 *
7869 * @note Locks this object for writing.
7870 */
7871HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7872 MachineState_T *aState /* = NULL */,
7873 BOOL *aRegistered /* = NULL */)
7874{
7875 AutoCaller autoCaller(this);
7876 AssertComRCReturnRC(autoCaller.rc());
7877
7878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7879
7880 HRESULT rc = i_checkStateDependency(aDepType);
7881 if (FAILED(rc)) return rc;
7882
7883 {
7884 if (mData->mMachineStateChangePending != 0)
7885 {
7886 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7887 * drop to zero so don't add more. It may make sense to wait a bit
7888 * and retry before reporting an error (since the pending state
7889 * transition should be really quick) but let's just assert for
7890 * now to see if it ever happens on practice. */
7891
7892 AssertFailed();
7893
7894 return setError(E_ACCESSDENIED,
7895 tr("Machine state change is in progress. Please retry the operation later."));
7896 }
7897
7898 ++mData->mMachineStateDeps;
7899 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7900 }
7901
7902 if (aState)
7903 *aState = mData->mMachineState;
7904 if (aRegistered)
7905 *aRegistered = mData->mRegistered;
7906
7907 return S_OK;
7908}
7909
7910/**
7911 * Decreases the number of objects dependent on the machine state.
7912 * Must always complete the #addStateDependency() call after the state
7913 * dependency is no more necessary.
7914 */
7915void Machine::i_releaseStateDependency()
7916{
7917 AutoCaller autoCaller(this);
7918 AssertComRCReturnVoid(autoCaller.rc());
7919
7920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7921
7922 /* releaseStateDependency() w/o addStateDependency()? */
7923 AssertReturnVoid(mData->mMachineStateDeps != 0);
7924 -- mData->mMachineStateDeps;
7925
7926 if (mData->mMachineStateDeps == 0)
7927 {
7928 /* inform i_ensureNoStateDependencies() that there are no more deps */
7929 if (mData->mMachineStateChangePending != 0)
7930 {
7931 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7932 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7933 }
7934 }
7935}
7936
7937Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7938{
7939 /* start with nothing found */
7940 Utf8Str strResult("");
7941
7942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7943
7944 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7945 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7946 // found:
7947 strResult = it->second; // source is a Utf8Str
7948
7949 return strResult;
7950}
7951
7952// protected methods
7953/////////////////////////////////////////////////////////////////////////////
7954
7955/**
7956 * Performs machine state checks based on the @a aDepType value. If a check
7957 * fails, this method will set extended error info, otherwise it will return
7958 * S_OK. It is supposed, that on failure, the caller will immediately return
7959 * the return value of this method to the upper level.
7960 *
7961 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7962 *
7963 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7964 * current state of this machine object allows to change settings of the
7965 * machine (i.e. the machine is not registered, or registered but not running
7966 * and not saved). It is useful to call this method from Machine setters
7967 * before performing any change.
7968 *
7969 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7970 * as for MutableStateDep except that if the machine is saved, S_OK is also
7971 * returned. This is useful in setters which allow changing machine
7972 * properties when it is in the saved state.
7973 *
7974 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7975 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7976 * Aborted).
7977 *
7978 * @param aDepType Dependency type to check.
7979 *
7980 * @note Non Machine based classes should use #addStateDependency() and
7981 * #releaseStateDependency() methods or the smart AutoStateDependency
7982 * template.
7983 *
7984 * @note This method must be called from under this object's read or write
7985 * lock.
7986 */
7987HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7988{
7989 switch (aDepType)
7990 {
7991 case AnyStateDep:
7992 {
7993 break;
7994 }
7995 case MutableStateDep:
7996 {
7997 if ( mData->mRegistered
7998 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7999 Paused should actually be included here... (Live Migration) */
8000 || ( mData->mMachineState != MachineState_Paused
8001 && mData->mMachineState != MachineState_Running
8002 && mData->mMachineState != MachineState_Aborted
8003 && mData->mMachineState != MachineState_Teleported
8004 && mData->mMachineState != MachineState_PoweredOff
8005 )
8006 )
8007 )
8008 return setError(VBOX_E_INVALID_VM_STATE,
8009 tr("The machine is not mutable (state is %s)"),
8010 Global::stringifyMachineState(mData->mMachineState));
8011 break;
8012 }
8013 case MutableOrSavedStateDep:
8014 {
8015 if ( mData->mRegistered
8016 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8017 Paused should actually be included here... (Live Migration) */
8018 || ( mData->mMachineState != MachineState_Paused
8019 && mData->mMachineState != MachineState_Running
8020 && mData->mMachineState != MachineState_Aborted
8021 && mData->mMachineState != MachineState_Teleported
8022 && mData->mMachineState != MachineState_Saved
8023 && mData->mMachineState != MachineState_PoweredOff
8024 )
8025 )
8026 )
8027 return setError(VBOX_E_INVALID_VM_STATE,
8028 tr("The machine is not mutable (state is %s)"),
8029 Global::stringifyMachineState(mData->mMachineState));
8030 break;
8031 }
8032 case OfflineStateDep:
8033 {
8034 if ( mData->mRegistered
8035 && ( !i_isSessionMachine()
8036 || ( mData->mMachineState != MachineState_PoweredOff
8037 && mData->mMachineState != MachineState_Saved
8038 && mData->mMachineState != MachineState_Aborted
8039 && mData->mMachineState != MachineState_Teleported
8040 )
8041 )
8042 )
8043 return setError(VBOX_E_INVALID_VM_STATE,
8044 tr("The machine is not offline (state is %s)"),
8045 Global::stringifyMachineState(mData->mMachineState));
8046 break;
8047 }
8048 }
8049
8050 return S_OK;
8051}
8052
8053/**
8054 * Helper to initialize all associated child objects and allocate data
8055 * structures.
8056 *
8057 * This method must be called as a part of the object's initialization procedure
8058 * (usually done in the #init() method).
8059 *
8060 * @note Must be called only from #init() or from #registeredInit().
8061 */
8062HRESULT Machine::initDataAndChildObjects()
8063{
8064 AutoCaller autoCaller(this);
8065 AssertComRCReturnRC(autoCaller.rc());
8066 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8067 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8068
8069 AssertReturn(!mData->mAccessible, E_FAIL);
8070
8071 /* allocate data structures */
8072 mSSData.allocate();
8073 mUserData.allocate();
8074 mHWData.allocate();
8075 mMediaData.allocate();
8076 mStorageControllers.allocate();
8077 mUSBControllers.allocate();
8078
8079 /* initialize mOSTypeId */
8080 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8081
8082 /* create associated BIOS settings object */
8083 unconst(mBIOSSettings).createObject();
8084 mBIOSSettings->init(this);
8085
8086 /* create an associated VRDE object (default is disabled) */
8087 unconst(mVRDEServer).createObject();
8088 mVRDEServer->init(this);
8089
8090 /* create associated serial port objects */
8091 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8092 {
8093 unconst(mSerialPorts[slot]).createObject();
8094 mSerialPorts[slot]->init(this, slot);
8095 }
8096
8097 /* create associated parallel port objects */
8098 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8099 {
8100 unconst(mParallelPorts[slot]).createObject();
8101 mParallelPorts[slot]->init(this, slot);
8102 }
8103
8104 /* create the audio adapter object (always present, default is disabled) */
8105 unconst(mAudioAdapter).createObject();
8106 mAudioAdapter->init(this);
8107
8108 /* create the USB device filters object (always present) */
8109 unconst(mUSBDeviceFilters).createObject();
8110 mUSBDeviceFilters->init(this);
8111
8112 /* create associated network adapter objects */
8113 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8114 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8115 {
8116 unconst(mNetworkAdapters[slot]).createObject();
8117 mNetworkAdapters[slot]->init(this, slot);
8118 }
8119
8120 /* create the bandwidth control */
8121 unconst(mBandwidthControl).createObject();
8122 mBandwidthControl->init(this);
8123
8124 return S_OK;
8125}
8126
8127/**
8128 * Helper to uninitialize all associated child objects and to free all data
8129 * structures.
8130 *
8131 * This method must be called as a part of the object's uninitialization
8132 * procedure (usually done in the #uninit() method).
8133 *
8134 * @note Must be called only from #uninit() or from #registeredInit().
8135 */
8136void Machine::uninitDataAndChildObjects()
8137{
8138 AutoCaller autoCaller(this);
8139 AssertComRCReturnVoid(autoCaller.rc());
8140 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8141 || getObjectState().getState() == ObjectState::Limited);
8142
8143 /* tell all our other child objects we've been uninitialized */
8144 if (mBandwidthControl)
8145 {
8146 mBandwidthControl->uninit();
8147 unconst(mBandwidthControl).setNull();
8148 }
8149
8150 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8151 {
8152 if (mNetworkAdapters[slot])
8153 {
8154 mNetworkAdapters[slot]->uninit();
8155 unconst(mNetworkAdapters[slot]).setNull();
8156 }
8157 }
8158
8159 if (mUSBDeviceFilters)
8160 {
8161 mUSBDeviceFilters->uninit();
8162 unconst(mUSBDeviceFilters).setNull();
8163 }
8164
8165 if (mAudioAdapter)
8166 {
8167 mAudioAdapter->uninit();
8168 unconst(mAudioAdapter).setNull();
8169 }
8170
8171 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8172 {
8173 if (mParallelPorts[slot])
8174 {
8175 mParallelPorts[slot]->uninit();
8176 unconst(mParallelPorts[slot]).setNull();
8177 }
8178 }
8179
8180 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8181 {
8182 if (mSerialPorts[slot])
8183 {
8184 mSerialPorts[slot]->uninit();
8185 unconst(mSerialPorts[slot]).setNull();
8186 }
8187 }
8188
8189 if (mVRDEServer)
8190 {
8191 mVRDEServer->uninit();
8192 unconst(mVRDEServer).setNull();
8193 }
8194
8195 if (mBIOSSettings)
8196 {
8197 mBIOSSettings->uninit();
8198 unconst(mBIOSSettings).setNull();
8199 }
8200
8201 /* Deassociate media (only when a real Machine or a SnapshotMachine
8202 * instance is uninitialized; SessionMachine instances refer to real
8203 * Machine media). This is necessary for a clean re-initialization of
8204 * the VM after successfully re-checking the accessibility state. Note
8205 * that in case of normal Machine or SnapshotMachine uninitialization (as
8206 * a result of unregistering or deleting the snapshot), outdated media
8207 * attachments will already be uninitialized and deleted, so this
8208 * code will not affect them. */
8209 if ( !!mMediaData
8210 && (!i_isSessionMachine())
8211 )
8212 {
8213 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8214 it != mMediaData->mAttachments.end();
8215 ++it)
8216 {
8217 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8218 if (pMedium.isNull())
8219 continue;
8220 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8221 AssertComRC(rc);
8222 }
8223 }
8224
8225 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8226 {
8227 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8228 if (mData->mFirstSnapshot)
8229 {
8230 // snapshots tree is protected by machine write lock; strictly
8231 // this isn't necessary here since we're deleting the entire
8232 // machine, but otherwise we assert in Snapshot::uninit()
8233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8234 mData->mFirstSnapshot->uninit();
8235 mData->mFirstSnapshot.setNull();
8236 }
8237
8238 mData->mCurrentSnapshot.setNull();
8239 }
8240
8241 /* free data structures (the essential mData structure is not freed here
8242 * since it may be still in use) */
8243 mMediaData.free();
8244 mStorageControllers.free();
8245 mUSBControllers.free();
8246 mHWData.free();
8247 mUserData.free();
8248 mSSData.free();
8249}
8250
8251/**
8252 * Returns a pointer to the Machine object for this machine that acts like a
8253 * parent for complex machine data objects such as shared folders, etc.
8254 *
8255 * For primary Machine objects and for SnapshotMachine objects, returns this
8256 * object's pointer itself. For SessionMachine objects, returns the peer
8257 * (primary) machine pointer.
8258 */
8259Machine* Machine::i_getMachine()
8260{
8261 if (i_isSessionMachine())
8262 return (Machine*)mPeer;
8263 return this;
8264}
8265
8266/**
8267 * Makes sure that there are no machine state dependents. If necessary, waits
8268 * for the number of dependents to drop to zero.
8269 *
8270 * Make sure this method is called from under this object's write lock to
8271 * guarantee that no new dependents may be added when this method returns
8272 * control to the caller.
8273 *
8274 * @note Locks this object for writing. The lock will be released while waiting
8275 * (if necessary).
8276 *
8277 * @warning To be used only in methods that change the machine state!
8278 */
8279void Machine::i_ensureNoStateDependencies()
8280{
8281 AssertReturnVoid(isWriteLockOnCurrentThread());
8282
8283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8284
8285 /* Wait for all state dependents if necessary */
8286 if (mData->mMachineStateDeps != 0)
8287 {
8288 /* lazy semaphore creation */
8289 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8290 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8291
8292 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8293 mData->mMachineStateDeps));
8294
8295 ++mData->mMachineStateChangePending;
8296
8297 /* reset the semaphore before waiting, the last dependent will signal
8298 * it */
8299 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8300
8301 alock.release();
8302
8303 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8304
8305 alock.acquire();
8306
8307 -- mData->mMachineStateChangePending;
8308 }
8309}
8310
8311/**
8312 * Changes the machine state and informs callbacks.
8313 *
8314 * This method is not intended to fail so it either returns S_OK or asserts (and
8315 * returns a failure).
8316 *
8317 * @note Locks this object for writing.
8318 */
8319HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8320{
8321 LogFlowThisFuncEnter();
8322 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8323
8324 AutoCaller autoCaller(this);
8325 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8326
8327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8328
8329 /* wait for state dependents to drop to zero */
8330 i_ensureNoStateDependencies();
8331
8332 if (mData->mMachineState != aMachineState)
8333 {
8334 mData->mMachineState = aMachineState;
8335
8336 RTTimeNow(&mData->mLastStateChange);
8337
8338 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8339 }
8340
8341 LogFlowThisFuncLeave();
8342 return S_OK;
8343}
8344
8345/**
8346 * Searches for a shared folder with the given logical name
8347 * in the collection of shared folders.
8348 *
8349 * @param aName logical name of the shared folder
8350 * @param aSharedFolder where to return the found object
8351 * @param aSetError whether to set the error info if the folder is
8352 * not found
8353 * @return
8354 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8355 *
8356 * @note
8357 * must be called from under the object's lock!
8358 */
8359HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8360 ComObjPtr<SharedFolder> &aSharedFolder,
8361 bool aSetError /* = false */)
8362{
8363 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8364 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8365 it != mHWData->mSharedFolders.end();
8366 ++it)
8367 {
8368 SharedFolder *pSF = *it;
8369 AutoCaller autoCaller(pSF);
8370 if (pSF->i_getName() == aName)
8371 {
8372 aSharedFolder = pSF;
8373 rc = S_OK;
8374 break;
8375 }
8376 }
8377
8378 if (aSetError && FAILED(rc))
8379 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8380
8381 return rc;
8382}
8383
8384/**
8385 * Initializes all machine instance data from the given settings structures
8386 * from XML. The exception is the machine UUID which needs special handling
8387 * depending on the caller's use case, so the caller needs to set that herself.
8388 *
8389 * This gets called in several contexts during machine initialization:
8390 *
8391 * -- When machine XML exists on disk already and needs to be loaded into memory,
8392 * for example, from registeredInit() to load all registered machines on
8393 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8394 * attached to the machine should be part of some media registry already.
8395 *
8396 * -- During OVF import, when a machine config has been constructed from an
8397 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8398 * ensure that the media listed as attachments in the config (which have
8399 * been imported from the OVF) receive the correct registry ID.
8400 *
8401 * -- During VM cloning.
8402 *
8403 * @param config Machine settings from XML.
8404 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8405 * for each attached medium in the config.
8406 * @return
8407 */
8408HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8409 const Guid *puuidRegistry)
8410{
8411 // copy name, description, OS type, teleporter, UTC etc.
8412 mUserData->s = config.machineUserData;
8413
8414 // Decode the Icon overide data from config userdata and set onto Machine.
8415 #define DECODE_STR_MAX _1M
8416 const char* pszStr = config.machineUserData.ovIcon.c_str();
8417 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8418 if (cbOut > DECODE_STR_MAX)
8419 return setError(E_FAIL,
8420 tr("Icon Data too long.'%d' > '%d'"),
8421 cbOut,
8422 DECODE_STR_MAX);
8423 com::SafeArray<BYTE> iconByte(cbOut);
8424 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8425 if (FAILED(rc))
8426 return setError(E_FAIL,
8427 tr("Failure to Decode Icon Data. '%s' (%d)"),
8428 pszStr,
8429 rc);
8430 mUserData->mIcon.resize(iconByte.size());
8431 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8432
8433 // look up the object by Id to check it is valid
8434 ComPtr<IGuestOSType> guestOSType;
8435 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8436 guestOSType.asOutParam());
8437 if (FAILED(rc)) return rc;
8438
8439 // stateFile (optional)
8440 if (config.strStateFile.isEmpty())
8441 mSSData->strStateFilePath.setNull();
8442 else
8443 {
8444 Utf8Str stateFilePathFull(config.strStateFile);
8445 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8446 if (RT_FAILURE(vrc))
8447 return setError(E_FAIL,
8448 tr("Invalid saved state file path '%s' (%Rrc)"),
8449 config.strStateFile.c_str(),
8450 vrc);
8451 mSSData->strStateFilePath = stateFilePathFull;
8452 }
8453
8454 // snapshot folder needs special processing so set it again
8455 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8456 if (FAILED(rc)) return rc;
8457
8458 /* Copy the extra data items (Not in any case config is already the same as
8459 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8460 * make sure the extra data map is copied). */
8461 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8462
8463 /* currentStateModified (optional, default is true) */
8464 mData->mCurrentStateModified = config.fCurrentStateModified;
8465
8466 mData->mLastStateChange = config.timeLastStateChange;
8467
8468 /*
8469 * note: all mUserData members must be assigned prior this point because
8470 * we need to commit changes in order to let mUserData be shared by all
8471 * snapshot machine instances.
8472 */
8473 mUserData.commitCopy();
8474
8475 // machine registry, if present (must be loaded before snapshots)
8476 if (config.canHaveOwnMediaRegistry())
8477 {
8478 // determine machine folder
8479 Utf8Str strMachineFolder = i_getSettingsFileFull();
8480 strMachineFolder.stripFilename();
8481 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8482 config.mediaRegistry,
8483 strMachineFolder);
8484 if (FAILED(rc)) return rc;
8485 }
8486
8487 /* Snapshot node (optional) */
8488 size_t cRootSnapshots;
8489 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8490 {
8491 // there must be only one root snapshot
8492 Assert(cRootSnapshots == 1);
8493
8494 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8495
8496 rc = i_loadSnapshot(snap,
8497 config.uuidCurrentSnapshot,
8498 NULL); // no parent == first snapshot
8499 if (FAILED(rc)) return rc;
8500 }
8501
8502 // hardware data
8503 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8504 if (FAILED(rc)) return rc;
8505
8506 // load storage controllers
8507 rc = i_loadStorageControllers(config.storageMachine,
8508 puuidRegistry,
8509 NULL /* puuidSnapshot */);
8510 if (FAILED(rc)) return rc;
8511
8512 /*
8513 * NOTE: the assignment below must be the last thing to do,
8514 * otherwise it will be not possible to change the settings
8515 * somewhere in the code above because all setters will be
8516 * blocked by i_checkStateDependency(MutableStateDep).
8517 */
8518
8519 /* set the machine state to Aborted or Saved when appropriate */
8520 if (config.fAborted)
8521 {
8522 mSSData->strStateFilePath.setNull();
8523
8524 /* no need to use i_setMachineState() during init() */
8525 mData->mMachineState = MachineState_Aborted;
8526 }
8527 else if (!mSSData->strStateFilePath.isEmpty())
8528 {
8529 /* no need to use i_setMachineState() during init() */
8530 mData->mMachineState = MachineState_Saved;
8531 }
8532
8533 // after loading settings, we are no longer different from the XML on disk
8534 mData->flModifications = 0;
8535
8536 return S_OK;
8537}
8538
8539/**
8540 * Recursively loads all snapshots starting from the given.
8541 *
8542 * @param aNode <Snapshot> node.
8543 * @param aCurSnapshotId Current snapshot ID from the settings file.
8544 * @param aParentSnapshot Parent snapshot.
8545 */
8546HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8547 const Guid &aCurSnapshotId,
8548 Snapshot *aParentSnapshot)
8549{
8550 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8551 AssertReturn(!i_isSessionMachine(), E_FAIL);
8552
8553 HRESULT rc = S_OK;
8554
8555 Utf8Str strStateFile;
8556 if (!data.strStateFile.isEmpty())
8557 {
8558 /* optional */
8559 strStateFile = data.strStateFile;
8560 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8561 if (RT_FAILURE(vrc))
8562 return setError(E_FAIL,
8563 tr("Invalid saved state file path '%s' (%Rrc)"),
8564 strStateFile.c_str(),
8565 vrc);
8566 }
8567
8568 /* create a snapshot machine object */
8569 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8570 pSnapshotMachine.createObject();
8571 rc = pSnapshotMachine->initFromSettings(this,
8572 data.hardware,
8573 &data.debugging,
8574 &data.autostart,
8575 data.storage,
8576 data.uuid.ref(),
8577 strStateFile);
8578 if (FAILED(rc)) return rc;
8579
8580 /* create a snapshot object */
8581 ComObjPtr<Snapshot> pSnapshot;
8582 pSnapshot.createObject();
8583 /* initialize the snapshot */
8584 rc = pSnapshot->init(mParent, // VirtualBox object
8585 data.uuid,
8586 data.strName,
8587 data.strDescription,
8588 data.timestamp,
8589 pSnapshotMachine,
8590 aParentSnapshot);
8591 if (FAILED(rc)) return rc;
8592
8593 /* memorize the first snapshot if necessary */
8594 if (!mData->mFirstSnapshot)
8595 mData->mFirstSnapshot = pSnapshot;
8596
8597 /* memorize the current snapshot when appropriate */
8598 if ( !mData->mCurrentSnapshot
8599 && pSnapshot->i_getId() == aCurSnapshotId
8600 )
8601 mData->mCurrentSnapshot = pSnapshot;
8602
8603 // now create the children
8604 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8605 it != data.llChildSnapshots.end();
8606 ++it)
8607 {
8608 const settings::Snapshot &childData = *it;
8609 // recurse
8610 rc = i_loadSnapshot(childData,
8611 aCurSnapshotId,
8612 pSnapshot); // parent = the one we created above
8613 if (FAILED(rc)) return rc;
8614 }
8615
8616 return rc;
8617}
8618
8619/**
8620 * Loads settings into mHWData.
8621 *
8622 * @param data Reference to the hardware settings.
8623 * @param pDbg Pointer to the debugging settings.
8624 * @param pAutostart Pointer to the autostart settings.
8625 */
8626HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8627 const settings::Autostart *pAutostart)
8628{
8629 AssertReturn(!i_isSessionMachine(), E_FAIL);
8630
8631 HRESULT rc = S_OK;
8632
8633 try
8634 {
8635 /* The hardware version attribute (optional). */
8636 mHWData->mHWVersion = data.strVersion;
8637 mHWData->mHardwareUUID = data.uuid;
8638
8639 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8640 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8641 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8642 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8643 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8644 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8645 mHWData->mPAEEnabled = data.fPAE;
8646 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8647 mHWData->mLongMode = data.enmLongMode;
8648 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8649 mHWData->mCPUCount = data.cCPUs;
8650 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8651 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8652
8653 // cpu
8654 if (mHWData->mCPUHotPlugEnabled)
8655 {
8656 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8657 it != data.llCpus.end();
8658 ++it)
8659 {
8660 const settings::Cpu &cpu = *it;
8661
8662 mHWData->mCPUAttached[cpu.ulId] = true;
8663 }
8664 }
8665
8666 // cpuid leafs
8667 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8668 it != data.llCpuIdLeafs.end();
8669 ++it)
8670 {
8671 const settings::CpuIdLeaf &leaf = *it;
8672
8673 switch (leaf.ulId)
8674 {
8675 case 0x0:
8676 case 0x1:
8677 case 0x2:
8678 case 0x3:
8679 case 0x4:
8680 case 0x5:
8681 case 0x6:
8682 case 0x7:
8683 case 0x8:
8684 case 0x9:
8685 case 0xA:
8686 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8687 break;
8688
8689 case 0x80000000:
8690 case 0x80000001:
8691 case 0x80000002:
8692 case 0x80000003:
8693 case 0x80000004:
8694 case 0x80000005:
8695 case 0x80000006:
8696 case 0x80000007:
8697 case 0x80000008:
8698 case 0x80000009:
8699 case 0x8000000A:
8700 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8701 break;
8702
8703 default:
8704 /* just ignore */
8705 break;
8706 }
8707 }
8708
8709 mHWData->mMemorySize = data.ulMemorySizeMB;
8710 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8711
8712 // boot order
8713 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8714 {
8715 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8716 if (it == data.mapBootOrder.end())
8717 mHWData->mBootOrder[i] = DeviceType_Null;
8718 else
8719 mHWData->mBootOrder[i] = it->second;
8720 }
8721
8722 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8723 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8724 mHWData->mMonitorCount = data.cMonitors;
8725 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8726 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8727 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8728 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8729 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8730 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8731 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8732 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8733 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8734 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8735 if (!data.strVideoCaptureFile.isEmpty())
8736 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8737 else
8738 mHWData->mVideoCaptureFile.setNull();
8739 mHWData->mFirmwareType = data.firmwareType;
8740 mHWData->mPointingHIDType = data.pointingHIDType;
8741 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8742 mHWData->mChipsetType = data.chipsetType;
8743 mHWData->mParavirtProvider = data.paravirtProvider;
8744 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8745 mHWData->mHPETEnabled = data.fHPETEnabled;
8746
8747 /* VRDEServer */
8748 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8749 if (FAILED(rc)) return rc;
8750
8751 /* BIOS */
8752 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8753 if (FAILED(rc)) return rc;
8754
8755 // Bandwidth control (must come before network adapters)
8756 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8757 if (FAILED(rc)) return rc;
8758
8759 /* Shared folders */
8760 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8761 it != data.usbSettings.llUSBControllers.end();
8762 ++it)
8763 {
8764 const settings::USBController &settingsCtrl = *it;
8765 ComObjPtr<USBController> newCtrl;
8766
8767 newCtrl.createObject();
8768 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8769 mUSBControllers->push_back(newCtrl);
8770 }
8771
8772 /* USB device filters */
8773 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8774 if (FAILED(rc)) return rc;
8775
8776 // network adapters
8777 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8778 uint32_t oldCount = mNetworkAdapters.size();
8779 if (newCount > oldCount)
8780 {
8781 mNetworkAdapters.resize(newCount);
8782 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8783 {
8784 unconst(mNetworkAdapters[slot]).createObject();
8785 mNetworkAdapters[slot]->init(this, slot);
8786 }
8787 }
8788 else if (newCount < oldCount)
8789 mNetworkAdapters.resize(newCount);
8790 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8791 it != data.llNetworkAdapters.end();
8792 ++it)
8793 {
8794 const settings::NetworkAdapter &nic = *it;
8795
8796 /* slot unicity is guaranteed by XML Schema */
8797 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8798 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8799 if (FAILED(rc)) return rc;
8800 }
8801
8802 // serial ports
8803 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8804 it != data.llSerialPorts.end();
8805 ++it)
8806 {
8807 const settings::SerialPort &s = *it;
8808
8809 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8810 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8811 if (FAILED(rc)) return rc;
8812 }
8813
8814 // parallel ports (optional)
8815 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8816 it != data.llParallelPorts.end();
8817 ++it)
8818 {
8819 const settings::ParallelPort &p = *it;
8820
8821 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8822 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8823 if (FAILED(rc)) return rc;
8824 }
8825
8826 /* AudioAdapter */
8827 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8828 if (FAILED(rc)) return rc;
8829
8830 /* Shared folders */
8831 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8832 it != data.llSharedFolders.end();
8833 ++it)
8834 {
8835 const settings::SharedFolder &sf = *it;
8836
8837 ComObjPtr<SharedFolder> sharedFolder;
8838 /* Check for double entries. Not allowed! */
8839 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8840 if (SUCCEEDED(rc))
8841 return setError(VBOX_E_OBJECT_IN_USE,
8842 tr("Shared folder named '%s' already exists"),
8843 sf.strName.c_str());
8844
8845 /* Create the new shared folder. Don't break on error. This will be
8846 * reported when the machine starts. */
8847 sharedFolder.createObject();
8848 rc = sharedFolder->init(i_getMachine(),
8849 sf.strName,
8850 sf.strHostPath,
8851 RT_BOOL(sf.fWritable),
8852 RT_BOOL(sf.fAutoMount),
8853 false /* fFailOnError */);
8854 if (FAILED(rc)) return rc;
8855 mHWData->mSharedFolders.push_back(sharedFolder);
8856 }
8857
8858 // Clipboard
8859 mHWData->mClipboardMode = data.clipboardMode;
8860
8861 // drag'n'drop
8862 mHWData->mDnDMode = data.dndMode;
8863
8864 // guest settings
8865 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8866
8867 // IO settings
8868 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8869 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8870
8871 // Host PCI devices
8872 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8873 it != data.pciAttachments.end();
8874 ++it)
8875 {
8876 const settings::HostPCIDeviceAttachment &hpda = *it;
8877 ComObjPtr<PCIDeviceAttachment> pda;
8878
8879 pda.createObject();
8880 pda->i_loadSettings(this, hpda);
8881 mHWData->mPCIDeviceAssignments.push_back(pda);
8882 }
8883
8884 /*
8885 * (The following isn't really real hardware, but it lives in HWData
8886 * for reasons of convenience.)
8887 */
8888
8889#ifdef VBOX_WITH_GUEST_PROPS
8890 /* Guest properties (optional) */
8891 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8892 it != data.llGuestProperties.end();
8893 ++it)
8894 {
8895 const settings::GuestProperty &prop = *it;
8896 uint32_t fFlags = guestProp::NILFLAG;
8897 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8898 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8899 mHWData->mGuestProperties[prop.strName] = property;
8900 }
8901
8902 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8903#endif /* VBOX_WITH_GUEST_PROPS defined */
8904
8905 rc = i_loadDebugging(pDbg);
8906 if (FAILED(rc))
8907 return rc;
8908
8909 mHWData->mAutostart = *pAutostart;
8910
8911 /* default frontend */
8912 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8913 }
8914 catch(std::bad_alloc &)
8915 {
8916 return E_OUTOFMEMORY;
8917 }
8918
8919 AssertComRC(rc);
8920 return rc;
8921}
8922
8923/**
8924 * Called from Machine::loadHardware() to load the debugging settings of the
8925 * machine.
8926 *
8927 * @param pDbg Pointer to the settings.
8928 */
8929HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8930{
8931 mHWData->mDebugging = *pDbg;
8932 /* no more processing currently required, this will probably change. */
8933 return S_OK;
8934}
8935
8936/**
8937 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8938 *
8939 * @param data
8940 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8941 * @param puuidSnapshot
8942 * @return
8943 */
8944HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8945 const Guid *puuidRegistry,
8946 const Guid *puuidSnapshot)
8947{
8948 AssertReturn(!i_isSessionMachine(), E_FAIL);
8949
8950 HRESULT rc = S_OK;
8951
8952 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8953 it != data.llStorageControllers.end();
8954 ++it)
8955 {
8956 const settings::StorageController &ctlData = *it;
8957
8958 ComObjPtr<StorageController> pCtl;
8959 /* Try to find one with the name first. */
8960 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8961 if (SUCCEEDED(rc))
8962 return setError(VBOX_E_OBJECT_IN_USE,
8963 tr("Storage controller named '%s' already exists"),
8964 ctlData.strName.c_str());
8965
8966 pCtl.createObject();
8967 rc = pCtl->init(this,
8968 ctlData.strName,
8969 ctlData.storageBus,
8970 ctlData.ulInstance,
8971 ctlData.fBootable);
8972 if (FAILED(rc)) return rc;
8973
8974 mStorageControllers->push_back(pCtl);
8975
8976 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8977 if (FAILED(rc)) return rc;
8978
8979 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8980 if (FAILED(rc)) return rc;
8981
8982 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8983 if (FAILED(rc)) return rc;
8984
8985 /* Set IDE emulation settings (only for AHCI controller). */
8986 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8987 {
8988 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8989 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8990 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8991 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8992 )
8993 return rc;
8994 }
8995
8996 /* Load the attached devices now. */
8997 rc = i_loadStorageDevices(pCtl,
8998 ctlData,
8999 puuidRegistry,
9000 puuidSnapshot);
9001 if (FAILED(rc)) return rc;
9002 }
9003
9004 return S_OK;
9005}
9006
9007/**
9008 * Called from i_loadStorageControllers for a controller's devices.
9009 *
9010 * @param aStorageController
9011 * @param data
9012 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9013 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9014 * @return
9015 */
9016HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9017 const settings::StorageController &data,
9018 const Guid *puuidRegistry,
9019 const Guid *puuidSnapshot)
9020{
9021 HRESULT rc = S_OK;
9022
9023 /* paranoia: detect duplicate attachments */
9024 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9025 it != data.llAttachedDevices.end();
9026 ++it)
9027 {
9028 const settings::AttachedDevice &ad = *it;
9029
9030 for (settings::AttachedDevicesList::const_iterator it2 = it;
9031 it2 != data.llAttachedDevices.end();
9032 ++it2)
9033 {
9034 if (it == it2)
9035 continue;
9036
9037 const settings::AttachedDevice &ad2 = *it2;
9038
9039 if ( ad.lPort == ad2.lPort
9040 && ad.lDevice == ad2.lDevice)
9041 {
9042 return setError(E_FAIL,
9043 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9044 aStorageController->i_getName().c_str(),
9045 ad.lPort,
9046 ad.lDevice,
9047 mUserData->s.strName.c_str());
9048 }
9049 }
9050 }
9051
9052 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9053 it != data.llAttachedDevices.end();
9054 ++it)
9055 {
9056 const settings::AttachedDevice &dev = *it;
9057 ComObjPtr<Medium> medium;
9058
9059 switch (dev.deviceType)
9060 {
9061 case DeviceType_Floppy:
9062 case DeviceType_DVD:
9063 if (dev.strHostDriveSrc.isNotEmpty())
9064 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9065 false /* fRefresh */, medium);
9066 else
9067 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9068 dev.uuid,
9069 false /* fRefresh */,
9070 false /* aSetError */,
9071 medium);
9072 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9073 // This is not an error. The host drive or UUID might have vanished, so just go
9074 // ahead without this removeable medium attachment
9075 rc = S_OK;
9076 break;
9077
9078 case DeviceType_HardDisk:
9079 {
9080 /* find a hard disk by UUID */
9081 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9082 if (FAILED(rc))
9083 {
9084 if (i_isSnapshotMachine())
9085 {
9086 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9087 // so the user knows that the bad disk is in a snapshot somewhere
9088 com::ErrorInfo info;
9089 return setError(E_FAIL,
9090 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9091 puuidSnapshot->raw(),
9092 info.getText().raw());
9093 }
9094 else
9095 return rc;
9096 }
9097
9098 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9099
9100 if (medium->i_getType() == MediumType_Immutable)
9101 {
9102 if (i_isSnapshotMachine())
9103 return setError(E_FAIL,
9104 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9105 "of the virtual machine '%s' ('%s')"),
9106 medium->i_getLocationFull().c_str(),
9107 dev.uuid.raw(),
9108 puuidSnapshot->raw(),
9109 mUserData->s.strName.c_str(),
9110 mData->m_strConfigFileFull.c_str());
9111
9112 return setError(E_FAIL,
9113 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9114 medium->i_getLocationFull().c_str(),
9115 dev.uuid.raw(),
9116 mUserData->s.strName.c_str(),
9117 mData->m_strConfigFileFull.c_str());
9118 }
9119
9120 if (medium->i_getType() == MediumType_MultiAttach)
9121 {
9122 if (i_isSnapshotMachine())
9123 return setError(E_FAIL,
9124 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9125 "of the virtual machine '%s' ('%s')"),
9126 medium->i_getLocationFull().c_str(),
9127 dev.uuid.raw(),
9128 puuidSnapshot->raw(),
9129 mUserData->s.strName.c_str(),
9130 mData->m_strConfigFileFull.c_str());
9131
9132 return setError(E_FAIL,
9133 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9134 medium->i_getLocationFull().c_str(),
9135 dev.uuid.raw(),
9136 mUserData->s.strName.c_str(),
9137 mData->m_strConfigFileFull.c_str());
9138 }
9139
9140 if ( !i_isSnapshotMachine()
9141 && medium->i_getChildren().size() != 0
9142 )
9143 return setError(E_FAIL,
9144 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9145 "because it has %d differencing child hard disks"),
9146 medium->i_getLocationFull().c_str(),
9147 dev.uuid.raw(),
9148 mUserData->s.strName.c_str(),
9149 mData->m_strConfigFileFull.c_str(),
9150 medium->i_getChildren().size());
9151
9152 if (i_findAttachment(mMediaData->mAttachments,
9153 medium))
9154 return setError(E_FAIL,
9155 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9156 medium->i_getLocationFull().c_str(),
9157 dev.uuid.raw(),
9158 mUserData->s.strName.c_str(),
9159 mData->m_strConfigFileFull.c_str());
9160
9161 break;
9162 }
9163
9164 default:
9165 return setError(E_FAIL,
9166 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9167 medium->i_getLocationFull().c_str(),
9168 mUserData->s.strName.c_str(),
9169 mData->m_strConfigFileFull.c_str());
9170 }
9171
9172 if (FAILED(rc))
9173 break;
9174
9175 /* Bandwidth groups are loaded at this point. */
9176 ComObjPtr<BandwidthGroup> pBwGroup;
9177
9178 if (!dev.strBwGroup.isEmpty())
9179 {
9180 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9181 if (FAILED(rc))
9182 return setError(E_FAIL,
9183 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9184 medium->i_getLocationFull().c_str(),
9185 dev.strBwGroup.c_str(),
9186 mUserData->s.strName.c_str(),
9187 mData->m_strConfigFileFull.c_str());
9188 pBwGroup->i_reference();
9189 }
9190
9191 const Bstr controllerName = aStorageController->i_getName();
9192 ComObjPtr<MediumAttachment> pAttachment;
9193 pAttachment.createObject();
9194 rc = pAttachment->init(this,
9195 medium,
9196 controllerName,
9197 dev.lPort,
9198 dev.lDevice,
9199 dev.deviceType,
9200 false,
9201 dev.fPassThrough,
9202 dev.fTempEject,
9203 dev.fNonRotational,
9204 dev.fDiscard,
9205 dev.fHotPluggable,
9206 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9207 if (FAILED(rc)) break;
9208
9209 /* associate the medium with this machine and snapshot */
9210 if (!medium.isNull())
9211 {
9212 AutoCaller medCaller(medium);
9213 if (FAILED(medCaller.rc())) return medCaller.rc();
9214 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9215
9216 if (i_isSnapshotMachine())
9217 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9218 else
9219 rc = medium->i_addBackReference(mData->mUuid);
9220 /* If the medium->addBackReference fails it sets an appropriate
9221 * error message, so no need to do any guesswork here. */
9222
9223 if (puuidRegistry)
9224 // caller wants registry ID to be set on all attached media (OVF import case)
9225 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9226 }
9227
9228 if (FAILED(rc))
9229 break;
9230
9231 /* back up mMediaData to let registeredInit() properly rollback on failure
9232 * (= limited accessibility) */
9233 i_setModified(IsModified_Storage);
9234 mMediaData.backup();
9235 mMediaData->mAttachments.push_back(pAttachment);
9236 }
9237
9238 return rc;
9239}
9240
9241/**
9242 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9243 *
9244 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9245 * @param aSnapshot where to return the found snapshot
9246 * @param aSetError true to set extended error info on failure
9247 */
9248HRESULT Machine::i_findSnapshotById(const Guid &aId,
9249 ComObjPtr<Snapshot> &aSnapshot,
9250 bool aSetError /* = false */)
9251{
9252 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9253
9254 if (!mData->mFirstSnapshot)
9255 {
9256 if (aSetError)
9257 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9258 return E_FAIL;
9259 }
9260
9261 if (aId.isZero())
9262 aSnapshot = mData->mFirstSnapshot;
9263 else
9264 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9265
9266 if (!aSnapshot)
9267 {
9268 if (aSetError)
9269 return setError(E_FAIL,
9270 tr("Could not find a snapshot with UUID {%s}"),
9271 aId.toString().c_str());
9272 return E_FAIL;
9273 }
9274
9275 return S_OK;
9276}
9277
9278/**
9279 * Returns the snapshot with the given name or fails of no such snapshot.
9280 *
9281 * @param aName snapshot name to find
9282 * @param aSnapshot where to return the found snapshot
9283 * @param aSetError true to set extended error info on failure
9284 */
9285HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9286 ComObjPtr<Snapshot> &aSnapshot,
9287 bool aSetError /* = false */)
9288{
9289 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9290
9291 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9292
9293 if (!mData->mFirstSnapshot)
9294 {
9295 if (aSetError)
9296 return setError(VBOX_E_OBJECT_NOT_FOUND,
9297 tr("This machine does not have any snapshots"));
9298 return VBOX_E_OBJECT_NOT_FOUND;
9299 }
9300
9301 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9302
9303 if (!aSnapshot)
9304 {
9305 if (aSetError)
9306 return setError(VBOX_E_OBJECT_NOT_FOUND,
9307 tr("Could not find a snapshot named '%s'"), strName.c_str());
9308 return VBOX_E_OBJECT_NOT_FOUND;
9309 }
9310
9311 return S_OK;
9312}
9313
9314/**
9315 * Returns a storage controller object with the given name.
9316 *
9317 * @param aName storage controller name to find
9318 * @param aStorageController where to return the found storage controller
9319 * @param aSetError true to set extended error info on failure
9320 */
9321HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9322 ComObjPtr<StorageController> &aStorageController,
9323 bool aSetError /* = false */)
9324{
9325 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9326
9327 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9328 it != mStorageControllers->end();
9329 ++it)
9330 {
9331 if ((*it)->i_getName() == aName)
9332 {
9333 aStorageController = (*it);
9334 return S_OK;
9335 }
9336 }
9337
9338 if (aSetError)
9339 return setError(VBOX_E_OBJECT_NOT_FOUND,
9340 tr("Could not find a storage controller named '%s'"),
9341 aName.c_str());
9342 return VBOX_E_OBJECT_NOT_FOUND;
9343}
9344
9345/**
9346 * Returns a USB controller object with the given name.
9347 *
9348 * @param aName USB controller name to find
9349 * @param aUSBController where to return the found USB controller
9350 * @param aSetError true to set extended error info on failure
9351 */
9352HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9353 ComObjPtr<USBController> &aUSBController,
9354 bool aSetError /* = false */)
9355{
9356 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9357
9358 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9359 it != mUSBControllers->end();
9360 ++it)
9361 {
9362 if ((*it)->i_getName() == aName)
9363 {
9364 aUSBController = (*it);
9365 return S_OK;
9366 }
9367 }
9368
9369 if (aSetError)
9370 return setError(VBOX_E_OBJECT_NOT_FOUND,
9371 tr("Could not find a storage controller named '%s'"),
9372 aName.c_str());
9373 return VBOX_E_OBJECT_NOT_FOUND;
9374}
9375
9376/**
9377 * Returns the number of USB controller instance of the given type.
9378 *
9379 * @param enmType USB controller type.
9380 */
9381ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9382{
9383 ULONG cCtrls = 0;
9384
9385 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9386 it != mUSBControllers->end();
9387 ++it)
9388 {
9389 if ((*it)->i_getControllerType() == enmType)
9390 cCtrls++;
9391 }
9392
9393 return cCtrls;
9394}
9395
9396HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9397 MediaData::AttachmentList &atts)
9398{
9399 AutoCaller autoCaller(this);
9400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9401
9402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9403
9404 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9405 it != mMediaData->mAttachments.end();
9406 ++it)
9407 {
9408 const ComObjPtr<MediumAttachment> &pAtt = *it;
9409 // should never happen, but deal with NULL pointers in the list.
9410 AssertStmt(!pAtt.isNull(), continue);
9411
9412 // getControllerName() needs caller+read lock
9413 AutoCaller autoAttCaller(pAtt);
9414 if (FAILED(autoAttCaller.rc()))
9415 {
9416 atts.clear();
9417 return autoAttCaller.rc();
9418 }
9419 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9420
9421 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9422 atts.push_back(pAtt);
9423 }
9424
9425 return S_OK;
9426}
9427
9428
9429/**
9430 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9431 * file if the machine name was changed and about creating a new settings file
9432 * if this is a new machine.
9433 *
9434 * @note Must be never called directly but only from #saveSettings().
9435 */
9436HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9437{
9438 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9439
9440 HRESULT rc = S_OK;
9441
9442 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9443
9444 /// @todo need to handle primary group change, too
9445
9446 /* attempt to rename the settings file if machine name is changed */
9447 if ( mUserData->s.fNameSync
9448 && mUserData.isBackedUp()
9449 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9450 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9451 )
9452 {
9453 bool dirRenamed = false;
9454 bool fileRenamed = false;
9455
9456 Utf8Str configFile, newConfigFile;
9457 Utf8Str configFilePrev, newConfigFilePrev;
9458 Utf8Str configDir, newConfigDir;
9459
9460 do
9461 {
9462 int vrc = VINF_SUCCESS;
9463
9464 Utf8Str name = mUserData.backedUpData()->s.strName;
9465 Utf8Str newName = mUserData->s.strName;
9466 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9467 if (group == "/")
9468 group.setNull();
9469 Utf8Str newGroup = mUserData->s.llGroups.front();
9470 if (newGroup == "/")
9471 newGroup.setNull();
9472
9473 configFile = mData->m_strConfigFileFull;
9474
9475 /* first, rename the directory if it matches the group and machine name */
9476 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9477 group.c_str(), RTPATH_DELIMITER, name.c_str());
9478 /** @todo hack, make somehow use of ComposeMachineFilename */
9479 if (mUserData->s.fDirectoryIncludesUUID)
9480 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9481 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9482 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9483 /** @todo hack, make somehow use of ComposeMachineFilename */
9484 if (mUserData->s.fDirectoryIncludesUUID)
9485 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9486 configDir = configFile;
9487 configDir.stripFilename();
9488 newConfigDir = configDir;
9489 if ( configDir.length() >= groupPlusName.length()
9490 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9491 groupPlusName.c_str()))
9492 {
9493 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9494 Utf8Str newConfigBaseDir(newConfigDir);
9495 newConfigDir.append(newGroupPlusName);
9496 /* consistency: use \ if appropriate on the platform */
9497 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9498 /* new dir and old dir cannot be equal here because of 'if'
9499 * above and because name != newName */
9500 Assert(configDir != newConfigDir);
9501 if (!fSettingsFileIsNew)
9502 {
9503 /* perform real rename only if the machine is not new */
9504 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9505 if ( vrc == VERR_FILE_NOT_FOUND
9506 || vrc == VERR_PATH_NOT_FOUND)
9507 {
9508 /* create the parent directory, then retry renaming */
9509 Utf8Str parent(newConfigDir);
9510 parent.stripFilename();
9511 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9512 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9513 }
9514 if (RT_FAILURE(vrc))
9515 {
9516 rc = setError(E_FAIL,
9517 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9518 configDir.c_str(),
9519 newConfigDir.c_str(),
9520 vrc);
9521 break;
9522 }
9523 /* delete subdirectories which are no longer needed */
9524 Utf8Str dir(configDir);
9525 dir.stripFilename();
9526 while (dir != newConfigBaseDir && dir != ".")
9527 {
9528 vrc = RTDirRemove(dir.c_str());
9529 if (RT_FAILURE(vrc))
9530 break;
9531 dir.stripFilename();
9532 }
9533 dirRenamed = true;
9534 }
9535 }
9536
9537 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9538 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9539
9540 /* then try to rename the settings file itself */
9541 if (newConfigFile != configFile)
9542 {
9543 /* get the path to old settings file in renamed directory */
9544 configFile = Utf8StrFmt("%s%c%s",
9545 newConfigDir.c_str(),
9546 RTPATH_DELIMITER,
9547 RTPathFilename(configFile.c_str()));
9548 if (!fSettingsFileIsNew)
9549 {
9550 /* perform real rename only if the machine is not new */
9551 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9552 if (RT_FAILURE(vrc))
9553 {
9554 rc = setError(E_FAIL,
9555 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9556 configFile.c_str(),
9557 newConfigFile.c_str(),
9558 vrc);
9559 break;
9560 }
9561 fileRenamed = true;
9562 configFilePrev = configFile;
9563 configFilePrev += "-prev";
9564 newConfigFilePrev = newConfigFile;
9565 newConfigFilePrev += "-prev";
9566 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9567 }
9568 }
9569
9570 // update m_strConfigFileFull amd mConfigFile
9571 mData->m_strConfigFileFull = newConfigFile;
9572 // compute the relative path too
9573 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9574
9575 // store the old and new so that VirtualBox::i_saveSettings() can update
9576 // the media registry
9577 if ( mData->mRegistered
9578 && (configDir != newConfigDir || configFile != newConfigFile))
9579 {
9580 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9581
9582 if (pfNeedsGlobalSaveSettings)
9583 *pfNeedsGlobalSaveSettings = true;
9584 }
9585
9586 // in the saved state file path, replace the old directory with the new directory
9587 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9588 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9589
9590 // and do the same thing for the saved state file paths of all the online snapshots
9591 if (mData->mFirstSnapshot)
9592 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9593 newConfigDir.c_str());
9594 }
9595 while (0);
9596
9597 if (FAILED(rc))
9598 {
9599 /* silently try to rename everything back */
9600 if (fileRenamed)
9601 {
9602 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9603 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9604 }
9605 if (dirRenamed)
9606 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9607 }
9608
9609 if (FAILED(rc)) return rc;
9610 }
9611
9612 if (fSettingsFileIsNew)
9613 {
9614 /* create a virgin config file */
9615 int vrc = VINF_SUCCESS;
9616
9617 /* ensure the settings directory exists */
9618 Utf8Str path(mData->m_strConfigFileFull);
9619 path.stripFilename();
9620 if (!RTDirExists(path.c_str()))
9621 {
9622 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9623 if (RT_FAILURE(vrc))
9624 {
9625 return setError(E_FAIL,
9626 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9627 path.c_str(),
9628 vrc);
9629 }
9630 }
9631
9632 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9633 path = Utf8Str(mData->m_strConfigFileFull);
9634 RTFILE f = NIL_RTFILE;
9635 vrc = RTFileOpen(&f, path.c_str(),
9636 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9637 if (RT_FAILURE(vrc))
9638 return setError(E_FAIL,
9639 tr("Could not create the settings file '%s' (%Rrc)"),
9640 path.c_str(),
9641 vrc);
9642 RTFileClose(f);
9643 }
9644
9645 return rc;
9646}
9647
9648/**
9649 * Saves and commits machine data, user data and hardware data.
9650 *
9651 * Note that on failure, the data remains uncommitted.
9652 *
9653 * @a aFlags may combine the following flags:
9654 *
9655 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9656 * Used when saving settings after an operation that makes them 100%
9657 * correspond to the settings from the current snapshot.
9658 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9659 * #isReallyModified() returns false. This is necessary for cases when we
9660 * change machine data directly, not through the backup()/commit() mechanism.
9661 * - SaveS_Force: settings will be saved without doing a deep compare of the
9662 * settings structures. This is used when this is called because snapshots
9663 * have changed to avoid the overhead of the deep compare.
9664 *
9665 * @note Must be called from under this object's write lock. Locks children for
9666 * writing.
9667 *
9668 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9669 * initialized to false and that will be set to true by this function if
9670 * the caller must invoke VirtualBox::i_saveSettings() because the global
9671 * settings have changed. This will happen if a machine rename has been
9672 * saved and the global machine and media registries will therefore need
9673 * updating.
9674 */
9675HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9676 int aFlags /*= 0*/)
9677{
9678 LogFlowThisFuncEnter();
9679
9680 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9681
9682 /* make sure child objects are unable to modify the settings while we are
9683 * saving them */
9684 i_ensureNoStateDependencies();
9685
9686 AssertReturn(!i_isSnapshotMachine(),
9687 E_FAIL);
9688
9689 HRESULT rc = S_OK;
9690 bool fNeedsWrite = false;
9691
9692 /* First, prepare to save settings. It will care about renaming the
9693 * settings directory and file if the machine name was changed and about
9694 * creating a new settings file if this is a new machine. */
9695 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9696 if (FAILED(rc)) return rc;
9697
9698 // keep a pointer to the current settings structures
9699 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9700 settings::MachineConfigFile *pNewConfig = NULL;
9701
9702 try
9703 {
9704 // make a fresh one to have everyone write stuff into
9705 pNewConfig = new settings::MachineConfigFile(NULL);
9706 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9707
9708 // now go and copy all the settings data from COM to the settings structures
9709 // (this calles i_saveSettings() on all the COM objects in the machine)
9710 i_copyMachineDataToSettings(*pNewConfig);
9711
9712 if (aFlags & SaveS_ResetCurStateModified)
9713 {
9714 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9715 mData->mCurrentStateModified = FALSE;
9716 fNeedsWrite = true; // always, no need to compare
9717 }
9718 else if (aFlags & SaveS_Force)
9719 {
9720 fNeedsWrite = true; // always, no need to compare
9721 }
9722 else
9723 {
9724 if (!mData->mCurrentStateModified)
9725 {
9726 // do a deep compare of the settings that we just saved with the settings
9727 // previously stored in the config file; this invokes MachineConfigFile::operator==
9728 // which does a deep compare of all the settings, which is expensive but less expensive
9729 // than writing out XML in vain
9730 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9731
9732 // could still be modified if any settings changed
9733 mData->mCurrentStateModified = fAnySettingsChanged;
9734
9735 fNeedsWrite = fAnySettingsChanged;
9736 }
9737 else
9738 fNeedsWrite = true;
9739 }
9740
9741 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9742
9743 if (fNeedsWrite)
9744 // now spit it all out!
9745 pNewConfig->write(mData->m_strConfigFileFull);
9746
9747 mData->pMachineConfigFile = pNewConfig;
9748 delete pOldConfig;
9749 i_commit();
9750
9751 // after saving settings, we are no longer different from the XML on disk
9752 mData->flModifications = 0;
9753 }
9754 catch (HRESULT err)
9755 {
9756 // we assume that error info is set by the thrower
9757 rc = err;
9758
9759 // restore old config
9760 delete pNewConfig;
9761 mData->pMachineConfigFile = pOldConfig;
9762 }
9763 catch (...)
9764 {
9765 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9766 }
9767
9768 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9769 {
9770 /* Fire the data change event, even on failure (since we've already
9771 * committed all data). This is done only for SessionMachines because
9772 * mutable Machine instances are always not registered (i.e. private
9773 * to the client process that creates them) and thus don't need to
9774 * inform callbacks. */
9775 if (i_isSessionMachine())
9776 mParent->i_onMachineDataChange(mData->mUuid);
9777 }
9778
9779 LogFlowThisFunc(("rc=%08X\n", rc));
9780 LogFlowThisFuncLeave();
9781 return rc;
9782}
9783
9784/**
9785 * Implementation for saving the machine settings into the given
9786 * settings::MachineConfigFile instance. This copies machine extradata
9787 * from the previous machine config file in the instance data, if any.
9788 *
9789 * This gets called from two locations:
9790 *
9791 * -- Machine::i_saveSettings(), during the regular XML writing;
9792 *
9793 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9794 * exported to OVF and we write the VirtualBox proprietary XML
9795 * into a <vbox:Machine> tag.
9796 *
9797 * This routine fills all the fields in there, including snapshots, *except*
9798 * for the following:
9799 *
9800 * -- fCurrentStateModified. There is some special logic associated with that.
9801 *
9802 * The caller can then call MachineConfigFile::write() or do something else
9803 * with it.
9804 *
9805 * Caller must hold the machine lock!
9806 *
9807 * This throws XML errors and HRESULT, so the caller must have a catch block!
9808 */
9809void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9810{
9811 // deep copy extradata
9812 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9813
9814 config.uuid = mData->mUuid;
9815
9816 // copy name, description, OS type, teleport, UTC etc.
9817 config.machineUserData = mUserData->s;
9818
9819 // Encode the Icon Override data from Machine and store on config userdata.
9820 com::SafeArray<BYTE> iconByte;
9821 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9822 ssize_t cbData = iconByte.size();
9823 if (cbData > 0)
9824 {
9825 ssize_t cchOut = RTBase64EncodedLength(cbData);
9826 Utf8Str strIconData;
9827 strIconData.reserve(cchOut+1);
9828 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9829 strIconData.mutableRaw(), strIconData.capacity(),
9830 NULL);
9831 if (RT_FAILURE(vrc))
9832 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9833 strIconData.jolt();
9834 config.machineUserData.ovIcon = strIconData;
9835 }
9836 else
9837 config.machineUserData.ovIcon.setNull();
9838
9839 if ( mData->mMachineState == MachineState_Saved
9840 || mData->mMachineState == MachineState_Restoring
9841 // when deleting a snapshot we may or may not have a saved state in the current state,
9842 // so let's not assert here please
9843 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9844 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9845 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9846 && (!mSSData->strStateFilePath.isEmpty())
9847 )
9848 )
9849 {
9850 Assert(!mSSData->strStateFilePath.isEmpty());
9851 /* try to make the file name relative to the settings file dir */
9852 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9853 }
9854 else
9855 {
9856 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9857 config.strStateFile.setNull();
9858 }
9859
9860 if (mData->mCurrentSnapshot)
9861 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9862 else
9863 config.uuidCurrentSnapshot.clear();
9864
9865 config.timeLastStateChange = mData->mLastStateChange;
9866 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9867 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9868
9869 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9870 if (FAILED(rc)) throw rc;
9871
9872 rc = i_saveStorageControllers(config.storageMachine);
9873 if (FAILED(rc)) throw rc;
9874
9875 // save machine's media registry if this is VirtualBox 4.0 or later
9876 if (config.canHaveOwnMediaRegistry())
9877 {
9878 // determine machine folder
9879 Utf8Str strMachineFolder = i_getSettingsFileFull();
9880 strMachineFolder.stripFilename();
9881 mParent->i_saveMediaRegistry(config.mediaRegistry,
9882 i_getId(), // only media with registry ID == machine UUID
9883 strMachineFolder);
9884 // this throws HRESULT
9885 }
9886
9887 // save snapshots
9888 rc = i_saveAllSnapshots(config);
9889 if (FAILED(rc)) throw rc;
9890}
9891
9892/**
9893 * Saves all snapshots of the machine into the given machine config file. Called
9894 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9895 * @param config
9896 * @return
9897 */
9898HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9899{
9900 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9901
9902 HRESULT rc = S_OK;
9903
9904 try
9905 {
9906 config.llFirstSnapshot.clear();
9907
9908 if (mData->mFirstSnapshot)
9909 {
9910 settings::Snapshot snapNew;
9911 config.llFirstSnapshot.push_back(snapNew);
9912
9913 // get reference to the fresh copy of the snapshot on the list and
9914 // work on that copy directly to avoid excessive copying later
9915 settings::Snapshot &snap = config.llFirstSnapshot.front();
9916
9917 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9918 if (FAILED(rc)) throw rc;
9919 }
9920
9921// if (mType == IsSessionMachine)
9922// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9923
9924 }
9925 catch (HRESULT err)
9926 {
9927 /* we assume that error info is set by the thrower */
9928 rc = err;
9929 }
9930 catch (...)
9931 {
9932 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9933 }
9934
9935 return rc;
9936}
9937
9938/**
9939 * Saves the VM hardware configuration. It is assumed that the
9940 * given node is empty.
9941 *
9942 * @param data Reference to the settings object for the hardware config.
9943 * @param pDbg Pointer to the settings object for the debugging config
9944 * which happens to live in mHWData.
9945 * @param pAutostart Pointer to the settings object for the autostart config
9946 * which happens to live in mHWData.
9947 */
9948HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9949 settings::Autostart *pAutostart)
9950{
9951 HRESULT rc = S_OK;
9952
9953 try
9954 {
9955 /* The hardware version attribute (optional).
9956 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9957 if ( mHWData->mHWVersion == "1"
9958 && mSSData->strStateFilePath.isEmpty()
9959 )
9960 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9961 other point needs to be found where this can be done. */
9962
9963 data.strVersion = mHWData->mHWVersion;
9964 data.uuid = mHWData->mHardwareUUID;
9965
9966 // CPU
9967 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9968 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9969 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9970 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9971 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9972 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9973 data.fPAE = !!mHWData->mPAEEnabled;
9974 data.enmLongMode = mHWData->mLongMode;
9975 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9976 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9977
9978 /* Standard and Extended CPUID leafs. */
9979 data.llCpuIdLeafs.clear();
9980 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9981 {
9982 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9983 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9984 }
9985 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9986 {
9987 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9988 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9989 }
9990
9991 data.cCPUs = mHWData->mCPUCount;
9992 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9993 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9994
9995 data.llCpus.clear();
9996 if (data.fCpuHotPlug)
9997 {
9998 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9999 {
10000 if (mHWData->mCPUAttached[idx])
10001 {
10002 settings::Cpu cpu;
10003 cpu.ulId = idx;
10004 data.llCpus.push_back(cpu);
10005 }
10006 }
10007 }
10008
10009 // memory
10010 data.ulMemorySizeMB = mHWData->mMemorySize;
10011 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10012
10013 // firmware
10014 data.firmwareType = mHWData->mFirmwareType;
10015
10016 // HID
10017 data.pointingHIDType = mHWData->mPointingHIDType;
10018 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10019
10020 // chipset
10021 data.chipsetType = mHWData->mChipsetType;
10022
10023 // paravirt
10024 data.paravirtProvider = mHWData->mParavirtProvider;
10025
10026
10027 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10028
10029 // HPET
10030 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10031
10032 // boot order
10033 data.mapBootOrder.clear();
10034 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10035 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10036
10037 // display
10038 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10039 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10040 data.cMonitors = mHWData->mMonitorCount;
10041 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10042 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10043 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10044 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10045 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10046 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10047 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10048 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10049 {
10050 if (mHWData->maVideoCaptureScreens[i])
10051 ASMBitSet(&data.u64VideoCaptureScreens, i);
10052 else
10053 ASMBitClear(&data.u64VideoCaptureScreens, i);
10054 }
10055 /* store relative video capture file if possible */
10056 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10057
10058 /* VRDEServer settings (optional) */
10059 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10060 if (FAILED(rc)) throw rc;
10061
10062 /* BIOS (required) */
10063 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10064 if (FAILED(rc)) throw rc;
10065
10066 /* USB Controller (required) */
10067 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10068 {
10069 ComObjPtr<USBController> ctrl = *it;
10070 settings::USBController settingsCtrl;
10071
10072 settingsCtrl.strName = ctrl->i_getName();
10073 settingsCtrl.enmType = ctrl->i_getControllerType();
10074
10075 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10076 }
10077
10078 /* USB device filters (required) */
10079 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10080 if (FAILED(rc)) throw rc;
10081
10082 /* Network adapters (required) */
10083 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10084 data.llNetworkAdapters.clear();
10085 /* Write out only the nominal number of network adapters for this
10086 * chipset type. Since Machine::commit() hasn't been called there
10087 * may be extra NIC settings in the vector. */
10088 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10089 {
10090 settings::NetworkAdapter nic;
10091 nic.ulSlot = slot;
10092 /* paranoia check... must not be NULL, but must not crash either. */
10093 if (mNetworkAdapters[slot])
10094 {
10095 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10096 if (FAILED(rc)) throw rc;
10097
10098 data.llNetworkAdapters.push_back(nic);
10099 }
10100 }
10101
10102 /* Serial ports */
10103 data.llSerialPorts.clear();
10104 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10105 {
10106 settings::SerialPort s;
10107 s.ulSlot = slot;
10108 rc = mSerialPorts[slot]->i_saveSettings(s);
10109 if (FAILED(rc)) return rc;
10110
10111 data.llSerialPorts.push_back(s);
10112 }
10113
10114 /* Parallel ports */
10115 data.llParallelPorts.clear();
10116 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10117 {
10118 settings::ParallelPort p;
10119 p.ulSlot = slot;
10120 rc = mParallelPorts[slot]->i_saveSettings(p);
10121 if (FAILED(rc)) return rc;
10122
10123 data.llParallelPorts.push_back(p);
10124 }
10125
10126 /* Audio adapter */
10127 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10128 if (FAILED(rc)) return rc;
10129
10130 /* Shared folders */
10131 data.llSharedFolders.clear();
10132 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10133 it != mHWData->mSharedFolders.end();
10134 ++it)
10135 {
10136 SharedFolder *pSF = *it;
10137 AutoCaller sfCaller(pSF);
10138 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10139 settings::SharedFolder sf;
10140 sf.strName = pSF->i_getName();
10141 sf.strHostPath = pSF->i_getHostPath();
10142 sf.fWritable = !!pSF->i_isWritable();
10143 sf.fAutoMount = !!pSF->i_isAutoMounted();
10144
10145 data.llSharedFolders.push_back(sf);
10146 }
10147
10148 // clipboard
10149 data.clipboardMode = mHWData->mClipboardMode;
10150
10151 // drag'n'drop
10152 data.dndMode = mHWData->mDnDMode;
10153
10154 /* Guest */
10155 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10156
10157 // IO settings
10158 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10159 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10160
10161 /* BandwidthControl (required) */
10162 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10163 if (FAILED(rc)) throw rc;
10164
10165 /* Host PCI devices */
10166 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10167 it != mHWData->mPCIDeviceAssignments.end();
10168 ++it)
10169 {
10170 ComObjPtr<PCIDeviceAttachment> pda = *it;
10171 settings::HostPCIDeviceAttachment hpda;
10172
10173 rc = pda->i_saveSettings(hpda);
10174 if (FAILED(rc)) throw rc;
10175
10176 data.pciAttachments.push_back(hpda);
10177 }
10178
10179
10180 // guest properties
10181 data.llGuestProperties.clear();
10182#ifdef VBOX_WITH_GUEST_PROPS
10183 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10184 it != mHWData->mGuestProperties.end();
10185 ++it)
10186 {
10187 HWData::GuestProperty property = it->second;
10188
10189 /* Remove transient guest properties at shutdown unless we
10190 * are saving state */
10191 if ( ( mData->mMachineState == MachineState_PoweredOff
10192 || mData->mMachineState == MachineState_Aborted
10193 || mData->mMachineState == MachineState_Teleported)
10194 && ( property.mFlags & guestProp::TRANSIENT
10195 || property.mFlags & guestProp::TRANSRESET))
10196 continue;
10197 settings::GuestProperty prop;
10198 prop.strName = it->first;
10199 prop.strValue = property.strValue;
10200 prop.timestamp = property.mTimestamp;
10201 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10202 guestProp::writeFlags(property.mFlags, szFlags);
10203 prop.strFlags = szFlags;
10204
10205 data.llGuestProperties.push_back(prop);
10206 }
10207
10208 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10209 /* I presume this doesn't require a backup(). */
10210 mData->mGuestPropertiesModified = FALSE;
10211#endif /* VBOX_WITH_GUEST_PROPS defined */
10212
10213 *pDbg = mHWData->mDebugging;
10214 *pAutostart = mHWData->mAutostart;
10215
10216 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10217 }
10218 catch(std::bad_alloc &)
10219 {
10220 return E_OUTOFMEMORY;
10221 }
10222
10223 AssertComRC(rc);
10224 return rc;
10225}
10226
10227/**
10228 * Saves the storage controller configuration.
10229 *
10230 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10231 */
10232HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10233{
10234 data.llStorageControllers.clear();
10235
10236 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10237 it != mStorageControllers->end();
10238 ++it)
10239 {
10240 HRESULT rc;
10241 ComObjPtr<StorageController> pCtl = *it;
10242
10243 settings::StorageController ctl;
10244 ctl.strName = pCtl->i_getName();
10245 ctl.controllerType = pCtl->i_getControllerType();
10246 ctl.storageBus = pCtl->i_getStorageBus();
10247 ctl.ulInstance = pCtl->i_getInstance();
10248 ctl.fBootable = pCtl->i_getBootable();
10249
10250 /* Save the port count. */
10251 ULONG portCount;
10252 rc = pCtl->COMGETTER(PortCount)(&portCount);
10253 ComAssertComRCRet(rc, rc);
10254 ctl.ulPortCount = portCount;
10255
10256 /* Save fUseHostIOCache */
10257 BOOL fUseHostIOCache;
10258 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10259 ComAssertComRCRet(rc, rc);
10260 ctl.fUseHostIOCache = !!fUseHostIOCache;
10261
10262 /* Save IDE emulation settings. */
10263 if (ctl.controllerType == StorageControllerType_IntelAhci)
10264 {
10265 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10266 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10267 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10268 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10269 )
10270 ComAssertComRCRet(rc, rc);
10271 }
10272
10273 /* save the devices now. */
10274 rc = i_saveStorageDevices(pCtl, ctl);
10275 ComAssertComRCRet(rc, rc);
10276
10277 data.llStorageControllers.push_back(ctl);
10278 }
10279
10280 return S_OK;
10281}
10282
10283/**
10284 * Saves the hard disk configuration.
10285 */
10286HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10287 settings::StorageController &data)
10288{
10289 MediaData::AttachmentList atts;
10290
10291 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10292 if (FAILED(rc)) return rc;
10293
10294 data.llAttachedDevices.clear();
10295 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10296 it != atts.end();
10297 ++it)
10298 {
10299 settings::AttachedDevice dev;
10300 IMediumAttachment *iA = *it;
10301 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10302 Medium *pMedium = pAttach->i_getMedium();
10303
10304 dev.deviceType = pAttach->i_getType();
10305 dev.lPort = pAttach->i_getPort();
10306 dev.lDevice = pAttach->i_getDevice();
10307 dev.fPassThrough = pAttach->i_getPassthrough();
10308 dev.fHotPluggable = pAttach->i_getHotPluggable();
10309 if (pMedium)
10310 {
10311 if (pMedium->i_isHostDrive())
10312 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10313 else
10314 dev.uuid = pMedium->i_getId();
10315 dev.fTempEject = pAttach->i_getTempEject();
10316 dev.fNonRotational = pAttach->i_getNonRotational();
10317 dev.fDiscard = pAttach->i_getDiscard();
10318 }
10319
10320 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10321
10322 data.llAttachedDevices.push_back(dev);
10323 }
10324
10325 return S_OK;
10326}
10327
10328/**
10329 * Saves machine state settings as defined by aFlags
10330 * (SaveSTS_* values).
10331 *
10332 * @param aFlags Combination of SaveSTS_* flags.
10333 *
10334 * @note Locks objects for writing.
10335 */
10336HRESULT Machine::i_saveStateSettings(int aFlags)
10337{
10338 if (aFlags == 0)
10339 return S_OK;
10340
10341 AutoCaller autoCaller(this);
10342 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10343
10344 /* This object's write lock is also necessary to serialize file access
10345 * (prevent concurrent reads and writes) */
10346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10347
10348 HRESULT rc = S_OK;
10349
10350 Assert(mData->pMachineConfigFile);
10351
10352 try
10353 {
10354 if (aFlags & SaveSTS_CurStateModified)
10355 mData->pMachineConfigFile->fCurrentStateModified = true;
10356
10357 if (aFlags & SaveSTS_StateFilePath)
10358 {
10359 if (!mSSData->strStateFilePath.isEmpty())
10360 /* try to make the file name relative to the settings file dir */
10361 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10362 else
10363 mData->pMachineConfigFile->strStateFile.setNull();
10364 }
10365
10366 if (aFlags & SaveSTS_StateTimeStamp)
10367 {
10368 Assert( mData->mMachineState != MachineState_Aborted
10369 || mSSData->strStateFilePath.isEmpty());
10370
10371 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10372
10373 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10374//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10375 }
10376
10377 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10378 }
10379 catch (...)
10380 {
10381 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10382 }
10383
10384 return rc;
10385}
10386
10387/**
10388 * Ensures that the given medium is added to a media registry. If this machine
10389 * was created with 4.0 or later, then the machine registry is used. Otherwise
10390 * the global VirtualBox media registry is used.
10391 *
10392 * Caller must NOT hold machine lock, media tree or any medium locks!
10393 *
10394 * @param pMedium
10395 */
10396void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10397{
10398 /* Paranoia checks: do not hold machine or media tree locks. */
10399 AssertReturnVoid(!isWriteLockOnCurrentThread());
10400 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10401
10402 ComObjPtr<Medium> pBase;
10403 {
10404 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10405 pBase = pMedium->i_getBase();
10406 }
10407
10408 /* Paranoia checks: do not hold medium locks. */
10409 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10410 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10411
10412 // decide which medium registry to use now that the medium is attached:
10413 Guid uuid;
10414 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10415 // machine XML is VirtualBox 4.0 or higher:
10416 uuid = i_getId(); // machine UUID
10417 else
10418 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10419
10420 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10421 mParent->i_markRegistryModified(uuid);
10422
10423 /* For more complex hard disk structures it can happen that the base
10424 * medium isn't yet associated with any medium registry. Do that now. */
10425 if (pMedium != pBase)
10426 {
10427 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10428 mParent->i_markRegistryModified(uuid);
10429 }
10430}
10431
10432/**
10433 * Creates differencing hard disks for all normal hard disks attached to this
10434 * machine and a new set of attachments to refer to created disks.
10435 *
10436 * Used when taking a snapshot or when deleting the current state. Gets called
10437 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10438 *
10439 * This method assumes that mMediaData contains the original hard disk attachments
10440 * it needs to create diffs for. On success, these attachments will be replaced
10441 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10442 * called to delete created diffs which will also rollback mMediaData and restore
10443 * whatever was backed up before calling this method.
10444 *
10445 * Attachments with non-normal hard disks are left as is.
10446 *
10447 * If @a aOnline is @c false then the original hard disks that require implicit
10448 * diffs will be locked for reading. Otherwise it is assumed that they are
10449 * already locked for writing (when the VM was started). Note that in the latter
10450 * case it is responsibility of the caller to lock the newly created diffs for
10451 * writing if this method succeeds.
10452 *
10453 * @param aProgress Progress object to run (must contain at least as
10454 * many operations left as the number of hard disks
10455 * attached).
10456 * @param aOnline Whether the VM was online prior to this operation.
10457 *
10458 * @note The progress object is not marked as completed, neither on success nor
10459 * on failure. This is a responsibility of the caller.
10460 *
10461 * @note Locks this object and the media tree for writing.
10462 */
10463HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10464 ULONG aWeight,
10465 bool aOnline)
10466{
10467 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10468
10469 AutoCaller autoCaller(this);
10470 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10471
10472 AutoMultiWriteLock2 alock(this->lockHandle(),
10473 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10474
10475 /* must be in a protective state because we release the lock below */
10476 AssertReturn( mData->mMachineState == MachineState_Saving
10477 || mData->mMachineState == MachineState_LiveSnapshotting
10478 || mData->mMachineState == MachineState_RestoringSnapshot
10479 || mData->mMachineState == MachineState_DeletingSnapshot
10480 , E_FAIL);
10481
10482 HRESULT rc = S_OK;
10483
10484 // use appropriate locked media map (online or offline)
10485 MediumLockListMap lockedMediaOffline;
10486 MediumLockListMap *lockedMediaMap;
10487 if (aOnline)
10488 lockedMediaMap = &mData->mSession.mLockedMedia;
10489 else
10490 lockedMediaMap = &lockedMediaOffline;
10491
10492 try
10493 {
10494 if (!aOnline)
10495 {
10496 /* lock all attached hard disks early to detect "in use"
10497 * situations before creating actual diffs */
10498 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10499 it != mMediaData->mAttachments.end();
10500 ++it)
10501 {
10502 MediumAttachment* pAtt = *it;
10503 if (pAtt->i_getType() == DeviceType_HardDisk)
10504 {
10505 Medium* pMedium = pAtt->i_getMedium();
10506 Assert(pMedium);
10507
10508 MediumLockList *pMediumLockList(new MediumLockList());
10509 alock.release();
10510 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10511 false /* fMediumLockWrite */,
10512 NULL,
10513 *pMediumLockList);
10514 alock.acquire();
10515 if (FAILED(rc))
10516 {
10517 delete pMediumLockList;
10518 throw rc;
10519 }
10520 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10521 if (FAILED(rc))
10522 {
10523 throw setError(rc,
10524 tr("Collecting locking information for all attached media failed"));
10525 }
10526 }
10527 }
10528
10529 /* Now lock all media. If this fails, nothing is locked. */
10530 alock.release();
10531 rc = lockedMediaMap->Lock();
10532 alock.acquire();
10533 if (FAILED(rc))
10534 {
10535 throw setError(rc,
10536 tr("Locking of attached media failed"));
10537 }
10538 }
10539
10540 /* remember the current list (note that we don't use backup() since
10541 * mMediaData may be already backed up) */
10542 MediaData::AttachmentList atts = mMediaData->mAttachments;
10543
10544 /* start from scratch */
10545 mMediaData->mAttachments.clear();
10546
10547 /* go through remembered attachments and create diffs for normal hard
10548 * disks and attach them */
10549 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10550 it != atts.end();
10551 ++it)
10552 {
10553 MediumAttachment* pAtt = *it;
10554
10555 DeviceType_T devType = pAtt->i_getType();
10556 Medium* pMedium = pAtt->i_getMedium();
10557
10558 if ( devType != DeviceType_HardDisk
10559 || pMedium == NULL
10560 || pMedium->i_getType() != MediumType_Normal)
10561 {
10562 /* copy the attachment as is */
10563
10564 /** @todo the progress object created in Console::TakeSnaphot
10565 * only expects operations for hard disks. Later other
10566 * device types need to show up in the progress as well. */
10567 if (devType == DeviceType_HardDisk)
10568 {
10569 if (pMedium == NULL)
10570 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10571 aWeight); // weight
10572 else
10573 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10574 pMedium->i_getBase()->i_getName().c_str()).raw(),
10575 aWeight); // weight
10576 }
10577
10578 mMediaData->mAttachments.push_back(pAtt);
10579 continue;
10580 }
10581
10582 /* need a diff */
10583 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10584 pMedium->i_getBase()->i_getName().c_str()).raw(),
10585 aWeight); // weight
10586
10587 Utf8Str strFullSnapshotFolder;
10588 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10589
10590 ComObjPtr<Medium> diff;
10591 diff.createObject();
10592 // store the diff in the same registry as the parent
10593 // (this cannot fail here because we can't create implicit diffs for
10594 // unregistered images)
10595 Guid uuidRegistryParent;
10596 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10597 Assert(fInRegistry); NOREF(fInRegistry);
10598 rc = diff->init(mParent,
10599 pMedium->i_getPreferredDiffFormat(),
10600 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10601 uuidRegistryParent);
10602 if (FAILED(rc)) throw rc;
10603
10604 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10605 * the push_back? Looks like we're going to release medium with the
10606 * wrong kind of lock (general issue with if we fail anywhere at all)
10607 * and an orphaned VDI in the snapshots folder. */
10608
10609 /* update the appropriate lock list */
10610 MediumLockList *pMediumLockList;
10611 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10612 AssertComRCThrowRC(rc);
10613 if (aOnline)
10614 {
10615 alock.release();
10616 /* The currently attached medium will be read-only, change
10617 * the lock type to read. */
10618 rc = pMediumLockList->Update(pMedium, false);
10619 alock.acquire();
10620 AssertComRCThrowRC(rc);
10621 }
10622
10623 /* release the locks before the potentially lengthy operation */
10624 alock.release();
10625 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10626 pMediumLockList,
10627 NULL /* aProgress */,
10628 true /* aWait */);
10629 alock.acquire();
10630 if (FAILED(rc)) throw rc;
10631
10632 /* actual lock list update is done in Medium::commitMedia */
10633
10634 rc = diff->i_addBackReference(mData->mUuid);
10635 AssertComRCThrowRC(rc);
10636
10637 /* add a new attachment */
10638 ComObjPtr<MediumAttachment> attachment;
10639 attachment.createObject();
10640 rc = attachment->init(this,
10641 diff,
10642 pAtt->i_getControllerName(),
10643 pAtt->i_getPort(),
10644 pAtt->i_getDevice(),
10645 DeviceType_HardDisk,
10646 true /* aImplicit */,
10647 false /* aPassthrough */,
10648 false /* aTempEject */,
10649 pAtt->i_getNonRotational(),
10650 pAtt->i_getDiscard(),
10651 pAtt->i_getHotPluggable(),
10652 pAtt->i_getBandwidthGroup());
10653 if (FAILED(rc)) throw rc;
10654
10655 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10656 AssertComRCThrowRC(rc);
10657 mMediaData->mAttachments.push_back(attachment);
10658 }
10659 }
10660 catch (HRESULT aRC) { rc = aRC; }
10661
10662 /* unlock all hard disks we locked when there is no VM */
10663 if (!aOnline)
10664 {
10665 ErrorInfoKeeper eik;
10666
10667 HRESULT rc1 = lockedMediaMap->Clear();
10668 AssertComRC(rc1);
10669 }
10670
10671 return rc;
10672}
10673
10674/**
10675 * Deletes implicit differencing hard disks created either by
10676 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10677 *
10678 * Note that to delete hard disks created by #AttachDevice() this method is
10679 * called from #fixupMedia() when the changes are rolled back.
10680 *
10681 * @note Locks this object and the media tree for writing.
10682 */
10683HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10684{
10685 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10686
10687 AutoCaller autoCaller(this);
10688 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10689
10690 AutoMultiWriteLock2 alock(this->lockHandle(),
10691 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10692
10693 /* We absolutely must have backed up state. */
10694 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10695
10696 /* Check if there are any implicitly created diff images. */
10697 bool fImplicitDiffs = false;
10698 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10699 it != mMediaData->mAttachments.end();
10700 ++it)
10701 {
10702 const ComObjPtr<MediumAttachment> &pAtt = *it;
10703 if (pAtt->i_isImplicit())
10704 {
10705 fImplicitDiffs = true;
10706 break;
10707 }
10708 }
10709 /* If there is nothing to do, leave early. This saves lots of image locking
10710 * effort. It also avoids a MachineStateChanged event without real reason.
10711 * This is important e.g. when loading a VM config, because there should be
10712 * no events. Otherwise API clients can become thoroughly confused for
10713 * inaccessible VMs (the code for loading VM configs uses this method for
10714 * cleanup if the config makes no sense), as they take such events as an
10715 * indication that the VM is alive, and they would force the VM config to
10716 * be reread, leading to an endless loop. */
10717 if (!fImplicitDiffs)
10718 return S_OK;
10719
10720 HRESULT rc = S_OK;
10721 MachineState_T oldState = mData->mMachineState;
10722
10723 /* will release the lock before the potentially lengthy operation,
10724 * so protect with the special state (unless already protected) */
10725 if ( oldState != MachineState_Saving
10726 && oldState != MachineState_LiveSnapshotting
10727 && oldState != MachineState_RestoringSnapshot
10728 && oldState != MachineState_DeletingSnapshot
10729 && oldState != MachineState_DeletingSnapshotOnline
10730 && oldState != MachineState_DeletingSnapshotPaused
10731 )
10732 i_setMachineState(MachineState_SettingUp);
10733
10734 // use appropriate locked media map (online or offline)
10735 MediumLockListMap lockedMediaOffline;
10736 MediumLockListMap *lockedMediaMap;
10737 if (aOnline)
10738 lockedMediaMap = &mData->mSession.mLockedMedia;
10739 else
10740 lockedMediaMap = &lockedMediaOffline;
10741
10742 try
10743 {
10744 if (!aOnline)
10745 {
10746 /* lock all attached hard disks early to detect "in use"
10747 * situations before deleting actual diffs */
10748 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10749 it != mMediaData->mAttachments.end();
10750 ++it)
10751 {
10752 MediumAttachment* pAtt = *it;
10753 if (pAtt->i_getType() == DeviceType_HardDisk)
10754 {
10755 Medium* pMedium = pAtt->i_getMedium();
10756 Assert(pMedium);
10757
10758 MediumLockList *pMediumLockList(new MediumLockList());
10759 alock.release();
10760 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10761 false /* fMediumLockWrite */,
10762 NULL,
10763 *pMediumLockList);
10764 alock.acquire();
10765
10766 if (FAILED(rc))
10767 {
10768 delete pMediumLockList;
10769 throw rc;
10770 }
10771
10772 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10773 if (FAILED(rc))
10774 throw rc;
10775 }
10776 }
10777
10778 if (FAILED(rc))
10779 throw rc;
10780 } // end of offline
10781
10782 /* Lock lists are now up to date and include implicitly created media */
10783
10784 /* Go through remembered attachments and delete all implicitly created
10785 * diffs and fix up the attachment information */
10786 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10787 MediaData::AttachmentList implicitAtts;
10788 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10789 it != mMediaData->mAttachments.end();
10790 ++it)
10791 {
10792 ComObjPtr<MediumAttachment> pAtt = *it;
10793 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10794 if (pMedium.isNull())
10795 continue;
10796
10797 // Implicit attachments go on the list for deletion and back references are removed.
10798 if (pAtt->i_isImplicit())
10799 {
10800 /* Deassociate and mark for deletion */
10801 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10802 rc = pMedium->i_removeBackReference(mData->mUuid);
10803 if (FAILED(rc))
10804 throw rc;
10805 implicitAtts.push_back(pAtt);
10806 continue;
10807 }
10808
10809 /* Was this medium attached before? */
10810 if (!i_findAttachment(oldAtts, pMedium))
10811 {
10812 /* no: de-associate */
10813 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10814 rc = pMedium->i_removeBackReference(mData->mUuid);
10815 if (FAILED(rc))
10816 throw rc;
10817 continue;
10818 }
10819 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10820 }
10821
10822 /* If there are implicit attachments to delete, throw away the lock
10823 * map contents (which will unlock all media) since the medium
10824 * attachments will be rolled back. Below we need to completely
10825 * recreate the lock map anyway since it is infinitely complex to
10826 * do this incrementally (would need reconstructing each attachment
10827 * change, which would be extremely hairy). */
10828 if (implicitAtts.size() != 0)
10829 {
10830 ErrorInfoKeeper eik;
10831
10832 HRESULT rc1 = lockedMediaMap->Clear();
10833 AssertComRC(rc1);
10834 }
10835
10836 /* rollback hard disk changes */
10837 mMediaData.rollback();
10838
10839 MultiResult mrc(S_OK);
10840
10841 // Delete unused implicit diffs.
10842 if (implicitAtts.size() != 0)
10843 {
10844 alock.release();
10845
10846 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10847 {
10848 // Remove medium associated with this attachment.
10849 ComObjPtr<MediumAttachment> pAtt = *it;
10850 Assert(pAtt);
10851 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10852 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10853 Assert(pMedium);
10854
10855 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10856 // continue on delete failure, just collect error messages
10857 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10858 pMedium->i_getLocationFull().c_str() ));
10859 mrc = rc;
10860 }
10861
10862 alock.acquire();
10863
10864 /* if there is a VM recreate media lock map as mentioned above,
10865 * otherwise it is a waste of time and we leave things unlocked */
10866 if (aOnline)
10867 {
10868 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10869 /* must never be NULL, but better safe than sorry */
10870 if (!pMachine.isNull())
10871 {
10872 alock.release();
10873 rc = mData->mSession.mMachine->lockMedia();
10874 alock.acquire();
10875 if (FAILED(rc))
10876 throw rc;
10877 }
10878 }
10879 }
10880 }
10881 catch (HRESULT aRC) {rc = aRC;}
10882
10883 if (mData->mMachineState == MachineState_SettingUp)
10884 i_setMachineState(oldState);
10885
10886 /* unlock all hard disks we locked when there is no VM */
10887 if (!aOnline)
10888 {
10889 ErrorInfoKeeper eik;
10890
10891 HRESULT rc1 = lockedMediaMap->Clear();
10892 AssertComRC(rc1);
10893 }
10894
10895 return rc;
10896}
10897
10898
10899/**
10900 * Looks through the given list of media attachments for one with the given parameters
10901 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10902 * can be searched as well if needed.
10903 *
10904 * @param list
10905 * @param aControllerName
10906 * @param aControllerPort
10907 * @param aDevice
10908 * @return
10909 */
10910MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10911 IN_BSTR aControllerName,
10912 LONG aControllerPort,
10913 LONG aDevice)
10914{
10915 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10916 {
10917 MediumAttachment *pAttach = *it;
10918 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10919 return pAttach;
10920 }
10921
10922 return NULL;
10923}
10924
10925/**
10926 * Looks through the given list of media attachments for one with the given parameters
10927 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10928 * can be searched as well if needed.
10929 *
10930 * @param list
10931 * @param aControllerName
10932 * @param aControllerPort
10933 * @param aDevice
10934 * @return
10935 */
10936MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10937 ComObjPtr<Medium> pMedium)
10938{
10939 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10940 {
10941 MediumAttachment *pAttach = *it;
10942 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10943 if (pMediumThis == pMedium)
10944 return pAttach;
10945 }
10946
10947 return NULL;
10948}
10949
10950/**
10951 * Looks through the given list of media attachments for one with the given parameters
10952 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10953 * can be searched as well if needed.
10954 *
10955 * @param list
10956 * @param aControllerName
10957 * @param aControllerPort
10958 * @param aDevice
10959 * @return
10960 */
10961MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10962 Guid &id)
10963{
10964 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10965 {
10966 MediumAttachment *pAttach = *it;
10967 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10968 if (pMediumThis->i_getId() == id)
10969 return pAttach;
10970 }
10971
10972 return NULL;
10973}
10974
10975/**
10976 * Main implementation for Machine::DetachDevice. This also gets called
10977 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10978 *
10979 * @param pAttach Medium attachment to detach.
10980 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10981 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10982 * SnapshotMachine, and this must be its snapshot.
10983 * @return
10984 */
10985HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10986 AutoWriteLock &writeLock,
10987 Snapshot *pSnapshot)
10988{
10989 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10990 DeviceType_T mediumType = pAttach->i_getType();
10991
10992 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10993
10994 if (pAttach->i_isImplicit())
10995 {
10996 /* attempt to implicitly delete the implicitly created diff */
10997
10998 /// @todo move the implicit flag from MediumAttachment to Medium
10999 /// and forbid any hard disk operation when it is implicit. Or maybe
11000 /// a special media state for it to make it even more simple.
11001
11002 Assert(mMediaData.isBackedUp());
11003
11004 /* will release the lock before the potentially lengthy operation, so
11005 * protect with the special state */
11006 MachineState_T oldState = mData->mMachineState;
11007 i_setMachineState(MachineState_SettingUp);
11008
11009 writeLock.release();
11010
11011 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11012 true /*aWait*/);
11013
11014 writeLock.acquire();
11015
11016 i_setMachineState(oldState);
11017
11018 if (FAILED(rc)) return rc;
11019 }
11020
11021 i_setModified(IsModified_Storage);
11022 mMediaData.backup();
11023 mMediaData->mAttachments.remove(pAttach);
11024
11025 if (!oldmedium.isNull())
11026 {
11027 // if this is from a snapshot, do not defer detachment to commitMedia()
11028 if (pSnapshot)
11029 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11030 // else if non-hard disk media, do not defer detachment to commitMedia() either
11031 else if (mediumType != DeviceType_HardDisk)
11032 oldmedium->i_removeBackReference(mData->mUuid);
11033 }
11034
11035 return S_OK;
11036}
11037
11038/**
11039 * Goes thru all media of the given list and
11040 *
11041 * 1) calls i_detachDevice() on each of them for this machine and
11042 * 2) adds all Medium objects found in the process to the given list,
11043 * depending on cleanupMode.
11044 *
11045 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11046 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11047 * media to the list.
11048 *
11049 * This gets called from Machine::Unregister, both for the actual Machine and
11050 * the SnapshotMachine objects that might be found in the snapshots.
11051 *
11052 * Requires caller and locking. The machine lock must be passed in because it
11053 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11054 *
11055 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11056 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11057 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11058 * Full, then all media get added;
11059 * otherwise no media get added.
11060 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11061 * @return
11062 */
11063HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11064 Snapshot *pSnapshot,
11065 CleanupMode_T cleanupMode,
11066 MediaList &llMedia)
11067{
11068 Assert(isWriteLockOnCurrentThread());
11069
11070 HRESULT rc;
11071
11072 // make a temporary list because i_detachDevice invalidates iterators into
11073 // mMediaData->mAttachments
11074 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11075
11076 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11077 {
11078 ComObjPtr<MediumAttachment> &pAttach = *it;
11079 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11080
11081 if (!pMedium.isNull())
11082 {
11083 AutoCaller mac(pMedium);
11084 if (FAILED(mac.rc())) return mac.rc();
11085 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11086 DeviceType_T devType = pMedium->i_getDeviceType();
11087 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11088 && devType == DeviceType_HardDisk)
11089 || (cleanupMode == CleanupMode_Full)
11090 )
11091 {
11092 llMedia.push_back(pMedium);
11093 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11094 /*
11095 * Search for medias which are not attached to any machine, but
11096 * in the chain to an attached disk. Mediums are only consided
11097 * if they are:
11098 * - have only one child
11099 * - no references to any machines
11100 * - are of normal medium type
11101 */
11102 while (!pParent.isNull())
11103 {
11104 AutoCaller mac1(pParent);
11105 if (FAILED(mac1.rc())) return mac1.rc();
11106 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11107 if (pParent->i_getChildren().size() == 1)
11108 {
11109 if ( pParent->i_getMachineBackRefCount() == 0
11110 && pParent->i_getType() == MediumType_Normal
11111 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11112 llMedia.push_back(pParent);
11113 }
11114 else
11115 break;
11116 pParent = pParent->i_getParent();
11117 }
11118 }
11119 }
11120
11121 // real machine: then we need to use the proper method
11122 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11123
11124 if (FAILED(rc))
11125 return rc;
11126 }
11127
11128 return S_OK;
11129}
11130
11131/**
11132 * Perform deferred hard disk detachments.
11133 *
11134 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11135 * backed up).
11136 *
11137 * If @a aOnline is @c true then this method will also unlock the old hard disks
11138 * for which the new implicit diffs were created and will lock these new diffs for
11139 * writing.
11140 *
11141 * @param aOnline Whether the VM was online prior to this operation.
11142 *
11143 * @note Locks this object for writing!
11144 */
11145void Machine::i_commitMedia(bool aOnline /*= false*/)
11146{
11147 AutoCaller autoCaller(this);
11148 AssertComRCReturnVoid(autoCaller.rc());
11149
11150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11151
11152 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11153
11154 HRESULT rc = S_OK;
11155
11156 /* no attach/detach operations -- nothing to do */
11157 if (!mMediaData.isBackedUp())
11158 return;
11159
11160 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11161 bool fMediaNeedsLocking = false;
11162
11163 /* enumerate new attachments */
11164 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11165 it != mMediaData->mAttachments.end();
11166 ++it)
11167 {
11168 MediumAttachment *pAttach = *it;
11169
11170 pAttach->i_commit();
11171
11172 Medium* pMedium = pAttach->i_getMedium();
11173 bool fImplicit = pAttach->i_isImplicit();
11174
11175 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11176 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11177 fImplicit));
11178
11179 /** @todo convert all this Machine-based voodoo to MediumAttachment
11180 * based commit logic. */
11181 if (fImplicit)
11182 {
11183 /* convert implicit attachment to normal */
11184 pAttach->i_setImplicit(false);
11185
11186 if ( aOnline
11187 && pMedium
11188 && pAttach->i_getType() == DeviceType_HardDisk
11189 )
11190 {
11191 ComObjPtr<Medium> parent = pMedium->i_getParent();
11192 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11193
11194 /* update the appropriate lock list */
11195 MediumLockList *pMediumLockList;
11196 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11197 AssertComRC(rc);
11198 if (pMediumLockList)
11199 {
11200 /* unlock if there's a need to change the locking */
11201 if (!fMediaNeedsLocking)
11202 {
11203 rc = mData->mSession.mLockedMedia.Unlock();
11204 AssertComRC(rc);
11205 fMediaNeedsLocking = true;
11206 }
11207 rc = pMediumLockList->Update(parent, false);
11208 AssertComRC(rc);
11209 rc = pMediumLockList->Append(pMedium, true);
11210 AssertComRC(rc);
11211 }
11212 }
11213
11214 continue;
11215 }
11216
11217 if (pMedium)
11218 {
11219 /* was this medium attached before? */
11220 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11221 {
11222 MediumAttachment *pOldAttach = *oldIt;
11223 if (pOldAttach->i_getMedium() == pMedium)
11224 {
11225 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11226
11227 /* yes: remove from old to avoid de-association */
11228 oldAtts.erase(oldIt);
11229 break;
11230 }
11231 }
11232 }
11233 }
11234
11235 /* enumerate remaining old attachments and de-associate from the
11236 * current machine state */
11237 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11238 {
11239 MediumAttachment *pAttach = *it;
11240 Medium* pMedium = pAttach->i_getMedium();
11241
11242 /* Detach only hard disks, since DVD/floppy media is detached
11243 * instantly in MountMedium. */
11244 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11245 {
11246 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11247
11248 /* now de-associate from the current machine state */
11249 rc = pMedium->i_removeBackReference(mData->mUuid);
11250 AssertComRC(rc);
11251
11252 if (aOnline)
11253 {
11254 /* unlock since medium is not used anymore */
11255 MediumLockList *pMediumLockList;
11256 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11257 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11258 {
11259 /* this happens for online snapshots, there the attachment
11260 * is changing, but only to a diff image created under
11261 * the old one, so there is no separate lock list */
11262 Assert(!pMediumLockList);
11263 }
11264 else
11265 {
11266 AssertComRC(rc);
11267 if (pMediumLockList)
11268 {
11269 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11270 AssertComRC(rc);
11271 }
11272 }
11273 }
11274 }
11275 }
11276
11277 /* take media locks again so that the locking state is consistent */
11278 if (fMediaNeedsLocking)
11279 {
11280 Assert(aOnline);
11281 rc = mData->mSession.mLockedMedia.Lock();
11282 AssertComRC(rc);
11283 }
11284
11285 /* commit the hard disk changes */
11286 mMediaData.commit();
11287
11288 if (i_isSessionMachine())
11289 {
11290 /*
11291 * Update the parent machine to point to the new owner.
11292 * This is necessary because the stored parent will point to the
11293 * session machine otherwise and cause crashes or errors later
11294 * when the session machine gets invalid.
11295 */
11296 /** @todo Change the MediumAttachment class to behave like any other
11297 * class in this regard by creating peer MediumAttachment
11298 * objects for session machines and share the data with the peer
11299 * machine.
11300 */
11301 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11302 it != mMediaData->mAttachments.end();
11303 ++it)
11304 (*it)->i_updateParentMachine(mPeer);
11305
11306 /* attach new data to the primary machine and reshare it */
11307 mPeer->mMediaData.attach(mMediaData);
11308 }
11309
11310 return;
11311}
11312
11313/**
11314 * Perform deferred deletion of implicitly created diffs.
11315 *
11316 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11317 * backed up).
11318 *
11319 * @note Locks this object for writing!
11320 */
11321void Machine::i_rollbackMedia()
11322{
11323 AutoCaller autoCaller(this);
11324 AssertComRCReturnVoid(autoCaller.rc());
11325
11326 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11327 LogFlowThisFunc(("Entering rollbackMedia\n"));
11328
11329 HRESULT rc = S_OK;
11330
11331 /* no attach/detach operations -- nothing to do */
11332 if (!mMediaData.isBackedUp())
11333 return;
11334
11335 /* enumerate new attachments */
11336 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11337 it != mMediaData->mAttachments.end();
11338 ++it)
11339 {
11340 MediumAttachment *pAttach = *it;
11341 /* Fix up the backrefs for DVD/floppy media. */
11342 if (pAttach->i_getType() != DeviceType_HardDisk)
11343 {
11344 Medium* pMedium = pAttach->i_getMedium();
11345 if (pMedium)
11346 {
11347 rc = pMedium->i_removeBackReference(mData->mUuid);
11348 AssertComRC(rc);
11349 }
11350 }
11351
11352 (*it)->i_rollback();
11353
11354 pAttach = *it;
11355 /* Fix up the backrefs for DVD/floppy media. */
11356 if (pAttach->i_getType() != DeviceType_HardDisk)
11357 {
11358 Medium* pMedium = pAttach->i_getMedium();
11359 if (pMedium)
11360 {
11361 rc = pMedium->i_addBackReference(mData->mUuid);
11362 AssertComRC(rc);
11363 }
11364 }
11365 }
11366
11367 /** @todo convert all this Machine-based voodoo to MediumAttachment
11368 * based rollback logic. */
11369 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11370
11371 return;
11372}
11373
11374/**
11375 * Returns true if the settings file is located in the directory named exactly
11376 * as the machine; this means, among other things, that the machine directory
11377 * should be auto-renamed.
11378 *
11379 * @param aSettingsDir if not NULL, the full machine settings file directory
11380 * name will be assigned there.
11381 *
11382 * @note Doesn't lock anything.
11383 * @note Not thread safe (must be called from this object's lock).
11384 */
11385bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11386{
11387 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11388 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11389 if (aSettingsDir)
11390 *aSettingsDir = strMachineDirName;
11391 strMachineDirName.stripPath(); // vmname
11392 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11393 strConfigFileOnly.stripPath() // vmname.vbox
11394 .stripSuffix(); // vmname
11395 /** @todo hack, make somehow use of ComposeMachineFilename */
11396 if (mUserData->s.fDirectoryIncludesUUID)
11397 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11398
11399 AssertReturn(!strMachineDirName.isEmpty(), false);
11400 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11401
11402 return strMachineDirName == strConfigFileOnly;
11403}
11404
11405/**
11406 * Discards all changes to machine settings.
11407 *
11408 * @param aNotify Whether to notify the direct session about changes or not.
11409 *
11410 * @note Locks objects for writing!
11411 */
11412void Machine::i_rollback(bool aNotify)
11413{
11414 AutoCaller autoCaller(this);
11415 AssertComRCReturn(autoCaller.rc(), (void)0);
11416
11417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11418
11419 if (!mStorageControllers.isNull())
11420 {
11421 if (mStorageControllers.isBackedUp())
11422 {
11423 /* unitialize all new devices (absent in the backed up list). */
11424 StorageControllerList::const_iterator it = mStorageControllers->begin();
11425 StorageControllerList *backedList = mStorageControllers.backedUpData();
11426 while (it != mStorageControllers->end())
11427 {
11428 if ( std::find(backedList->begin(), backedList->end(), *it)
11429 == backedList->end()
11430 )
11431 {
11432 (*it)->uninit();
11433 }
11434 ++it;
11435 }
11436
11437 /* restore the list */
11438 mStorageControllers.rollback();
11439 }
11440
11441 /* rollback any changes to devices after restoring the list */
11442 if (mData->flModifications & IsModified_Storage)
11443 {
11444 StorageControllerList::const_iterator it = mStorageControllers->begin();
11445 while (it != mStorageControllers->end())
11446 {
11447 (*it)->i_rollback();
11448 ++it;
11449 }
11450 }
11451 }
11452
11453 if (!mUSBControllers.isNull())
11454 {
11455 if (mUSBControllers.isBackedUp())
11456 {
11457 /* unitialize all new devices (absent in the backed up list). */
11458 USBControllerList::const_iterator it = mUSBControllers->begin();
11459 USBControllerList *backedList = mUSBControllers.backedUpData();
11460 while (it != mUSBControllers->end())
11461 {
11462 if ( std::find(backedList->begin(), backedList->end(), *it)
11463 == backedList->end()
11464 )
11465 {
11466 (*it)->uninit();
11467 }
11468 ++it;
11469 }
11470
11471 /* restore the list */
11472 mUSBControllers.rollback();
11473 }
11474
11475 /* rollback any changes to devices after restoring the list */
11476 if (mData->flModifications & IsModified_USB)
11477 {
11478 USBControllerList::const_iterator it = mUSBControllers->begin();
11479 while (it != mUSBControllers->end())
11480 {
11481 (*it)->i_rollback();
11482 ++it;
11483 }
11484 }
11485 }
11486
11487 mUserData.rollback();
11488
11489 mHWData.rollback();
11490
11491 if (mData->flModifications & IsModified_Storage)
11492 i_rollbackMedia();
11493
11494 if (mBIOSSettings)
11495 mBIOSSettings->i_rollback();
11496
11497 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11498 mVRDEServer->i_rollback();
11499
11500 if (mAudioAdapter)
11501 mAudioAdapter->i_rollback();
11502
11503 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11504 mUSBDeviceFilters->i_rollback();
11505
11506 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11507 mBandwidthControl->i_rollback();
11508
11509 if (!mHWData.isNull())
11510 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11511 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11512 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11513 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11514
11515 if (mData->flModifications & IsModified_NetworkAdapters)
11516 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11517 if ( mNetworkAdapters[slot]
11518 && mNetworkAdapters[slot]->i_isModified())
11519 {
11520 mNetworkAdapters[slot]->i_rollback();
11521 networkAdapters[slot] = mNetworkAdapters[slot];
11522 }
11523
11524 if (mData->flModifications & IsModified_SerialPorts)
11525 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11526 if ( mSerialPorts[slot]
11527 && mSerialPorts[slot]->i_isModified())
11528 {
11529 mSerialPorts[slot]->i_rollback();
11530 serialPorts[slot] = mSerialPorts[slot];
11531 }
11532
11533 if (mData->flModifications & IsModified_ParallelPorts)
11534 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11535 if ( mParallelPorts[slot]
11536 && mParallelPorts[slot]->i_isModified())
11537 {
11538 mParallelPorts[slot]->i_rollback();
11539 parallelPorts[slot] = mParallelPorts[slot];
11540 }
11541
11542 if (aNotify)
11543 {
11544 /* inform the direct session about changes */
11545
11546 ComObjPtr<Machine> that = this;
11547 uint32_t flModifications = mData->flModifications;
11548 alock.release();
11549
11550 if (flModifications & IsModified_SharedFolders)
11551 that->i_onSharedFolderChange();
11552
11553 if (flModifications & IsModified_VRDEServer)
11554 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11555 if (flModifications & IsModified_USB)
11556 that->i_onUSBControllerChange();
11557
11558 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11559 if (networkAdapters[slot])
11560 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11561 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11562 if (serialPorts[slot])
11563 that->i_onSerialPortChange(serialPorts[slot]);
11564 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11565 if (parallelPorts[slot])
11566 that->i_onParallelPortChange(parallelPorts[slot]);
11567
11568 if (flModifications & IsModified_Storage)
11569 that->i_onStorageControllerChange();
11570
11571#if 0
11572 if (flModifications & IsModified_BandwidthControl)
11573 that->onBandwidthControlChange();
11574#endif
11575 }
11576}
11577
11578/**
11579 * Commits all the changes to machine settings.
11580 *
11581 * Note that this operation is supposed to never fail.
11582 *
11583 * @note Locks this object and children for writing.
11584 */
11585void Machine::i_commit()
11586{
11587 AutoCaller autoCaller(this);
11588 AssertComRCReturnVoid(autoCaller.rc());
11589
11590 AutoCaller peerCaller(mPeer);
11591 AssertComRCReturnVoid(peerCaller.rc());
11592
11593 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11594
11595 /*
11596 * use safe commit to ensure Snapshot machines (that share mUserData)
11597 * will still refer to a valid memory location
11598 */
11599 mUserData.commitCopy();
11600
11601 mHWData.commit();
11602
11603 if (mMediaData.isBackedUp())
11604 i_commitMedia(Global::IsOnline(mData->mMachineState));
11605
11606 mBIOSSettings->i_commit();
11607 mVRDEServer->i_commit();
11608 mAudioAdapter->i_commit();
11609 mUSBDeviceFilters->i_commit();
11610 mBandwidthControl->i_commit();
11611
11612 /* Since mNetworkAdapters is a list which might have been changed (resized)
11613 * without using the Backupable<> template we need to handle the copying
11614 * of the list entries manually, including the creation of peers for the
11615 * new objects. */
11616 bool commitNetworkAdapters = false;
11617 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11618 if (mPeer)
11619 {
11620 /* commit everything, even the ones which will go away */
11621 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11622 mNetworkAdapters[slot]->i_commit();
11623 /* copy over the new entries, creating a peer and uninit the original */
11624 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11625 for (size_t slot = 0; slot < newSize; slot++)
11626 {
11627 /* look if this adapter has a peer device */
11628 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11629 if (!peer)
11630 {
11631 /* no peer means the adapter is a newly created one;
11632 * create a peer owning data this data share it with */
11633 peer.createObject();
11634 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11635 }
11636 mPeer->mNetworkAdapters[slot] = peer;
11637 }
11638 /* uninit any no longer needed network adapters */
11639 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11640 mNetworkAdapters[slot]->uninit();
11641 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11642 {
11643 if (mPeer->mNetworkAdapters[slot])
11644 mPeer->mNetworkAdapters[slot]->uninit();
11645 }
11646 /* Keep the original network adapter count until this point, so that
11647 * discarding a chipset type change will not lose settings. */
11648 mNetworkAdapters.resize(newSize);
11649 mPeer->mNetworkAdapters.resize(newSize);
11650 }
11651 else
11652 {
11653 /* we have no peer (our parent is the newly created machine);
11654 * just commit changes to the network adapters */
11655 commitNetworkAdapters = true;
11656 }
11657 if (commitNetworkAdapters)
11658 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11659 mNetworkAdapters[slot]->i_commit();
11660
11661 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11662 mSerialPorts[slot]->i_commit();
11663 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11664 mParallelPorts[slot]->i_commit();
11665
11666 bool commitStorageControllers = false;
11667
11668 if (mStorageControllers.isBackedUp())
11669 {
11670 mStorageControllers.commit();
11671
11672 if (mPeer)
11673 {
11674 /* Commit all changes to new controllers (this will reshare data with
11675 * peers for those who have peers) */
11676 StorageControllerList *newList = new StorageControllerList();
11677 StorageControllerList::const_iterator it = mStorageControllers->begin();
11678 while (it != mStorageControllers->end())
11679 {
11680 (*it)->i_commit();
11681
11682 /* look if this controller has a peer device */
11683 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11684 if (!peer)
11685 {
11686 /* no peer means the device is a newly created one;
11687 * create a peer owning data this device share it with */
11688 peer.createObject();
11689 peer->init(mPeer, *it, true /* aReshare */);
11690 }
11691 else
11692 {
11693 /* remove peer from the old list */
11694 mPeer->mStorageControllers->remove(peer);
11695 }
11696 /* and add it to the new list */
11697 newList->push_back(peer);
11698
11699 ++it;
11700 }
11701
11702 /* uninit old peer's controllers that are left */
11703 it = mPeer->mStorageControllers->begin();
11704 while (it != mPeer->mStorageControllers->end())
11705 {
11706 (*it)->uninit();
11707 ++it;
11708 }
11709
11710 /* attach new list of controllers to our peer */
11711 mPeer->mStorageControllers.attach(newList);
11712 }
11713 else
11714 {
11715 /* we have no peer (our parent is the newly created machine);
11716 * just commit changes to devices */
11717 commitStorageControllers = true;
11718 }
11719 }
11720 else
11721 {
11722 /* the list of controllers itself is not changed,
11723 * just commit changes to controllers themselves */
11724 commitStorageControllers = true;
11725 }
11726
11727 if (commitStorageControllers)
11728 {
11729 StorageControllerList::const_iterator it = mStorageControllers->begin();
11730 while (it != mStorageControllers->end())
11731 {
11732 (*it)->i_commit();
11733 ++it;
11734 }
11735 }
11736
11737 bool commitUSBControllers = false;
11738
11739 if (mUSBControllers.isBackedUp())
11740 {
11741 mUSBControllers.commit();
11742
11743 if (mPeer)
11744 {
11745 /* Commit all changes to new controllers (this will reshare data with
11746 * peers for those who have peers) */
11747 USBControllerList *newList = new USBControllerList();
11748 USBControllerList::const_iterator it = mUSBControllers->begin();
11749 while (it != mUSBControllers->end())
11750 {
11751 (*it)->i_commit();
11752
11753 /* look if this controller has a peer device */
11754 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11755 if (!peer)
11756 {
11757 /* no peer means the device is a newly created one;
11758 * create a peer owning data this device share it with */
11759 peer.createObject();
11760 peer->init(mPeer, *it, true /* aReshare */);
11761 }
11762 else
11763 {
11764 /* remove peer from the old list */
11765 mPeer->mUSBControllers->remove(peer);
11766 }
11767 /* and add it to the new list */
11768 newList->push_back(peer);
11769
11770 ++it;
11771 }
11772
11773 /* uninit old peer's controllers that are left */
11774 it = mPeer->mUSBControllers->begin();
11775 while (it != mPeer->mUSBControllers->end())
11776 {
11777 (*it)->uninit();
11778 ++it;
11779 }
11780
11781 /* attach new list of controllers to our peer */
11782 mPeer->mUSBControllers.attach(newList);
11783 }
11784 else
11785 {
11786 /* we have no peer (our parent is the newly created machine);
11787 * just commit changes to devices */
11788 commitUSBControllers = true;
11789 }
11790 }
11791 else
11792 {
11793 /* the list of controllers itself is not changed,
11794 * just commit changes to controllers themselves */
11795 commitUSBControllers = true;
11796 }
11797
11798 if (commitUSBControllers)
11799 {
11800 USBControllerList::const_iterator it = mUSBControllers->begin();
11801 while (it != mUSBControllers->end())
11802 {
11803 (*it)->i_commit();
11804 ++it;
11805 }
11806 }
11807
11808 if (i_isSessionMachine())
11809 {
11810 /* attach new data to the primary machine and reshare it */
11811 mPeer->mUserData.attach(mUserData);
11812 mPeer->mHWData.attach(mHWData);
11813 /* mMediaData is reshared by fixupMedia */
11814 // mPeer->mMediaData.attach(mMediaData);
11815 Assert(mPeer->mMediaData.data() == mMediaData.data());
11816 }
11817}
11818
11819/**
11820 * Copies all the hardware data from the given machine.
11821 *
11822 * Currently, only called when the VM is being restored from a snapshot. In
11823 * particular, this implies that the VM is not running during this method's
11824 * call.
11825 *
11826 * @note This method must be called from under this object's lock.
11827 *
11828 * @note This method doesn't call #commit(), so all data remains backed up and
11829 * unsaved.
11830 */
11831void Machine::i_copyFrom(Machine *aThat)
11832{
11833 AssertReturnVoid(!i_isSnapshotMachine());
11834 AssertReturnVoid(aThat->i_isSnapshotMachine());
11835
11836 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11837
11838 mHWData.assignCopy(aThat->mHWData);
11839
11840 // create copies of all shared folders (mHWData after attaching a copy
11841 // contains just references to original objects)
11842 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11843 it != mHWData->mSharedFolders.end();
11844 ++it)
11845 {
11846 ComObjPtr<SharedFolder> folder;
11847 folder.createObject();
11848 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11849 AssertComRC(rc);
11850 *it = folder;
11851 }
11852
11853 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11854 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11855 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11856 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11857 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11858
11859 /* create private copies of all controllers */
11860 mStorageControllers.backup();
11861 mStorageControllers->clear();
11862 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11863 it != aThat->mStorageControllers->end();
11864 ++it)
11865 {
11866 ComObjPtr<StorageController> ctrl;
11867 ctrl.createObject();
11868 ctrl->initCopy(this, *it);
11869 mStorageControllers->push_back(ctrl);
11870 }
11871
11872 /* create private copies of all USB controllers */
11873 mUSBControllers.backup();
11874 mUSBControllers->clear();
11875 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11876 it != aThat->mUSBControllers->end();
11877 ++it)
11878 {
11879 ComObjPtr<USBController> ctrl;
11880 ctrl.createObject();
11881 ctrl->initCopy(this, *it);
11882 mUSBControllers->push_back(ctrl);
11883 }
11884
11885 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11886 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11887 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11888 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11889 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11890 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11891 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11892}
11893
11894/**
11895 * Returns whether the given storage controller is hotplug capable.
11896 *
11897 * @returns true if the controller supports hotplugging
11898 * false otherwise.
11899 * @param enmCtrlType The controller type to check for.
11900 */
11901bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11902{
11903 ComPtr<ISystemProperties> systemProperties;
11904 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11905 if (FAILED(rc))
11906 return false;
11907
11908 BOOL aHotplugCapable = FALSE;
11909 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11910
11911 return RT_BOOL(aHotplugCapable);
11912}
11913
11914#ifdef VBOX_WITH_RESOURCE_USAGE_API
11915
11916void Machine::i_getDiskList(MediaList &list)
11917{
11918 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11919 it != mMediaData->mAttachments.end();
11920 ++it)
11921 {
11922 MediumAttachment* pAttach = *it;
11923 /* just in case */
11924 AssertStmt(pAttach, continue);
11925
11926 AutoCaller localAutoCallerA(pAttach);
11927 if (FAILED(localAutoCallerA.rc())) continue;
11928
11929 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11930
11931 if (pAttach->i_getType() == DeviceType_HardDisk)
11932 list.push_back(pAttach->i_getMedium());
11933 }
11934}
11935
11936void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11937{
11938 AssertReturnVoid(isWriteLockOnCurrentThread());
11939 AssertPtrReturnVoid(aCollector);
11940
11941 pm::CollectorHAL *hal = aCollector->getHAL();
11942 /* Create sub metrics */
11943 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11944 "Percentage of processor time spent in user mode by the VM process.");
11945 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11946 "Percentage of processor time spent in kernel mode by the VM process.");
11947 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11948 "Size of resident portion of VM process in memory.");
11949 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11950 "Actual size of all VM disks combined.");
11951 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11952 "Network receive rate.");
11953 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11954 "Network transmit rate.");
11955 /* Create and register base metrics */
11956 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11957 cpuLoadUser, cpuLoadKernel);
11958 aCollector->registerBaseMetric(cpuLoad);
11959 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11960 ramUsageUsed);
11961 aCollector->registerBaseMetric(ramUsage);
11962 MediaList disks;
11963 i_getDiskList(disks);
11964 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11965 diskUsageUsed);
11966 aCollector->registerBaseMetric(diskUsage);
11967
11968 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11969 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11970 new pm::AggregateAvg()));
11971 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11972 new pm::AggregateMin()));
11973 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11974 new pm::AggregateMax()));
11975 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11976 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11977 new pm::AggregateAvg()));
11978 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11979 new pm::AggregateMin()));
11980 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11981 new pm::AggregateMax()));
11982
11983 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11984 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11985 new pm::AggregateAvg()));
11986 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11987 new pm::AggregateMin()));
11988 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11989 new pm::AggregateMax()));
11990
11991 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11992 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11993 new pm::AggregateAvg()));
11994 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11995 new pm::AggregateMin()));
11996 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11997 new pm::AggregateMax()));
11998
11999
12000 /* Guest metrics collector */
12001 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12002 aCollector->registerGuest(mCollectorGuest);
12003 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12004 this, __PRETTY_FUNCTION__, mCollectorGuest));
12005
12006 /* Create sub metrics */
12007 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12008 "Percentage of processor time spent in user mode as seen by the guest.");
12009 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12010 "Percentage of processor time spent in kernel mode as seen by the guest.");
12011 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12012 "Percentage of processor time spent idling as seen by the guest.");
12013
12014 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12015 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12016 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12017 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12018 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12019 pm::SubMetric *guestMemCache = new pm::SubMetric(
12020 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12021
12022 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12023 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12024
12025 /* Create and register base metrics */
12026 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12027 machineNetRx, machineNetTx);
12028 aCollector->registerBaseMetric(machineNetRate);
12029
12030 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12031 guestLoadUser, guestLoadKernel, guestLoadIdle);
12032 aCollector->registerBaseMetric(guestCpuLoad);
12033
12034 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12035 guestMemTotal, guestMemFree,
12036 guestMemBalloon, guestMemShared,
12037 guestMemCache, guestPagedTotal);
12038 aCollector->registerBaseMetric(guestCpuMem);
12039
12040 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12041 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12042 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12043 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12044
12045 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12046 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12047 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12048 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12049
12050 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12051 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12052 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12053 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12054
12055 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12056 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12057 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12058 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12059
12060 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12061 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12062 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12063 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12064
12065 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12066 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12067 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12068 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12069
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12071 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12073 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12074
12075 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12076 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12077 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12078 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12079
12080 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12081 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12082 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12083 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12084
12085 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12086 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12087 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12088 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12089
12090 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12091 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12092 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12093 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12094}
12095
12096void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12097{
12098 AssertReturnVoid(isWriteLockOnCurrentThread());
12099
12100 if (aCollector)
12101 {
12102 aCollector->unregisterMetricsFor(aMachine);
12103 aCollector->unregisterBaseMetricsFor(aMachine);
12104 }
12105}
12106
12107#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12108
12109
12110////////////////////////////////////////////////////////////////////////////////
12111
12112DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12113
12114HRESULT SessionMachine::FinalConstruct()
12115{
12116 LogFlowThisFunc(("\n"));
12117
12118 mClientToken = NULL;
12119
12120 return BaseFinalConstruct();
12121}
12122
12123void SessionMachine::FinalRelease()
12124{
12125 LogFlowThisFunc(("\n"));
12126
12127 Assert(!mClientToken);
12128 /* paranoia, should not hang around any more */
12129 if (mClientToken)
12130 {
12131 delete mClientToken;
12132 mClientToken = NULL;
12133 }
12134
12135 uninit(Uninit::Unexpected);
12136
12137 BaseFinalRelease();
12138}
12139
12140/**
12141 * @note Must be called only by Machine::LockMachine() from its own write lock.
12142 */
12143HRESULT SessionMachine::init(Machine *aMachine)
12144{
12145 LogFlowThisFuncEnter();
12146 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12147
12148 AssertReturn(aMachine, E_INVALIDARG);
12149
12150 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12151
12152 /* Enclose the state transition NotReady->InInit->Ready */
12153 AutoInitSpan autoInitSpan(this);
12154 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12155
12156 HRESULT rc = S_OK;
12157
12158 /* create the machine client token */
12159 try
12160 {
12161 mClientToken = new ClientToken(aMachine, this);
12162 if (!mClientToken->isReady())
12163 {
12164 delete mClientToken;
12165 mClientToken = NULL;
12166 rc = E_FAIL;
12167 }
12168 }
12169 catch (std::bad_alloc &)
12170 {
12171 rc = E_OUTOFMEMORY;
12172 }
12173 if (FAILED(rc))
12174 return rc;
12175
12176 /* memorize the peer Machine */
12177 unconst(mPeer) = aMachine;
12178 /* share the parent pointer */
12179 unconst(mParent) = aMachine->mParent;
12180
12181 /* take the pointers to data to share */
12182 mData.share(aMachine->mData);
12183 mSSData.share(aMachine->mSSData);
12184
12185 mUserData.share(aMachine->mUserData);
12186 mHWData.share(aMachine->mHWData);
12187 mMediaData.share(aMachine->mMediaData);
12188
12189 mStorageControllers.allocate();
12190 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12191 it != aMachine->mStorageControllers->end();
12192 ++it)
12193 {
12194 ComObjPtr<StorageController> ctl;
12195 ctl.createObject();
12196 ctl->init(this, *it);
12197 mStorageControllers->push_back(ctl);
12198 }
12199
12200 mUSBControllers.allocate();
12201 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12202 it != aMachine->mUSBControllers->end();
12203 ++it)
12204 {
12205 ComObjPtr<USBController> ctl;
12206 ctl.createObject();
12207 ctl->init(this, *it);
12208 mUSBControllers->push_back(ctl);
12209 }
12210
12211 unconst(mBIOSSettings).createObject();
12212 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12213 /* create another VRDEServer object that will be mutable */
12214 unconst(mVRDEServer).createObject();
12215 mVRDEServer->init(this, aMachine->mVRDEServer);
12216 /* create another audio adapter object that will be mutable */
12217 unconst(mAudioAdapter).createObject();
12218 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12219 /* create a list of serial ports that will be mutable */
12220 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12221 {
12222 unconst(mSerialPorts[slot]).createObject();
12223 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12224 }
12225 /* create a list of parallel ports that will be mutable */
12226 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12227 {
12228 unconst(mParallelPorts[slot]).createObject();
12229 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12230 }
12231
12232 /* create another USB device filters object that will be mutable */
12233 unconst(mUSBDeviceFilters).createObject();
12234 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12235
12236 /* create a list of network adapters that will be mutable */
12237 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12238 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12239 {
12240 unconst(mNetworkAdapters[slot]).createObject();
12241 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12242 }
12243
12244 /* create another bandwidth control object that will be mutable */
12245 unconst(mBandwidthControl).createObject();
12246 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12247
12248 /* default is to delete saved state on Saved -> PoweredOff transition */
12249 mRemoveSavedState = true;
12250
12251 /* Confirm a successful initialization when it's the case */
12252 autoInitSpan.setSucceeded();
12253
12254 miNATNetworksStarted = 0;
12255
12256 LogFlowThisFuncLeave();
12257 return rc;
12258}
12259
12260/**
12261 * Uninitializes this session object. If the reason is other than
12262 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12263 * or the client watcher code.
12264 *
12265 * @param aReason uninitialization reason
12266 *
12267 * @note Locks mParent + this object for writing.
12268 */
12269void SessionMachine::uninit(Uninit::Reason aReason)
12270{
12271 LogFlowThisFuncEnter();
12272 LogFlowThisFunc(("reason=%d\n", aReason));
12273
12274 /*
12275 * Strongly reference ourselves to prevent this object deletion after
12276 * mData->mSession.mMachine.setNull() below (which can release the last
12277 * reference and call the destructor). Important: this must be done before
12278 * accessing any members (and before AutoUninitSpan that does it as well).
12279 * This self reference will be released as the very last step on return.
12280 */
12281 ComObjPtr<SessionMachine> selfRef = this;
12282
12283 /* Enclose the state transition Ready->InUninit->NotReady */
12284 AutoUninitSpan autoUninitSpan(this);
12285 if (autoUninitSpan.uninitDone())
12286 {
12287 LogFlowThisFunc(("Already uninitialized\n"));
12288 LogFlowThisFuncLeave();
12289 return;
12290 }
12291
12292 if (autoUninitSpan.initFailed())
12293 {
12294 /* We've been called by init() because it's failed. It's not really
12295 * necessary (nor it's safe) to perform the regular uninit sequence
12296 * below, the following is enough.
12297 */
12298 LogFlowThisFunc(("Initialization failed.\n"));
12299 /* destroy the machine client token */
12300 if (mClientToken)
12301 {
12302 delete mClientToken;
12303 mClientToken = NULL;
12304 }
12305 uninitDataAndChildObjects();
12306 mData.free();
12307 unconst(mParent) = NULL;
12308 unconst(mPeer) = NULL;
12309 LogFlowThisFuncLeave();
12310 return;
12311 }
12312
12313 MachineState_T lastState;
12314 {
12315 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12316 lastState = mData->mMachineState;
12317 }
12318 NOREF(lastState);
12319
12320#ifdef VBOX_WITH_USB
12321 // release all captured USB devices, but do this before requesting the locks below
12322 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12323 {
12324 /* Console::captureUSBDevices() is called in the VM process only after
12325 * setting the machine state to Starting or Restoring.
12326 * Console::detachAllUSBDevices() will be called upon successful
12327 * termination. So, we need to release USB devices only if there was
12328 * an abnormal termination of a running VM.
12329 *
12330 * This is identical to SessionMachine::DetachAllUSBDevices except
12331 * for the aAbnormal argument. */
12332 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12333 AssertComRC(rc);
12334 NOREF(rc);
12335
12336 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12337 if (service)
12338 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12339 }
12340#endif /* VBOX_WITH_USB */
12341
12342 // we need to lock this object in uninit() because the lock is shared
12343 // with mPeer (as well as data we modify below). mParent lock is needed
12344 // by several calls to it, and USB needs host lock.
12345 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12346
12347#ifdef VBOX_WITH_RESOURCE_USAGE_API
12348 /*
12349 * It is safe to call Machine::i_unregisterMetrics() here because
12350 * PerformanceCollector::samplerCallback no longer accesses guest methods
12351 * holding the lock.
12352 */
12353 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12354 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12355 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12356 this, __PRETTY_FUNCTION__, mCollectorGuest));
12357 if (mCollectorGuest)
12358 {
12359 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12360 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12361 mCollectorGuest = NULL;
12362 }
12363#endif
12364
12365 if (aReason == Uninit::Abnormal)
12366 {
12367 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12368 Global::IsOnlineOrTransient(lastState)));
12369
12370 /* reset the state to Aborted */
12371 if (mData->mMachineState != MachineState_Aborted)
12372 i_setMachineState(MachineState_Aborted);
12373 }
12374
12375 // any machine settings modified?
12376 if (mData->flModifications)
12377 {
12378 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12379 i_rollback(false /* aNotify */);
12380 }
12381
12382 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12383 || !mConsoleTaskData.mSnapshot);
12384 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12385 {
12386 LogWarningThisFunc(("canceling failed save state request!\n"));
12387 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12388 }
12389 else if (!mConsoleTaskData.mSnapshot.isNull())
12390 {
12391 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12392
12393 /* delete all differencing hard disks created (this will also attach
12394 * their parents back by rolling back mMediaData) */
12395 i_rollbackMedia();
12396
12397 // delete the saved state file (it might have been already created)
12398 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12399 // think it's still in use
12400 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12401 mConsoleTaskData.mSnapshot->uninit();
12402 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12403 }
12404
12405 mData->mSession.mPID = NIL_RTPROCESS;
12406
12407 if (aReason == Uninit::Unexpected)
12408 {
12409 /* Uninitialization didn't come from #checkForDeath(), so tell the
12410 * client watcher thread to update the set of machines that have open
12411 * sessions. */
12412 mParent->i_updateClientWatcher();
12413 }
12414
12415 /* uninitialize all remote controls */
12416 if (mData->mSession.mRemoteControls.size())
12417 {
12418 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12419 mData->mSession.mRemoteControls.size()));
12420
12421 Data::Session::RemoteControlList::iterator it =
12422 mData->mSession.mRemoteControls.begin();
12423 while (it != mData->mSession.mRemoteControls.end())
12424 {
12425 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12426 HRESULT rc = (*it)->Uninitialize();
12427 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12428 if (FAILED(rc))
12429 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12430 ++it;
12431 }
12432 mData->mSession.mRemoteControls.clear();
12433 }
12434
12435 /* Remove all references to the NAT network service. The service will stop
12436 * if all references (also from other VMs) are removed. */
12437 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12438 {
12439 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12440 {
12441 NetworkAttachmentType_T type;
12442 HRESULT hrc;
12443
12444 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12445 if ( SUCCEEDED(hrc)
12446 && type == NetworkAttachmentType_NATNetwork)
12447 {
12448 Bstr name;
12449 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12450 if (SUCCEEDED(hrc))
12451 {
12452 multilock.release();
12453 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12454 mUserData->s.strName.c_str(), name.raw()));
12455 mParent->i_natNetworkRefDec(name.raw());
12456 multilock.acquire();
12457 }
12458 }
12459 }
12460 }
12461
12462 /*
12463 * An expected uninitialization can come only from #checkForDeath().
12464 * Otherwise it means that something's gone really wrong (for example,
12465 * the Session implementation has released the VirtualBox reference
12466 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12467 * etc). However, it's also possible, that the client releases the IPC
12468 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12469 * but the VirtualBox release event comes first to the server process.
12470 * This case is practically possible, so we should not assert on an
12471 * unexpected uninit, just log a warning.
12472 */
12473
12474 if ((aReason == Uninit::Unexpected))
12475 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12476
12477 if (aReason != Uninit::Normal)
12478 {
12479 mData->mSession.mDirectControl.setNull();
12480 }
12481 else
12482 {
12483 /* this must be null here (see #OnSessionEnd()) */
12484 Assert(mData->mSession.mDirectControl.isNull());
12485 Assert(mData->mSession.mState == SessionState_Unlocking);
12486 Assert(!mData->mSession.mProgress.isNull());
12487 }
12488 if (mData->mSession.mProgress)
12489 {
12490 if (aReason == Uninit::Normal)
12491 mData->mSession.mProgress->i_notifyComplete(S_OK);
12492 else
12493 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12494 COM_IIDOF(ISession),
12495 getComponentName(),
12496 tr("The VM session was aborted"));
12497 mData->mSession.mProgress.setNull();
12498 }
12499
12500 /* remove the association between the peer machine and this session machine */
12501 Assert( (SessionMachine*)mData->mSession.mMachine == this
12502 || aReason == Uninit::Unexpected);
12503
12504 /* reset the rest of session data */
12505 mData->mSession.mMachine.setNull();
12506 mData->mSession.mState = SessionState_Unlocked;
12507 mData->mSession.mType.setNull();
12508
12509 /* destroy the machine client token before leaving the exclusive lock */
12510 if (mClientToken)
12511 {
12512 delete mClientToken;
12513 mClientToken = NULL;
12514 }
12515
12516 /* fire an event */
12517 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12518
12519 uninitDataAndChildObjects();
12520
12521 /* free the essential data structure last */
12522 mData.free();
12523
12524 /* release the exclusive lock before setting the below two to NULL */
12525 multilock.release();
12526
12527 unconst(mParent) = NULL;
12528 unconst(mPeer) = NULL;
12529
12530 LogFlowThisFuncLeave();
12531}
12532
12533// util::Lockable interface
12534////////////////////////////////////////////////////////////////////////////////
12535
12536/**
12537 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12538 * with the primary Machine instance (mPeer).
12539 */
12540RWLockHandle *SessionMachine::lockHandle() const
12541{
12542 AssertReturn(mPeer != NULL, NULL);
12543 return mPeer->lockHandle();
12544}
12545
12546// IInternalMachineControl methods
12547////////////////////////////////////////////////////////////////////////////////
12548
12549/**
12550 * Passes collected guest statistics to performance collector object
12551 */
12552STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12553 ULONG aCpuKernel, ULONG aCpuIdle,
12554 ULONG aMemTotal, ULONG aMemFree,
12555 ULONG aMemBalloon, ULONG aMemShared,
12556 ULONG aMemCache, ULONG aPageTotal,
12557 ULONG aAllocVMM, ULONG aFreeVMM,
12558 ULONG aBalloonedVMM, ULONG aSharedVMM,
12559 ULONG aVmNetRx, ULONG aVmNetTx)
12560{
12561#ifdef VBOX_WITH_RESOURCE_USAGE_API
12562 if (mCollectorGuest)
12563 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12564 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12565 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12566 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12567
12568 return S_OK;
12569#else
12570 NOREF(aValidStats);
12571 NOREF(aCpuUser);
12572 NOREF(aCpuKernel);
12573 NOREF(aCpuIdle);
12574 NOREF(aMemTotal);
12575 NOREF(aMemFree);
12576 NOREF(aMemBalloon);
12577 NOREF(aMemShared);
12578 NOREF(aMemCache);
12579 NOREF(aPageTotal);
12580 NOREF(aAllocVMM);
12581 NOREF(aFreeVMM);
12582 NOREF(aBalloonedVMM);
12583 NOREF(aSharedVMM);
12584 NOREF(aVmNetRx);
12585 NOREF(aVmNetTx);
12586 return E_NOTIMPL;
12587#endif
12588}
12589
12590/**
12591 * @note Locks this object for writing.
12592 */
12593STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12594{
12595 AutoCaller autoCaller(this);
12596 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12597
12598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12599
12600 mRemoveSavedState = aRemove;
12601
12602 return S_OK;
12603}
12604
12605/**
12606 * @note Locks the same as #i_setMachineState() does.
12607 */
12608STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12609{
12610 return i_setMachineState(aMachineState);
12611}
12612
12613/**
12614 * @note Locks this object for writing.
12615 */
12616STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12617{
12618 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12619 AutoCaller autoCaller(this);
12620 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12621
12622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12623
12624 if (mData->mSession.mState != SessionState_Locked)
12625 return VBOX_E_INVALID_OBJECT_STATE;
12626
12627 if (!mData->mSession.mProgress.isNull())
12628 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12629
12630 /* If we didn't reference the NAT network service yet, add a reference to
12631 * force a start */
12632 if (miNATNetworksStarted < 1)
12633 {
12634 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12635 {
12636 NetworkAttachmentType_T type;
12637 HRESULT hrc;
12638 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12639 if ( SUCCEEDED(hrc)
12640 && type == NetworkAttachmentType_NATNetwork)
12641 {
12642 Bstr name;
12643 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12644 if (SUCCEEDED(hrc))
12645 {
12646 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12647 mUserData->s.strName.c_str(), name.raw()));
12648 mPeer->lockHandle()->unlockWrite();
12649 mParent->i_natNetworkRefInc(name.raw());
12650#ifdef RT_LOCK_STRICT
12651 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12652#else
12653 mPeer->lockHandle()->lockWrite();
12654#endif
12655 }
12656 }
12657 }
12658 miNATNetworksStarted++;
12659 }
12660
12661 LogFlowThisFunc(("returns S_OK.\n"));
12662 return S_OK;
12663}
12664
12665/**
12666 * @note Locks this object for writing.
12667 */
12668STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12669{
12670 AutoCaller autoCaller(this);
12671 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12672
12673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12674
12675 if (mData->mSession.mState != SessionState_Locked)
12676 return VBOX_E_INVALID_OBJECT_STATE;
12677
12678 /* Finalize the LaunchVMProcess progress object. */
12679 if (mData->mSession.mProgress)
12680 {
12681 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12682 mData->mSession.mProgress.setNull();
12683 }
12684
12685 if (SUCCEEDED((HRESULT)iResult))
12686 {
12687#ifdef VBOX_WITH_RESOURCE_USAGE_API
12688 /* The VM has been powered up successfully, so it makes sense
12689 * now to offer the performance metrics for a running machine
12690 * object. Doing it earlier wouldn't be safe. */
12691 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12692 mData->mSession.mPID);
12693#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12694 }
12695
12696 return S_OK;
12697}
12698
12699/**
12700 * @note Locks this object for writing.
12701 */
12702STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12703{
12704 LogFlowThisFuncEnter();
12705
12706 AutoCaller autoCaller(this);
12707 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12708
12709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12710
12711 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12712 E_FAIL);
12713
12714 /* create a progress object to track operation completion */
12715 ComObjPtr<Progress> pProgress;
12716 pProgress.createObject();
12717 pProgress->init(i_getVirtualBox(),
12718 static_cast<IMachine *>(this) /* aInitiator */,
12719 Bstr(tr("Stopping the virtual machine")).raw(),
12720 FALSE /* aCancelable */);
12721
12722 /* fill in the console task data */
12723 mConsoleTaskData.mLastState = mData->mMachineState;
12724 mConsoleTaskData.mProgress = pProgress;
12725
12726 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12727 i_setMachineState(MachineState_Stopping);
12728
12729 pProgress.queryInterfaceTo(aProgress);
12730
12731 return S_OK;
12732}
12733
12734/**
12735 * @note Locks this object for writing.
12736 */
12737STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12738{
12739 LogFlowThisFuncEnter();
12740
12741 AutoCaller autoCaller(this);
12742 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12743
12744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12745
12746 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12747 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12748 && mConsoleTaskData.mLastState != MachineState_Null,
12749 E_FAIL);
12750
12751 /*
12752 * On failure, set the state to the state we had when BeginPoweringDown()
12753 * was called (this is expected by Console::PowerDown() and the associated
12754 * task). On success the VM process already changed the state to
12755 * MachineState_PoweredOff, so no need to do anything.
12756 */
12757 if (FAILED(iResult))
12758 i_setMachineState(mConsoleTaskData.mLastState);
12759
12760 /* notify the progress object about operation completion */
12761 Assert(mConsoleTaskData.mProgress);
12762 if (SUCCEEDED(iResult))
12763 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12764 else
12765 {
12766 Utf8Str strErrMsg(aErrMsg);
12767 if (strErrMsg.length())
12768 mConsoleTaskData.mProgress->i_notifyComplete(iResult,
12769 COM_IIDOF(ISession),
12770 getComponentName(),
12771 strErrMsg.c_str());
12772 else
12773 mConsoleTaskData.mProgress->i_notifyComplete(iResult);
12774 }
12775
12776 /* clear out the temporary saved state data */
12777 mConsoleTaskData.mLastState = MachineState_Null;
12778 mConsoleTaskData.mProgress.setNull();
12779
12780 LogFlowThisFuncLeave();
12781 return S_OK;
12782}
12783
12784
12785/**
12786 * Goes through the USB filters of the given machine to see if the given
12787 * device matches any filter or not.
12788 *
12789 * @note Locks the same as USBController::hasMatchingFilter() does.
12790 */
12791STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12792 BOOL *aMatched,
12793 ULONG *aMaskedIfs)
12794{
12795 LogFlowThisFunc(("\n"));
12796
12797 AutoCaller autoCaller(this);
12798 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12799
12800#ifdef VBOX_WITH_USB
12801 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
12802#else
12803 NOREF(aUSBDevice);
12804 NOREF(aMaskedIfs);
12805 *aMatched = FALSE;
12806#endif
12807
12808 return S_OK;
12809}
12810
12811/**
12812 * @note Locks the same as Host::captureUSBDevice() does.
12813 */
12814STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12815{
12816 LogFlowThisFunc(("\n"));
12817
12818 AutoCaller autoCaller(this);
12819 AssertComRCReturnRC(autoCaller.rc());
12820
12821#ifdef VBOX_WITH_USB
12822 /* if captureDeviceForVM() fails, it must have set extended error info */
12823 clearError();
12824 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12825 if (FAILED(rc)) return rc;
12826
12827 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12828 AssertReturn(service, E_FAIL);
12829 return service->captureDeviceForVM(this, Guid(aId).ref());
12830#else
12831 NOREF(aId);
12832 return E_NOTIMPL;
12833#endif
12834}
12835
12836/**
12837 * @note Locks the same as Host::detachUSBDevice() does.
12838 */
12839STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12840{
12841 LogFlowThisFunc(("\n"));
12842
12843 AutoCaller autoCaller(this);
12844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12845
12846#ifdef VBOX_WITH_USB
12847 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12848 AssertReturn(service, E_FAIL);
12849 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12850#else
12851 NOREF(aId);
12852 NOREF(aDone);
12853 return E_NOTIMPL;
12854#endif
12855}
12856
12857/**
12858 * Inserts all machine filters to the USB proxy service and then calls
12859 * Host::autoCaptureUSBDevices().
12860 *
12861 * Called by Console from the VM process upon VM startup.
12862 *
12863 * @note Locks what called methods lock.
12864 */
12865STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12866{
12867 LogFlowThisFunc(("\n"));
12868
12869 AutoCaller autoCaller(this);
12870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12871
12872#ifdef VBOX_WITH_USB
12873 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12874 AssertComRC(rc);
12875 NOREF(rc);
12876
12877 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12878 AssertReturn(service, E_FAIL);
12879 return service->autoCaptureDevicesForVM(this);
12880#else
12881 return S_OK;
12882#endif
12883}
12884
12885/**
12886 * Removes all machine filters from the USB proxy service and then calls
12887 * Host::detachAllUSBDevices().
12888 *
12889 * Called by Console from the VM process upon normal VM termination or by
12890 * SessionMachine::uninit() upon abnormal VM termination (from under the
12891 * Machine/SessionMachine lock).
12892 *
12893 * @note Locks what called methods lock.
12894 */
12895STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12896{
12897 LogFlowThisFunc(("\n"));
12898
12899 AutoCaller autoCaller(this);
12900 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12901
12902#ifdef VBOX_WITH_USB
12903 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12904 AssertComRC(rc);
12905 NOREF(rc);
12906
12907 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12908 AssertReturn(service, E_FAIL);
12909 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12910#else
12911 NOREF(aDone);
12912 return S_OK;
12913#endif
12914}
12915
12916/**
12917 * @note Locks this object for writing.
12918 */
12919STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12920 IProgress **aProgress)
12921{
12922 LogFlowThisFuncEnter();
12923
12924 AssertReturn(aSession, E_INVALIDARG);
12925 AssertReturn(aProgress, E_INVALIDARG);
12926
12927 AutoCaller autoCaller(this);
12928
12929 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12930 /*
12931 * We don't assert below because it might happen that a non-direct session
12932 * informs us it is closed right after we've been uninitialized -- it's ok.
12933 */
12934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12935
12936 /* get IInternalSessionControl interface */
12937 ComPtr<IInternalSessionControl> control(aSession);
12938
12939 ComAssertRet(!control.isNull(), E_INVALIDARG);
12940
12941 /* Creating a Progress object requires the VirtualBox lock, and
12942 * thus locking it here is required by the lock order rules. */
12943 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12944
12945 if (control == mData->mSession.mDirectControl)
12946 {
12947 ComAssertRet(aProgress, E_POINTER);
12948
12949 /* The direct session is being normally closed by the client process
12950 * ----------------------------------------------------------------- */
12951
12952 /* go to the closing state (essential for all open*Session() calls and
12953 * for #checkForDeath()) */
12954 Assert(mData->mSession.mState == SessionState_Locked);
12955 mData->mSession.mState = SessionState_Unlocking;
12956
12957 /* set direct control to NULL to release the remote instance */
12958 mData->mSession.mDirectControl.setNull();
12959 LogFlowThisFunc(("Direct control is set to NULL\n"));
12960
12961 if (mData->mSession.mProgress)
12962 {
12963 /* finalize the progress, someone might wait if a frontend
12964 * closes the session before powering on the VM. */
12965 mData->mSession.mProgress->notifyComplete(E_FAIL,
12966 COM_IIDOF(ISession),
12967 getComponentName(),
12968 tr("The VM session was closed before any attempt to power it on"));
12969 mData->mSession.mProgress.setNull();
12970 }
12971
12972 /* Create the progress object the client will use to wait until
12973 * #checkForDeath() is called to uninitialize this session object after
12974 * it releases the IPC semaphore.
12975 * Note! Because we're "reusing" mProgress here, this must be a proxy
12976 * object just like for LaunchVMProcess. */
12977 Assert(mData->mSession.mProgress.isNull());
12978 ComObjPtr<ProgressProxy> progress;
12979 progress.createObject();
12980 ComPtr<IUnknown> pPeer(mPeer);
12981 progress->init(mParent, pPeer,
12982 Bstr(tr("Closing session")).raw(),
12983 FALSE /* aCancelable */);
12984 progress.queryInterfaceTo(aProgress);
12985 mData->mSession.mProgress = progress;
12986 }
12987 else
12988 {
12989 /* the remote session is being normally closed */
12990 Data::Session::RemoteControlList::iterator it =
12991 mData->mSession.mRemoteControls.begin();
12992 while (it != mData->mSession.mRemoteControls.end())
12993 {
12994 if (control == *it)
12995 break;
12996 ++it;
12997 }
12998 BOOL found = it != mData->mSession.mRemoteControls.end();
12999 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13000 E_INVALIDARG);
13001 // This MUST be erase(it), not remove(*it) as the latter triggers a
13002 // very nasty use after free due to the place where the value "lives".
13003 mData->mSession.mRemoteControls.erase(it);
13004 }
13005
13006 /* signal the client watcher thread, because the client is going away */
13007 mParent->i_updateClientWatcher();
13008
13009 LogFlowThisFuncLeave();
13010 return S_OK;
13011}
13012
13013/**
13014 * @note Locks this object for writing.
13015 */
13016STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13017{
13018 LogFlowThisFuncEnter();
13019
13020 CheckComArgOutPointerValid(aProgress);
13021 CheckComArgOutPointerValid(aStateFilePath);
13022
13023 AutoCaller autoCaller(this);
13024 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13025
13026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13027
13028 AssertReturn( mData->mMachineState == MachineState_Paused
13029 && mConsoleTaskData.mLastState == MachineState_Null
13030 && mConsoleTaskData.strStateFilePath.isEmpty(),
13031 E_FAIL);
13032
13033 /* create a progress object to track operation completion */
13034 ComObjPtr<Progress> pProgress;
13035 pProgress.createObject();
13036 pProgress->init(i_getVirtualBox(),
13037 static_cast<IMachine *>(this) /* aInitiator */,
13038 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13039 FALSE /* aCancelable */);
13040
13041 Utf8Str strStateFilePath;
13042 /* stateFilePath is null when the machine is not running */
13043 if (mData->mMachineState == MachineState_Paused)
13044 i_composeSavedStateFilename(strStateFilePath);
13045
13046 /* fill in the console task data */
13047 mConsoleTaskData.mLastState = mData->mMachineState;
13048 mConsoleTaskData.strStateFilePath = strStateFilePath;
13049 mConsoleTaskData.mProgress = pProgress;
13050
13051 /* set the state to Saving (this is expected by Console::SaveState()) */
13052 i_setMachineState(MachineState_Saving);
13053
13054 strStateFilePath.cloneTo(aStateFilePath);
13055 pProgress.queryInterfaceTo(aProgress);
13056
13057 return S_OK;
13058}
13059
13060/**
13061 * @note Locks mParent + this object for writing.
13062 */
13063STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13064{
13065 LogFlowThisFunc(("\n"));
13066
13067 AutoCaller autoCaller(this);
13068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13069
13070 /* endSavingState() need mParent lock */
13071 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13072
13073 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13074 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13075 && mConsoleTaskData.mLastState != MachineState_Null
13076 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13077 E_FAIL);
13078
13079 /*
13080 * On failure, set the state to the state we had when BeginSavingState()
13081 * was called (this is expected by Console::SaveState() and the associated
13082 * task). On success the VM process already changed the state to
13083 * MachineState_Saved, so no need to do anything.
13084 */
13085 if (FAILED(iResult))
13086 i_setMachineState(mConsoleTaskData.mLastState);
13087
13088 return endSavingState(iResult, aErrMsg);
13089}
13090
13091/**
13092 * @note Locks this object for writing.
13093 */
13094STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13095{
13096 LogFlowThisFunc(("\n"));
13097
13098 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13099
13100 AutoCaller autoCaller(this);
13101 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13102
13103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13104
13105 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13106 || mData->mMachineState == MachineState_Teleported
13107 || mData->mMachineState == MachineState_Aborted
13108 , E_FAIL); /** @todo setError. */
13109
13110 Utf8Str stateFilePathFull = aSavedStateFile;
13111 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
13112 if (RT_FAILURE(vrc))
13113 return setError(VBOX_E_FILE_ERROR,
13114 tr("Invalid saved state file path '%ls' (%Rrc)"),
13115 aSavedStateFile,
13116 vrc);
13117
13118 mSSData->strStateFilePath = stateFilePathFull;
13119
13120 /* The below i_setMachineState() will detect the state transition and will
13121 * update the settings file */
13122
13123 return i_setMachineState(MachineState_Saved);
13124}
13125
13126STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13127 ComSafeArrayOut(BSTR, aValues),
13128 ComSafeArrayOut(LONG64, aTimestamps),
13129 ComSafeArrayOut(BSTR, aFlags))
13130{
13131 LogFlowThisFunc(("\n"));
13132
13133#ifdef VBOX_WITH_GUEST_PROPS
13134 using namespace guestProp;
13135
13136 AutoCaller autoCaller(this);
13137 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13138
13139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13140
13141 CheckComArgOutSafeArrayPointerValid(aNames);
13142 CheckComArgOutSafeArrayPointerValid(aValues);
13143 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13144 CheckComArgOutSafeArrayPointerValid(aFlags);
13145
13146 size_t cEntries = mHWData->mGuestProperties.size();
13147 com::SafeArray<BSTR> names(cEntries);
13148 com::SafeArray<BSTR> values(cEntries);
13149 com::SafeArray<LONG64> timestamps(cEntries);
13150 com::SafeArray<BSTR> flags(cEntries);
13151 unsigned i = 0;
13152 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13153 it != mHWData->mGuestProperties.end();
13154 ++it)
13155 {
13156 char szFlags[MAX_FLAGS_LEN + 1];
13157 it->first.cloneTo(&names[i]);
13158 it->second.strValue.cloneTo(&values[i]);
13159 timestamps[i] = it->second.mTimestamp;
13160 /* If it is NULL, keep it NULL. */
13161 if (it->second.mFlags)
13162 {
13163 writeFlags(it->second.mFlags, szFlags);
13164 Bstr(szFlags).cloneTo(&flags[i]);
13165 }
13166 else
13167 flags[i] = NULL;
13168 ++i;
13169 }
13170 names.detachTo(ComSafeArrayOutArg(aNames));
13171 values.detachTo(ComSafeArrayOutArg(aValues));
13172 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13173 flags.detachTo(ComSafeArrayOutArg(aFlags));
13174 return S_OK;
13175#else
13176 ReturnComNotImplemented();
13177#endif
13178}
13179
13180STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13181 IN_BSTR aValue,
13182 LONG64 aTimestamp,
13183 IN_BSTR aFlags)
13184{
13185 LogFlowThisFunc(("\n"));
13186
13187#ifdef VBOX_WITH_GUEST_PROPS
13188 using namespace guestProp;
13189
13190 CheckComArgStrNotEmptyOrNull(aName);
13191 CheckComArgNotNull(aValue);
13192 CheckComArgNotNull(aFlags);
13193
13194 try
13195 {
13196 /*
13197 * Convert input up front.
13198 */
13199 Utf8Str utf8Name(aName);
13200 uint32_t fFlags = NILFLAG;
13201 if (aFlags)
13202 {
13203 Utf8Str utf8Flags(aFlags);
13204 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13205 AssertRCReturn(vrc, E_INVALIDARG);
13206 }
13207
13208 /*
13209 * Now grab the object lock, validate the state and do the update.
13210 */
13211 AutoCaller autoCaller(this);
13212 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13213
13214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13215
13216 switch (mData->mMachineState)
13217 {
13218 case MachineState_Paused:
13219 case MachineState_Running:
13220 case MachineState_Teleporting:
13221 case MachineState_TeleportingPausedVM:
13222 case MachineState_LiveSnapshotting:
13223 case MachineState_DeletingSnapshotOnline:
13224 case MachineState_DeletingSnapshotPaused:
13225 case MachineState_Saving:
13226 case MachineState_Stopping:
13227 break;
13228
13229 default:
13230 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13231 VBOX_E_INVALID_VM_STATE);
13232 }
13233
13234 i_setModified(IsModified_MachineData);
13235 mHWData.backup();
13236
13237 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13238 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13239 if (it != mHWData->mGuestProperties.end())
13240 {
13241 if (!fDelete)
13242 {
13243 it->second.strValue = aValue;
13244 it->second.mTimestamp = aTimestamp;
13245 it->second.mFlags = fFlags;
13246 }
13247 else
13248 mHWData->mGuestProperties.erase(it);
13249
13250 mData->mGuestPropertiesModified = TRUE;
13251 }
13252 else if (!fDelete)
13253 {
13254 HWData::GuestProperty prop;
13255 prop.strValue = aValue;
13256 prop.mTimestamp = aTimestamp;
13257 prop.mFlags = fFlags;
13258
13259 mHWData->mGuestProperties[utf8Name] = prop;
13260 mData->mGuestPropertiesModified = TRUE;
13261 }
13262
13263 /*
13264 * Send a callback notification if appropriate
13265 */
13266 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13267 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13268 RTSTR_MAX,
13269 utf8Name.c_str(),
13270 RTSTR_MAX, NULL)
13271 )
13272 {
13273 alock.release();
13274
13275 mParent->i_onGuestPropertyChange(mData->mUuid,
13276 aName,
13277 aValue,
13278 aFlags);
13279 }
13280 }
13281 catch (...)
13282 {
13283 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13284 }
13285 return S_OK;
13286#else
13287 ReturnComNotImplemented();
13288#endif
13289}
13290
13291STDMETHODIMP SessionMachine::LockMedia()
13292{
13293 AutoCaller autoCaller(this);
13294 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13295
13296 AutoMultiWriteLock2 alock(this->lockHandle(),
13297 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13298
13299 AssertReturn( mData->mMachineState == MachineState_Starting
13300 || mData->mMachineState == MachineState_Restoring
13301 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13302
13303 clearError();
13304 alock.release();
13305 return lockMedia();
13306}
13307
13308STDMETHODIMP SessionMachine::UnlockMedia()
13309{
13310 HRESULT hrc = unlockMedia();
13311 return hrc;
13312}
13313
13314STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13315 IMediumAttachment **aNewAttachment)
13316{
13317 CheckComArgNotNull(aAttachment);
13318 CheckComArgOutPointerValid(aNewAttachment);
13319
13320 AutoCaller autoCaller(this);
13321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13322
13323 // request the host lock first, since might be calling Host methods for getting host drives;
13324 // next, protect the media tree all the while we're in here, as well as our member variables
13325 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13326 this->lockHandle(),
13327 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13328
13329 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13330
13331 Bstr ctrlName;
13332 LONG lPort;
13333 LONG lDevice;
13334 bool fTempEject;
13335 {
13336 AutoCaller autoAttachCaller(this);
13337 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13338
13339 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13340
13341 /* Need to query the details first, as the IMediumAttachment reference
13342 * might be to the original settings, which we are going to change. */
13343 ctrlName = pAttach->i_getControllerName();
13344 lPort = pAttach->i_getPort();
13345 lDevice = pAttach->i_getDevice();
13346 fTempEject = pAttach->i_getTempEject();
13347 }
13348
13349 if (!fTempEject)
13350 {
13351 /* Remember previously mounted medium. The medium before taking the
13352 * backup is not necessarily the same thing. */
13353 ComObjPtr<Medium> oldmedium;
13354 oldmedium = pAttach->i_getMedium();
13355
13356 i_setModified(IsModified_Storage);
13357 mMediaData.backup();
13358
13359 // The backup operation makes the pAttach reference point to the
13360 // old settings. Re-get the correct reference.
13361 pAttach = i_findAttachment(mMediaData->mAttachments,
13362 ctrlName.raw(),
13363 lPort,
13364 lDevice);
13365
13366 {
13367 AutoCaller autoAttachCaller(this);
13368 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13369
13370 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13371 if (!oldmedium.isNull())
13372 oldmedium->i_removeBackReference(mData->mUuid);
13373
13374 pAttach->i_updateMedium(NULL);
13375 pAttach->i_updateEjected();
13376 }
13377
13378 i_setModified(IsModified_Storage);
13379 }
13380 else
13381 {
13382 {
13383 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13384 pAttach->i_updateEjected();
13385 }
13386 }
13387
13388 pAttach.queryInterfaceTo(aNewAttachment);
13389
13390 return S_OK;
13391}
13392
13393// public methods only for internal purposes
13394/////////////////////////////////////////////////////////////////////////////
13395
13396#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13397/**
13398 * Called from the client watcher thread to check for expected or unexpected
13399 * death of the client process that has a direct session to this machine.
13400 *
13401 * On Win32 and on OS/2, this method is called only when we've got the
13402 * mutex (i.e. the client has either died or terminated normally) so it always
13403 * returns @c true (the client is terminated, the session machine is
13404 * uninitialized).
13405 *
13406 * On other platforms, the method returns @c true if the client process has
13407 * terminated normally or abnormally and the session machine was uninitialized,
13408 * and @c false if the client process is still alive.
13409 *
13410 * @note Locks this object for writing.
13411 */
13412bool SessionMachine::i_checkForDeath()
13413{
13414 Uninit::Reason reason;
13415 bool terminated = false;
13416
13417 /* Enclose autoCaller with a block because calling uninit() from under it
13418 * will deadlock. */
13419 {
13420 AutoCaller autoCaller(this);
13421 if (!autoCaller.isOk())
13422 {
13423 /* return true if not ready, to cause the client watcher to exclude
13424 * the corresponding session from watching */
13425 LogFlowThisFunc(("Already uninitialized!\n"));
13426 return true;
13427 }
13428
13429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13430
13431 /* Determine the reason of death: if the session state is Closing here,
13432 * everything is fine. Otherwise it means that the client did not call
13433 * OnSessionEnd() before it released the IPC semaphore. This may happen
13434 * either because the client process has abnormally terminated, or
13435 * because it simply forgot to call ISession::Close() before exiting. We
13436 * threat the latter also as an abnormal termination (see
13437 * Session::uninit() for details). */
13438 reason = mData->mSession.mState == SessionState_Unlocking ?
13439 Uninit::Normal :
13440 Uninit::Abnormal;
13441
13442 if (mClientToken)
13443 terminated = mClientToken->release();
13444 } /* AutoCaller block */
13445
13446 if (terminated)
13447 uninit(reason);
13448
13449 return terminated;
13450}
13451
13452void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13453{
13454 LogFlowThisFunc(("\n"));
13455
13456 strTokenId.setNull();
13457
13458 AutoCaller autoCaller(this);
13459 AssertComRCReturnVoid(autoCaller.rc());
13460
13461 Assert(mClientToken);
13462 if (mClientToken)
13463 mClientToken->getId(strTokenId);
13464}
13465#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13466IToken *SessionMachine::i_getToken()
13467{
13468 LogFlowThisFunc(("\n"));
13469
13470 AutoCaller autoCaller(this);
13471 AssertComRCReturn(autoCaller.rc(), NULL);
13472
13473 Assert(mClientToken);
13474 if (mClientToken)
13475 return mClientToken->getToken();
13476 else
13477 return NULL;
13478}
13479#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13480
13481Machine::ClientToken *SessionMachine::i_getClientToken()
13482{
13483 LogFlowThisFunc(("\n"));
13484
13485 AutoCaller autoCaller(this);
13486 AssertComRCReturn(autoCaller.rc(), NULL);
13487
13488 return mClientToken;
13489}
13490
13491
13492/**
13493 * @note Locks this object for reading.
13494 */
13495HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13496{
13497 LogFlowThisFunc(("\n"));
13498
13499 AutoCaller autoCaller(this);
13500 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13501
13502 ComPtr<IInternalSessionControl> directControl;
13503 {
13504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13505 directControl = mData->mSession.mDirectControl;
13506 }
13507
13508 /* ignore notifications sent after #OnSessionEnd() is called */
13509 if (!directControl)
13510 return S_OK;
13511
13512 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13513}
13514
13515/**
13516 * @note Locks this object for reading.
13517 */
13518HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13519 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13520 IN_BSTR aGuestIp, LONG aGuestPort)
13521{
13522 LogFlowThisFunc(("\n"));
13523
13524 AutoCaller autoCaller(this);
13525 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13526
13527 ComPtr<IInternalSessionControl> directControl;
13528 {
13529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13530 directControl = mData->mSession.mDirectControl;
13531 }
13532
13533 /* ignore notifications sent after #OnSessionEnd() is called */
13534 if (!directControl)
13535 return S_OK;
13536 /*
13537 * instead acting like callback we ask IVirtualBox deliver corresponding event
13538 */
13539
13540 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13541 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13542 return S_OK;
13543}
13544
13545/**
13546 * @note Locks this object for reading.
13547 */
13548HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13549{
13550 LogFlowThisFunc(("\n"));
13551
13552 AutoCaller autoCaller(this);
13553 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13554
13555 ComPtr<IInternalSessionControl> directControl;
13556 {
13557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13558 directControl = mData->mSession.mDirectControl;
13559 }
13560
13561 /* ignore notifications sent after #OnSessionEnd() is called */
13562 if (!directControl)
13563 return S_OK;
13564
13565 return directControl->OnSerialPortChange(serialPort);
13566}
13567
13568/**
13569 * @note Locks this object for reading.
13570 */
13571HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13572{
13573 LogFlowThisFunc(("\n"));
13574
13575 AutoCaller autoCaller(this);
13576 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13577
13578 ComPtr<IInternalSessionControl> directControl;
13579 {
13580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13581 directControl = mData->mSession.mDirectControl;
13582 }
13583
13584 /* ignore notifications sent after #OnSessionEnd() is called */
13585 if (!directControl)
13586 return S_OK;
13587
13588 return directControl->OnParallelPortChange(parallelPort);
13589}
13590
13591/**
13592 * @note Locks this object for reading.
13593 */
13594HRESULT SessionMachine::i_onStorageControllerChange()
13595{
13596 LogFlowThisFunc(("\n"));
13597
13598 AutoCaller autoCaller(this);
13599 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13600
13601 ComPtr<IInternalSessionControl> directControl;
13602 {
13603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13604 directControl = mData->mSession.mDirectControl;
13605 }
13606
13607 /* ignore notifications sent after #OnSessionEnd() is called */
13608 if (!directControl)
13609 return S_OK;
13610
13611 return directControl->OnStorageControllerChange();
13612}
13613
13614/**
13615 * @note Locks this object for reading.
13616 */
13617HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13618{
13619 LogFlowThisFunc(("\n"));
13620
13621 AutoCaller autoCaller(this);
13622 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13623
13624 ComPtr<IInternalSessionControl> directControl;
13625 {
13626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13627 directControl = mData->mSession.mDirectControl;
13628 }
13629
13630 /* ignore notifications sent after #OnSessionEnd() is called */
13631 if (!directControl)
13632 return S_OK;
13633
13634 return directControl->OnMediumChange(aAttachment, aForce);
13635}
13636
13637/**
13638 * @note Locks this object for reading.
13639 */
13640HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13641{
13642 LogFlowThisFunc(("\n"));
13643
13644 AutoCaller autoCaller(this);
13645 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13646
13647 ComPtr<IInternalSessionControl> directControl;
13648 {
13649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13650 directControl = mData->mSession.mDirectControl;
13651 }
13652
13653 /* ignore notifications sent after #OnSessionEnd() is called */
13654 if (!directControl)
13655 return S_OK;
13656
13657 return directControl->OnCPUChange(aCPU, aRemove);
13658}
13659
13660HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13661{
13662 LogFlowThisFunc(("\n"));
13663
13664 AutoCaller autoCaller(this);
13665 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13666
13667 ComPtr<IInternalSessionControl> directControl;
13668 {
13669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13670 directControl = mData->mSession.mDirectControl;
13671 }
13672
13673 /* ignore notifications sent after #OnSessionEnd() is called */
13674 if (!directControl)
13675 return S_OK;
13676
13677 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13678}
13679
13680/**
13681 * @note Locks this object for reading.
13682 */
13683HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13684{
13685 LogFlowThisFunc(("\n"));
13686
13687 AutoCaller autoCaller(this);
13688 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13689
13690 ComPtr<IInternalSessionControl> directControl;
13691 {
13692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13693 directControl = mData->mSession.mDirectControl;
13694 }
13695
13696 /* ignore notifications sent after #OnSessionEnd() is called */
13697 if (!directControl)
13698 return S_OK;
13699
13700 return directControl->OnVRDEServerChange(aRestart);
13701}
13702
13703/**
13704 * @note Locks this object for reading.
13705 */
13706HRESULT SessionMachine::i_onVideoCaptureChange()
13707{
13708 LogFlowThisFunc(("\n"));
13709
13710 AutoCaller autoCaller(this);
13711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13712
13713 ComPtr<IInternalSessionControl> directControl;
13714 {
13715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13716 directControl = mData->mSession.mDirectControl;
13717 }
13718
13719 /* ignore notifications sent after #OnSessionEnd() is called */
13720 if (!directControl)
13721 return S_OK;
13722
13723 return directControl->OnVideoCaptureChange();
13724}
13725
13726/**
13727 * @note Locks this object for reading.
13728 */
13729HRESULT SessionMachine::i_onUSBControllerChange()
13730{
13731 LogFlowThisFunc(("\n"));
13732
13733 AutoCaller autoCaller(this);
13734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13735
13736 ComPtr<IInternalSessionControl> directControl;
13737 {
13738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
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->OnUSBControllerChange();
13747}
13748
13749/**
13750 * @note Locks this object for reading.
13751 */
13752HRESULT SessionMachine::i_onSharedFolderChange()
13753{
13754 LogFlowThisFunc(("\n"));
13755
13756 AutoCaller autoCaller(this);
13757 AssertComRCReturnRC(autoCaller.rc());
13758
13759 ComPtr<IInternalSessionControl> directControl;
13760 {
13761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13762 directControl = mData->mSession.mDirectControl;
13763 }
13764
13765 /* ignore notifications sent after #OnSessionEnd() is called */
13766 if (!directControl)
13767 return S_OK;
13768
13769 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13770}
13771
13772/**
13773 * @note Locks this object for reading.
13774 */
13775HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13776{
13777 LogFlowThisFunc(("\n"));
13778
13779 AutoCaller autoCaller(this);
13780 AssertComRCReturnRC(autoCaller.rc());
13781
13782 ComPtr<IInternalSessionControl> directControl;
13783 {
13784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13785 directControl = mData->mSession.mDirectControl;
13786 }
13787
13788 /* ignore notifications sent after #OnSessionEnd() is called */
13789 if (!directControl)
13790 return S_OK;
13791
13792 return directControl->OnClipboardModeChange(aClipboardMode);
13793}
13794
13795/**
13796 * @note Locks this object for reading.
13797 */
13798HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13799{
13800 LogFlowThisFunc(("\n"));
13801
13802 AutoCaller autoCaller(this);
13803 AssertComRCReturnRC(autoCaller.rc());
13804
13805 ComPtr<IInternalSessionControl> directControl;
13806 {
13807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13808 directControl = mData->mSession.mDirectControl;
13809 }
13810
13811 /* ignore notifications sent after #OnSessionEnd() is called */
13812 if (!directControl)
13813 return S_OK;
13814
13815 return directControl->OnDnDModeChange(aDnDMode);
13816}
13817
13818/**
13819 * @note Locks this object for reading.
13820 */
13821HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13822{
13823 LogFlowThisFunc(("\n"));
13824
13825 AutoCaller autoCaller(this);
13826 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13827
13828 ComPtr<IInternalSessionControl> directControl;
13829 {
13830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13831 directControl = mData->mSession.mDirectControl;
13832 }
13833
13834 /* ignore notifications sent after #OnSessionEnd() is called */
13835 if (!directControl)
13836 return S_OK;
13837
13838 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13839}
13840
13841/**
13842 * @note Locks this object for reading.
13843 */
13844HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13845{
13846 LogFlowThisFunc(("\n"));
13847
13848 AutoCaller autoCaller(this);
13849 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13850
13851 ComPtr<IInternalSessionControl> directControl;
13852 {
13853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13854 directControl = mData->mSession.mDirectControl;
13855 }
13856
13857 /* ignore notifications sent after #OnSessionEnd() is called */
13858 if (!directControl)
13859 return S_OK;
13860
13861 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13862}
13863
13864/**
13865 * Returns @c true if this machine's USB controller reports it has a matching
13866 * filter for the given USB device and @c false otherwise.
13867 *
13868 * @note locks this object for reading.
13869 */
13870bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13871{
13872 AutoCaller autoCaller(this);
13873 /* silently return if not ready -- this method may be called after the
13874 * direct machine session has been called */
13875 if (!autoCaller.isOk())
13876 return false;
13877
13878#ifdef VBOX_WITH_USB
13879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13880
13881 switch (mData->mMachineState)
13882 {
13883 case MachineState_Starting:
13884 case MachineState_Restoring:
13885 case MachineState_TeleportingIn:
13886 case MachineState_Paused:
13887 case MachineState_Running:
13888 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13889 * elsewhere... */
13890 alock.release();
13891 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13892 default: break;
13893 }
13894#else
13895 NOREF(aDevice);
13896 NOREF(aMaskedIfs);
13897#endif
13898 return false;
13899}
13900
13901/**
13902 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13903 */
13904HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13905 IVirtualBoxErrorInfo *aError,
13906 ULONG aMaskedIfs)
13907{
13908 LogFlowThisFunc(("\n"));
13909
13910 AutoCaller autoCaller(this);
13911
13912 /* This notification may happen after the machine object has been
13913 * uninitialized (the session was closed), so don't assert. */
13914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13915
13916 ComPtr<IInternalSessionControl> directControl;
13917 {
13918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13919 directControl = mData->mSession.mDirectControl;
13920 }
13921
13922 /* fail on notifications sent after #OnSessionEnd() is called, it is
13923 * expected by the caller */
13924 if (!directControl)
13925 return E_FAIL;
13926
13927 /* No locks should be held at this point. */
13928 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13929 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13930
13931 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13932}
13933
13934/**
13935 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13936 */
13937HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13938 IVirtualBoxErrorInfo *aError)
13939{
13940 LogFlowThisFunc(("\n"));
13941
13942 AutoCaller autoCaller(this);
13943
13944 /* This notification may happen after the machine object has been
13945 * uninitialized (the session was closed), so don't assert. */
13946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13947
13948 ComPtr<IInternalSessionControl> directControl;
13949 {
13950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13951 directControl = mData->mSession.mDirectControl;
13952 }
13953
13954 /* fail on notifications sent after #OnSessionEnd() is called, it is
13955 * expected by the caller */
13956 if (!directControl)
13957 return E_FAIL;
13958
13959 /* No locks should be held at this point. */
13960 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13961 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13962
13963 return directControl->OnUSBDeviceDetach(aId, aError);
13964}
13965
13966// protected methods
13967/////////////////////////////////////////////////////////////////////////////
13968
13969/**
13970 * Helper method to finalize saving the state.
13971 *
13972 * @note Must be called from under this object's lock.
13973 *
13974 * @param aRc S_OK if the snapshot has been taken successfully
13975 * @param aErrMsg human readable error message for failure
13976 *
13977 * @note Locks mParent + this objects for writing.
13978 */
13979HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13980{
13981 LogFlowThisFuncEnter();
13982
13983 AutoCaller autoCaller(this);
13984 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13985
13986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13987
13988 HRESULT rc = S_OK;
13989
13990 if (SUCCEEDED(aRc))
13991 {
13992 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13993
13994 /* save all VM settings */
13995 rc = i_saveSettings(NULL);
13996 // no need to check whether VirtualBox.xml needs saving also since
13997 // we can't have a name change pending at this point
13998 }
13999 else
14000 {
14001 // delete the saved state file (it might have been already created);
14002 // we need not check whether this is shared with a snapshot here because
14003 // we certainly created this saved state file here anew
14004 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14005 }
14006
14007 /* notify the progress object about operation completion */
14008 Assert(mConsoleTaskData.mProgress);
14009 if (SUCCEEDED(aRc))
14010 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
14011 else
14012 {
14013 if (aErrMsg.length())
14014 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
14015 COM_IIDOF(ISession),
14016 getComponentName(),
14017 aErrMsg.c_str());
14018 else
14019 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
14020 }
14021
14022 /* clear out the temporary saved state data */
14023 mConsoleTaskData.mLastState = MachineState_Null;
14024 mConsoleTaskData.strStateFilePath.setNull();
14025 mConsoleTaskData.mProgress.setNull();
14026
14027 LogFlowThisFuncLeave();
14028 return rc;
14029}
14030
14031/**
14032 * Deletes the given file if it is no longer in use by either the current machine state
14033 * (if the machine is "saved") or any of the machine's snapshots.
14034 *
14035 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14036 * but is different for each SnapshotMachine. When calling this, the order of calling this
14037 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14038 * is therefore critical. I know, it's all rather messy.
14039 *
14040 * @param strStateFile
14041 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14042 * the test for whether the saved state file is in use.
14043 */
14044void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14045 Snapshot *pSnapshotToIgnore)
14046{
14047 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14048 if ( (strStateFile.isNotEmpty())
14049 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14050 )
14051 // ... and it must also not be shared with other snapshots
14052 if ( !mData->mFirstSnapshot
14053 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14054 // this checks the SnapshotMachine's state file paths
14055 )
14056 RTFileDelete(strStateFile.c_str());
14057}
14058
14059/**
14060 * Locks the attached media.
14061 *
14062 * All attached hard disks are locked for writing and DVD/floppy are locked for
14063 * reading. Parents of attached hard disks (if any) are locked for reading.
14064 *
14065 * This method also performs accessibility check of all media it locks: if some
14066 * media is inaccessible, the method will return a failure and a bunch of
14067 * extended error info objects per each inaccessible medium.
14068 *
14069 * Note that this method is atomic: if it returns a success, all media are
14070 * locked as described above; on failure no media is locked at all (all
14071 * succeeded individual locks will be undone).
14072 *
14073 * The caller is responsible for doing the necessary state sanity checks.
14074 *
14075 * The locks made by this method must be undone by calling #unlockMedia() when
14076 * no more needed.
14077 */
14078HRESULT SessionMachine::lockMedia()
14079{
14080 AutoCaller autoCaller(this);
14081 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14082
14083 AutoMultiWriteLock2 alock(this->lockHandle(),
14084 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14085
14086 /* bail out if trying to lock things with already set up locking */
14087 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14088
14089 MultiResult mrc(S_OK);
14090
14091 /* Collect locking information for all medium objects attached to the VM. */
14092 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14093 it != mMediaData->mAttachments.end();
14094 ++it)
14095 {
14096 MediumAttachment* pAtt = *it;
14097 DeviceType_T devType = pAtt->i_getType();
14098 Medium *pMedium = pAtt->i_getMedium();
14099
14100 MediumLockList *pMediumLockList(new MediumLockList());
14101 // There can be attachments without a medium (floppy/dvd), and thus
14102 // it's impossible to create a medium lock list. It still makes sense
14103 // to have the empty medium lock list in the map in case a medium is
14104 // attached later.
14105 if (pMedium != NULL)
14106 {
14107 MediumType_T mediumType = pMedium->i_getType();
14108 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14109 || mediumType == MediumType_Shareable;
14110 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14111
14112 alock.release();
14113 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14114 !fIsReadOnlyLock /* fMediumLockWrite */,
14115 NULL,
14116 *pMediumLockList);
14117 alock.acquire();
14118 if (FAILED(mrc))
14119 {
14120 delete pMediumLockList;
14121 mData->mSession.mLockedMedia.Clear();
14122 break;
14123 }
14124 }
14125
14126 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14127 if (FAILED(rc))
14128 {
14129 mData->mSession.mLockedMedia.Clear();
14130 mrc = setError(rc,
14131 tr("Collecting locking information for all attached media failed"));
14132 break;
14133 }
14134 }
14135
14136 if (SUCCEEDED(mrc))
14137 {
14138 /* Now lock all media. If this fails, nothing is locked. */
14139 alock.release();
14140 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14141 alock.acquire();
14142 if (FAILED(rc))
14143 {
14144 mrc = setError(rc,
14145 tr("Locking of attached media failed"));
14146 }
14147 }
14148
14149 return mrc;
14150}
14151
14152/**
14153 * Undoes the locks made by by #lockMedia().
14154 */
14155HRESULT SessionMachine::unlockMedia()
14156{
14157 AutoCaller autoCaller(this);
14158 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14159
14160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14161
14162 /* we may be holding important error info on the current thread;
14163 * preserve it */
14164 ErrorInfoKeeper eik;
14165
14166 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14167 AssertComRC(rc);
14168 return rc;
14169}
14170
14171/**
14172 * Helper to change the machine state (reimplementation).
14173 *
14174 * @note Locks this object for writing.
14175 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14176 * it can cause crashes in random places due to unexpectedly committing
14177 * the current settings. The caller is responsible for that. The call
14178 * to saveStateSettings is fine, because this method does not commit.
14179 */
14180HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14181{
14182 LogFlowThisFuncEnter();
14183 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14184
14185 AutoCaller autoCaller(this);
14186 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14187
14188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14189
14190 MachineState_T oldMachineState = mData->mMachineState;
14191
14192 AssertMsgReturn(oldMachineState != aMachineState,
14193 ("oldMachineState=%s, aMachineState=%s\n",
14194 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14195 E_FAIL);
14196
14197 HRESULT rc = S_OK;
14198
14199 int stsFlags = 0;
14200 bool deleteSavedState = false;
14201
14202 /* detect some state transitions */
14203
14204 if ( ( oldMachineState == MachineState_Saved
14205 && aMachineState == MachineState_Restoring)
14206 || ( ( oldMachineState == MachineState_PoweredOff
14207 || oldMachineState == MachineState_Teleported
14208 || oldMachineState == MachineState_Aborted
14209 )
14210 && ( aMachineState == MachineState_TeleportingIn
14211 || aMachineState == MachineState_Starting
14212 )
14213 )
14214 )
14215 {
14216 /* The EMT thread is about to start */
14217
14218 /* Nothing to do here for now... */
14219
14220 /// @todo NEWMEDIA don't let mDVDDrive and other children
14221 /// change anything when in the Starting/Restoring state
14222 }
14223 else if ( ( oldMachineState == MachineState_Running
14224 || oldMachineState == MachineState_Paused
14225 || oldMachineState == MachineState_Teleporting
14226 || oldMachineState == MachineState_LiveSnapshotting
14227 || oldMachineState == MachineState_Stuck
14228 || oldMachineState == MachineState_Starting
14229 || oldMachineState == MachineState_Stopping
14230 || oldMachineState == MachineState_Saving
14231 || oldMachineState == MachineState_Restoring
14232 || oldMachineState == MachineState_TeleportingPausedVM
14233 || oldMachineState == MachineState_TeleportingIn
14234 )
14235 && ( aMachineState == MachineState_PoweredOff
14236 || aMachineState == MachineState_Saved
14237 || aMachineState == MachineState_Teleported
14238 || aMachineState == MachineState_Aborted
14239 )
14240 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14241 * snapshot */
14242 && ( mConsoleTaskData.mSnapshot.isNull()
14243 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14244 )
14245 )
14246 {
14247 /* The EMT thread has just stopped, unlock attached media. Note that as
14248 * opposed to locking that is done from Console, we do unlocking here
14249 * because the VM process may have aborted before having a chance to
14250 * properly unlock all media it locked. */
14251
14252 unlockMedia();
14253 }
14254
14255 if (oldMachineState == MachineState_Restoring)
14256 {
14257 if (aMachineState != MachineState_Saved)
14258 {
14259 /*
14260 * delete the saved state file once the machine has finished
14261 * restoring from it (note that Console sets the state from
14262 * Restoring to Saved if the VM couldn't restore successfully,
14263 * to give the user an ability to fix an error and retry --
14264 * we keep the saved state file in this case)
14265 */
14266 deleteSavedState = true;
14267 }
14268 }
14269 else if ( oldMachineState == MachineState_Saved
14270 && ( aMachineState == MachineState_PoweredOff
14271 || aMachineState == MachineState_Aborted
14272 || aMachineState == MachineState_Teleported
14273 )
14274 )
14275 {
14276 /*
14277 * delete the saved state after Console::ForgetSavedState() is called
14278 * or if the VM process (owning a direct VM session) crashed while the
14279 * VM was Saved
14280 */
14281
14282 /// @todo (dmik)
14283 // Not sure that deleting the saved state file just because of the
14284 // client death before it attempted to restore the VM is a good
14285 // thing. But when it crashes we need to go to the Aborted state
14286 // which cannot have the saved state file associated... The only
14287 // way to fix this is to make the Aborted condition not a VM state
14288 // but a bool flag: i.e., when a crash occurs, set it to true and
14289 // change the state to PoweredOff or Saved depending on the
14290 // saved state presence.
14291
14292 deleteSavedState = true;
14293 mData->mCurrentStateModified = TRUE;
14294 stsFlags |= SaveSTS_CurStateModified;
14295 }
14296
14297 if ( aMachineState == MachineState_Starting
14298 || aMachineState == MachineState_Restoring
14299 || aMachineState == MachineState_TeleportingIn
14300 )
14301 {
14302 /* set the current state modified flag to indicate that the current
14303 * state is no more identical to the state in the
14304 * current snapshot */
14305 if (!mData->mCurrentSnapshot.isNull())
14306 {
14307 mData->mCurrentStateModified = TRUE;
14308 stsFlags |= SaveSTS_CurStateModified;
14309 }
14310 }
14311
14312 if (deleteSavedState)
14313 {
14314 if (mRemoveSavedState)
14315 {
14316 Assert(!mSSData->strStateFilePath.isEmpty());
14317
14318 // it is safe to delete the saved state file if ...
14319 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14320 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14321 // ... none of the snapshots share the saved state file
14322 )
14323 RTFileDelete(mSSData->strStateFilePath.c_str());
14324 }
14325
14326 mSSData->strStateFilePath.setNull();
14327 stsFlags |= SaveSTS_StateFilePath;
14328 }
14329
14330 /* redirect to the underlying peer machine */
14331 mPeer->i_setMachineState(aMachineState);
14332
14333 if ( aMachineState == MachineState_PoweredOff
14334 || aMachineState == MachineState_Teleported
14335 || aMachineState == MachineState_Aborted
14336 || aMachineState == MachineState_Saved)
14337 {
14338 /* the machine has stopped execution
14339 * (or the saved state file was adopted) */
14340 stsFlags |= SaveSTS_StateTimeStamp;
14341 }
14342
14343 if ( ( oldMachineState == MachineState_PoweredOff
14344 || oldMachineState == MachineState_Aborted
14345 || oldMachineState == MachineState_Teleported
14346 )
14347 && aMachineState == MachineState_Saved)
14348 {
14349 /* the saved state file was adopted */
14350 Assert(!mSSData->strStateFilePath.isEmpty());
14351 stsFlags |= SaveSTS_StateFilePath;
14352 }
14353
14354#ifdef VBOX_WITH_GUEST_PROPS
14355 if ( aMachineState == MachineState_PoweredOff
14356 || aMachineState == MachineState_Aborted
14357 || aMachineState == MachineState_Teleported)
14358 {
14359 /* Make sure any transient guest properties get removed from the
14360 * property store on shutdown. */
14361
14362 HWData::GuestPropertyMap::const_iterator it;
14363 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14364 if (!fNeedsSaving)
14365 for (it = mHWData->mGuestProperties.begin();
14366 it != mHWData->mGuestProperties.end(); ++it)
14367 if ( (it->second.mFlags & guestProp::TRANSIENT)
14368 || (it->second.mFlags & guestProp::TRANSRESET))
14369 {
14370 fNeedsSaving = true;
14371 break;
14372 }
14373 if (fNeedsSaving)
14374 {
14375 mData->mCurrentStateModified = TRUE;
14376 stsFlags |= SaveSTS_CurStateModified;
14377 }
14378 }
14379#endif
14380
14381 rc = i_saveStateSettings(stsFlags);
14382
14383 if ( ( oldMachineState != MachineState_PoweredOff
14384 && oldMachineState != MachineState_Aborted
14385 && oldMachineState != MachineState_Teleported
14386 )
14387 && ( aMachineState == MachineState_PoweredOff
14388 || aMachineState == MachineState_Aborted
14389 || aMachineState == MachineState_Teleported
14390 )
14391 )
14392 {
14393 /* we've been shut down for any reason */
14394 /* no special action so far */
14395 }
14396
14397 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14398 LogFlowThisFuncLeave();
14399 return rc;
14400}
14401
14402/**
14403 * Sends the current machine state value to the VM process.
14404 *
14405 * @note Locks this object for reading, then calls a client process.
14406 */
14407HRESULT SessionMachine::i_updateMachineStateOnClient()
14408{
14409 AutoCaller autoCaller(this);
14410 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14411
14412 ComPtr<IInternalSessionControl> directControl;
14413 {
14414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14415 AssertReturn(!!mData, E_FAIL);
14416 directControl = mData->mSession.mDirectControl;
14417
14418 /* directControl may be already set to NULL here in #OnSessionEnd()
14419 * called too early by the direct session process while there is still
14420 * some operation (like deleting the snapshot) in progress. The client
14421 * process in this case is waiting inside Session::close() for the
14422 * "end session" process object to complete, while #uninit() called by
14423 * #checkForDeath() on the Watcher thread is waiting for the pending
14424 * operation to complete. For now, we accept this inconsistent behavior
14425 * and simply do nothing here. */
14426
14427 if (mData->mSession.mState == SessionState_Unlocking)
14428 return S_OK;
14429
14430 AssertReturn(!directControl.isNull(), E_FAIL);
14431 }
14432
14433 return directControl->UpdateMachineState(mData->mMachineState);
14434}
14435
14436HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14437{
14438 NOREF(aRemove);
14439 ReturnComNotImplemented();
14440}
14441
14442HRESULT Machine::updateState(MachineState_T aState)
14443{
14444 NOREF(aState);
14445 ReturnComNotImplemented();
14446}
14447
14448HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14449{
14450 NOREF(aProgress);
14451 ReturnComNotImplemented();
14452}
14453
14454HRESULT Machine::endPowerUp(LONG aResult)
14455{
14456 NOREF(aResult);
14457 ReturnComNotImplemented();
14458}
14459
14460HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14461{
14462 NOREF(aProgress);
14463 ReturnComNotImplemented();
14464}
14465
14466HRESULT Machine::endPoweringDown(LONG aResult,
14467 const com::Utf8Str &aErrMsg)
14468{
14469 NOREF(aResult);
14470 NOREF(aErrMsg);
14471 ReturnComNotImplemented();
14472}
14473
14474HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14475 BOOL *aMatched,
14476 ULONG *aMaskedInterfaces)
14477{
14478 NOREF(aDevice);
14479 NOREF(aMatched);
14480 NOREF(aMaskedInterfaces);
14481 ReturnComNotImplemented();
14482
14483}
14484
14485HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14486{
14487 NOREF(aId);
14488 ReturnComNotImplemented();
14489}
14490
14491HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14492 BOOL aDone)
14493{
14494 NOREF(aId);
14495 NOREF(aDone);
14496 ReturnComNotImplemented();
14497}
14498
14499HRESULT Machine::autoCaptureUSBDevices()
14500{
14501 ReturnComNotImplemented();
14502}
14503
14504HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14505{
14506 NOREF(aDone);
14507 ReturnComNotImplemented();
14508}
14509
14510HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14511 ComPtr<IProgress> &aProgress)
14512{
14513 NOREF(aSession);
14514 NOREF(aProgress);
14515 ReturnComNotImplemented();
14516}
14517
14518HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14519 com::Utf8Str &aStateFilePath)
14520{
14521 NOREF(aProgress);
14522 NOREF(aStateFilePath);
14523 ReturnComNotImplemented();
14524}
14525
14526HRESULT Machine::endSavingState(LONG aResult,
14527 const com::Utf8Str &aErrMsg)
14528{
14529 NOREF(aResult);
14530 NOREF(aErrMsg);
14531 ReturnComNotImplemented();
14532}
14533
14534HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14535{
14536 NOREF(aSavedStateFile);
14537 ReturnComNotImplemented();
14538}
14539
14540HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14541 const com::Utf8Str &aName,
14542 const com::Utf8Str &aDescription,
14543 const ComPtr<IProgress> &aConsoleProgress,
14544 BOOL aFTakingSnapshotOnline,
14545 com::Utf8Str &aStateFilePath)
14546{
14547 NOREF(aInitiator);
14548 NOREF(aName);
14549 NOREF(aDescription);
14550 NOREF(aConsoleProgress);
14551 NOREF(aFTakingSnapshotOnline);
14552 NOREF(aStateFilePath);
14553 ReturnComNotImplemented();
14554}
14555
14556HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14557{
14558 NOREF(aSuccess);
14559 ReturnComNotImplemented();
14560}
14561
14562HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14563 const com::Guid &aStartId,
14564 const com::Guid &aEndId,
14565 BOOL aDeleteAllChildren,
14566 MachineState_T *aMachineState,
14567 ComPtr<IProgress> &aProgress)
14568{
14569 NOREF(aInitiator);
14570 NOREF(aStartId);
14571 NOREF(aEndId);
14572 NOREF(aDeleteAllChildren);
14573 NOREF(aMachineState);
14574 NOREF(aProgress);
14575 ReturnComNotImplemented();
14576}
14577
14578HRESULT Machine::finishOnlineMergeMedium()
14579{
14580 ReturnComNotImplemented();
14581}
14582
14583HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14584 const ComPtr<ISnapshot> &aSnapshot,
14585 MachineState_T *aMachineState,
14586 ComPtr<IProgress> &aProgress)
14587{
14588 NOREF(aInitiator);
14589 NOREF(aSnapshot);
14590 NOREF(aMachineState);
14591 NOREF(aProgress);
14592 ReturnComNotImplemented();
14593}
14594
14595HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14596 std::vector<com::Utf8Str> &aValues,
14597 std::vector<LONG64> &aTimestamps,
14598 std::vector<com::Utf8Str> &aFlags)
14599{
14600 NOREF(aNames);
14601 NOREF(aValues);
14602 NOREF(aTimestamps);
14603 NOREF(aFlags);
14604 ReturnComNotImplemented();
14605}
14606
14607HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14608 const com::Utf8Str &aValue,
14609 LONG64 aTimestamp,
14610 const com::Utf8Str &aFlags)
14611{
14612 NOREF(aName);
14613 NOREF(aValue);
14614 NOREF(aTimestamp);
14615 NOREF(aFlags);
14616 ReturnComNotImplemented();
14617}
14618
14619HRESULT Machine::lockMedia()
14620{
14621 ReturnComNotImplemented();
14622}
14623
14624HRESULT Machine::unlockMedia()
14625{
14626 ReturnComNotImplemented();
14627}
14628
14629HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14630 ComPtr<IMediumAttachment> &aNewAttachment)
14631{
14632 NOREF(aAttachment);
14633 NOREF(aNewAttachment);
14634 ReturnComNotImplemented();
14635}
14636
14637HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14638 ULONG aCpuUser,
14639 ULONG aCpuKernel,
14640 ULONG aCpuIdle,
14641 ULONG aMemTotal,
14642 ULONG aMemFree,
14643 ULONG aMemBalloon,
14644 ULONG aMemShared,
14645 ULONG aMemCache,
14646 ULONG aPagedTotal,
14647 ULONG aMemAllocTotal,
14648 ULONG aMemFreeTotal,
14649 ULONG aMemBalloonTotal,
14650 ULONG aMemSharedTotal,
14651 ULONG aVmNetRx,
14652 ULONG aVmNetTx)
14653{
14654 NOREF(aValidStats);
14655 NOREF(aCpuUser);
14656 NOREF(aCpuKernel);
14657 NOREF(aCpuIdle);
14658 NOREF(aMemTotal);
14659 NOREF(aMemFree);
14660 NOREF(aMemBalloon);
14661 NOREF(aMemShared);
14662 NOREF(aMemCache);
14663 NOREF(aPagedTotal);
14664 NOREF(aMemAllocTotal);
14665 NOREF(aMemFreeTotal);
14666 NOREF(aMemBalloonTotal);
14667 NOREF(aMemSharedTotal);
14668 NOREF(aVmNetRx);
14669 NOREF(aVmNetTx);
14670 ReturnComNotImplemented();
14671}
Note: See TracBrowser for help on using the repository browser.

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