VirtualBox

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

Last change on this file since 51441 was 51190, checked in by vboxsync, 11 years ago

VBoxSVC: save settings at exit: update VirtualBox.xml if VM name was previously changed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 509.3 KB
Line 
1/* $Id: MachineImpl.cpp 51190 2014-05-05 18:01:30Z 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 mVideoCaptureEnabled = false;
169 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
170 maVideoCaptureScreens[i] = true;
171
172 mHWVirtExEnabled = true;
173 mHWVirtExNestedPagingEnabled = true;
174#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
175 mHWVirtExLargePagesEnabled = true;
176#else
177 /* Not supported on 32 bits hosts. */
178 mHWVirtExLargePagesEnabled = false;
179#endif
180 mHWVirtExVPIDEnabled = true;
181 mHWVirtExUXEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
189 mSyntheticCpu = false;
190 mTripleFaultReset = false;
191 mHPETEnabled = false;
192
193 /* default boot order: floppy - DVD - HDD */
194 mBootOrder[0] = DeviceType_Floppy;
195 mBootOrder[1] = DeviceType_DVD;
196 mBootOrder[2] = DeviceType_HardDisk;
197 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
198 mBootOrder[i] = DeviceType_Null;
199
200 mClipboardMode = ClipboardMode_Disabled;
201 mDragAndDropMode = DragAndDropMode_Disabled;
202 mGuestPropertyNotificationPatterns = "";
203
204 mFirmwareType = FirmwareType_BIOS;
205 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
206 mPointingHIDType = PointingHIDType_PS2Mouse;
207 mChipsetType = ChipsetType_PIIX3;
208 mParavirtProvider = ParavirtProvider_Default;
209 mEmulatedUSBCardReaderEnabled = FALSE;
210
211 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
212 mCPUAttached[i] = false;
213
214 mIOCacheEnabled = true;
215 mIOCacheSize = 5; /* 5MB */
216
217 /* Maximum CPU execution cap by default. */
218 mCpuExecutionCap = 100;
219}
220
221Machine::HWData::~HWData()
222{
223}
224
225/////////////////////////////////////////////////////////////////////////////
226// Machine::HDData structure
227/////////////////////////////////////////////////////////////////////////////
228
229Machine::MediaData::MediaData()
230{
231}
232
233Machine::MediaData::~MediaData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 GuestOSType *aOsType,
291 const Guid &aId,
292 bool fForceOverwrite,
293 bool fDirectoryIncludesUUID)
294{
295 LogFlowThisFuncEnter();
296 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
297
298 /* Enclose the state transition NotReady->InInit->Ready */
299 AutoInitSpan autoInitSpan(this);
300 AssertReturn(autoInitSpan.isOk(), E_FAIL);
301
302 HRESULT rc = initImpl(aParent, strConfigFile);
303 if (FAILED(rc)) return rc;
304
305 rc = tryCreateMachineConfigFile(fForceOverwrite);
306 if (FAILED(rc)) return rc;
307
308 if (SUCCEEDED(rc))
309 {
310 // create an empty machine config
311 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
312
313 rc = initDataAndChildObjects();
314 }
315
316 if (SUCCEEDED(rc))
317 {
318 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
319 mData->mAccessible = TRUE;
320
321 unconst(mData->mUuid) = aId;
322
323 mUserData->s.strName = strName;
324
325 mUserData->s.llGroups = llGroups;
326
327 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
328 // the "name sync" flag determines whether the machine directory gets renamed along
329 // with the machine file; say so if the settings file name is the same as the
330 // settings file parent directory (machine directory)
331 mUserData->s.fNameSync = isInOwnDir();
332
333 // initialize the default snapshots folder
334 rc = COMSETTER(SnapshotFolder)(NULL);
335 AssertComRC(rc);
336
337 if (aOsType)
338 {
339 /* Store OS type */
340 mUserData->s.strOsType = aOsType->i_id();
341
342 /* Apply BIOS defaults */
343 mBIOSSettings->i_applyDefaults(aOsType);
344
345 /* Apply network adapters defaults */
346 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
347 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
348
349 /* Apply serial port defaults */
350 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
351 mSerialPorts[slot]->i_applyDefaults(aOsType);
352
353 /* Let the OS type select 64-bit ness. */
354 mHWData->mLongMode = aOsType->i_is64Bit()
355 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
356 }
357
358 /* At this point the changing of the current state modification
359 * flag is allowed. */
360 allowStateModification();
361
362 /* commit all changes made during the initialization */
363 commit();
364 }
365
366 /* Confirm a successful initialization when it's the case */
367 if (SUCCEEDED(rc))
368 {
369 if (mData->mAccessible)
370 autoInitSpan.setSucceeded();
371 else
372 autoInitSpan.setLimited();
373 }
374
375 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
376 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
377 mData->mRegistered,
378 mData->mAccessible,
379 rc));
380
381 LogFlowThisFuncLeave();
382
383 return rc;
384}
385
386/**
387 * Initializes a new instance with data from machine XML (formerly Init_Registered).
388 * Gets called in two modes:
389 *
390 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
391 * UUID is specified and we mark the machine as "registered";
392 *
393 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
394 * and the machine remains unregistered until RegisterMachine() is called.
395 *
396 * @param aParent Associated parent object
397 * @param aConfigFile Local file system path to the VM settings file (can
398 * be relative to the VirtualBox config directory).
399 * @param aId UUID of the machine or NULL (see above).
400 *
401 * @return Success indicator. if not S_OK, the machine object is invalid
402 */
403HRESULT Machine::initFromSettings(VirtualBox *aParent,
404 const Utf8Str &strConfigFile,
405 const Guid *aId)
406{
407 LogFlowThisFuncEnter();
408 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
409
410 /* Enclose the state transition NotReady->InInit->Ready */
411 AutoInitSpan autoInitSpan(this);
412 AssertReturn(autoInitSpan.isOk(), E_FAIL);
413
414 HRESULT rc = initImpl(aParent, strConfigFile);
415 if (FAILED(rc)) return rc;
416
417 if (aId)
418 {
419 // loading a registered VM:
420 unconst(mData->mUuid) = *aId;
421 mData->mRegistered = TRUE;
422 // now load the settings from XML:
423 rc = registeredInit();
424 // this calls initDataAndChildObjects() and loadSettings()
425 }
426 else
427 {
428 // opening an unregistered VM (VirtualBox::OpenMachine()):
429 rc = initDataAndChildObjects();
430
431 if (SUCCEEDED(rc))
432 {
433 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
434 mData->mAccessible = TRUE;
435
436 try
437 {
438 // load and parse machine XML; this will throw on XML or logic errors
439 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
440
441 // reject VM UUID duplicates, they can happen if someone
442 // tries to register an already known VM config again
443 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
444 true /* fPermitInaccessible */,
445 false /* aDoSetError */,
446 NULL) != VBOX_E_OBJECT_NOT_FOUND)
447 {
448 throw setError(E_FAIL,
449 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
450 mData->m_strConfigFile.c_str());
451 }
452
453 // use UUID from machine config
454 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
455
456 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
457 NULL /* puuidRegistry */);
458 if (FAILED(rc)) throw rc;
459
460 /* At this point the changing of the current state modification
461 * flag is allowed. */
462 allowStateModification();
463
464 commit();
465 }
466 catch (HRESULT err)
467 {
468 /* we assume that error info is set by the thrower */
469 rc = err;
470 }
471 catch (...)
472 {
473 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
474 }
475 }
476 }
477
478 /* Confirm a successful initialization when it's the case */
479 if (SUCCEEDED(rc))
480 {
481 if (mData->mAccessible)
482 autoInitSpan.setSucceeded();
483 else
484 {
485 autoInitSpan.setLimited();
486
487 // uninit media from this machine's media registry, or else
488 // reloading the settings will fail
489 mParent->i_unregisterMachineMedia(getId());
490 }
491 }
492
493 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
494 "rc=%08X\n",
495 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
496 mData->mRegistered, mData->mAccessible, rc));
497
498 LogFlowThisFuncLeave();
499
500 return rc;
501}
502
503/**
504 * Initializes a new instance from a machine config that is already in memory
505 * (import OVF case). Since we are importing, the UUID in the machine
506 * config is ignored and we always generate a fresh one.
507 *
508 * @param strName Name for the new machine; this overrides what is specified in config and is used
509 * for the settings file as well.
510 * @param config Machine configuration loaded and parsed from XML.
511 *
512 * @return Success indicator. if not S_OK, the machine object is invalid
513 */
514HRESULT Machine::init(VirtualBox *aParent,
515 const Utf8Str &strName,
516 const settings::MachineConfigFile &config)
517{
518 LogFlowThisFuncEnter();
519
520 /* Enclose the state transition NotReady->InInit->Ready */
521 AutoInitSpan autoInitSpan(this);
522 AssertReturn(autoInitSpan.isOk(), E_FAIL);
523
524 Utf8Str strConfigFile;
525 aParent->i_getDefaultMachineFolder(strConfigFile);
526 strConfigFile.append(RTPATH_DELIMITER);
527 strConfigFile.append(strName);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(".vbox");
531
532 HRESULT rc = initImpl(aParent, strConfigFile);
533 if (FAILED(rc)) return rc;
534
535 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
536 if (FAILED(rc)) return rc;
537
538 rc = initDataAndChildObjects();
539
540 if (SUCCEEDED(rc))
541 {
542 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
543 mData->mAccessible = TRUE;
544
545 // create empty machine config for instance data
546 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
547
548 // generate fresh UUID, ignore machine config
549 unconst(mData->mUuid).create();
550
551 rc = loadMachineDataFromSettings(config,
552 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
553
554 // override VM name as well, it may be different
555 mUserData->s.strName = strName;
556
557 if (SUCCEEDED(rc))
558 {
559 /* At this point the changing of the current state modification
560 * flag is allowed. */
561 allowStateModification();
562
563 /* commit all changes made during the initialization */
564 commit();
565 }
566 }
567
568 /* Confirm a successful initialization when it's the case */
569 if (SUCCEEDED(rc))
570 {
571 if (mData->mAccessible)
572 autoInitSpan.setSucceeded();
573 else
574 {
575 /* Ignore all errors from unregistering, they would destroy
576 * the more interesting error information we already have,
577 * pinpointing the issue with the VM config. */
578 ErrorInfoKeeper eik;
579 autoInitSpan.setLimited();
580
581 // uninit media from this machine's media registry, or else
582 // reloading the settings will fail
583 mParent->i_unregisterMachineMedia(getId());
584 }
585 }
586
587 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
588 "rc=%08X\n",
589 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
590 mData->mRegistered, mData->mAccessible, rc));
591
592 LogFlowThisFuncLeave();
593
594 return rc;
595}
596
597/**
598 * Shared code between the various init() implementations.
599 * @param aParent
600 * @return
601 */
602HRESULT Machine::initImpl(VirtualBox *aParent,
603 const Utf8Str &strConfigFile)
604{
605 LogFlowThisFuncEnter();
606
607 AssertReturn(aParent, E_INVALIDARG);
608 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
609
610 HRESULT rc = S_OK;
611
612 /* share the parent weakly */
613 unconst(mParent) = aParent;
614
615 /* allocate the essential machine data structure (the rest will be
616 * allocated later by initDataAndChildObjects() */
617 mData.allocate();
618
619 /* memorize the config file name (as provided) */
620 mData->m_strConfigFile = strConfigFile;
621
622 /* get the full file name */
623 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
624 if (RT_FAILURE(vrc1))
625 return setError(VBOX_E_FILE_ERROR,
626 tr("Invalid machine settings file name '%s' (%Rrc)"),
627 strConfigFile.c_str(),
628 vrc1);
629
630 LogFlowThisFuncLeave();
631
632 return rc;
633}
634
635/**
636 * Tries to create a machine settings file in the path stored in the machine
637 * instance data. Used when a new machine is created to fail gracefully if
638 * the settings file could not be written (e.g. because machine dir is read-only).
639 * @return
640 */
641HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
642{
643 HRESULT rc = S_OK;
644
645 // when we create a new machine, we must be able to create the settings file
646 RTFILE f = NIL_RTFILE;
647 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
648 if ( RT_SUCCESS(vrc)
649 || vrc == VERR_SHARING_VIOLATION
650 )
651 {
652 if (RT_SUCCESS(vrc))
653 RTFileClose(f);
654 if (!fForceOverwrite)
655 rc = setError(VBOX_E_FILE_ERROR,
656 tr("Machine settings file '%s' already exists"),
657 mData->m_strConfigFileFull.c_str());
658 else
659 {
660 /* try to delete the config file, as otherwise the creation
661 * of a new settings file will fail. */
662 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
663 if (RT_FAILURE(vrc2))
664 rc = setError(VBOX_E_FILE_ERROR,
665 tr("Could not delete the existing settings file '%s' (%Rrc)"),
666 mData->m_strConfigFileFull.c_str(), vrc2);
667 }
668 }
669 else if ( vrc != VERR_FILE_NOT_FOUND
670 && vrc != VERR_PATH_NOT_FOUND
671 )
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Invalid machine settings file name '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(),
675 vrc);
676 return rc;
677}
678
679/**
680 * Initializes the registered machine by loading the settings file.
681 * This method is separated from #init() in order to make it possible to
682 * retry the operation after VirtualBox startup instead of refusing to
683 * startup the whole VirtualBox server in case if the settings file of some
684 * registered VM is invalid or inaccessible.
685 *
686 * @note Must be always called from this object's write lock
687 * (unless called from #init() that doesn't need any locking).
688 * @note Locks the mUSBController method for writing.
689 * @note Subclasses must not call this method.
690 */
691HRESULT Machine::registeredInit()
692{
693 AssertReturn(!isSessionMachine(), E_FAIL);
694 AssertReturn(!isSnapshotMachine(), E_FAIL);
695 AssertReturn(mData->mUuid.isValid(), E_FAIL);
696 AssertReturn(!mData->mAccessible, E_FAIL);
697
698 HRESULT rc = initDataAndChildObjects();
699
700 if (SUCCEEDED(rc))
701 {
702 /* Temporarily reset the registered flag in order to let setters
703 * potentially called from loadSettings() succeed (isMutable() used in
704 * all setters will return FALSE for a Machine instance if mRegistered
705 * is TRUE). */
706 mData->mRegistered = FALSE;
707
708 try
709 {
710 // load and parse machine XML; this will throw on XML or logic errors
711 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
712
713 if (mData->mUuid != mData->pMachineConfigFile->uuid)
714 throw setError(E_FAIL,
715 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
716 mData->pMachineConfigFile->uuid.raw(),
717 mData->m_strConfigFileFull.c_str(),
718 mData->mUuid.toString().c_str(),
719 mParent->i_settingsFilePath().c_str());
720
721 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
722 NULL /* const Guid *puuidRegistry */);
723 if (FAILED(rc)) throw rc;
724 }
725 catch (HRESULT err)
726 {
727 /* we assume that error info is set by the thrower */
728 rc = err;
729 }
730 catch (...)
731 {
732 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
733 }
734
735 /* Restore the registered flag (even on failure) */
736 mData->mRegistered = TRUE;
737 }
738
739 if (SUCCEEDED(rc))
740 {
741 /* Set mAccessible to TRUE only if we successfully locked and loaded
742 * the settings file */
743 mData->mAccessible = TRUE;
744
745 /* commit all changes made during loading the settings file */
746 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
747 /// @todo r=klaus for some reason the settings loading logic backs up
748 // the settings, and therefore a commit is needed. Should probably be changed.
749 }
750 else
751 {
752 /* If the machine is registered, then, instead of returning a
753 * failure, we mark it as inaccessible and set the result to
754 * success to give it a try later */
755
756 /* fetch the current error info */
757 mData->mAccessError = com::ErrorInfo();
758 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
759 mData->mUuid.raw(),
760 mData->mAccessError.getText().raw()));
761
762 /* rollback all changes */
763 rollback(false /* aNotify */);
764
765 // uninit media from this machine's media registry, or else
766 // reloading the settings will fail
767 mParent->i_unregisterMachineMedia(getId());
768
769 /* uninitialize the common part to make sure all data is reset to
770 * default (null) values */
771 uninitDataAndChildObjects();
772
773 rc = S_OK;
774 }
775
776 return rc;
777}
778
779/**
780 * Uninitializes the instance.
781 * Called either from FinalRelease() or by the parent when it gets destroyed.
782 *
783 * @note The caller of this method must make sure that this object
784 * a) doesn't have active callers on the current thread and b) is not locked
785 * by the current thread; otherwise uninit() will hang either a) due to
786 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
787 * a dead-lock caused by this thread waiting for all callers on the other
788 * threads are done but preventing them from doing so by holding a lock.
789 */
790void Machine::uninit()
791{
792 LogFlowThisFuncEnter();
793
794 Assert(!isWriteLockOnCurrentThread());
795
796 Assert(!uRegistryNeedsSaving);
797 if (uRegistryNeedsSaving)
798 {
799 AutoCaller autoCaller(this);
800 if (SUCCEEDED(autoCaller.rc()))
801 {
802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
803 saveSettings(NULL, Machine::SaveS_Force);
804 }
805 }
806
807 /* Enclose the state transition Ready->InUninit->NotReady */
808 AutoUninitSpan autoUninitSpan(this);
809 if (autoUninitSpan.uninitDone())
810 return;
811
812 Assert(!isSnapshotMachine());
813 Assert(!isSessionMachine());
814 Assert(!!mData);
815
816 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
817 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
818
819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
820
821 if (!mData->mSession.mMachine.isNull())
822 {
823 /* Theoretically, this can only happen if the VirtualBox server has been
824 * terminated while there were clients running that owned open direct
825 * sessions. Since in this case we are definitely called by
826 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
827 * won't happen on the client watcher thread (because it does
828 * VirtualBox::addCaller() for the duration of the
829 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
830 * cannot happen until the VirtualBox caller is released). This is
831 * important, because SessionMachine::uninit() cannot correctly operate
832 * after we return from this method (it expects the Machine instance is
833 * still valid). We'll call it ourselves below.
834 */
835 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
836 (SessionMachine*)mData->mSession.mMachine));
837
838 if (Global::IsOnlineOrTransient(mData->mMachineState))
839 {
840 LogWarningThisFunc(("Setting state to Aborted!\n"));
841 /* set machine state using SessionMachine reimplementation */
842 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
843 }
844
845 /*
846 * Uninitialize SessionMachine using public uninit() to indicate
847 * an unexpected uninitialization.
848 */
849 mData->mSession.mMachine->uninit();
850 /* SessionMachine::uninit() must set mSession.mMachine to null */
851 Assert(mData->mSession.mMachine.isNull());
852 }
853
854 // uninit media from this machine's media registry, if they're still there
855 Guid uuidMachine(getId());
856
857 /* the lock is no more necessary (SessionMachine is uninitialized) */
858 alock.release();
859
860 /* XXX This will fail with
861 * "cannot be closed because it is still attached to 1 virtual machines"
862 * because at this point we did not call uninitDataAndChildObjects() yet
863 * and therefore also removeBackReference() for all these mediums was not called! */
864
865 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
866 mParent->i_unregisterMachineMedia(uuidMachine);
867
868 // has machine been modified?
869 if (mData->flModifications)
870 {
871 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
872 rollback(false /* aNotify */);
873 }
874
875 if (mData->mAccessible)
876 uninitDataAndChildObjects();
877
878 /* free the essential data structure last */
879 mData.free();
880
881 LogFlowThisFuncLeave();
882}
883
884// IMachine properties
885/////////////////////////////////////////////////////////////////////////////
886
887STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
888{
889 CheckComArgOutPointerValid(aParent);
890
891 AutoLimitedCaller autoCaller(this);
892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
893
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 pVirtualBox.queryInterfaceTo(aParent);
897
898 return S_OK;
899}
900
901STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
902{
903 CheckComArgOutPointerValid(aAccessible);
904
905 AutoLimitedCaller autoCaller(this);
906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
907
908 LogFlowThisFunc(("ENTER\n"));
909
910 /* In some cases (medium registry related), it is necessary to be able to
911 * go through the list of all machines. Happens when an inaccessible VM
912 * has a sensible medium registry. */
913 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
915
916 HRESULT rc = S_OK;
917
918 if (!mData->mAccessible)
919 {
920 /* try to initialize the VM once more if not accessible */
921
922 AutoReinitSpan autoReinitSpan(this);
923 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
924
925#ifdef DEBUG
926 LogFlowThisFunc(("Dumping media backreferences\n"));
927 mParent->i_dumpAllBackRefs();
928#endif
929
930 if (mData->pMachineConfigFile)
931 {
932 // reset the XML file to force loadSettings() (called from registeredInit())
933 // to parse it again; the file might have changed
934 delete mData->pMachineConfigFile;
935 mData->pMachineConfigFile = NULL;
936 }
937
938 rc = registeredInit();
939
940 if (SUCCEEDED(rc) && mData->mAccessible)
941 {
942 autoReinitSpan.setSucceeded();
943
944 /* make sure interesting parties will notice the accessibility
945 * state change */
946 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
947 mParent->i_onMachineDataChange(mData->mUuid);
948 }
949 }
950
951 if (SUCCEEDED(rc))
952 *aAccessible = mData->mAccessible;
953
954 LogFlowThisFuncLeave();
955
956 return rc;
957}
958
959STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
960{
961 CheckComArgOutPointerValid(aAccessError);
962
963 AutoLimitedCaller autoCaller(this);
964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
965
966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
967
968 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
969 {
970 /* return shortly */
971 aAccessError = NULL;
972 return S_OK;
973 }
974
975 HRESULT rc = S_OK;
976
977 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
978 rc = errorInfo.createObject();
979 if (SUCCEEDED(rc))
980 {
981 errorInfo->init(mData->mAccessError.getResultCode(),
982 mData->mAccessError.getInterfaceID().ref(),
983 Utf8Str(mData->mAccessError.getComponent()).c_str(),
984 Utf8Str(mData->mAccessError.getText()));
985 rc = errorInfo.queryInterfaceTo(aAccessError);
986 }
987
988 return rc;
989}
990
991STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
992{
993 CheckComArgOutPointerValid(aName);
994
995 AutoCaller autoCaller(this);
996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
997
998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 mUserData->s.strName.cloneTo(aName);
1001
1002 return S_OK;
1003}
1004
1005STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1006{
1007 CheckComArgStrNotEmptyOrNull(aName);
1008
1009 AutoCaller autoCaller(this);
1010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1011
1012 // prohibit setting a UUID only as the machine name, or else it can
1013 // never be found by findMachine()
1014 Guid test(aName);
1015
1016 if (test.isValid())
1017 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1018
1019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1020
1021 HRESULT rc = checkStateDependency(MutableStateDep);
1022 if (FAILED(rc)) return rc;
1023
1024 setModified(IsModified_MachineData);
1025 mUserData.backup();
1026 mUserData->s.strName = aName;
1027
1028 return S_OK;
1029}
1030
1031STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1032{
1033 CheckComArgOutPointerValid(aDescription);
1034
1035 AutoCaller autoCaller(this);
1036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1037
1038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 mUserData->s.strDescription.cloneTo(aDescription);
1041
1042 return S_OK;
1043}
1044
1045STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1046{
1047 AutoCaller autoCaller(this);
1048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1049
1050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1051
1052 // this can be done in principle in any state as it doesn't affect the VM
1053 // significantly, but play safe by not messing around while complex
1054 // activities are going on
1055 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1056 if (FAILED(rc)) return rc;
1057
1058 setModified(IsModified_MachineData);
1059 mUserData.backup();
1060 mUserData->s.strDescription = aDescription;
1061
1062 return S_OK;
1063}
1064
1065STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1066{
1067 CheckComArgOutPointerValid(aId);
1068
1069 AutoLimitedCaller autoCaller(this);
1070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1071
1072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1073
1074 mData->mUuid.toUtf16().cloneTo(aId);
1075
1076 return S_OK;
1077}
1078
1079STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1080{
1081 CheckComArgOutSafeArrayPointerValid(aGroups);
1082
1083 AutoCaller autoCaller(this);
1084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1085
1086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1087 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1088 size_t i = 0;
1089 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1090 it != mUserData->s.llGroups.end();
1091 ++it, i++)
1092 {
1093 Bstr tmp = *it;
1094 tmp.cloneTo(&groups[i]);
1095 }
1096 groups.detachTo(ComSafeArrayOutArg(aGroups));
1097
1098 return S_OK;
1099}
1100
1101STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1102{
1103 AutoCaller autoCaller(this);
1104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1105 std::vector<com::Utf8Str> tmplist;
1106 SafeArray<IN_BSTR> tmp(ComSafeArrayInArg(aGroups));
1107 tmplist.resize(tmp.size());
1108 for (size_t i = 0; i < tmp.size(); ++i)
1109 tmplist[i] = Utf8Str(tmp[i]);
1110
1111 StringsList llGroups;
1112 HRESULT rc = mParent->i_convertMachineGroups(tmplist, &llGroups);
1113 if (FAILED(rc))
1114 return rc;
1115
1116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1117
1118 // changing machine groups is possible while the VM is offline
1119 rc = checkStateDependency(OfflineStateDep);
1120 if (FAILED(rc)) return rc;
1121
1122 setModified(IsModified_MachineData);
1123 mUserData.backup();
1124 mUserData->s.llGroups = llGroups;
1125
1126 return S_OK;
1127}
1128
1129STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1130{
1131 CheckComArgOutPointerValid(aOSTypeId);
1132
1133 AutoCaller autoCaller(this);
1134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1135
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 mUserData->s.strOsType.cloneTo(aOSTypeId);
1139
1140 return S_OK;
1141}
1142
1143STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1144{
1145 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1146
1147 AutoCaller autoCaller(this);
1148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1149
1150 /* look up the object by Id to check it is valid */
1151 ComPtr<IGuestOSType> guestOSType;
1152 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1153 if (FAILED(rc)) return rc;
1154
1155 /* when setting, always use the "etalon" value for consistency -- lookup
1156 * by ID is case-insensitive and the input value may have different case */
1157 Bstr osTypeId;
1158 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1159 if (FAILED(rc)) return rc;
1160
1161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1162
1163 rc = checkStateDependency(MutableStateDep);
1164 if (FAILED(rc)) return rc;
1165
1166 setModified(IsModified_MachineData);
1167 mUserData.backup();
1168 mUserData->s.strOsType = osTypeId;
1169
1170 return S_OK;
1171}
1172
1173
1174STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1175{
1176 CheckComArgOutPointerValid(aFirmwareType);
1177
1178 AutoCaller autoCaller(this);
1179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1180
1181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1182
1183 *aFirmwareType = mHWData->mFirmwareType;
1184
1185 return S_OK;
1186}
1187
1188STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1189{
1190 AutoCaller autoCaller(this);
1191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 setModified(IsModified_MachineData);
1198 mHWData.backup();
1199 mHWData->mFirmwareType = aFirmwareType;
1200
1201 return S_OK;
1202}
1203
1204STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1205{
1206 CheckComArgOutPointerValid(aKeyboardHIDType);
1207
1208 AutoCaller autoCaller(this);
1209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1210
1211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1212
1213 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1214
1215 return S_OK;
1216}
1217
1218STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1219{
1220 AutoCaller autoCaller(this);
1221 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1223
1224 HRESULT rc = checkStateDependency(MutableStateDep);
1225 if (FAILED(rc)) return rc;
1226
1227 setModified(IsModified_MachineData);
1228 mHWData.backup();
1229 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1230
1231 return S_OK;
1232}
1233
1234STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1235{
1236 CheckComArgOutPointerValid(aPointingHIDType);
1237
1238 AutoCaller autoCaller(this);
1239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1240
1241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 *aPointingHIDType = mHWData->mPointingHIDType;
1244
1245 return S_OK;
1246}
1247
1248STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1249{
1250 AutoCaller autoCaller(this);
1251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 HRESULT rc = checkStateDependency(MutableStateDep);
1255 if (FAILED(rc)) return rc;
1256
1257 setModified(IsModified_MachineData);
1258 mHWData.backup();
1259 mHWData->mPointingHIDType = aPointingHIDType;
1260
1261 return S_OK;
1262}
1263
1264STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1265{
1266 CheckComArgOutPointerValid(aChipsetType);
1267
1268 AutoCaller autoCaller(this);
1269 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1270
1271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1272
1273 *aChipsetType = mHWData->mChipsetType;
1274
1275 return S_OK;
1276}
1277
1278STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1279{
1280 AutoCaller autoCaller(this);
1281 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1283
1284 HRESULT rc = checkStateDependency(MutableStateDep);
1285 if (FAILED(rc)) return rc;
1286
1287 if (aChipsetType != mHWData->mChipsetType)
1288 {
1289 setModified(IsModified_MachineData);
1290 mHWData.backup();
1291 mHWData->mChipsetType = aChipsetType;
1292
1293 // Resize network adapter array, to be finalized on commit/rollback.
1294 // We must not throw away entries yet, otherwise settings are lost
1295 // without a way to roll back.
1296 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1297 size_t oldCount = mNetworkAdapters.size();
1298 if (newCount > oldCount)
1299 {
1300 mNetworkAdapters.resize(newCount);
1301 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1302 {
1303 unconst(mNetworkAdapters[slot]).createObject();
1304 mNetworkAdapters[slot]->init(this, slot);
1305 }
1306 }
1307 }
1308
1309 return S_OK;
1310}
1311
1312STDMETHODIMP Machine::COMGETTER(ParavirtProvider)(ParavirtProvider_T *aParavirtProvider)
1313{
1314 CheckComArgOutPointerValid(aParavirtProvider);
1315
1316 AutoCaller autoCaller(this);
1317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1318
1319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1320
1321 *aParavirtProvider = mHWData->mParavirtProvider;
1322
1323 return S_OK;
1324}
1325
1326STDMETHODIMP Machine::COMSETTER(ParavirtProvider)(ParavirtProvider_T aParavirtProvider)
1327{
1328 AutoCaller autoCaller(this);
1329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1331
1332 HRESULT rc = checkStateDependency(MutableStateDep);
1333 if (FAILED(rc)) return rc;
1334
1335 if (aParavirtProvider != mHWData->mParavirtProvider)
1336 {
1337 setModified(IsModified_MachineData);
1338 mHWData.backup();
1339 mHWData->mParavirtProvider = aParavirtProvider;
1340 }
1341
1342 return S_OK;
1343}
1344
1345STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1346{
1347 CheckComArgOutPointerValid(aHWVersion);
1348
1349 AutoCaller autoCaller(this);
1350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1351
1352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1353
1354 mHWData->mHWVersion.cloneTo(aHWVersion);
1355
1356 return S_OK;
1357}
1358
1359STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1360{
1361 /* check known version */
1362 Utf8Str hwVersion = aHWVersion;
1363 if ( hwVersion.compare("1") != 0
1364 && hwVersion.compare("2") != 0)
1365 return setError(E_INVALIDARG,
1366 tr("Invalid hardware version: %ls\n"), aHWVersion);
1367
1368 AutoCaller autoCaller(this);
1369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1370
1371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1372
1373 HRESULT rc = checkStateDependency(MutableStateDep);
1374 if (FAILED(rc)) return rc;
1375
1376 setModified(IsModified_MachineData);
1377 mHWData.backup();
1378 mHWData->mHWVersion = hwVersion;
1379
1380 return S_OK;
1381}
1382
1383STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1384{
1385 CheckComArgOutPointerValid(aUUID);
1386
1387 AutoCaller autoCaller(this);
1388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1389
1390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1391
1392 if (!mHWData->mHardwareUUID.isZero())
1393 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1394 else
1395 mData->mUuid.toUtf16().cloneTo(aUUID);
1396
1397 return S_OK;
1398}
1399
1400STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1401{
1402 Guid hardwareUUID(aUUID);
1403 if (!hardwareUUID.isValid())
1404 return E_INVALIDARG;
1405
1406 AutoCaller autoCaller(this);
1407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1408
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 HRESULT rc = checkStateDependency(MutableStateDep);
1412 if (FAILED(rc)) return rc;
1413
1414 setModified(IsModified_MachineData);
1415 mHWData.backup();
1416 if (hardwareUUID == mData->mUuid)
1417 mHWData->mHardwareUUID.clear();
1418 else
1419 mHWData->mHardwareUUID = hardwareUUID;
1420
1421 return S_OK;
1422}
1423
1424STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1425{
1426 CheckComArgOutPointerValid(memorySize);
1427
1428 AutoCaller autoCaller(this);
1429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1430
1431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 *memorySize = mHWData->mMemorySize;
1434
1435 return S_OK;
1436}
1437
1438STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1439{
1440 /* check RAM limits */
1441 if ( memorySize < MM_RAM_MIN_IN_MB
1442 || memorySize > MM_RAM_MAX_IN_MB
1443 )
1444 return setError(E_INVALIDARG,
1445 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1446 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1447
1448 AutoCaller autoCaller(this);
1449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1450
1451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1452
1453 HRESULT rc = checkStateDependency(MutableStateDep);
1454 if (FAILED(rc)) return rc;
1455
1456 setModified(IsModified_MachineData);
1457 mHWData.backup();
1458 mHWData->mMemorySize = memorySize;
1459
1460 return S_OK;
1461}
1462
1463STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1464{
1465 CheckComArgOutPointerValid(CPUCount);
1466
1467 AutoCaller autoCaller(this);
1468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1469
1470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1471
1472 *CPUCount = mHWData->mCPUCount;
1473
1474 return S_OK;
1475}
1476
1477STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1478{
1479 /* check CPU limits */
1480 if ( CPUCount < SchemaDefs::MinCPUCount
1481 || CPUCount > SchemaDefs::MaxCPUCount
1482 )
1483 return setError(E_INVALIDARG,
1484 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1485 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1486
1487 AutoCaller autoCaller(this);
1488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1489
1490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1491
1492 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1493 if (mHWData->mCPUHotPlugEnabled)
1494 {
1495 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1496 {
1497 if (mHWData->mCPUAttached[idx])
1498 return setError(E_INVALIDARG,
1499 tr("There is still a CPU attached to socket %lu."
1500 "Detach the CPU before removing the socket"),
1501 CPUCount, idx+1);
1502 }
1503 }
1504
1505 HRESULT rc = checkStateDependency(MutableStateDep);
1506 if (FAILED(rc)) return rc;
1507
1508 setModified(IsModified_MachineData);
1509 mHWData.backup();
1510 mHWData->mCPUCount = CPUCount;
1511
1512 return S_OK;
1513}
1514
1515STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1516{
1517 CheckComArgOutPointerValid(aExecutionCap);
1518
1519 AutoCaller autoCaller(this);
1520 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1521
1522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1523
1524 *aExecutionCap = mHWData->mCpuExecutionCap;
1525
1526 return S_OK;
1527}
1528
1529STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1530{
1531 HRESULT rc = S_OK;
1532
1533 /* check throttle limits */
1534 if ( aExecutionCap < 1
1535 || aExecutionCap > 100
1536 )
1537 return setError(E_INVALIDARG,
1538 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1539 aExecutionCap, 1, 100);
1540
1541 AutoCaller autoCaller(this);
1542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1543
1544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
1546 alock.release();
1547 rc = onCPUExecutionCapChange(aExecutionCap);
1548 alock.acquire();
1549 if (FAILED(rc)) return rc;
1550
1551 setModified(IsModified_MachineData);
1552 mHWData.backup();
1553 mHWData->mCpuExecutionCap = aExecutionCap;
1554
1555 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1556 if (Global::IsOnline(mData->mMachineState))
1557 saveSettings(NULL);
1558
1559 return S_OK;
1560}
1561
1562
1563STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1564{
1565 CheckComArgOutPointerValid(aEnabled);
1566
1567 AutoCaller autoCaller(this);
1568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1569
1570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1571
1572 *aEnabled = mHWData->mCPUHotPlugEnabled;
1573
1574 return S_OK;
1575}
1576
1577STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1578{
1579 HRESULT rc = S_OK;
1580
1581 AutoCaller autoCaller(this);
1582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1583
1584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1585
1586 rc = checkStateDependency(MutableStateDep);
1587 if (FAILED(rc)) return rc;
1588
1589 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1590 {
1591 if (aEnabled)
1592 {
1593 setModified(IsModified_MachineData);
1594 mHWData.backup();
1595
1596 /* Add the amount of CPUs currently attached */
1597 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1598 {
1599 mHWData->mCPUAttached[i] = true;
1600 }
1601 }
1602 else
1603 {
1604 /*
1605 * We can disable hotplug only if the amount of maximum CPUs is equal
1606 * to the amount of attached CPUs
1607 */
1608 unsigned cCpusAttached = 0;
1609 unsigned iHighestId = 0;
1610
1611 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1612 {
1613 if (mHWData->mCPUAttached[i])
1614 {
1615 cCpusAttached++;
1616 iHighestId = i;
1617 }
1618 }
1619
1620 if ( (cCpusAttached != mHWData->mCPUCount)
1621 || (iHighestId >= mHWData->mCPUCount))
1622 return setError(E_INVALIDARG,
1623 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1624
1625 setModified(IsModified_MachineData);
1626 mHWData.backup();
1627 }
1628 }
1629
1630 mHWData->mCPUHotPlugEnabled = aEnabled;
1631
1632 return rc;
1633}
1634
1635STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1636{
1637#ifdef VBOX_WITH_USB_CARDREADER
1638 CheckComArgOutPointerValid(aEnabled);
1639
1640 AutoCaller autoCaller(this);
1641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1642
1643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1644
1645 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1646
1647 return S_OK;
1648#else
1649 NOREF(aEnabled);
1650 return E_NOTIMPL;
1651#endif
1652}
1653
1654STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1655{
1656#ifdef VBOX_WITH_USB_CARDREADER
1657 AutoCaller autoCaller(this);
1658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1660
1661 HRESULT rc = checkStateDependency(MutableStateDep);
1662 if (FAILED(rc)) return rc;
1663
1664 setModified(IsModified_MachineData);
1665 mHWData.backup();
1666 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1667
1668 return S_OK;
1669#else
1670 NOREF(aEnabled);
1671 return E_NOTIMPL;
1672#endif
1673}
1674
1675STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1676{
1677 CheckComArgOutPointerValid(aEnabled);
1678
1679 AutoCaller autoCaller(this);
1680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 *aEnabled = mHWData->mHPETEnabled;
1684
1685 return S_OK;
1686}
1687
1688STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1689{
1690 HRESULT rc = S_OK;
1691
1692 AutoCaller autoCaller(this);
1693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1695
1696 rc = checkStateDependency(MutableStateDep);
1697 if (FAILED(rc)) return rc;
1698
1699 setModified(IsModified_MachineData);
1700 mHWData.backup();
1701
1702 mHWData->mHPETEnabled = aEnabled;
1703
1704 return rc;
1705}
1706
1707STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1708{
1709 AutoCaller autoCaller(this);
1710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1711
1712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1713
1714 *fEnabled = mHWData->mVideoCaptureEnabled;
1715 return S_OK;
1716}
1717
1718STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1719{
1720 HRESULT rc = S_OK;
1721
1722 AutoCaller autoCaller(this);
1723 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1725
1726 setModified(IsModified_MachineData);
1727 mHWData.backup();
1728 mHWData->mVideoCaptureEnabled = fEnabled;
1729
1730 alock.release();
1731 rc = onVideoCaptureChange();
1732 alock.acquire();
1733 if (FAILED(rc))
1734 {
1735 /*
1736 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1737 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1738 * determine if it should start or stop capturing. Therefore we need to manually
1739 * undo change.
1740 */
1741 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1742 return rc;
1743 }
1744
1745 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1746 if (Global::IsOnline(mData->mMachineState))
1747 saveSettings(NULL);
1748
1749 return rc;
1750}
1751
1752STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1753{
1754 CheckComArgOutSafeArrayPointerValid(aScreens);
1755
1756 AutoCaller autoCaller(this);
1757 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1758
1759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1760
1761 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1762 for (unsigned i = 0; i < screens.size(); i++)
1763 screens[i] = mHWData->maVideoCaptureScreens[i];
1764 screens.detachTo(ComSafeArrayOutArg(aScreens));
1765 return S_OK;
1766}
1767
1768STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1769{
1770 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1771 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1772 bool fChanged = false;
1773
1774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1775
1776 for (unsigned i = 0; i < screens.size(); i++)
1777 {
1778 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1779 {
1780 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1781 fChanged = true;
1782 }
1783 }
1784 if (fChanged)
1785 {
1786 alock.release();
1787 HRESULT rc = onVideoCaptureChange();
1788 alock.acquire();
1789 if (FAILED(rc)) return rc;
1790 setModified(IsModified_MachineData);
1791
1792 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1793 if (Global::IsOnline(mData->mMachineState))
1794 saveSettings(NULL);
1795 }
1796
1797 return S_OK;
1798}
1799
1800STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1801{
1802 AutoCaller autoCaller(this);
1803 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1804
1805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1806 if (mHWData->mVideoCaptureFile.isEmpty())
1807 {
1808 Utf8Str defaultFile;
1809 getDefaultVideoCaptureFile(defaultFile);
1810 defaultFile.cloneTo(apFile);
1811 }
1812 else
1813 mHWData->mVideoCaptureFile.cloneTo(apFile);
1814 return S_OK;
1815}
1816
1817STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1818{
1819 Utf8Str strFile(aFile);
1820 AutoCaller autoCaller(this);
1821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1822
1823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1824
1825 if ( Global::IsOnline(mData->mMachineState)
1826 && mHWData->mVideoCaptureEnabled)
1827 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1828
1829 if (!RTPathStartsWithRoot(strFile.c_str()))
1830 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1831
1832 if (!strFile.isEmpty())
1833 {
1834 Utf8Str defaultFile;
1835 getDefaultVideoCaptureFile(defaultFile);
1836 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1837 strFile.setNull();
1838 }
1839
1840 setModified(IsModified_MachineData);
1841 mHWData.backup();
1842 mHWData->mVideoCaptureFile = strFile;
1843
1844 return S_OK;
1845}
1846
1847STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1848{
1849 AutoCaller autoCaller(this);
1850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1851
1852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1853 *aHorzRes = mHWData->mVideoCaptureWidth;
1854 return S_OK;
1855}
1856
1857STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1858{
1859 AutoCaller autoCaller(this);
1860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1861
1862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 if ( Global::IsOnline(mData->mMachineState)
1865 && mHWData->mVideoCaptureEnabled)
1866 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1867
1868 setModified(IsModified_MachineData);
1869 mHWData.backup();
1870 mHWData->mVideoCaptureWidth = aHorzRes;
1871
1872 return S_OK;
1873}
1874
1875STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1876{
1877 AutoCaller autoCaller(this);
1878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1879
1880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1881 *aVertRes = mHWData->mVideoCaptureHeight;
1882 return S_OK;
1883}
1884
1885STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1886{
1887 AutoCaller autoCaller(this);
1888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1889
1890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 if ( Global::IsOnline(mData->mMachineState)
1893 && mHWData->mVideoCaptureEnabled)
1894 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1895
1896 setModified(IsModified_MachineData);
1897 mHWData.backup();
1898 mHWData->mVideoCaptureHeight = aVertRes;
1899
1900 return S_OK;
1901}
1902
1903STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1904{
1905 AutoCaller autoCaller(this);
1906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1907
1908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1909 *aRate = mHWData->mVideoCaptureRate;
1910 return S_OK;
1911}
1912
1913STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1914{
1915 AutoCaller autoCaller(this);
1916 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1917
1918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1919
1920 if ( Global::IsOnline(mData->mMachineState)
1921 && mHWData->mVideoCaptureEnabled)
1922 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1923
1924 setModified(IsModified_MachineData);
1925 mHWData.backup();
1926 mHWData->mVideoCaptureRate = aRate;
1927
1928 return S_OK;
1929}
1930
1931STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1932{
1933 AutoCaller autoCaller(this);
1934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1935
1936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1937 *aFPS = mHWData->mVideoCaptureFPS;
1938 return S_OK;
1939}
1940
1941STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1942{
1943 AutoCaller autoCaller(this);
1944 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1945
1946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1947
1948 if ( Global::IsOnline(mData->mMachineState)
1949 && mHWData->mVideoCaptureEnabled)
1950 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1951
1952 setModified(IsModified_MachineData);
1953 mHWData.backup();
1954 mHWData->mVideoCaptureFPS = aFPS;
1955
1956 return S_OK;
1957}
1958
1959STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1960{
1961 CheckComArgOutPointerValid(aGraphicsControllerType);
1962
1963 AutoCaller autoCaller(this);
1964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1965
1966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1967
1968 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1969
1970 return S_OK;
1971}
1972
1973STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1974{
1975 switch (aGraphicsControllerType)
1976 {
1977 case GraphicsControllerType_Null:
1978 case GraphicsControllerType_VBoxVGA:
1979#ifdef VBOX_WITH_VMSVGA
1980 case GraphicsControllerType_VMSVGA:
1981#endif
1982 break;
1983 default:
1984 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1985 }
1986
1987 AutoCaller autoCaller(this);
1988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1989
1990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1991
1992 HRESULT rc = checkStateDependency(MutableStateDep);
1993 if (FAILED(rc)) return rc;
1994
1995 setModified(IsModified_MachineData);
1996 mHWData.backup();
1997 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1998
1999 return S_OK;
2000}
2001
2002STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
2003{
2004 CheckComArgOutPointerValid(memorySize);
2005
2006 AutoCaller autoCaller(this);
2007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2008
2009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 *memorySize = mHWData->mVRAMSize;
2012
2013 return S_OK;
2014}
2015
2016STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2017{
2018 /* check VRAM limits */
2019 if (memorySize < SchemaDefs::MinGuestVRAM ||
2020 memorySize > SchemaDefs::MaxGuestVRAM)
2021 return setError(E_INVALIDARG,
2022 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2023 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2024
2025 AutoCaller autoCaller(this);
2026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2027
2028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 HRESULT rc = checkStateDependency(MutableStateDep);
2031 if (FAILED(rc)) return rc;
2032
2033 setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mVRAMSize = memorySize;
2036
2037 return S_OK;
2038}
2039
2040/** @todo this method should not be public */
2041STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2042{
2043 CheckComArgOutPointerValid(memoryBalloonSize);
2044
2045 AutoCaller autoCaller(this);
2046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2047
2048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2051
2052 return S_OK;
2053}
2054
2055/**
2056 * Set the memory balloon size.
2057 *
2058 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2059 * we have to make sure that we never call IGuest from here.
2060 */
2061STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2062{
2063 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2064#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2065 /* check limits */
2066 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2067 return setError(E_INVALIDARG,
2068 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2069 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2070
2071 AutoCaller autoCaller(this);
2072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2073
2074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 setModified(IsModified_MachineData);
2077 mHWData.backup();
2078 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2079
2080 return S_OK;
2081#else
2082 NOREF(memoryBalloonSize);
2083 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2084#endif
2085}
2086
2087STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2088{
2089 CheckComArgOutPointerValid(aEnabled);
2090
2091 AutoCaller autoCaller(this);
2092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2093
2094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2095
2096 *aEnabled = mHWData->mPageFusionEnabled;
2097 return S_OK;
2098}
2099
2100STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2101{
2102#ifdef VBOX_WITH_PAGE_SHARING
2103 AutoCaller autoCaller(this);
2104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2105
2106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2107
2108 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2109 setModified(IsModified_MachineData);
2110 mHWData.backup();
2111 mHWData->mPageFusionEnabled = aEnabled;
2112 return S_OK;
2113#else
2114 NOREF(aEnabled);
2115 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2116#endif
2117}
2118
2119STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2120{
2121 CheckComArgOutPointerValid(aEnabled);
2122
2123 AutoCaller autoCaller(this);
2124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2125
2126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2127
2128 *aEnabled = mHWData->mAccelerate3DEnabled;
2129
2130 return S_OK;
2131}
2132
2133STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2134{
2135 AutoCaller autoCaller(this);
2136 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2137
2138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2139
2140 HRESULT rc = checkStateDependency(MutableStateDep);
2141 if (FAILED(rc)) return rc;
2142
2143 /** @todo check validity! */
2144
2145 setModified(IsModified_MachineData);
2146 mHWData.backup();
2147 mHWData->mAccelerate3DEnabled = enable;
2148
2149 return S_OK;
2150}
2151
2152
2153STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2154{
2155 CheckComArgOutPointerValid(aEnabled);
2156
2157 AutoCaller autoCaller(this);
2158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2159
2160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2161
2162 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2163
2164 return S_OK;
2165}
2166
2167STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2168{
2169 AutoCaller autoCaller(this);
2170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2171
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 HRESULT rc = checkStateDependency(MutableStateDep);
2175 if (FAILED(rc)) return rc;
2176
2177 /** @todo check validity! */
2178
2179 setModified(IsModified_MachineData);
2180 mHWData.backup();
2181 mHWData->mAccelerate2DVideoEnabled = enable;
2182
2183 return S_OK;
2184}
2185
2186STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2187{
2188 CheckComArgOutPointerValid(monitorCount);
2189
2190 AutoCaller autoCaller(this);
2191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2192
2193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2194
2195 *monitorCount = mHWData->mMonitorCount;
2196
2197 return S_OK;
2198}
2199
2200STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2201{
2202 /* make sure monitor count is a sensible number */
2203 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2204 return setError(E_INVALIDARG,
2205 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2206 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2207
2208 AutoCaller autoCaller(this);
2209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2210
2211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2212
2213 HRESULT rc = checkStateDependency(MutableStateDep);
2214 if (FAILED(rc)) return rc;
2215
2216 setModified(IsModified_MachineData);
2217 mHWData.backup();
2218 mHWData->mMonitorCount = monitorCount;
2219
2220 return S_OK;
2221}
2222
2223STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2224{
2225 CheckComArgOutPointerValid(biosSettings);
2226
2227 AutoCaller autoCaller(this);
2228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2229
2230 /* mBIOSSettings is constant during life time, no need to lock */
2231 mBIOSSettings.queryInterfaceTo(biosSettings);
2232
2233 return S_OK;
2234}
2235
2236STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2237{
2238 CheckComArgOutPointerValid(aVal);
2239
2240 AutoCaller autoCaller(this);
2241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2242
2243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2244
2245 switch (property)
2246 {
2247 case CPUPropertyType_PAE:
2248 *aVal = mHWData->mPAEEnabled;
2249 break;
2250
2251 case CPUPropertyType_Synthetic:
2252 *aVal = mHWData->mSyntheticCpu;
2253 break;
2254
2255 case CPUPropertyType_LongMode:
2256 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2257 *aVal = TRUE;
2258 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2259 *aVal = FALSE;
2260#if HC_ARCH_BITS == 64
2261 else
2262 *aVal = TRUE;
2263#else
2264 else
2265 {
2266 *aVal = FALSE;
2267
2268 ComPtr<IGuestOSType> ptrGuestOSType;
2269 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2270 if (SUCCEEDED(hrc2))
2271 {
2272 BOOL fIs64Bit = FALSE;
2273 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2274 if (SUCCEEDED(hrc2) && fIs64Bit)
2275 {
2276 ComObjPtr<Host> ptrHost = mParent->i_host();
2277 alock.release();
2278
2279 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2280 if (FAILED(hrc2))
2281 *aVal = FALSE;
2282 }
2283 }
2284 }
2285#endif
2286 break;
2287
2288 case CPUPropertyType_TripleFaultReset:
2289 *aVal = mHWData->mTripleFaultReset;
2290 break;
2291
2292 default:
2293 return E_INVALIDARG;
2294 }
2295 return S_OK;
2296}
2297
2298STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2299{
2300 AutoCaller autoCaller(this);
2301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2302
2303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2304
2305 HRESULT rc = checkStateDependency(MutableStateDep);
2306 if (FAILED(rc)) return rc;
2307
2308 switch (property)
2309 {
2310 case CPUPropertyType_PAE:
2311 setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mPAEEnabled = !!aVal;
2314 break;
2315
2316 case CPUPropertyType_Synthetic:
2317 setModified(IsModified_MachineData);
2318 mHWData.backup();
2319 mHWData->mSyntheticCpu = !!aVal;
2320 break;
2321
2322 case CPUPropertyType_LongMode:
2323 setModified(IsModified_MachineData);
2324 mHWData.backup();
2325 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2326 break;
2327
2328 case CPUPropertyType_TripleFaultReset:
2329 setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 mHWData->mTripleFaultReset = !!aVal;
2332 break;
2333
2334 default:
2335 return E_INVALIDARG;
2336 }
2337 return S_OK;
2338}
2339
2340STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2341{
2342 CheckComArgOutPointerValid(aValEax);
2343 CheckComArgOutPointerValid(aValEbx);
2344 CheckComArgOutPointerValid(aValEcx);
2345 CheckComArgOutPointerValid(aValEdx);
2346
2347 AutoCaller autoCaller(this);
2348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2349
2350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2351
2352 switch(aId)
2353 {
2354 case 0x0:
2355 case 0x1:
2356 case 0x2:
2357 case 0x3:
2358 case 0x4:
2359 case 0x5:
2360 case 0x6:
2361 case 0x7:
2362 case 0x8:
2363 case 0x9:
2364 case 0xA:
2365 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2366 return E_INVALIDARG;
2367
2368 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2369 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2370 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2371 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2372 break;
2373
2374 case 0x80000000:
2375 case 0x80000001:
2376 case 0x80000002:
2377 case 0x80000003:
2378 case 0x80000004:
2379 case 0x80000005:
2380 case 0x80000006:
2381 case 0x80000007:
2382 case 0x80000008:
2383 case 0x80000009:
2384 case 0x8000000A:
2385 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2386 return E_INVALIDARG;
2387
2388 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2389 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2390 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2391 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2392 break;
2393
2394 default:
2395 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2396 }
2397 return S_OK;
2398}
2399
2400STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2401{
2402 AutoCaller autoCaller(this);
2403 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2404
2405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2406
2407 HRESULT rc = checkStateDependency(MutableStateDep);
2408 if (FAILED(rc)) return rc;
2409
2410 switch(aId)
2411 {
2412 case 0x0:
2413 case 0x1:
2414 case 0x2:
2415 case 0x3:
2416 case 0x4:
2417 case 0x5:
2418 case 0x6:
2419 case 0x7:
2420 case 0x8:
2421 case 0x9:
2422 case 0xA:
2423 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2424 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2425 setModified(IsModified_MachineData);
2426 mHWData.backup();
2427 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2428 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2429 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2430 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2431 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2432 break;
2433
2434 case 0x80000000:
2435 case 0x80000001:
2436 case 0x80000002:
2437 case 0x80000003:
2438 case 0x80000004:
2439 case 0x80000005:
2440 case 0x80000006:
2441 case 0x80000007:
2442 case 0x80000008:
2443 case 0x80000009:
2444 case 0x8000000A:
2445 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2446 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2447 setModified(IsModified_MachineData);
2448 mHWData.backup();
2449 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2450 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2451 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2452 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2453 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2454 break;
2455
2456 default:
2457 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2458 }
2459 return S_OK;
2460}
2461
2462STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2463{
2464 AutoCaller autoCaller(this);
2465 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2466
2467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2468
2469 HRESULT rc = checkStateDependency(MutableStateDep);
2470 if (FAILED(rc)) return rc;
2471
2472 switch(aId)
2473 {
2474 case 0x0:
2475 case 0x1:
2476 case 0x2:
2477 case 0x3:
2478 case 0x4:
2479 case 0x5:
2480 case 0x6:
2481 case 0x7:
2482 case 0x8:
2483 case 0x9:
2484 case 0xA:
2485 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2486 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2487 setModified(IsModified_MachineData);
2488 mHWData.backup();
2489 /* Invalidate leaf. */
2490 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2491 break;
2492
2493 case 0x80000000:
2494 case 0x80000001:
2495 case 0x80000002:
2496 case 0x80000003:
2497 case 0x80000004:
2498 case 0x80000005:
2499 case 0x80000006:
2500 case 0x80000007:
2501 case 0x80000008:
2502 case 0x80000009:
2503 case 0x8000000A:
2504 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2505 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2506 setModified(IsModified_MachineData);
2507 mHWData.backup();
2508 /* Invalidate leaf. */
2509 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2510 break;
2511
2512 default:
2513 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2514 }
2515 return S_OK;
2516}
2517
2518STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2519{
2520 AutoCaller autoCaller(this);
2521 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2522
2523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 HRESULT rc = checkStateDependency(MutableStateDep);
2526 if (FAILED(rc)) return rc;
2527
2528 setModified(IsModified_MachineData);
2529 mHWData.backup();
2530
2531 /* Invalidate all standard leafs. */
2532 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2533 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2534
2535 /* Invalidate all extended leafs. */
2536 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2537 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2538
2539 return S_OK;
2540}
2541
2542STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2543{
2544 CheckComArgOutPointerValid(aVal);
2545
2546 AutoCaller autoCaller(this);
2547 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2548
2549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2550
2551 switch(property)
2552 {
2553 case HWVirtExPropertyType_Enabled:
2554 *aVal = mHWData->mHWVirtExEnabled;
2555 break;
2556
2557 case HWVirtExPropertyType_VPID:
2558 *aVal = mHWData->mHWVirtExVPIDEnabled;
2559 break;
2560
2561 case HWVirtExPropertyType_NestedPaging:
2562 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2563 break;
2564
2565 case HWVirtExPropertyType_UnrestrictedExecution:
2566 *aVal = mHWData->mHWVirtExUXEnabled;
2567 break;
2568
2569 case HWVirtExPropertyType_LargePages:
2570 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2571#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2572 *aVal = FALSE;
2573#endif
2574 break;
2575
2576 case HWVirtExPropertyType_Force:
2577 *aVal = mHWData->mHWVirtExForceEnabled;
2578 break;
2579
2580 default:
2581 return E_INVALIDARG;
2582 }
2583 return S_OK;
2584}
2585
2586STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2587{
2588 AutoCaller autoCaller(this);
2589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2590
2591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 HRESULT rc = checkStateDependency(MutableStateDep);
2594 if (FAILED(rc)) return rc;
2595
2596 switch(property)
2597 {
2598 case HWVirtExPropertyType_Enabled:
2599 setModified(IsModified_MachineData);
2600 mHWData.backup();
2601 mHWData->mHWVirtExEnabled = !!aVal;
2602 break;
2603
2604 case HWVirtExPropertyType_VPID:
2605 setModified(IsModified_MachineData);
2606 mHWData.backup();
2607 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2608 break;
2609
2610 case HWVirtExPropertyType_NestedPaging:
2611 setModified(IsModified_MachineData);
2612 mHWData.backup();
2613 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2614 break;
2615
2616 case HWVirtExPropertyType_UnrestrictedExecution:
2617 setModified(IsModified_MachineData);
2618 mHWData.backup();
2619 mHWData->mHWVirtExUXEnabled = !!aVal;
2620 break;
2621
2622 case HWVirtExPropertyType_LargePages:
2623 setModified(IsModified_MachineData);
2624 mHWData.backup();
2625 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2626 break;
2627
2628 case HWVirtExPropertyType_Force:
2629 setModified(IsModified_MachineData);
2630 mHWData.backup();
2631 mHWData->mHWVirtExForceEnabled = !!aVal;
2632 break;
2633
2634 default:
2635 return E_INVALIDARG;
2636 }
2637
2638 return S_OK;
2639}
2640
2641STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2642{
2643 CheckComArgOutPointerValid(aSnapshotFolder);
2644
2645 AutoCaller autoCaller(this);
2646 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2647
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 Utf8Str strFullSnapshotFolder;
2651 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2652 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2653
2654 return S_OK;
2655}
2656
2657STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2658{
2659 /* @todo (r=dmik):
2660 * 1. Allow to change the name of the snapshot folder containing snapshots
2661 * 2. Rename the folder on disk instead of just changing the property
2662 * value (to be smart and not to leave garbage). Note that it cannot be
2663 * done here because the change may be rolled back. Thus, the right
2664 * place is #saveSettings().
2665 */
2666
2667 AutoCaller autoCaller(this);
2668 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2669
2670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2671
2672 HRESULT rc = checkStateDependency(MutableStateDep);
2673 if (FAILED(rc)) return rc;
2674
2675 if (!mData->mCurrentSnapshot.isNull())
2676 return setError(E_FAIL,
2677 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2678
2679 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2680
2681 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2682 if (strSnapshotFolder.isEmpty())
2683 strSnapshotFolder = "Snapshots";
2684 int vrc = calculateFullPath(strSnapshotFolder,
2685 strSnapshotFolder);
2686 if (RT_FAILURE(vrc))
2687 return setError(E_FAIL,
2688 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2689 aSnapshotFolder, vrc);
2690
2691 setModified(IsModified_MachineData);
2692 mUserData.backup();
2693
2694 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2695
2696 return S_OK;
2697}
2698
2699STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2700{
2701 CheckComArgOutSafeArrayPointerValid(aAttachments);
2702
2703 AutoCaller autoCaller(this);
2704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2705
2706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2707
2708 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2709 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2710
2711 return S_OK;
2712}
2713
2714STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2715{
2716 CheckComArgOutPointerValid(vrdeServer);
2717
2718 AutoCaller autoCaller(this);
2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 Assert(!!mVRDEServer);
2724 mVRDEServer.queryInterfaceTo(vrdeServer);
2725
2726 return S_OK;
2727}
2728
2729STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2730{
2731 CheckComArgOutPointerValid(audioAdapter);
2732
2733 AutoCaller autoCaller(this);
2734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2735
2736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 mAudioAdapter.queryInterfaceTo(audioAdapter);
2739 return S_OK;
2740}
2741
2742STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2743{
2744#ifdef VBOX_WITH_VUSB
2745 CheckComArgOutPointerValid(aUSBControllers);
2746
2747 AutoCaller autoCaller(this);
2748 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2749
2750 clearError();
2751 MultiResult rc(S_OK);
2752
2753# ifdef VBOX_WITH_USB
2754 rc = mParent->i_host()->i_checkUSBProxyService();
2755 if (FAILED(rc)) return rc;
2756# endif
2757
2758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2761 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2762 return S_OK;
2763#else
2764 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2765 * extended error info to indicate that USB is simply not available
2766 * (w/o treating it as a failure), for example, as in OSE */
2767 NOREF(aUSBControllers);
2768 ReturnComNotImplemented();
2769#endif /* VBOX_WITH_VUSB */
2770}
2771
2772STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2773{
2774#ifdef VBOX_WITH_VUSB
2775 CheckComArgOutPointerValid(aUSBDeviceFilters);
2776
2777 AutoCaller autoCaller(this);
2778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2779
2780 clearError();
2781 MultiResult rc(S_OK);
2782
2783# ifdef VBOX_WITH_USB
2784 rc = mParent->i_host()->i_checkUSBProxyService();
2785 if (FAILED(rc)) return rc;
2786# endif
2787
2788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2791#else
2792 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2793 * extended error info to indicate that USB is simply not available
2794 * (w/o treating it as a failure), for example, as in OSE */
2795 NOREF(aUSBDeviceFilters);
2796 ReturnComNotImplemented();
2797#endif /* VBOX_WITH_VUSB */
2798}
2799
2800STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2801{
2802 CheckComArgOutPointerValid(aFilePath);
2803
2804 AutoLimitedCaller autoCaller(this);
2805 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2806
2807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2808
2809 mData->m_strConfigFileFull.cloneTo(aFilePath);
2810 return S_OK;
2811}
2812
2813STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2814{
2815 CheckComArgOutPointerValid(aModified);
2816
2817 AutoCaller autoCaller(this);
2818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2819
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 HRESULT rc = checkStateDependency(MutableStateDep);
2823 if (FAILED(rc)) return rc;
2824
2825 if (!mData->pMachineConfigFile->fileExists())
2826 // this is a new machine, and no config file exists yet:
2827 *aModified = TRUE;
2828 else
2829 *aModified = (mData->flModifications != 0);
2830
2831 return S_OK;
2832}
2833
2834STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2835{
2836 CheckComArgOutPointerValid(aSessionState);
2837
2838 AutoCaller autoCaller(this);
2839 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2840
2841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2842
2843 *aSessionState = mData->mSession.mState;
2844
2845 return S_OK;
2846}
2847
2848STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2849{
2850 CheckComArgOutPointerValid(aSessionType);
2851
2852 AutoCaller autoCaller(this);
2853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2854
2855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 mData->mSession.mType.cloneTo(aSessionType);
2858
2859 return S_OK;
2860}
2861
2862STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2863{
2864 CheckComArgOutPointerValid(aSessionPID);
2865
2866 AutoCaller autoCaller(this);
2867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2868
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870
2871 *aSessionPID = mData->mSession.mPID;
2872
2873 return S_OK;
2874}
2875
2876STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2877{
2878 CheckComArgOutPointerValid(machineState);
2879
2880 AutoCaller autoCaller(this);
2881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2882
2883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2884
2885 *machineState = mData->mMachineState;
2886
2887 return S_OK;
2888}
2889
2890STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2891{
2892 CheckComArgOutPointerValid(aLastStateChange);
2893
2894 AutoCaller autoCaller(this);
2895 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2896
2897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2900
2901 return S_OK;
2902}
2903
2904STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2905{
2906 CheckComArgOutPointerValid(aStateFilePath);
2907
2908 AutoCaller autoCaller(this);
2909 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2910
2911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2912
2913 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2919{
2920 CheckComArgOutPointerValid(aLogFolder);
2921
2922 AutoCaller autoCaller(this);
2923 AssertComRCReturnRC(autoCaller.rc());
2924
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 Utf8Str logFolder;
2928 getLogFolder(logFolder);
2929 logFolder.cloneTo(aLogFolder);
2930
2931 return S_OK;
2932}
2933
2934STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2935{
2936 CheckComArgOutPointerValid(aCurrentSnapshot);
2937
2938 AutoCaller autoCaller(this);
2939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2940
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2944
2945 return S_OK;
2946}
2947
2948STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2949{
2950 CheckComArgOutPointerValid(aSnapshotCount);
2951
2952 AutoCaller autoCaller(this);
2953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2954
2955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2956
2957 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2958 ? 0
2959 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2960
2961 return S_OK;
2962}
2963
2964STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2965{
2966 CheckComArgOutPointerValid(aCurrentStateModified);
2967
2968 AutoCaller autoCaller(this);
2969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2970
2971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2972
2973 /* Note: for machines with no snapshots, we always return FALSE
2974 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2975 * reasons :) */
2976
2977 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2978 ? FALSE
2979 : mData->mCurrentStateModified;
2980
2981 return S_OK;
2982}
2983
2984STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2985{
2986 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2987
2988 AutoCaller autoCaller(this);
2989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2990
2991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2992
2993 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2994 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2995
2996 return S_OK;
2997}
2998
2999STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
3000{
3001 CheckComArgOutPointerValid(aClipboardMode);
3002
3003 AutoCaller autoCaller(this);
3004 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3005
3006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3007
3008 *aClipboardMode = mHWData->mClipboardMode;
3009
3010 return S_OK;
3011}
3012
3013STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
3014{
3015 HRESULT rc = S_OK;
3016
3017 AutoCaller autoCaller(this);
3018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3019
3020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3021
3022 alock.release();
3023 rc = onClipboardModeChange(aClipboardMode);
3024 alock.acquire();
3025 if (FAILED(rc)) return rc;
3026
3027 setModified(IsModified_MachineData);
3028 mHWData.backup();
3029 mHWData->mClipboardMode = aClipboardMode;
3030
3031 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3032 if (Global::IsOnline(mData->mMachineState))
3033 saveSettings(NULL);
3034
3035 return S_OK;
3036}
3037
3038STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3039{
3040 CheckComArgOutPointerValid(aDragAndDropMode);
3041
3042 AutoCaller autoCaller(this);
3043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3044
3045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 *aDragAndDropMode = mHWData->mDragAndDropMode;
3048
3049 return S_OK;
3050}
3051
3052STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3053{
3054 HRESULT rc = S_OK;
3055
3056 AutoCaller autoCaller(this);
3057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3058
3059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3060
3061 alock.release();
3062 rc = onDragAndDropModeChange(aDragAndDropMode);
3063 alock.acquire();
3064 if (FAILED(rc)) return rc;
3065
3066 setModified(IsModified_MachineData);
3067 mHWData.backup();
3068 mHWData->mDragAndDropMode = aDragAndDropMode;
3069
3070 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3071 if (Global::IsOnline(mData->mMachineState))
3072 saveSettings(NULL);
3073
3074 return S_OK;
3075}
3076
3077STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3078{
3079 CheckComArgOutPointerValid(aPatterns);
3080
3081 AutoCaller autoCaller(this);
3082 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3083
3084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3085
3086 try
3087 {
3088 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3089 }
3090 catch (...)
3091 {
3092 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3093 }
3094
3095 return S_OK;
3096}
3097
3098STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3099{
3100 AutoCaller autoCaller(this);
3101 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3102
3103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 HRESULT rc = checkStateDependency(MutableStateDep);
3106 if (FAILED(rc)) return rc;
3107
3108 setModified(IsModified_MachineData);
3109 mHWData.backup();
3110 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3111 return rc;
3112}
3113
3114STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3115{
3116 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3117
3118 AutoCaller autoCaller(this);
3119 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3120
3121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3122
3123 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3124 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3125
3126 return S_OK;
3127}
3128
3129STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3130{
3131 CheckComArgOutPointerValid(aEnabled);
3132
3133 AutoCaller autoCaller(this);
3134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3135
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 *aEnabled = mUserData->s.fTeleporterEnabled;
3139
3140 return S_OK;
3141}
3142
3143STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3144{
3145 AutoCaller autoCaller(this);
3146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3147
3148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 /* Only allow it to be set to true when PoweredOff or Aborted.
3151 (Clearing it is always permitted.) */
3152 if ( aEnabled
3153 && mData->mRegistered
3154 && ( !isSessionMachine()
3155 || ( mData->mMachineState != MachineState_PoweredOff
3156 && mData->mMachineState != MachineState_Teleported
3157 && mData->mMachineState != MachineState_Aborted
3158 )
3159 )
3160 )
3161 return setError(VBOX_E_INVALID_VM_STATE,
3162 tr("The machine is not powered off (state is %s)"),
3163 Global::stringifyMachineState(mData->mMachineState));
3164
3165 setModified(IsModified_MachineData);
3166 mUserData.backup();
3167 mUserData->s.fTeleporterEnabled = !!aEnabled;
3168
3169 return S_OK;
3170}
3171
3172STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3173{
3174 CheckComArgOutPointerValid(aPort);
3175
3176 AutoCaller autoCaller(this);
3177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3178
3179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3182
3183 return S_OK;
3184}
3185
3186STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3187{
3188 if (aPort >= _64K)
3189 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3190
3191 AutoCaller autoCaller(this);
3192 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3193
3194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3195
3196 HRESULT rc = checkStateDependency(MutableStateDep);
3197 if (FAILED(rc)) return rc;
3198
3199 setModified(IsModified_MachineData);
3200 mUserData.backup();
3201 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3202
3203 return S_OK;
3204}
3205
3206STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3207{
3208 CheckComArgOutPointerValid(aAddress);
3209
3210 AutoCaller autoCaller(this);
3211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3212
3213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3214
3215 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3216
3217 return S_OK;
3218}
3219
3220STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3221{
3222 AutoCaller autoCaller(this);
3223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3224
3225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3226
3227 HRESULT rc = checkStateDependency(MutableStateDep);
3228 if (FAILED(rc)) return rc;
3229
3230 setModified(IsModified_MachineData);
3231 mUserData.backup();
3232 mUserData->s.strTeleporterAddress = aAddress;
3233
3234 return S_OK;
3235}
3236
3237STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3238{
3239 CheckComArgOutPointerValid(aPassword);
3240
3241 AutoCaller autoCaller(this);
3242 HRESULT hrc = autoCaller.rc();
3243 if (SUCCEEDED(hrc))
3244 {
3245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3246 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3247 }
3248
3249 return hrc;
3250}
3251
3252STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3253{
3254 /*
3255 * Hash the password first.
3256 */
3257 Utf8Str strPassword(aPassword);
3258 if (!strPassword.isEmpty())
3259 {
3260 if (VBoxIsPasswordHashed(&strPassword))
3261 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3262 VBoxHashPassword(&strPassword);
3263 }
3264
3265 /*
3266 * Do the update.
3267 */
3268 AutoCaller autoCaller(this);
3269 HRESULT hrc = autoCaller.rc();
3270 if (SUCCEEDED(hrc))
3271 {
3272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3273 hrc = checkStateDependency(MutableStateDep);
3274 if (SUCCEEDED(hrc))
3275 {
3276 setModified(IsModified_MachineData);
3277 mUserData.backup();
3278 mUserData->s.strTeleporterPassword = strPassword;
3279 }
3280 }
3281
3282 return hrc;
3283}
3284
3285STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3286{
3287 CheckComArgOutPointerValid(aState);
3288
3289 AutoCaller autoCaller(this);
3290 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3291
3292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3293
3294 *aState = mUserData->s.enmFaultToleranceState;
3295 return S_OK;
3296}
3297
3298STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3299{
3300 AutoCaller autoCaller(this);
3301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3302
3303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3304
3305 /* @todo deal with running state change. */
3306 HRESULT rc = checkStateDependency(MutableStateDep);
3307 if (FAILED(rc)) return rc;
3308
3309 setModified(IsModified_MachineData);
3310 mUserData.backup();
3311 mUserData->s.enmFaultToleranceState = aState;
3312 return S_OK;
3313}
3314
3315STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3316{
3317 CheckComArgOutPointerValid(aAddress);
3318
3319 AutoCaller autoCaller(this);
3320 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3321
3322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3323
3324 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3325 return S_OK;
3326}
3327
3328STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3329{
3330 AutoCaller autoCaller(this);
3331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3332
3333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3334
3335 /* @todo deal with running state change. */
3336 HRESULT rc = checkStateDependency(MutableStateDep);
3337 if (FAILED(rc)) return rc;
3338
3339 setModified(IsModified_MachineData);
3340 mUserData.backup();
3341 mUserData->s.strFaultToleranceAddress = aAddress;
3342 return S_OK;
3343}
3344
3345STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3346{
3347 CheckComArgOutPointerValid(aPort);
3348
3349 AutoCaller autoCaller(this);
3350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3351
3352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3353
3354 *aPort = mUserData->s.uFaultTolerancePort;
3355 return S_OK;
3356}
3357
3358STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3359{
3360 AutoCaller autoCaller(this);
3361 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3362
3363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3364
3365 /* @todo deal with running state change. */
3366 HRESULT rc = checkStateDependency(MutableStateDep);
3367 if (FAILED(rc)) return rc;
3368
3369 setModified(IsModified_MachineData);
3370 mUserData.backup();
3371 mUserData->s.uFaultTolerancePort = aPort;
3372 return S_OK;
3373}
3374
3375STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3376{
3377 CheckComArgOutPointerValid(aPassword);
3378
3379 AutoCaller autoCaller(this);
3380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3381
3382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3383
3384 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3385
3386 return S_OK;
3387}
3388
3389STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3390{
3391 AutoCaller autoCaller(this);
3392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3393
3394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3395
3396 /* @todo deal with running state change. */
3397 HRESULT rc = checkStateDependency(MutableStateDep);
3398 if (FAILED(rc)) return rc;
3399
3400 setModified(IsModified_MachineData);
3401 mUserData.backup();
3402 mUserData->s.strFaultTolerancePassword = aPassword;
3403
3404 return S_OK;
3405}
3406
3407STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3408{
3409 CheckComArgOutPointerValid(aInterval);
3410
3411 AutoCaller autoCaller(this);
3412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3413
3414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3415
3416 *aInterval = mUserData->s.uFaultToleranceInterval;
3417 return S_OK;
3418}
3419
3420STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3421{
3422 AutoCaller autoCaller(this);
3423 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3424
3425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3426
3427 /* @todo deal with running state change. */
3428 HRESULT rc = checkStateDependency(MutableStateDep);
3429 if (FAILED(rc)) return rc;
3430
3431 setModified(IsModified_MachineData);
3432 mUserData.backup();
3433 mUserData->s.uFaultToleranceInterval = aInterval;
3434 return S_OK;
3435}
3436
3437STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3438{
3439 CheckComArgOutPointerValid(aEnabled);
3440
3441 AutoCaller autoCaller(this);
3442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3443
3444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3445
3446 *aEnabled = mUserData->s.fRTCUseUTC;
3447
3448 return S_OK;
3449}
3450
3451STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3452{
3453 AutoCaller autoCaller(this);
3454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3455
3456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3457
3458 /* Only allow it to be set to true when PoweredOff or Aborted.
3459 (Clearing it is always permitted.) */
3460 if ( aEnabled
3461 && mData->mRegistered
3462 && ( !isSessionMachine()
3463 || ( mData->mMachineState != MachineState_PoweredOff
3464 && mData->mMachineState != MachineState_Teleported
3465 && mData->mMachineState != MachineState_Aborted
3466 )
3467 )
3468 )
3469 return setError(VBOX_E_INVALID_VM_STATE,
3470 tr("The machine is not powered off (state is %s)"),
3471 Global::stringifyMachineState(mData->mMachineState));
3472
3473 setModified(IsModified_MachineData);
3474 mUserData.backup();
3475 mUserData->s.fRTCUseUTC = !!aEnabled;
3476
3477 return S_OK;
3478}
3479
3480STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3481{
3482 CheckComArgOutPointerValid(aEnabled);
3483
3484 AutoCaller autoCaller(this);
3485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3486
3487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3488
3489 *aEnabled = mHWData->mIOCacheEnabled;
3490
3491 return S_OK;
3492}
3493
3494STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3495{
3496 AutoCaller autoCaller(this);
3497 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3498
3499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3500
3501 HRESULT rc = checkStateDependency(MutableStateDep);
3502 if (FAILED(rc)) return rc;
3503
3504 setModified(IsModified_MachineData);
3505 mHWData.backup();
3506 mHWData->mIOCacheEnabled = aEnabled;
3507
3508 return S_OK;
3509}
3510
3511STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3512{
3513 CheckComArgOutPointerValid(aIOCacheSize);
3514
3515 AutoCaller autoCaller(this);
3516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3517
3518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3519
3520 *aIOCacheSize = mHWData->mIOCacheSize;
3521
3522 return S_OK;
3523}
3524
3525STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3526{
3527 AutoCaller autoCaller(this);
3528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3529
3530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3531
3532 HRESULT rc = checkStateDependency(MutableStateDep);
3533 if (FAILED(rc)) return rc;
3534
3535 setModified(IsModified_MachineData);
3536 mHWData.backup();
3537 mHWData->mIOCacheSize = aIOCacheSize;
3538
3539 return S_OK;
3540}
3541
3542
3543/**
3544 * @note Locks objects!
3545 */
3546STDMETHODIMP Machine::LockMachine(ISession *aSession,
3547 LockType_T lockType)
3548{
3549 CheckComArgNotNull(aSession);
3550
3551 AutoCaller autoCaller(this);
3552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3553
3554 /* check the session state */
3555 SessionState_T state;
3556 HRESULT rc = aSession->COMGETTER(State)(&state);
3557 if (FAILED(rc)) return rc;
3558
3559 if (state != SessionState_Unlocked)
3560 return setError(VBOX_E_INVALID_OBJECT_STATE,
3561 tr("The given session is busy"));
3562
3563 // get the client's IInternalSessionControl interface
3564 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3565 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3566 E_INVALIDARG);
3567
3568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3569
3570 if (!mData->mRegistered)
3571 return setError(E_UNEXPECTED,
3572 tr("The machine '%s' is not registered"),
3573 mUserData->s.strName.c_str());
3574
3575 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3576
3577 SessionState_T oldState = mData->mSession.mState;
3578 /* Hack: in case the session is closing and there is a progress object
3579 * which allows waiting for the session to be closed, take the opportunity
3580 * and do a limited wait (max. 1 second). This helps a lot when the system
3581 * is busy and thus session closing can take a little while. */
3582 if ( mData->mSession.mState == SessionState_Unlocking
3583 && mData->mSession.mProgress)
3584 {
3585 alock.release();
3586 mData->mSession.mProgress->WaitForCompletion(1000);
3587 alock.acquire();
3588 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3589 }
3590
3591 // try again now
3592 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3593 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3594 )
3595 {
3596 // OK, share the session... we are now dealing with three processes:
3597 // 1) VBoxSVC (where this code runs);
3598 // 2) process C: the caller's client process (who wants a shared session);
3599 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3600
3601 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3602 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3603 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3604 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3605 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3606
3607 /*
3608 * Release the lock before calling the client process. It's safe here
3609 * since the only thing to do after we get the lock again is to add
3610 * the remote control to the list (which doesn't directly influence
3611 * anything).
3612 */
3613 alock.release();
3614
3615 // get the console of the session holding the write lock (this is a remote call)
3616 ComPtr<IConsole> pConsoleW;
3617 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3618 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3619 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3620 if (FAILED(rc))
3621 // the failure may occur w/o any error info (from RPC), so provide one
3622 return setError(VBOX_E_VM_ERROR,
3623 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3624
3625 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3626
3627 // share the session machine and W's console with the caller's session
3628 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3629 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3630 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3631
3632 if (FAILED(rc))
3633 // the failure may occur w/o any error info (from RPC), so provide one
3634 return setError(VBOX_E_VM_ERROR,
3635 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3636 alock.acquire();
3637
3638 // need to revalidate the state after acquiring the lock again
3639 if (mData->mSession.mState != SessionState_Locked)
3640 {
3641 pSessionControl->Uninitialize();
3642 return setError(VBOX_E_INVALID_SESSION_STATE,
3643 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3644 mUserData->s.strName.c_str());
3645 }
3646
3647 // add the caller's session to the list
3648 mData->mSession.mRemoteControls.push_back(pSessionControl);
3649 }
3650 else if ( mData->mSession.mState == SessionState_Locked
3651 || mData->mSession.mState == SessionState_Unlocking
3652 )
3653 {
3654 // sharing not permitted, or machine still unlocking:
3655 return setError(VBOX_E_INVALID_OBJECT_STATE,
3656 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3657 mUserData->s.strName.c_str());
3658 }
3659 else
3660 {
3661 // machine is not locked: then write-lock the machine (create the session machine)
3662
3663 // must not be busy
3664 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3665
3666 // get the caller's session PID
3667 RTPROCESS pid = NIL_RTPROCESS;
3668 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3669 pSessionControl->GetPID((ULONG*)&pid);
3670 Assert(pid != NIL_RTPROCESS);
3671
3672 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3673
3674 if (fLaunchingVMProcess)
3675 {
3676 if (mData->mSession.mPID == NIL_RTPROCESS)
3677 {
3678 // two or more clients racing for a lock, the one which set the
3679 // session state to Spawning will win, the others will get an
3680 // error as we can't decide here if waiting a little would help
3681 // (only for shared locks this would avoid an error)
3682 return setError(VBOX_E_INVALID_OBJECT_STATE,
3683 tr("The machine '%s' already has a lock request pending"),
3684 mUserData->s.strName.c_str());
3685 }
3686
3687 // this machine is awaiting for a spawning session to be opened:
3688 // then the calling process must be the one that got started by
3689 // LaunchVMProcess()
3690
3691 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3692 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3693
3694 if (mData->mSession.mPID != pid)
3695 return setError(E_ACCESSDENIED,
3696 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3697 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3698 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3699 }
3700
3701 // create the mutable SessionMachine from the current machine
3702 ComObjPtr<SessionMachine> sessionMachine;
3703 sessionMachine.createObject();
3704 rc = sessionMachine->init(this);
3705 AssertComRC(rc);
3706
3707 /* NOTE: doing return from this function after this point but
3708 * before the end is forbidden since it may call SessionMachine::uninit()
3709 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3710 * lock while still holding the Machine lock in alock so that a deadlock
3711 * is possible due to the wrong lock order. */
3712
3713 if (SUCCEEDED(rc))
3714 {
3715 /*
3716 * Set the session state to Spawning to protect against subsequent
3717 * attempts to open a session and to unregister the machine after
3718 * we release the lock.
3719 */
3720 SessionState_T origState = mData->mSession.mState;
3721 mData->mSession.mState = SessionState_Spawning;
3722
3723#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3724 /* Get the client token ID to be passed to the client process */
3725 Utf8Str strTokenId;
3726 sessionMachine->getTokenId(strTokenId);
3727 Assert(!strTokenId.isEmpty());
3728#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3729 /* Get the client token to be passed to the client process */
3730 ComPtr<IToken> pToken(sessionMachine->getToken());
3731 /* The token is now "owned" by pToken, fix refcount */
3732 if (!pToken.isNull())
3733 pToken->Release();
3734#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3735
3736 /*
3737 * Release the lock before calling the client process -- it will call
3738 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3739 * because the state is Spawning, so that LaunchVMProcess() and
3740 * LockMachine() calls will fail. This method, called before we
3741 * acquire the lock again, will fail because of the wrong PID.
3742 *
3743 * Note that mData->mSession.mRemoteControls accessed outside
3744 * the lock may not be modified when state is Spawning, so it's safe.
3745 */
3746 alock.release();
3747
3748 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3749#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3750 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3751#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3752 rc = pSessionControl->AssignMachine(sessionMachine, lockType, pToken);
3753 /* Now the token is owned by the client process. */
3754 pToken.setNull();
3755#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3756 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3757
3758 /* The failure may occur w/o any error info (from RPC), so provide one */
3759 if (FAILED(rc))
3760 setError(VBOX_E_VM_ERROR,
3761 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3762
3763 if ( SUCCEEDED(rc)
3764 && fLaunchingVMProcess
3765 )
3766 {
3767 /* complete the remote session initialization */
3768
3769 /* get the console from the direct session */
3770 ComPtr<IConsole> console;
3771 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3772 ComAssertComRC(rc);
3773
3774 if (SUCCEEDED(rc) && !console)
3775 {
3776 ComAssert(!!console);
3777 rc = E_FAIL;
3778 }
3779
3780 /* assign machine & console to the remote session */
3781 if (SUCCEEDED(rc))
3782 {
3783 /*
3784 * after LaunchVMProcess(), the first and the only
3785 * entry in remoteControls is that remote session
3786 */
3787 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3788 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3789 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3790
3791 /* The failure may occur w/o any error info (from RPC), so provide one */
3792 if (FAILED(rc))
3793 setError(VBOX_E_VM_ERROR,
3794 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3795 }
3796
3797 if (FAILED(rc))
3798 pSessionControl->Uninitialize();
3799 }
3800
3801 /* acquire the lock again */
3802 alock.acquire();
3803
3804 /* Restore the session state */
3805 mData->mSession.mState = origState;
3806 }
3807
3808 // finalize spawning anyway (this is why we don't return on errors above)
3809 if (fLaunchingVMProcess)
3810 {
3811 /* Note that the progress object is finalized later */
3812 /** @todo Consider checking mData->mSession.mProgress for cancellation
3813 * around here. */
3814
3815 /* We don't reset mSession.mPID here because it is necessary for
3816 * SessionMachine::uninit() to reap the child process later. */
3817
3818 if (FAILED(rc))
3819 {
3820 /* Close the remote session, remove the remote control from the list
3821 * and reset session state to Closed (@note keep the code in sync
3822 * with the relevant part in checkForSpawnFailure()). */
3823
3824 Assert(mData->mSession.mRemoteControls.size() == 1);
3825 if (mData->mSession.mRemoteControls.size() == 1)
3826 {
3827 ErrorInfoKeeper eik;
3828 mData->mSession.mRemoteControls.front()->Uninitialize();
3829 }
3830
3831 mData->mSession.mRemoteControls.clear();
3832 mData->mSession.mState = SessionState_Unlocked;
3833 }
3834 }
3835 else
3836 {
3837 /* memorize PID of the directly opened session */
3838 if (SUCCEEDED(rc))
3839 mData->mSession.mPID = pid;
3840 }
3841
3842 if (SUCCEEDED(rc))
3843 {
3844 /* memorize the direct session control and cache IUnknown for it */
3845 mData->mSession.mDirectControl = pSessionControl;
3846 mData->mSession.mState = SessionState_Locked;
3847 /* associate the SessionMachine with this Machine */
3848 mData->mSession.mMachine = sessionMachine;
3849
3850 /* request an IUnknown pointer early from the remote party for later
3851 * identity checks (it will be internally cached within mDirectControl
3852 * at least on XPCOM) */
3853 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3854 NOREF(unk);
3855 }
3856
3857 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3858 * would break the lock order */
3859 alock.release();
3860
3861 /* uninitialize the created session machine on failure */
3862 if (FAILED(rc))
3863 sessionMachine->uninit();
3864
3865 }
3866
3867 if (SUCCEEDED(rc))
3868 {
3869 /*
3870 * tell the client watcher thread to update the set of
3871 * machines that have open sessions
3872 */
3873 mParent->i_updateClientWatcher();
3874
3875 if (oldState != SessionState_Locked)
3876 /* fire an event */
3877 mParent->i_onSessionStateChange(getId(), SessionState_Locked);
3878 }
3879
3880 return rc;
3881}
3882
3883/**
3884 * @note Locks objects!
3885 */
3886STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3887 IN_BSTR aFrontend,
3888 IN_BSTR aEnvironment,
3889 IProgress **aProgress)
3890{
3891 CheckComArgStr(aFrontend);
3892 Utf8Str strFrontend(aFrontend);
3893 Utf8Str strEnvironment(aEnvironment);
3894 /* "emergencystop" doesn't need the session, so skip the checks/interface
3895 * retrieval. This code doesn't quite fit in here, but introducing a
3896 * special API method would be even more effort, and would require explicit
3897 * support by every API client. It's better to hide the feature a bit. */
3898 if (strFrontend != "emergencystop")
3899 CheckComArgNotNull(aSession);
3900 CheckComArgOutPointerValid(aProgress);
3901
3902 AutoCaller autoCaller(this);
3903 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3904
3905 HRESULT rc = S_OK;
3906 if (strFrontend.isEmpty())
3907 {
3908 Bstr bstrFrontend;
3909 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3910 if (FAILED(rc))
3911 return rc;
3912 strFrontend = bstrFrontend;
3913 if (strFrontend.isEmpty())
3914 {
3915 ComPtr<ISystemProperties> systemProperties;
3916 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3917 if (FAILED(rc))
3918 return rc;
3919 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3920 if (FAILED(rc))
3921 return rc;
3922 strFrontend = bstrFrontend;
3923 }
3924 /* paranoia - emergencystop is not a valid default */
3925 if (strFrontend == "emergencystop")
3926 strFrontend = Utf8Str::Empty;
3927 }
3928 /* default frontend: Qt GUI */
3929 if (strFrontend.isEmpty())
3930 strFrontend = "GUI/Qt";
3931
3932 if (strFrontend != "emergencystop")
3933 {
3934 /* check the session state */
3935 SessionState_T state;
3936 rc = aSession->COMGETTER(State)(&state);
3937 if (FAILED(rc))
3938 return rc;
3939
3940 if (state != SessionState_Unlocked)
3941 return setError(VBOX_E_INVALID_OBJECT_STATE,
3942 tr("The given session is busy"));
3943
3944 /* get the IInternalSessionControl interface */
3945 ComPtr<IInternalSessionControl> control(aSession);
3946 ComAssertMsgRet(!control.isNull(),
3947 ("No IInternalSessionControl interface"),
3948 E_INVALIDARG);
3949
3950 /* get the teleporter enable state for the progress object init. */
3951 BOOL fTeleporterEnabled;
3952 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3953 if (FAILED(rc))
3954 return rc;
3955
3956 /* create a progress object */
3957 ComObjPtr<ProgressProxy> progress;
3958 progress.createObject();
3959 rc = progress->init(mParent,
3960 static_cast<IMachine*>(this),
3961 Bstr(tr("Starting VM")).raw(),
3962 TRUE /* aCancelable */,
3963 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3964 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3965 2 /* uFirstOperationWeight */,
3966 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3967
3968 if (SUCCEEDED(rc))
3969 {
3970 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3971 if (SUCCEEDED(rc))
3972 {
3973 progress.queryInterfaceTo(aProgress);
3974
3975 /* signal the client watcher thread */
3976 mParent->i_updateClientWatcher();
3977
3978 /* fire an event */
3979 mParent->i_onSessionStateChange(getId(), SessionState_Spawning);
3980 }
3981 }
3982 }
3983 else
3984 {
3985 /* no progress object - either instant success or failure */
3986 *aProgress = NULL;
3987
3988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3989
3990 if (mData->mSession.mState != SessionState_Locked)
3991 return setError(VBOX_E_INVALID_OBJECT_STATE,
3992 tr("The machine '%s' is not locked by a session"),
3993 mUserData->s.strName.c_str());
3994
3995 /* must have a VM process associated - do not kill normal API clients
3996 * with an open session */
3997 if (!Global::IsOnline(mData->mMachineState))
3998 return setError(VBOX_E_INVALID_OBJECT_STATE,
3999 tr("The machine '%s' does not have a VM process"),
4000 mUserData->s.strName.c_str());
4001
4002 /* forcibly terminate the VM process */
4003 if (mData->mSession.mPID != NIL_RTPROCESS)
4004 RTProcTerminate(mData->mSession.mPID);
4005
4006 /* signal the client watcher thread, as most likely the client has
4007 * been terminated */
4008 mParent->i_updateClientWatcher();
4009 }
4010
4011 return rc;
4012}
4013
4014STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
4015{
4016 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4017 return setError(E_INVALIDARG,
4018 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4019 aPosition, SchemaDefs::MaxBootPosition);
4020
4021 if (aDevice == DeviceType_USB)
4022 return setError(E_NOTIMPL,
4023 tr("Booting from USB device is currently not supported"));
4024
4025 AutoCaller autoCaller(this);
4026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4027
4028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4029
4030 HRESULT rc = checkStateDependency(MutableStateDep);
4031 if (FAILED(rc)) return rc;
4032
4033 setModified(IsModified_MachineData);
4034 mHWData.backup();
4035 mHWData->mBootOrder[aPosition - 1] = aDevice;
4036
4037 return S_OK;
4038}
4039
4040STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
4041{
4042 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4043 return setError(E_INVALIDARG,
4044 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4045 aPosition, SchemaDefs::MaxBootPosition);
4046
4047 AutoCaller autoCaller(this);
4048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4049
4050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4051
4052 *aDevice = mHWData->mBootOrder[aPosition - 1];
4053
4054 return S_OK;
4055}
4056
4057STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4058 LONG aControllerPort,
4059 LONG aDevice,
4060 DeviceType_T aType,
4061 IMedium *aMedium)
4062{
4063 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4064 aControllerName, aControllerPort, aDevice, aType, aMedium));
4065
4066 CheckComArgStrNotEmptyOrNull(aControllerName);
4067
4068 AutoCaller autoCaller(this);
4069 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4070
4071 // request the host lock first, since might be calling Host methods for getting host drives;
4072 // next, protect the media tree all the while we're in here, as well as our member variables
4073 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
4074 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4075
4076 HRESULT rc = checkStateDependency(MutableStateDep);
4077 if (FAILED(rc)) return rc;
4078
4079 /// @todo NEWMEDIA implicit machine registration
4080 if (!mData->mRegistered)
4081 return setError(VBOX_E_INVALID_OBJECT_STATE,
4082 tr("Cannot attach storage devices to an unregistered machine"));
4083
4084 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4085
4086 /* Check for an existing controller. */
4087 ComObjPtr<StorageController> ctl;
4088 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4089 if (FAILED(rc)) return rc;
4090
4091 StorageControllerType_T ctrlType;
4092 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4093 if (FAILED(rc))
4094 return setError(E_FAIL,
4095 tr("Could not get type of controller '%ls'"),
4096 aControllerName);
4097
4098 bool fSilent = false;
4099 Utf8Str strReconfig;
4100
4101 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4102 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4103 if ( mData->mMachineState == MachineState_Paused
4104 && strReconfig == "1")
4105 fSilent = true;
4106
4107 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4108 bool fHotplug = false;
4109 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4110 fHotplug = true;
4111
4112 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4113 return setError(VBOX_E_INVALID_VM_STATE,
4114 tr("Controller '%ls' does not support hotplugging"),
4115 aControllerName);
4116
4117 // check that the port and device are not out of range
4118 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
4119 if (FAILED(rc)) return rc;
4120
4121 /* check if the device slot is already busy */
4122 MediumAttachment *pAttachTemp;
4123 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4124 aControllerName,
4125 aControllerPort,
4126 aDevice)))
4127 {
4128 Medium *pMedium = pAttachTemp->i_getMedium();
4129 if (pMedium)
4130 {
4131 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4132 return setError(VBOX_E_OBJECT_IN_USE,
4133 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4134 pMedium->i_getLocationFull().c_str(),
4135 aControllerPort,
4136 aDevice,
4137 aControllerName);
4138 }
4139 else
4140 return setError(VBOX_E_OBJECT_IN_USE,
4141 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4142 aControllerPort, aDevice, aControllerName);
4143 }
4144
4145 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4146 if (aMedium && medium.isNull())
4147 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4148
4149 AutoCaller mediumCaller(medium);
4150 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4151
4152 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4153
4154 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4155 && !medium.isNull()
4156 )
4157 return setError(VBOX_E_OBJECT_IN_USE,
4158 tr("Medium '%s' is already attached to this virtual machine"),
4159 medium->i_getLocationFull().c_str());
4160
4161 if (!medium.isNull())
4162 {
4163 MediumType_T mtype = medium->i_getType();
4164 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4165 // For DVDs it's not written to the config file, so needs no global config
4166 // version bump. For floppies it's a new attribute "type", which is ignored
4167 // by older VirtualBox version, so needs no global config version bump either.
4168 // For hard disks this type is not accepted.
4169 if (mtype == MediumType_MultiAttach)
4170 {
4171 // This type is new with VirtualBox 4.0 and therefore requires settings
4172 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4173 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4174 // two reasons: The medium type is a property of the media registry tree, which
4175 // can reside in the global config file (for pre-4.0 media); we would therefore
4176 // possibly need to bump the global config version. We don't want to do that though
4177 // because that might make downgrading to pre-4.0 impossible.
4178 // As a result, we can only use these two new types if the medium is NOT in the
4179 // global registry:
4180 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
4181 if ( medium->i_isInRegistry(uuidGlobalRegistry)
4182 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4183 )
4184 return setError(VBOX_E_INVALID_OBJECT_STATE,
4185 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4186 "to machines that were created with VirtualBox 4.0 or later"),
4187 medium->i_getLocationFull().c_str());
4188 }
4189 }
4190
4191 bool fIndirect = false;
4192 if (!medium.isNull())
4193 fIndirect = medium->i_isReadOnly();
4194 bool associate = true;
4195
4196 do
4197 {
4198 if ( aType == DeviceType_HardDisk
4199 && mMediaData.isBackedUp())
4200 {
4201 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4202
4203 /* check if the medium was attached to the VM before we started
4204 * changing attachments in which case the attachment just needs to
4205 * be restored */
4206 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4207 {
4208 AssertReturn(!fIndirect, E_FAIL);
4209
4210 /* see if it's the same bus/channel/device */
4211 if (pAttachTemp->i_matches(aControllerName, aControllerPort, aDevice))
4212 {
4213 /* the simplest case: restore the whole attachment
4214 * and return, nothing else to do */
4215 mMediaData->mAttachments.push_back(pAttachTemp);
4216
4217 /* Reattach the medium to the VM. */
4218 if (fHotplug || fSilent)
4219 {
4220 mediumLock.release();
4221 treeLock.release();
4222 alock.release();
4223
4224 MediumLockList *pMediumLockList(new MediumLockList());
4225
4226 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4227 true /* fMediumLockWrite */,
4228 NULL,
4229 *pMediumLockList);
4230 alock.acquire();
4231 if (FAILED(rc))
4232 delete pMediumLockList;
4233 else
4234 {
4235 mData->mSession.mLockedMedia.Unlock();
4236 alock.release();
4237 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4238 mData->mSession.mLockedMedia.Lock();
4239 alock.acquire();
4240 }
4241 alock.release();
4242
4243 if (SUCCEEDED(rc))
4244 {
4245 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4246 /* Remove lock list in case of error. */
4247 if (FAILED(rc))
4248 {
4249 mData->mSession.mLockedMedia.Unlock();
4250 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4251 mData->mSession.mLockedMedia.Lock();
4252 }
4253 }
4254 }
4255
4256 return S_OK;
4257 }
4258
4259 /* bus/channel/device differ; we need a new attachment object,
4260 * but don't try to associate it again */
4261 associate = false;
4262 break;
4263 }
4264 }
4265
4266 /* go further only if the attachment is to be indirect */
4267 if (!fIndirect)
4268 break;
4269
4270 /* perform the so called smart attachment logic for indirect
4271 * attachments. Note that smart attachment is only applicable to base
4272 * hard disks. */
4273
4274 if (medium->i_getParent().isNull())
4275 {
4276 /* first, investigate the backup copy of the current hard disk
4277 * attachments to make it possible to re-attach existing diffs to
4278 * another device slot w/o losing their contents */
4279 if (mMediaData.isBackedUp())
4280 {
4281 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4282
4283 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4284 uint32_t foundLevel = 0;
4285
4286 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4287 it != oldAtts.end();
4288 ++it)
4289 {
4290 uint32_t level = 0;
4291 MediumAttachment *pAttach = *it;
4292 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4293 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4294 if (pMedium.isNull())
4295 continue;
4296
4297 if (pMedium->i_getBase(&level) == medium)
4298 {
4299 /* skip the hard disk if its currently attached (we
4300 * cannot attach the same hard disk twice) */
4301 if (findAttachment(mMediaData->mAttachments,
4302 pMedium))
4303 continue;
4304
4305 /* matched device, channel and bus (i.e. attached to the
4306 * same place) will win and immediately stop the search;
4307 * otherwise the attachment that has the youngest
4308 * descendant of medium will be used
4309 */
4310 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
4311 {
4312 /* the simplest case: restore the whole attachment
4313 * and return, nothing else to do */
4314 mMediaData->mAttachments.push_back(*it);
4315
4316 /* Reattach the medium to the VM. */
4317 if (fHotplug || fSilent)
4318 {
4319 mediumLock.release();
4320 treeLock.release();
4321 alock.release();
4322
4323 MediumLockList *pMediumLockList(new MediumLockList());
4324
4325 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4326 true /* fMediumLockWrite */,
4327 NULL,
4328 *pMediumLockList);
4329 alock.acquire();
4330 if (FAILED(rc))
4331 delete pMediumLockList;
4332 else
4333 {
4334 mData->mSession.mLockedMedia.Unlock();
4335 alock.release();
4336 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4337 mData->mSession.mLockedMedia.Lock();
4338 alock.acquire();
4339 }
4340 alock.release();
4341
4342 if (SUCCEEDED(rc))
4343 {
4344 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4345 /* Remove lock list in case of error. */
4346 if (FAILED(rc))
4347 {
4348 mData->mSession.mLockedMedia.Unlock();
4349 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4350 mData->mSession.mLockedMedia.Lock();
4351 }
4352 }
4353 }
4354
4355 return S_OK;
4356 }
4357 else if ( foundIt == oldAtts.end()
4358 || level > foundLevel /* prefer younger */
4359 )
4360 {
4361 foundIt = it;
4362 foundLevel = level;
4363 }
4364 }
4365 }
4366
4367 if (foundIt != oldAtts.end())
4368 {
4369 /* use the previously attached hard disk */
4370 medium = (*foundIt)->i_getMedium();
4371 mediumCaller.attach(medium);
4372 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4373 mediumLock.attach(medium);
4374 /* not implicit, doesn't require association with this VM */
4375 fIndirect = false;
4376 associate = false;
4377 /* go right to the MediumAttachment creation */
4378 break;
4379 }
4380 }
4381
4382 /* must give up the medium lock and medium tree lock as below we
4383 * go over snapshots, which needs a lock with higher lock order. */
4384 mediumLock.release();
4385 treeLock.release();
4386
4387 /* then, search through snapshots for the best diff in the given
4388 * hard disk's chain to base the new diff on */
4389
4390 ComObjPtr<Medium> base;
4391 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4392 while (snap)
4393 {
4394 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4395
4396 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4397
4398 MediumAttachment *pAttachFound = NULL;
4399 uint32_t foundLevel = 0;
4400
4401 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4402 it != snapAtts.end();
4403 ++it)
4404 {
4405 MediumAttachment *pAttach = *it;
4406 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4407 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4408 if (pMedium.isNull())
4409 continue;
4410
4411 uint32_t level = 0;
4412 if (pMedium->i_getBase(&level) == medium)
4413 {
4414 /* matched device, channel and bus (i.e. attached to the
4415 * same place) will win and immediately stop the search;
4416 * otherwise the attachment that has the youngest
4417 * descendant of medium will be used
4418 */
4419 if ( pAttach->i_getDevice() == aDevice
4420 && pAttach->i_getPort() == aControllerPort
4421 && pAttach->i_getControllerName() == aControllerName
4422 )
4423 {
4424 pAttachFound = pAttach;
4425 break;
4426 }
4427 else if ( !pAttachFound
4428 || level > foundLevel /* prefer younger */
4429 )
4430 {
4431 pAttachFound = pAttach;
4432 foundLevel = level;
4433 }
4434 }
4435 }
4436
4437 if (pAttachFound)
4438 {
4439 base = pAttachFound->i_getMedium();
4440 break;
4441 }
4442
4443 snap = snap->i_getParent();
4444 }
4445
4446 /* re-lock medium tree and the medium, as we need it below */
4447 treeLock.acquire();
4448 mediumLock.acquire();
4449
4450 /* found a suitable diff, use it as a base */
4451 if (!base.isNull())
4452 {
4453 medium = base;
4454 mediumCaller.attach(medium);
4455 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4456 mediumLock.attach(medium);
4457 }
4458 }
4459
4460 Utf8Str strFullSnapshotFolder;
4461 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4462
4463 ComObjPtr<Medium> diff;
4464 diff.createObject();
4465 // store this diff in the same registry as the parent
4466 Guid uuidRegistryParent;
4467 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4468 {
4469 // parent image has no registry: this can happen if we're attaching a new immutable
4470 // image that has not yet been attached (medium then points to the base and we're
4471 // creating the diff image for the immutable, and the parent is not yet registered);
4472 // put the parent in the machine registry then
4473 mediumLock.release();
4474 treeLock.release();
4475 alock.release();
4476 addMediumToRegistry(medium);
4477 alock.acquire();
4478 treeLock.acquire();
4479 mediumLock.acquire();
4480 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4481 }
4482 rc = diff->init(mParent,
4483 medium->i_getPreferredDiffFormat(),
4484 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4485 uuidRegistryParent);
4486 if (FAILED(rc)) return rc;
4487
4488 /* Apply the normal locking logic to the entire chain. */
4489 MediumLockList *pMediumLockList(new MediumLockList());
4490 mediumLock.release();
4491 treeLock.release();
4492 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4493 true /* fMediumLockWrite */,
4494 medium,
4495 *pMediumLockList);
4496 treeLock.acquire();
4497 mediumLock.acquire();
4498 if (SUCCEEDED(rc))
4499 {
4500 mediumLock.release();
4501 treeLock.release();
4502 rc = pMediumLockList->Lock();
4503 treeLock.acquire();
4504 mediumLock.acquire();
4505 if (FAILED(rc))
4506 setError(rc,
4507 tr("Could not lock medium when creating diff '%s'"),
4508 diff->i_getLocationFull().c_str());
4509 else
4510 {
4511 /* will release the lock before the potentially lengthy
4512 * operation, so protect with the special state */
4513 MachineState_T oldState = mData->mMachineState;
4514 setMachineState(MachineState_SettingUp);
4515
4516 mediumLock.release();
4517 treeLock.release();
4518 alock.release();
4519
4520 rc = medium->i_createDiffStorage(diff,
4521 MediumVariant_Standard,
4522 pMediumLockList,
4523 NULL /* aProgress */,
4524 true /* aWait */);
4525
4526 alock.acquire();
4527 treeLock.acquire();
4528 mediumLock.acquire();
4529
4530 setMachineState(oldState);
4531 }
4532 }
4533
4534 /* Unlock the media and free the associated memory. */
4535 delete pMediumLockList;
4536
4537 if (FAILED(rc)) return rc;
4538
4539 /* use the created diff for the actual attachment */
4540 medium = diff;
4541 mediumCaller.attach(medium);
4542 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4543 mediumLock.attach(medium);
4544 }
4545 while (0);
4546
4547 ComObjPtr<MediumAttachment> attachment;
4548 attachment.createObject();
4549 rc = attachment->init(this,
4550 medium,
4551 aControllerName,
4552 aControllerPort,
4553 aDevice,
4554 aType,
4555 fIndirect,
4556 false /* fPassthrough */,
4557 false /* fTempEject */,
4558 false /* fNonRotational */,
4559 false /* fDiscard */,
4560 fHotplug /* fHotPluggable */,
4561 Utf8Str::Empty);
4562 if (FAILED(rc)) return rc;
4563
4564 if (associate && !medium.isNull())
4565 {
4566 // as the last step, associate the medium to the VM
4567 rc = medium->i_addBackReference(mData->mUuid);
4568 // here we can fail because of Deleting, or being in process of creating a Diff
4569 if (FAILED(rc)) return rc;
4570
4571 mediumLock.release();
4572 treeLock.release();
4573 alock.release();
4574 addMediumToRegistry(medium);
4575 alock.acquire();
4576 treeLock.acquire();
4577 mediumLock.acquire();
4578 }
4579
4580 /* success: finally remember the attachment */
4581 setModified(IsModified_Storage);
4582 mMediaData.backup();
4583 mMediaData->mAttachments.push_back(attachment);
4584
4585 mediumLock.release();
4586 treeLock.release();
4587 alock.release();
4588
4589 if (fHotplug || fSilent)
4590 {
4591 if (!medium.isNull())
4592 {
4593 MediumLockList *pMediumLockList(new MediumLockList());
4594
4595 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4596 true /* fMediumLockWrite */,
4597 NULL,
4598 *pMediumLockList);
4599 alock.acquire();
4600 if (FAILED(rc))
4601 delete pMediumLockList;
4602 else
4603 {
4604 mData->mSession.mLockedMedia.Unlock();
4605 alock.release();
4606 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4607 mData->mSession.mLockedMedia.Lock();
4608 alock.acquire();
4609 }
4610 alock.release();
4611 }
4612
4613 if (SUCCEEDED(rc))
4614 {
4615 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4616 /* Remove lock list in case of error. */
4617 if (FAILED(rc))
4618 {
4619 mData->mSession.mLockedMedia.Unlock();
4620 mData->mSession.mLockedMedia.Remove(attachment);
4621 mData->mSession.mLockedMedia.Lock();
4622 }
4623 }
4624 }
4625
4626 mParent->i_saveModifiedRegistries();
4627
4628 return rc;
4629}
4630
4631STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4632 LONG aDevice)
4633{
4634 CheckComArgStrNotEmptyOrNull(aControllerName);
4635
4636 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4637 aControllerName, aControllerPort, aDevice));
4638
4639 AutoCaller autoCaller(this);
4640 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4641
4642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4643
4644 HRESULT rc = checkStateDependency(MutableStateDep);
4645 if (FAILED(rc)) return rc;
4646
4647 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4648
4649 /* Check for an existing controller. */
4650 ComObjPtr<StorageController> ctl;
4651 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4652 if (FAILED(rc)) return rc;
4653
4654 StorageControllerType_T ctrlType;
4655 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4656 if (FAILED(rc))
4657 return setError(E_FAIL,
4658 tr("Could not get type of controller '%ls'"),
4659 aControllerName);
4660
4661 bool fSilent = false;
4662 Utf8Str strReconfig;
4663
4664 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4665 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4666 if ( mData->mMachineState == MachineState_Paused
4667 && strReconfig == "1")
4668 fSilent = true;
4669
4670 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4671 bool fHotplug = false;
4672 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4673 fHotplug = true;
4674
4675 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4676 return setError(VBOX_E_INVALID_VM_STATE,
4677 tr("Controller '%ls' does not support hotplugging"),
4678 aControllerName);
4679
4680 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4681 aControllerName,
4682 aControllerPort,
4683 aDevice);
4684 if (!pAttach)
4685 return setError(VBOX_E_OBJECT_NOT_FOUND,
4686 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4687 aDevice, aControllerPort, aControllerName);
4688
4689 if (fHotplug && !pAttach->i_getHotPluggable())
4690 return setError(VBOX_E_NOT_SUPPORTED,
4691 tr("The device slot %d on port %d of controller '%ls' does not support hotplugging"),
4692 aDevice, aControllerPort, aControllerName);
4693
4694 /*
4695 * The VM has to detach the device before we delete any implicit diffs.
4696 * If this fails we can roll back without loosing data.
4697 */
4698 if (fHotplug || fSilent)
4699 {
4700 alock.release();
4701 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4702 alock.acquire();
4703 }
4704 if (FAILED(rc)) return rc;
4705
4706 /* If we are here everything went well and we can delete the implicit now. */
4707 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4708
4709 alock.release();
4710
4711 mParent->i_saveModifiedRegistries();
4712
4713 return rc;
4714}
4715
4716STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4717 LONG aDevice, BOOL aPassthrough)
4718{
4719 CheckComArgStrNotEmptyOrNull(aControllerName);
4720
4721 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4722 aControllerName, aControllerPort, aDevice, aPassthrough));
4723
4724 AutoCaller autoCaller(this);
4725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4726
4727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4728
4729 HRESULT rc = checkStateDependency(MutableStateDep);
4730 if (FAILED(rc)) return rc;
4731
4732 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4733
4734 if (Global::IsOnlineOrTransient(mData->mMachineState))
4735 return setError(VBOX_E_INVALID_VM_STATE,
4736 tr("Invalid machine state: %s"),
4737 Global::stringifyMachineState(mData->mMachineState));
4738
4739 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4740 aControllerName,
4741 aControllerPort,
4742 aDevice);
4743 if (!pAttach)
4744 return setError(VBOX_E_OBJECT_NOT_FOUND,
4745 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4746 aDevice, aControllerPort, aControllerName);
4747
4748
4749 setModified(IsModified_Storage);
4750 mMediaData.backup();
4751
4752 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4753
4754 if (pAttach->i_getType() != DeviceType_DVD)
4755 return setError(E_INVALIDARG,
4756 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4757 aDevice, aControllerPort, aControllerName);
4758 pAttach->i_updatePassthrough(!!aPassthrough);
4759
4760 return S_OK;
4761}
4762
4763STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4764 LONG aDevice, BOOL aTemporaryEject)
4765{
4766 CheckComArgStrNotEmptyOrNull(aControllerName);
4767
4768 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4769 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4770
4771 AutoCaller autoCaller(this);
4772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4773
4774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4775
4776 HRESULT rc = checkStateDependency(MutableStateDep);
4777 if (FAILED(rc)) return rc;
4778
4779 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4780 aControllerName,
4781 aControllerPort,
4782 aDevice);
4783 if (!pAttach)
4784 return setError(VBOX_E_OBJECT_NOT_FOUND,
4785 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4786 aDevice, aControllerPort, aControllerName);
4787
4788
4789 setModified(IsModified_Storage);
4790 mMediaData.backup();
4791
4792 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4793
4794 if (pAttach->i_getType() != DeviceType_DVD)
4795 return setError(E_INVALIDARG,
4796 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4797 aDevice, aControllerPort, aControllerName);
4798 pAttach->i_updateTempEject(!!aTemporaryEject);
4799
4800 return S_OK;
4801}
4802
4803STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4804 LONG aDevice, BOOL aNonRotational)
4805{
4806 CheckComArgStrNotEmptyOrNull(aControllerName);
4807
4808 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4809 aControllerName, aControllerPort, aDevice, aNonRotational));
4810
4811 AutoCaller autoCaller(this);
4812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4813
4814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4815
4816 HRESULT rc = checkStateDependency(MutableStateDep);
4817 if (FAILED(rc)) return rc;
4818
4819 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4820
4821 if (Global::IsOnlineOrTransient(mData->mMachineState))
4822 return setError(VBOX_E_INVALID_VM_STATE,
4823 tr("Invalid machine state: %s"),
4824 Global::stringifyMachineState(mData->mMachineState));
4825
4826 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4827 aControllerName,
4828 aControllerPort,
4829 aDevice);
4830 if (!pAttach)
4831 return setError(VBOX_E_OBJECT_NOT_FOUND,
4832 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4833 aDevice, aControllerPort, aControllerName);
4834
4835
4836 setModified(IsModified_Storage);
4837 mMediaData.backup();
4838
4839 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4840
4841 if (pAttach->i_getType() != DeviceType_HardDisk)
4842 return setError(E_INVALIDARG,
4843 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4844 aDevice, aControllerPort, aControllerName);
4845 pAttach->i_updateNonRotational(!!aNonRotational);
4846
4847 return S_OK;
4848}
4849
4850STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4851 LONG aDevice, BOOL aDiscard)
4852{
4853 CheckComArgStrNotEmptyOrNull(aControllerName);
4854
4855 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4856 aControllerName, aControllerPort, aDevice, aDiscard));
4857
4858 AutoCaller autoCaller(this);
4859 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4860
4861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4862
4863 HRESULT rc = checkStateDependency(MutableStateDep);
4864 if (FAILED(rc)) return rc;
4865
4866 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4867
4868 if (Global::IsOnlineOrTransient(mData->mMachineState))
4869 return setError(VBOX_E_INVALID_VM_STATE,
4870 tr("Invalid machine state: %s"),
4871 Global::stringifyMachineState(mData->mMachineState));
4872
4873 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4874 aControllerName,
4875 aControllerPort,
4876 aDevice);
4877 if (!pAttach)
4878 return setError(VBOX_E_OBJECT_NOT_FOUND,
4879 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4880 aDevice, aControllerPort, aControllerName);
4881
4882
4883 setModified(IsModified_Storage);
4884 mMediaData.backup();
4885
4886 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4887
4888 if (pAttach->i_getType() != DeviceType_HardDisk)
4889 return setError(E_INVALIDARG,
4890 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4891 aDevice, aControllerPort, aControllerName);
4892 pAttach->i_updateDiscard(!!aDiscard);
4893
4894 return S_OK;
4895}
4896
4897STDMETHODIMP Machine::SetHotPluggableForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4898 LONG aDevice, BOOL aHotPluggable)
4899{
4900 CheckComArgStrNotEmptyOrNull(aControllerName);
4901
4902 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4903 aControllerName, aControllerPort, aDevice, aHotPluggable));
4904
4905 AutoCaller autoCaller(this);
4906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4907
4908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4909
4910 HRESULT rc = checkStateDependency(MutableStateDep);
4911 if (FAILED(rc)) return rc;
4912
4913 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4914
4915 if (Global::IsOnlineOrTransient(mData->mMachineState))
4916 return setError(VBOX_E_INVALID_VM_STATE,
4917 tr("Invalid machine state: %s"),
4918 Global::stringifyMachineState(mData->mMachineState));
4919
4920 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4921 aControllerName,
4922 aControllerPort,
4923 aDevice);
4924 if (!pAttach)
4925 return setError(VBOX_E_OBJECT_NOT_FOUND,
4926 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4927 aDevice, aControllerPort, aControllerName);
4928
4929 /* Check for an existing controller. */
4930 ComObjPtr<StorageController> ctl;
4931 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4932 if (FAILED(rc)) return rc;
4933
4934 StorageControllerType_T ctrlType;
4935 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4936 if (FAILED(rc))
4937 return setError(E_FAIL,
4938 tr("Could not get type of controller '%ls'"),
4939 aControllerName);
4940
4941 if (!isControllerHotplugCapable(ctrlType))
4942 return setError(VBOX_E_NOT_SUPPORTED,
4943 tr("Controller '%ls' does not support changing the hot-pluggable device flag"),
4944 aControllerName);
4945
4946 setModified(IsModified_Storage);
4947 mMediaData.backup();
4948
4949 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4950
4951 if (pAttach->i_getType() == DeviceType_Floppy)
4952 return setError(E_INVALIDARG,
4953 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%ls' is a floppy drive"),
4954 aDevice, aControllerPort, aControllerName);
4955 pAttach->i_updateHotPluggable(!!aHotPluggable);
4956
4957 return S_OK;
4958}
4959
4960STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4961 LONG aDevice)
4962{
4963 int rc = S_OK;
4964 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4965 aControllerName, aControllerPort, aDevice));
4966
4967 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4968
4969 return rc;
4970}
4971
4972STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4973 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4974{
4975 CheckComArgStrNotEmptyOrNull(aControllerName);
4976
4977 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4978 aControllerName, aControllerPort, aDevice));
4979
4980 AutoCaller autoCaller(this);
4981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4982
4983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4984
4985 HRESULT rc = checkStateDependency(MutableStateDep);
4986 if (FAILED(rc)) return rc;
4987
4988 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4989
4990 if (Global::IsOnlineOrTransient(mData->mMachineState))
4991 return setError(VBOX_E_INVALID_VM_STATE,
4992 tr("Invalid machine state: %s"),
4993 Global::stringifyMachineState(mData->mMachineState));
4994
4995 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4996 aControllerName,
4997 aControllerPort,
4998 aDevice);
4999 if (!pAttach)
5000 return setError(VBOX_E_OBJECT_NOT_FOUND,
5001 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5002 aDevice, aControllerPort, aControllerName);
5003
5004
5005 setModified(IsModified_Storage);
5006 mMediaData.backup();
5007
5008 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
5009 if (aBandwidthGroup && group.isNull())
5010 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
5011
5012 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5013
5014 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
5015 if (strBandwidthGroupOld.isNotEmpty())
5016 {
5017 /* Get the bandwidth group object and release it - this must not fail. */
5018 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
5019 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
5020 Assert(SUCCEEDED(rc));
5021
5022 pBandwidthGroupOld->i_release();
5023 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
5024 }
5025
5026 if (!group.isNull())
5027 {
5028 group->i_reference();
5029 pAttach->i_updateBandwidthGroup(group->i_getName());
5030 }
5031
5032 return S_OK;
5033}
5034
5035STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
5036 LONG aControllerPort,
5037 LONG aDevice,
5038 DeviceType_T aType)
5039{
5040 HRESULT rc = S_OK;
5041
5042 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
5043 aControllerName, aControllerPort, aDevice, aType));
5044
5045 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
5046
5047 return rc;
5048}
5049
5050
5051
5052STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
5053 LONG aControllerPort,
5054 LONG aDevice,
5055 BOOL aForce)
5056{
5057 int rc = S_OK;
5058 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
5059 aControllerName, aControllerPort, aForce));
5060
5061 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
5062
5063 return rc;
5064}
5065
5066STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
5067 LONG aControllerPort,
5068 LONG aDevice,
5069 IMedium *aMedium,
5070 BOOL aForce)
5071{
5072 int rc = S_OK;
5073 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
5074 aControllerName, aControllerPort, aDevice, aForce));
5075
5076 CheckComArgStrNotEmptyOrNull(aControllerName);
5077
5078 AutoCaller autoCaller(this);
5079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5080
5081 // request the host lock first, since might be calling Host methods for getting host drives;
5082 // next, protect the media tree all the while we're in here, as well as our member variables
5083 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
5084 this->lockHandle(),
5085 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5086
5087 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5088 aControllerName,
5089 aControllerPort,
5090 aDevice);
5091 if (pAttach.isNull())
5092 return setError(VBOX_E_OBJECT_NOT_FOUND,
5093 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
5094 aDevice, aControllerPort, aControllerName);
5095
5096 /* Remember previously mounted medium. The medium before taking the
5097 * backup is not necessarily the same thing. */
5098 ComObjPtr<Medium> oldmedium;
5099 oldmedium = pAttach->i_getMedium();
5100
5101 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5102 if (aMedium && pMedium.isNull())
5103 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5104
5105 AutoCaller mediumCaller(pMedium);
5106 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5107
5108 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5109 if (pMedium)
5110 {
5111 DeviceType_T mediumType = pAttach->i_getType();
5112 switch (mediumType)
5113 {
5114 case DeviceType_DVD:
5115 case DeviceType_Floppy:
5116 break;
5117
5118 default:
5119 return setError(VBOX_E_INVALID_OBJECT_STATE,
5120 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5121 aControllerPort,
5122 aDevice,
5123 aControllerName);
5124 }
5125 }
5126
5127 setModified(IsModified_Storage);
5128 mMediaData.backup();
5129
5130 {
5131 // The backup operation makes the pAttach reference point to the
5132 // old settings. Re-get the correct reference.
5133 pAttach = findAttachment(mMediaData->mAttachments,
5134 aControllerName,
5135 aControllerPort,
5136 aDevice);
5137 if (!oldmedium.isNull())
5138 oldmedium->i_removeBackReference(mData->mUuid);
5139 if (!pMedium.isNull())
5140 {
5141 pMedium->i_addBackReference(mData->mUuid);
5142
5143 mediumLock.release();
5144 multiLock.release();
5145 addMediumToRegistry(pMedium);
5146 multiLock.acquire();
5147 mediumLock.acquire();
5148 }
5149
5150 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5151 pAttach->i_updateMedium(pMedium);
5152 }
5153
5154 setModified(IsModified_Storage);
5155
5156 mediumLock.release();
5157 multiLock.release();
5158 rc = onMediumChange(pAttach, aForce);
5159 multiLock.acquire();
5160 mediumLock.acquire();
5161
5162 /* On error roll back this change only. */
5163 if (FAILED(rc))
5164 {
5165 if (!pMedium.isNull())
5166 pMedium->i_removeBackReference(mData->mUuid);
5167 pAttach = findAttachment(mMediaData->mAttachments,
5168 aControllerName,
5169 aControllerPort,
5170 aDevice);
5171 /* If the attachment is gone in the meantime, bail out. */
5172 if (pAttach.isNull())
5173 return rc;
5174 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5175 if (!oldmedium.isNull())
5176 oldmedium->i_addBackReference(mData->mUuid);
5177 pAttach->i_updateMedium(oldmedium);
5178 }
5179
5180 mediumLock.release();
5181 multiLock.release();
5182
5183 mParent->i_saveModifiedRegistries();
5184
5185 return rc;
5186}
5187
5188STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5189 LONG aControllerPort,
5190 LONG aDevice,
5191 IMedium **aMedium)
5192{
5193 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5194 aControllerName, aControllerPort, aDevice));
5195
5196 CheckComArgStrNotEmptyOrNull(aControllerName);
5197 CheckComArgOutPointerValid(aMedium);
5198
5199 AutoCaller autoCaller(this);
5200 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5201
5202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5203
5204 *aMedium = NULL;
5205
5206 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5207 aControllerName,
5208 aControllerPort,
5209 aDevice);
5210 if (pAttach.isNull())
5211 return setError(VBOX_E_OBJECT_NOT_FOUND,
5212 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5213 aDevice, aControllerPort, aControllerName);
5214
5215 pAttach->i_getMedium().queryInterfaceTo(aMedium);
5216
5217 return S_OK;
5218}
5219
5220STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5221{
5222 CheckComArgOutPointerValid(port);
5223 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5224
5225 AutoCaller autoCaller(this);
5226 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5227
5228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5229
5230 mSerialPorts[slot].queryInterfaceTo(port);
5231
5232 return S_OK;
5233}
5234
5235STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5236{
5237 CheckComArgOutPointerValid(port);
5238 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5239
5240 AutoCaller autoCaller(this);
5241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5242
5243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5244
5245 mParallelPorts[slot].queryInterfaceTo(port);
5246
5247 return S_OK;
5248}
5249
5250STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5251{
5252 CheckComArgOutPointerValid(adapter);
5253 /* Do not assert if slot is out of range, just return the advertised
5254 status. testdriver/vbox.py triggers this in logVmInfo. */
5255 if (slot >= mNetworkAdapters.size())
5256 return setError(E_INVALIDARG,
5257 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5258 slot, mNetworkAdapters.size());
5259
5260 AutoCaller autoCaller(this);
5261 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5262
5263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5264
5265 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5266
5267 return S_OK;
5268}
5269
5270STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5271{
5272 CheckComArgOutSafeArrayPointerValid(aKeys);
5273
5274 AutoCaller autoCaller(this);
5275 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5276
5277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5278
5279 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5280 int i = 0;
5281 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5282 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5283 ++it, ++i)
5284 {
5285 const Utf8Str &strKey = it->first;
5286 strKey.cloneTo(&saKeys[i]);
5287 }
5288 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5289
5290 return S_OK;
5291 }
5292
5293 /**
5294 * @note Locks this object for reading.
5295 */
5296STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5297 BSTR *aValue)
5298{
5299 CheckComArgStrNotEmptyOrNull(aKey);
5300 CheckComArgOutPointerValid(aValue);
5301
5302 AutoCaller autoCaller(this);
5303 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5304
5305 /* start with nothing found */
5306 Bstr bstrResult("");
5307
5308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5309
5310 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5311 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5312 // found:
5313 bstrResult = it->second; // source is a Utf8Str
5314
5315 /* return the result to caller (may be empty) */
5316 bstrResult.cloneTo(aValue);
5317
5318 return S_OK;
5319}
5320
5321 /**
5322 * @note Locks mParent for writing + this object for writing.
5323 */
5324STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5325{
5326 CheckComArgStrNotEmptyOrNull(aKey);
5327
5328 AutoCaller autoCaller(this);
5329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5330
5331 Utf8Str strKey(aKey);
5332 Utf8Str strValue(aValue);
5333 Utf8Str strOldValue; // empty
5334
5335 // locking note: we only hold the read lock briefly to look up the old value,
5336 // then release it and call the onExtraCanChange callbacks. There is a small
5337 // chance of a race insofar as the callback might be called twice if two callers
5338 // change the same key at the same time, but that's a much better solution
5339 // than the deadlock we had here before. The actual changing of the extradata
5340 // is then performed under the write lock and race-free.
5341
5342 // look up the old value first; if nothing has changed then we need not do anything
5343 {
5344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5345 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5346 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5347 strOldValue = it->second;
5348 }
5349
5350 bool fChanged;
5351 if ((fChanged = (strOldValue != strValue)))
5352 {
5353 // ask for permission from all listeners outside the locks;
5354 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5355 // lock to copy the list of callbacks to invoke
5356 Bstr error;
5357 Bstr bstrValue(aValue);
5358
5359 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5360 {
5361 const char *sep = error.isEmpty() ? "" : ": ";
5362 CBSTR err = error.raw();
5363 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5364 sep, err));
5365 return setError(E_ACCESSDENIED,
5366 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5367 aKey,
5368 bstrValue.raw(),
5369 sep,
5370 err);
5371 }
5372
5373 // data is changing and change not vetoed: then write it out under the lock
5374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5375
5376 if (isSnapshotMachine())
5377 {
5378 HRESULT rc = checkStateDependency(MutableStateDep);
5379 if (FAILED(rc)) return rc;
5380 }
5381
5382 if (strValue.isEmpty())
5383 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5384 else
5385 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5386 // creates a new key if needed
5387
5388 bool fNeedsGlobalSaveSettings = false;
5389 saveSettings(&fNeedsGlobalSaveSettings);
5390
5391 if (fNeedsGlobalSaveSettings)
5392 {
5393 // save the global settings; for that we should hold only the VirtualBox lock
5394 alock.release();
5395 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5396 mParent->i_saveSettings();
5397 }
5398 }
5399
5400 // fire notification outside the lock
5401 if (fChanged)
5402 mParent->i_onExtraDataChange(mData->mUuid, aKey, aValue);
5403
5404 return S_OK;
5405}
5406
5407STDMETHODIMP Machine::SetSettingsFilePath(IN_BSTR aFilePath, IProgress **aProgress)
5408{
5409 CheckComArgStrNotEmptyOrNull(aFilePath);
5410 CheckComArgOutPointerValid(aProgress);
5411
5412 AutoCaller autoCaller(this);
5413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5414
5415 *aProgress = NULL;
5416 ReturnComNotImplemented();
5417}
5418
5419STDMETHODIMP Machine::SaveSettings()
5420{
5421 AutoCaller autoCaller(this);
5422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5423
5424 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5425
5426 /* when there was auto-conversion, we want to save the file even if
5427 * the VM is saved */
5428 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5429 if (FAILED(rc)) return rc;
5430
5431 /* the settings file path may never be null */
5432 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5433
5434 /* save all VM data excluding snapshots */
5435 bool fNeedsGlobalSaveSettings = false;
5436 rc = saveSettings(&fNeedsGlobalSaveSettings);
5437 mlock.release();
5438
5439 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5440 {
5441 // save the global settings; for that we should hold only the VirtualBox lock
5442 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5443 rc = mParent->i_saveSettings();
5444 }
5445
5446 return rc;
5447}
5448
5449STDMETHODIMP Machine::DiscardSettings()
5450{
5451 AutoCaller autoCaller(this);
5452 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5453
5454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5455
5456 HRESULT rc = checkStateDependency(MutableStateDep);
5457 if (FAILED(rc)) return rc;
5458
5459 /*
5460 * during this rollback, the session will be notified if data has
5461 * been actually changed
5462 */
5463 rollback(true /* aNotify */);
5464
5465 return S_OK;
5466}
5467
5468/** @note Locks objects! */
5469STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5470 ComSafeArrayOut(IMedium*, aMedia))
5471{
5472 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5473 AutoLimitedCaller autoCaller(this);
5474 AssertComRCReturnRC(autoCaller.rc());
5475
5476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5477
5478 Guid id(getId());
5479
5480 if (mData->mSession.mState != SessionState_Unlocked)
5481 return setError(VBOX_E_INVALID_OBJECT_STATE,
5482 tr("Cannot unregister the machine '%s' while it is locked"),
5483 mUserData->s.strName.c_str());
5484
5485 // wait for state dependents to drop to zero
5486 ensureNoStateDependencies();
5487
5488 if (!mData->mAccessible)
5489 {
5490 // inaccessible maschines can only be unregistered; uninitialize ourselves
5491 // here because currently there may be no unregistered that are inaccessible
5492 // (this state combination is not supported). Note releasing the caller and
5493 // leaving the lock before calling uninit()
5494 alock.release();
5495 autoCaller.release();
5496
5497 uninit();
5498
5499 mParent->i_unregisterMachine(this, id);
5500 // calls VirtualBox::saveSettings()
5501
5502 return S_OK;
5503 }
5504
5505 HRESULT rc = S_OK;
5506
5507 // discard saved state
5508 if (mData->mMachineState == MachineState_Saved)
5509 {
5510 // add the saved state file to the list of files the caller should delete
5511 Assert(!mSSData->strStateFilePath.isEmpty());
5512 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5513
5514 mSSData->strStateFilePath.setNull();
5515
5516 // unconditionally set the machine state to powered off, we now
5517 // know no session has locked the machine
5518 mData->mMachineState = MachineState_PoweredOff;
5519 }
5520
5521 size_t cSnapshots = 0;
5522 if (mData->mFirstSnapshot)
5523 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5524 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5525 // fail now before we start detaching media
5526 return setError(VBOX_E_INVALID_OBJECT_STATE,
5527 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5528 mUserData->s.strName.c_str(), cSnapshots);
5529
5530 // This list collects the medium objects from all medium attachments
5531 // which we will detach from the machine and its snapshots, in a specific
5532 // order which allows for closing all media without getting "media in use"
5533 // errors, simply by going through the list from the front to the back:
5534 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5535 // and must be closed before the parent media from the snapshots, or closing the parents
5536 // will fail because they still have children);
5537 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5538 // the root ("first") snapshot of the machine.
5539 MediaList llMedia;
5540
5541 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5542 && mMediaData->mAttachments.size()
5543 )
5544 {
5545 // we have media attachments: detach them all and add the Medium objects to our list
5546 if (cleanupMode != CleanupMode_UnregisterOnly)
5547 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5548 else
5549 return setError(VBOX_E_INVALID_OBJECT_STATE,
5550 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5551 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5552 }
5553
5554 if (cSnapshots)
5555 {
5556 // autoCleanup must be true here, or we would have failed above
5557
5558 // add the media from the medium attachments of the snapshots to llMedia
5559 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5560 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5561 // into the children first
5562
5563 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5564 MachineState_T oldState = mData->mMachineState;
5565 mData->mMachineState = MachineState_DeletingSnapshot;
5566
5567 // make a copy of the first snapshot so the refcount does not drop to 0
5568 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5569 // because of the AutoCaller voodoo)
5570 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5571
5572 // GO!
5573 pFirstSnapshot->i_uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5574
5575 mData->mMachineState = oldState;
5576 }
5577
5578 if (FAILED(rc))
5579 {
5580 rollbackMedia();
5581 return rc;
5582 }
5583
5584 // commit all the media changes made above
5585 commitMedia();
5586
5587 mData->mRegistered = false;
5588
5589 // machine lock no longer needed
5590 alock.release();
5591
5592 // return media to caller
5593 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5594 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5595
5596 mParent->i_unregisterMachine(this, id);
5597 // calls VirtualBox::saveSettings() and VirtualBox::i_saveModifiedRegistries()
5598
5599 return S_OK;
5600}
5601
5602struct Machine::DeleteTask
5603{
5604 ComObjPtr<Machine> pMachine;
5605 RTCList<ComPtr<IMedium> > llMediums;
5606 StringsList llFilesToDelete;
5607 ComObjPtr<Progress> pProgress;
5608};
5609
5610STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5611{
5612 LogFlowFuncEnter();
5613
5614 AutoCaller autoCaller(this);
5615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5616
5617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5618
5619 HRESULT rc = checkStateDependency(MutableStateDep);
5620 if (FAILED(rc)) return rc;
5621
5622 if (mData->mRegistered)
5623 return setError(VBOX_E_INVALID_VM_STATE,
5624 tr("Cannot delete settings of a registered machine"));
5625
5626 DeleteTask *pTask = new DeleteTask;
5627 pTask->pMachine = this;
5628 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5629
5630 // collect files to delete
5631 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5632
5633 for (size_t i = 0; i < sfaMedia.size(); ++i)
5634 {
5635 IMedium *pIMedium(sfaMedia[i]);
5636 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5637 if (pMedium.isNull())
5638 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5639 SafeArray<BSTR> ids;
5640 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5641 if (FAILED(rc)) return rc;
5642 /* At this point the medium should not have any back references
5643 * anymore. If it has it is attached to another VM and *must* not
5644 * deleted. */
5645 if (ids.size() < 1)
5646 pTask->llMediums.append(pMedium);
5647 }
5648 if (mData->pMachineConfigFile->fileExists())
5649 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5650
5651 pTask->pProgress.createObject();
5652 pTask->pProgress->init(getVirtualBox(),
5653 static_cast<IMachine*>(this) /* aInitiator */,
5654 Bstr(tr("Deleting files")).raw(),
5655 true /* fCancellable */,
5656 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5657 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5658
5659 int vrc = RTThreadCreate(NULL,
5660 Machine::deleteThread,
5661 (void*)pTask,
5662 0,
5663 RTTHREADTYPE_MAIN_WORKER,
5664 0,
5665 "MachineDelete");
5666
5667 pTask->pProgress.queryInterfaceTo(aProgress);
5668
5669 if (RT_FAILURE(vrc))
5670 {
5671 delete pTask;
5672 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5673 }
5674
5675 LogFlowFuncLeave();
5676
5677 return S_OK;
5678}
5679
5680/**
5681 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5682 * calls Machine::deleteTaskWorker() on the actual machine object.
5683 * @param Thread
5684 * @param pvUser
5685 * @return
5686 */
5687/*static*/
5688DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5689{
5690 LogFlowFuncEnter();
5691
5692 DeleteTask *pTask = (DeleteTask*)pvUser;
5693 Assert(pTask);
5694 Assert(pTask->pMachine);
5695 Assert(pTask->pProgress);
5696
5697 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5698 pTask->pProgress->i_notifyComplete(rc);
5699
5700 delete pTask;
5701
5702 LogFlowFuncLeave();
5703
5704 NOREF(Thread);
5705
5706 return VINF_SUCCESS;
5707}
5708
5709/**
5710 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5711 * @param task
5712 * @return
5713 */
5714HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5715{
5716 AutoCaller autoCaller(this);
5717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5718
5719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5720
5721 HRESULT rc = S_OK;
5722
5723 try
5724 {
5725 ULONG uLogHistoryCount = 3;
5726 ComPtr<ISystemProperties> systemProperties;
5727 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5728 if (FAILED(rc)) throw rc;
5729
5730 if (!systemProperties.isNull())
5731 {
5732 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5733 if (FAILED(rc)) throw rc;
5734 }
5735
5736 MachineState_T oldState = mData->mMachineState;
5737 setMachineState(MachineState_SettingUp);
5738 alock.release();
5739 for (size_t i = 0; i < task.llMediums.size(); ++i)
5740 {
5741 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5742 {
5743 AutoCaller mac(pMedium);
5744 if (FAILED(mac.rc())) throw mac.rc();
5745 Utf8Str strLocation = pMedium->i_getLocationFull();
5746 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5747 if (FAILED(rc)) throw rc;
5748 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5749 }
5750 ComPtr<IProgress> pProgress2;
5751 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5752 if (FAILED(rc)) throw rc;
5753 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5754 if (FAILED(rc)) throw rc;
5755 /* Check the result of the asynchronous process. */
5756 LONG iRc;
5757 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5758 if (FAILED(rc)) throw rc;
5759 /* If the thread of the progress object has an error, then
5760 * retrieve the error info from there, or it'll be lost. */
5761 if (FAILED(iRc))
5762 throw setError(ProgressErrorInfo(pProgress2));
5763
5764 /* Close the medium, deliberately without checking the return
5765 * code, and without leaving any trace in the error info, as
5766 * a failure here is a very minor issue, which shouldn't happen
5767 * as above we even managed to delete the medium. */
5768 {
5769 ErrorInfoKeeper eik;
5770 pMedium->Close();
5771 }
5772 }
5773 setMachineState(oldState);
5774 alock.acquire();
5775
5776 // delete the files pushed on the task list by Machine::Delete()
5777 // (this includes saved states of the machine and snapshots and
5778 // medium storage files from the IMedium list passed in, and the
5779 // machine XML file)
5780 StringsList::const_iterator it = task.llFilesToDelete.begin();
5781 while (it != task.llFilesToDelete.end())
5782 {
5783 const Utf8Str &strFile = *it;
5784 LogFunc(("Deleting file %s\n", strFile.c_str()));
5785 int vrc = RTFileDelete(strFile.c_str());
5786 if (RT_FAILURE(vrc))
5787 throw setError(VBOX_E_IPRT_ERROR,
5788 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5789
5790 ++it;
5791 if (it == task.llFilesToDelete.end())
5792 {
5793 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5794 if (FAILED(rc)) throw rc;
5795 break;
5796 }
5797
5798 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5799 if (FAILED(rc)) throw rc;
5800 }
5801
5802 /* delete the settings only when the file actually exists */
5803 if (mData->pMachineConfigFile->fileExists())
5804 {
5805 /* Delete any backup or uncommitted XML files. Ignore failures.
5806 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5807 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5808 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5809 RTFileDelete(otherXml.c_str());
5810 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5811 RTFileDelete(otherXml.c_str());
5812
5813 /* delete the Logs folder, nothing important should be left
5814 * there (we don't check for errors because the user might have
5815 * some private files there that we don't want to delete) */
5816 Utf8Str logFolder;
5817 getLogFolder(logFolder);
5818 Assert(logFolder.length());
5819 if (RTDirExists(logFolder.c_str()))
5820 {
5821 /* Delete all VBox.log[.N] files from the Logs folder
5822 * (this must be in sync with the rotation logic in
5823 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5824 * files that may have been created by the GUI. */
5825 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5826 logFolder.c_str(), RTPATH_DELIMITER);
5827 RTFileDelete(log.c_str());
5828 log = Utf8StrFmt("%s%cVBox.png",
5829 logFolder.c_str(), RTPATH_DELIMITER);
5830 RTFileDelete(log.c_str());
5831 for (int i = uLogHistoryCount; i > 0; i--)
5832 {
5833 log = Utf8StrFmt("%s%cVBox.log.%d",
5834 logFolder.c_str(), RTPATH_DELIMITER, i);
5835 RTFileDelete(log.c_str());
5836 log = Utf8StrFmt("%s%cVBox.png.%d",
5837 logFolder.c_str(), RTPATH_DELIMITER, i);
5838 RTFileDelete(log.c_str());
5839 }
5840
5841 RTDirRemove(logFolder.c_str());
5842 }
5843
5844 /* delete the Snapshots folder, nothing important should be left
5845 * there (we don't check for errors because the user might have
5846 * some private files there that we don't want to delete) */
5847 Utf8Str strFullSnapshotFolder;
5848 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5849 Assert(!strFullSnapshotFolder.isEmpty());
5850 if (RTDirExists(strFullSnapshotFolder.c_str()))
5851 RTDirRemove(strFullSnapshotFolder.c_str());
5852
5853 // delete the directory that contains the settings file, but only
5854 // if it matches the VM name
5855 Utf8Str settingsDir;
5856 if (isInOwnDir(&settingsDir))
5857 RTDirRemove(settingsDir.c_str());
5858 }
5859
5860 alock.release();
5861
5862 mParent->i_saveModifiedRegistries();
5863 }
5864 catch (HRESULT aRC) { rc = aRC; }
5865
5866 return rc;
5867}
5868
5869STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5870{
5871 CheckComArgOutPointerValid(aSnapshot);
5872
5873 AutoCaller autoCaller(this);
5874 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5875
5876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5877
5878 ComObjPtr<Snapshot> pSnapshot;
5879 HRESULT rc;
5880
5881 if (!aNameOrId || !*aNameOrId)
5882 // null case (caller wants root snapshot): findSnapshotById() handles this
5883 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5884 else
5885 {
5886 Guid uuid(aNameOrId);
5887 if (uuid.isValid())
5888 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5889 else
5890 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5891 }
5892 pSnapshot.queryInterfaceTo(aSnapshot);
5893
5894 return rc;
5895}
5896
5897STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5898{
5899 CheckComArgStrNotEmptyOrNull(aName);
5900 CheckComArgStrNotEmptyOrNull(aHostPath);
5901
5902 AutoCaller autoCaller(this);
5903 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5904
5905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5906
5907 HRESULT rc = checkStateDependency(MutableStateDep);
5908 if (FAILED(rc)) return rc;
5909
5910 Utf8Str strName(aName);
5911
5912 ComObjPtr<SharedFolder> sharedFolder;
5913 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5914 if (SUCCEEDED(rc))
5915 return setError(VBOX_E_OBJECT_IN_USE,
5916 tr("Shared folder named '%s' already exists"),
5917 strName.c_str());
5918
5919 sharedFolder.createObject();
5920 rc = sharedFolder->init(getMachine(),
5921 strName,
5922 aHostPath,
5923 !!aWritable,
5924 !!aAutoMount,
5925 true /* fFailOnError */);
5926 if (FAILED(rc)) return rc;
5927
5928 setModified(IsModified_SharedFolders);
5929 mHWData.backup();
5930 mHWData->mSharedFolders.push_back(sharedFolder);
5931
5932 /* inform the direct session if any */
5933 alock.release();
5934 onSharedFolderChange();
5935
5936 return S_OK;
5937}
5938
5939STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5940{
5941 CheckComArgStrNotEmptyOrNull(aName);
5942
5943 AutoCaller autoCaller(this);
5944 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5945
5946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5947
5948 HRESULT rc = checkStateDependency(MutableStateDep);
5949 if (FAILED(rc)) return rc;
5950
5951 ComObjPtr<SharedFolder> sharedFolder;
5952 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5953 if (FAILED(rc)) return rc;
5954
5955 setModified(IsModified_SharedFolders);
5956 mHWData.backup();
5957 mHWData->mSharedFolders.remove(sharedFolder);
5958
5959 /* inform the direct session if any */
5960 alock.release();
5961 onSharedFolderChange();
5962
5963 return S_OK;
5964}
5965
5966STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5967{
5968 CheckComArgOutPointerValid(aCanShow);
5969
5970 /* start with No */
5971 *aCanShow = FALSE;
5972
5973 AutoCaller autoCaller(this);
5974 AssertComRCReturnRC(autoCaller.rc());
5975
5976 ComPtr<IInternalSessionControl> directControl;
5977 {
5978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5979
5980 if (mData->mSession.mState != SessionState_Locked)
5981 return setError(VBOX_E_INVALID_VM_STATE,
5982 tr("Machine is not locked for session (session state: %s)"),
5983 Global::stringifySessionState(mData->mSession.mState));
5984
5985 directControl = mData->mSession.mDirectControl;
5986 }
5987
5988 /* ignore calls made after #OnSessionEnd() is called */
5989 if (!directControl)
5990 return S_OK;
5991
5992 LONG64 dummy;
5993 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5994}
5995
5996STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5997{
5998 CheckComArgOutPointerValid(aWinId);
5999
6000 AutoCaller autoCaller(this);
6001 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6002
6003 ComPtr<IInternalSessionControl> directControl;
6004 {
6005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6006
6007 if (mData->mSession.mState != SessionState_Locked)
6008 return setError(E_FAIL,
6009 tr("Machine is not locked for session (session state: %s)"),
6010 Global::stringifySessionState(mData->mSession.mState));
6011
6012 directControl = mData->mSession.mDirectControl;
6013 }
6014
6015 /* ignore calls made after #OnSessionEnd() is called */
6016 if (!directControl)
6017 return S_OK;
6018
6019 BOOL dummy;
6020 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
6021}
6022
6023#ifdef VBOX_WITH_GUEST_PROPS
6024/**
6025 * Look up a guest property in VBoxSVC's internal structures.
6026 */
6027HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
6028 BSTR *aValue,
6029 LONG64 *aTimestamp,
6030 BSTR *aFlags) const
6031{
6032 using namespace guestProp;
6033
6034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6035 Utf8Str strName(aName);
6036 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
6037
6038 if (it != mHWData->mGuestProperties.end())
6039 {
6040 char szFlags[MAX_FLAGS_LEN + 1];
6041 it->second.strValue.cloneTo(aValue);
6042 *aTimestamp = it->second.mTimestamp;
6043 writeFlags(it->second.mFlags, szFlags);
6044 Bstr(szFlags).cloneTo(aFlags);
6045 }
6046
6047 return S_OK;
6048}
6049
6050/**
6051 * Query the VM that a guest property belongs to for the property.
6052 * @returns E_ACCESSDENIED if the VM process is not available or not
6053 * currently handling queries and the lookup should then be done in
6054 * VBoxSVC.
6055 */
6056HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
6057 BSTR *aValue,
6058 LONG64 *aTimestamp,
6059 BSTR *aFlags) const
6060{
6061 HRESULT rc;
6062 ComPtr<IInternalSessionControl> directControl;
6063 directControl = mData->mSession.mDirectControl;
6064
6065 /* fail if we were called after #OnSessionEnd() is called. This is a
6066 * silly race condition. */
6067
6068 /** @todo This code is bothering API clients (like python script clients) with
6069 * the AccessGuestProperty call, creating unncessary IPC. Need to
6070 * have a way of figuring out which kind of direct session it is... */
6071 if (!directControl)
6072 rc = E_ACCESSDENIED;
6073 else
6074 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
6075 false /* isSetter */,
6076 aValue, aTimestamp, aFlags);
6077 return rc;
6078}
6079#endif // VBOX_WITH_GUEST_PROPS
6080
6081STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
6082 BSTR *aValue,
6083 LONG64 *aTimestamp,
6084 BSTR *aFlags)
6085{
6086#ifndef VBOX_WITH_GUEST_PROPS
6087 ReturnComNotImplemented();
6088#else // VBOX_WITH_GUEST_PROPS
6089 CheckComArgStrNotEmptyOrNull(aName);
6090 CheckComArgOutPointerValid(aValue);
6091 CheckComArgOutPointerValid(aTimestamp);
6092 CheckComArgOutPointerValid(aFlags);
6093
6094 AutoCaller autoCaller(this);
6095 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6096
6097 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
6098 if (rc == E_ACCESSDENIED)
6099 /* The VM is not running or the service is not (yet) accessible */
6100 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
6101 return rc;
6102#endif // VBOX_WITH_GUEST_PROPS
6103}
6104
6105STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
6106{
6107 LONG64 dummyTimestamp;
6108 Bstr dummyFlags;
6109 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
6110}
6111
6112STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
6113{
6114 Bstr dummyValue;
6115 Bstr dummyFlags;
6116 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
6117}
6118
6119#ifdef VBOX_WITH_GUEST_PROPS
6120/**
6121 * Set a guest property in VBoxSVC's internal structures.
6122 */
6123HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6124 IN_BSTR aFlags)
6125{
6126 using namespace guestProp;
6127
6128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6129 HRESULT rc = S_OK;
6130
6131 rc = checkStateDependency(MutableStateDep);
6132 if (FAILED(rc)) return rc;
6133
6134 try
6135 {
6136 Utf8Str utf8Name(aName);
6137 Utf8Str utf8Flags(aFlags);
6138 uint32_t fFlags = NILFLAG;
6139 if ( aFlags != NULL
6140 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6141 return setError(E_INVALIDARG,
6142 tr("Invalid guest property flag values: '%ls'"),
6143 aFlags);
6144
6145 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6146 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6147 if (it == mHWData->mGuestProperties.end())
6148 {
6149 if (!fDelete)
6150 {
6151 setModified(IsModified_MachineData);
6152 mHWData.backupEx();
6153
6154 RTTIMESPEC time;
6155 HWData::GuestProperty prop;
6156 prop.strValue = aValue;
6157 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6158 prop.mFlags = fFlags;
6159 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6160 }
6161 }
6162 else
6163 {
6164 if (it->second.mFlags & (RDONLYHOST))
6165 {
6166 rc = setError(E_ACCESSDENIED,
6167 tr("The property '%ls' cannot be changed by the host"),
6168 aName);
6169 }
6170 else
6171 {
6172 setModified(IsModified_MachineData);
6173 mHWData.backupEx();
6174
6175 /* The backupEx() operation invalidates our iterator,
6176 * so get a new one. */
6177 it = mHWData->mGuestProperties.find(utf8Name);
6178 Assert(it != mHWData->mGuestProperties.end());
6179
6180 if (!fDelete)
6181 {
6182 RTTIMESPEC time;
6183 it->second.strValue = aValue;
6184 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6185 it->second.mFlags = fFlags;
6186 }
6187 else
6188 mHWData->mGuestProperties.erase(it);
6189 }
6190 }
6191
6192 if ( SUCCEEDED(rc)
6193 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6194 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6195 RTSTR_MAX,
6196 utf8Name.c_str(),
6197 RTSTR_MAX,
6198 NULL)
6199 )
6200 )
6201 {
6202 alock.release();
6203
6204 mParent->i_onGuestPropertyChange(mData->mUuid, aName,
6205 aValue ? aValue : Bstr("").raw(),
6206 aFlags ? aFlags : Bstr("").raw());
6207 }
6208 }
6209 catch (std::bad_alloc &)
6210 {
6211 rc = E_OUTOFMEMORY;
6212 }
6213
6214 return rc;
6215}
6216
6217/**
6218 * Set a property on the VM that that property belongs to.
6219 * @returns E_ACCESSDENIED if the VM process is not available or not
6220 * currently handling queries and the setting should then be done in
6221 * VBoxSVC.
6222 */
6223HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6224 IN_BSTR aFlags)
6225{
6226 HRESULT rc;
6227
6228 try
6229 {
6230 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6231
6232 BSTR dummy = NULL; /* will not be changed (setter) */
6233 LONG64 dummy64;
6234 if (!directControl)
6235 rc = E_ACCESSDENIED;
6236 else
6237 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6238 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6239 true /* isSetter */,
6240 &dummy, &dummy64, &dummy);
6241 }
6242 catch (std::bad_alloc &)
6243 {
6244 rc = E_OUTOFMEMORY;
6245 }
6246
6247 return rc;
6248}
6249#endif // VBOX_WITH_GUEST_PROPS
6250
6251STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6252 IN_BSTR aFlags)
6253{
6254#ifndef VBOX_WITH_GUEST_PROPS
6255 ReturnComNotImplemented();
6256#else // VBOX_WITH_GUEST_PROPS
6257 CheckComArgStrNotEmptyOrNull(aName);
6258 CheckComArgMaybeNull(aFlags);
6259 CheckComArgMaybeNull(aValue);
6260
6261 AutoCaller autoCaller(this);
6262 if (FAILED(autoCaller.rc()))
6263 return autoCaller.rc();
6264
6265 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6266 if (rc == E_ACCESSDENIED)
6267 /* The VM is not running or the service is not (yet) accessible */
6268 rc = setGuestPropertyToService(aName, aValue, aFlags);
6269 return rc;
6270#endif // VBOX_WITH_GUEST_PROPS
6271}
6272
6273STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6274{
6275 return SetGuestProperty(aName, aValue, NULL);
6276}
6277
6278STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6279{
6280 return SetGuestProperty(aName, NULL, NULL);
6281}
6282
6283#ifdef VBOX_WITH_GUEST_PROPS
6284/**
6285 * Enumerate the guest properties in VBoxSVC's internal structures.
6286 */
6287HRESULT Machine::enumerateGuestPropertiesInService
6288 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6289 ComSafeArrayOut(BSTR, aValues),
6290 ComSafeArrayOut(LONG64, aTimestamps),
6291 ComSafeArrayOut(BSTR, aFlags))
6292{
6293 using namespace guestProp;
6294
6295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6296 Utf8Str strPatterns(aPatterns);
6297
6298 HWData::GuestPropertyMap propMap;
6299
6300 /*
6301 * Look for matching patterns and build up a list.
6302 */
6303 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6304 while (it != mHWData->mGuestProperties.end())
6305 {
6306 if ( strPatterns.isEmpty()
6307 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6308 RTSTR_MAX,
6309 it->first.c_str(),
6310 RTSTR_MAX,
6311 NULL)
6312 )
6313 {
6314 propMap.insert(*it);
6315 }
6316
6317 it++;
6318 }
6319
6320 alock.release();
6321
6322 /*
6323 * And build up the arrays for returning the property information.
6324 */
6325 size_t cEntries = propMap.size();
6326 SafeArray<BSTR> names(cEntries);
6327 SafeArray<BSTR> values(cEntries);
6328 SafeArray<LONG64> timestamps(cEntries);
6329 SafeArray<BSTR> flags(cEntries);
6330 size_t iProp = 0;
6331
6332 it = propMap.begin();
6333 while (it != propMap.end())
6334 {
6335 char szFlags[MAX_FLAGS_LEN + 1];
6336 it->first.cloneTo(&names[iProp]);
6337 it->second.strValue.cloneTo(&values[iProp]);
6338 timestamps[iProp] = it->second.mTimestamp;
6339 writeFlags(it->second.mFlags, szFlags);
6340 Bstr(szFlags).cloneTo(&flags[iProp++]);
6341 it++;
6342 }
6343 names.detachTo(ComSafeArrayOutArg(aNames));
6344 values.detachTo(ComSafeArrayOutArg(aValues));
6345 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6346 flags.detachTo(ComSafeArrayOutArg(aFlags));
6347 return S_OK;
6348}
6349
6350/**
6351 * Enumerate the properties managed by a VM.
6352 * @returns E_ACCESSDENIED if the VM process is not available or not
6353 * currently handling queries and the setting should then be done in
6354 * VBoxSVC.
6355 */
6356HRESULT Machine::enumerateGuestPropertiesOnVM
6357 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6358 ComSafeArrayOut(BSTR, aValues),
6359 ComSafeArrayOut(LONG64, aTimestamps),
6360 ComSafeArrayOut(BSTR, aFlags))
6361{
6362 HRESULT rc;
6363 ComPtr<IInternalSessionControl> directControl;
6364 directControl = mData->mSession.mDirectControl;
6365
6366 if (!directControl)
6367 rc = E_ACCESSDENIED;
6368 else
6369 rc = directControl->EnumerateGuestProperties
6370 (aPatterns, ComSafeArrayOutArg(aNames),
6371 ComSafeArrayOutArg(aValues),
6372 ComSafeArrayOutArg(aTimestamps),
6373 ComSafeArrayOutArg(aFlags));
6374 return rc;
6375}
6376#endif // VBOX_WITH_GUEST_PROPS
6377
6378STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6379 ComSafeArrayOut(BSTR, aNames),
6380 ComSafeArrayOut(BSTR, aValues),
6381 ComSafeArrayOut(LONG64, aTimestamps),
6382 ComSafeArrayOut(BSTR, aFlags))
6383{
6384#ifndef VBOX_WITH_GUEST_PROPS
6385 ReturnComNotImplemented();
6386#else // VBOX_WITH_GUEST_PROPS
6387 CheckComArgMaybeNull(aPatterns);
6388 CheckComArgOutSafeArrayPointerValid(aNames);
6389 CheckComArgOutSafeArrayPointerValid(aValues);
6390 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6391 CheckComArgOutSafeArrayPointerValid(aFlags);
6392
6393 AutoCaller autoCaller(this);
6394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6395
6396 HRESULT rc = enumerateGuestPropertiesOnVM
6397 (aPatterns, ComSafeArrayOutArg(aNames),
6398 ComSafeArrayOutArg(aValues),
6399 ComSafeArrayOutArg(aTimestamps),
6400 ComSafeArrayOutArg(aFlags));
6401 if (rc == E_ACCESSDENIED)
6402 /* The VM is not running or the service is not (yet) accessible */
6403 rc = enumerateGuestPropertiesInService
6404 (aPatterns, ComSafeArrayOutArg(aNames),
6405 ComSafeArrayOutArg(aValues),
6406 ComSafeArrayOutArg(aTimestamps),
6407 ComSafeArrayOutArg(aFlags));
6408 return rc;
6409#endif // VBOX_WITH_GUEST_PROPS
6410}
6411
6412STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6413 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6414{
6415 MediaData::AttachmentList atts;
6416
6417 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6418 if (FAILED(rc)) return rc;
6419
6420 SafeIfaceArray<IMediumAttachment> attachments(atts);
6421 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6422
6423 return S_OK;
6424}
6425
6426STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6427 LONG aControllerPort,
6428 LONG aDevice,
6429 IMediumAttachment **aAttachment)
6430{
6431 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6432 aControllerName, aControllerPort, aDevice));
6433
6434 CheckComArgStrNotEmptyOrNull(aControllerName);
6435 CheckComArgOutPointerValid(aAttachment);
6436
6437 AutoCaller autoCaller(this);
6438 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6439
6440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6441
6442 *aAttachment = NULL;
6443
6444 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6445 aControllerName,
6446 aControllerPort,
6447 aDevice);
6448 if (pAttach.isNull())
6449 return setError(VBOX_E_OBJECT_NOT_FOUND,
6450 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6451 aDevice, aControllerPort, aControllerName);
6452
6453 pAttach.queryInterfaceTo(aAttachment);
6454
6455 return S_OK;
6456}
6457
6458STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6459 StorageBus_T aConnectionType,
6460 IStorageController **controller)
6461{
6462 CheckComArgStrNotEmptyOrNull(aName);
6463
6464 if ( (aConnectionType <= StorageBus_Null)
6465 || (aConnectionType > StorageBus_USB))
6466 return setError(E_INVALIDARG,
6467 tr("Invalid connection type: %d"),
6468 aConnectionType);
6469
6470 AutoCaller autoCaller(this);
6471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6472
6473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6474
6475 HRESULT rc = checkStateDependency(MutableStateDep);
6476 if (FAILED(rc)) return rc;
6477
6478 /* try to find one with the name first. */
6479 ComObjPtr<StorageController> ctrl;
6480
6481 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6482 if (SUCCEEDED(rc))
6483 return setError(VBOX_E_OBJECT_IN_USE,
6484 tr("Storage controller named '%ls' already exists"),
6485 aName);
6486
6487 ctrl.createObject();
6488
6489 /* get a new instance number for the storage controller */
6490 ULONG ulInstance = 0;
6491 bool fBootable = true;
6492 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6493 it != mStorageControllers->end();
6494 ++it)
6495 {
6496 if ((*it)->i_getStorageBus() == aConnectionType)
6497 {
6498 ULONG ulCurInst = (*it)->i_getInstance();
6499
6500 if (ulCurInst >= ulInstance)
6501 ulInstance = ulCurInst + 1;
6502
6503 /* Only one controller of each type can be marked as bootable. */
6504 if ((*it)->i_getBootable())
6505 fBootable = false;
6506 }
6507 }
6508
6509 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6510 if (FAILED(rc)) return rc;
6511
6512 setModified(IsModified_Storage);
6513 mStorageControllers.backup();
6514 mStorageControllers->push_back(ctrl);
6515
6516 ctrl.queryInterfaceTo(controller);
6517
6518 /* inform the direct session if any */
6519 alock.release();
6520 onStorageControllerChange();
6521
6522 return S_OK;
6523}
6524
6525STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6526 IStorageController **aStorageController)
6527{
6528 CheckComArgStrNotEmptyOrNull(aName);
6529
6530 AutoCaller autoCaller(this);
6531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6532
6533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6534
6535 ComObjPtr<StorageController> ctrl;
6536
6537 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6538 if (SUCCEEDED(rc))
6539 ctrl.queryInterfaceTo(aStorageController);
6540
6541 return rc;
6542}
6543
6544STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6545 IStorageController **aStorageController)
6546{
6547 AutoCaller autoCaller(this);
6548 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6549
6550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6551
6552 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6553 it != mStorageControllers->end();
6554 ++it)
6555 {
6556 if ((*it)->i_getInstance() == aInstance)
6557 {
6558 (*it).queryInterfaceTo(aStorageController);
6559 return S_OK;
6560 }
6561 }
6562
6563 return setError(VBOX_E_OBJECT_NOT_FOUND,
6564 tr("Could not find a storage controller with instance number '%lu'"),
6565 aInstance);
6566}
6567
6568STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6569{
6570 AutoCaller autoCaller(this);
6571 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6572
6573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6574
6575 HRESULT rc = checkStateDependency(MutableStateDep);
6576 if (FAILED(rc)) return rc;
6577
6578 ComObjPtr<StorageController> ctrl;
6579
6580 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6581 if (SUCCEEDED(rc))
6582 {
6583 /* Ensure that only one controller of each type is marked as bootable. */
6584 if (fBootable == TRUE)
6585 {
6586 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6587 it != mStorageControllers->end();
6588 ++it)
6589 {
6590 ComObjPtr<StorageController> aCtrl = (*it);
6591
6592 if ( (aCtrl->i_getName() != Utf8Str(aName))
6593 && aCtrl->i_getBootable() == TRUE
6594 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6595 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6596 {
6597 aCtrl->i_setBootable(FALSE);
6598 break;
6599 }
6600 }
6601 }
6602
6603 if (SUCCEEDED(rc))
6604 {
6605 ctrl->i_setBootable(fBootable);
6606 setModified(IsModified_Storage);
6607 }
6608 }
6609
6610 if (SUCCEEDED(rc))
6611 {
6612 /* inform the direct session if any */
6613 alock.release();
6614 onStorageControllerChange();
6615 }
6616
6617 return rc;
6618}
6619
6620STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6621{
6622 CheckComArgStrNotEmptyOrNull(aName);
6623
6624 AutoCaller autoCaller(this);
6625 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6626
6627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6628
6629 HRESULT rc = checkStateDependency(MutableStateDep);
6630 if (FAILED(rc)) return rc;
6631
6632 ComObjPtr<StorageController> ctrl;
6633 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6634 if (FAILED(rc)) return rc;
6635
6636 {
6637 /* find all attached devices to the appropriate storage controller and detach them all */
6638 // make a temporary list because detachDevice invalidates iterators into
6639 // mMediaData->mAttachments
6640 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6641
6642 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6643 it != llAttachments2.end();
6644 ++it)
6645 {
6646 MediumAttachment *pAttachTemp = *it;
6647
6648 AutoCaller localAutoCaller(pAttachTemp);
6649 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6650
6651 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6652
6653 if (pAttachTemp->i_getControllerName() == aName)
6654 {
6655 rc = detachDevice(pAttachTemp, alock, NULL);
6656 if (FAILED(rc)) return rc;
6657 }
6658 }
6659 }
6660
6661 /* We can remove it now. */
6662 setModified(IsModified_Storage);
6663 mStorageControllers.backup();
6664
6665 ctrl->i_unshare();
6666
6667 mStorageControllers->remove(ctrl);
6668
6669 /* inform the direct session if any */
6670 alock.release();
6671 onStorageControllerChange();
6672
6673 return S_OK;
6674}
6675
6676STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6677 IUSBController **controller)
6678{
6679 if ( (aType <= USBControllerType_Null)
6680 || (aType >= USBControllerType_Last))
6681 return setError(E_INVALIDARG,
6682 tr("Invalid USB controller type: %d"),
6683 aType);
6684
6685 AutoCaller autoCaller(this);
6686 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6687
6688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6689
6690 HRESULT rc = checkStateDependency(MutableStateDep);
6691 if (FAILED(rc)) return rc;
6692
6693 /* try to find one with the same type first. */
6694 ComObjPtr<USBController> ctrl;
6695
6696 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6697 if (SUCCEEDED(rc))
6698 return setError(VBOX_E_OBJECT_IN_USE,
6699 tr("USB controller named '%ls' already exists"),
6700 aName);
6701
6702 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6703 ULONG maxInstances;
6704 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6705 if (FAILED(rc))
6706 return rc;
6707
6708 ULONG cInstances = getUSBControllerCountByType(aType);
6709 if (cInstances >= maxInstances)
6710 return setError(E_INVALIDARG,
6711 tr("Too many USB controllers of this type"));
6712
6713 ctrl.createObject();
6714
6715 rc = ctrl->init(this, aName, aType);
6716 if (FAILED(rc)) return rc;
6717
6718 setModified(IsModified_USB);
6719 mUSBControllers.backup();
6720 mUSBControllers->push_back(ctrl);
6721
6722 ctrl.queryInterfaceTo(controller);
6723
6724 /* inform the direct session if any */
6725 alock.release();
6726 onUSBControllerChange();
6727
6728 return S_OK;
6729}
6730
6731STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6732{
6733 CheckComArgStrNotEmptyOrNull(aName);
6734
6735 AutoCaller autoCaller(this);
6736 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6737
6738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6739
6740 ComObjPtr<USBController> ctrl;
6741
6742 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6743 if (SUCCEEDED(rc))
6744 ctrl.queryInterfaceTo(aUSBController);
6745
6746 return rc;
6747}
6748
6749STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6750 ULONG *aControllers)
6751{
6752 CheckComArgOutPointerValid(aControllers);
6753
6754 if ( (aType <= USBControllerType_Null)
6755 || (aType >= USBControllerType_Last))
6756 return setError(E_INVALIDARG,
6757 tr("Invalid USB controller type: %d"),
6758 aType);
6759
6760 AutoCaller autoCaller(this);
6761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6762
6763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6764
6765 ComObjPtr<USBController> ctrl;
6766
6767 *aControllers = getUSBControllerCountByType(aType);
6768
6769 return S_OK;
6770}
6771
6772STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6773{
6774 CheckComArgStrNotEmptyOrNull(aName);
6775
6776 AutoCaller autoCaller(this);
6777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6778
6779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6780
6781 HRESULT rc = checkStateDependency(MutableStateDep);
6782 if (FAILED(rc)) return rc;
6783
6784 ComObjPtr<USBController> ctrl;
6785 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6786 if (FAILED(rc)) return rc;
6787
6788 setModified(IsModified_USB);
6789 mUSBControllers.backup();
6790
6791 ctrl->i_unshare();
6792
6793 mUSBControllers->remove(ctrl);
6794
6795 /* inform the direct session if any */
6796 alock.release();
6797 onUSBControllerChange();
6798
6799 return S_OK;
6800}
6801
6802STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6803 ULONG *puOriginX,
6804 ULONG *puOriginY,
6805 ULONG *puWidth,
6806 ULONG *puHeight,
6807 BOOL *pfEnabled)
6808{
6809 LogFlowThisFunc(("\n"));
6810
6811 CheckComArgNotNull(puOriginX);
6812 CheckComArgNotNull(puOriginY);
6813 CheckComArgNotNull(puWidth);
6814 CheckComArgNotNull(puHeight);
6815 CheckComArgNotNull(pfEnabled);
6816
6817 uint32_t u32OriginX= 0;
6818 uint32_t u32OriginY= 0;
6819 uint32_t u32Width = 0;
6820 uint32_t u32Height = 0;
6821 uint16_t u16Flags = 0;
6822
6823 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6824 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6825 if (RT_FAILURE(vrc))
6826 {
6827#ifdef RT_OS_WINDOWS
6828 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6829 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6830 * So just assign fEnable to TRUE again.
6831 * The right fix would be to change GUI API wrappers to make sure that parameters
6832 * are changed only if API succeeds.
6833 */
6834 *pfEnabled = TRUE;
6835#endif
6836 return setError(VBOX_E_IPRT_ERROR,
6837 tr("Saved guest size is not available (%Rrc)"),
6838 vrc);
6839 }
6840
6841 *puOriginX = u32OriginX;
6842 *puOriginY = u32OriginY;
6843 *puWidth = u32Width;
6844 *puHeight = u32Height;
6845 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6846
6847 return S_OK;
6848}
6849
6850STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6851{
6852 LogFlowThisFunc(("\n"));
6853
6854 CheckComArgNotNull(aSize);
6855 CheckComArgNotNull(aWidth);
6856 CheckComArgNotNull(aHeight);
6857
6858 if (aScreenId != 0)
6859 return E_NOTIMPL;
6860
6861 AutoCaller autoCaller(this);
6862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6863
6864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6865
6866 uint8_t *pu8Data = NULL;
6867 uint32_t cbData = 0;
6868 uint32_t u32Width = 0;
6869 uint32_t u32Height = 0;
6870
6871 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6872
6873 if (RT_FAILURE(vrc))
6874 return setError(VBOX_E_IPRT_ERROR,
6875 tr("Saved screenshot data is not available (%Rrc)"),
6876 vrc);
6877
6878 *aSize = cbData;
6879 *aWidth = u32Width;
6880 *aHeight = u32Height;
6881
6882 freeSavedDisplayScreenshot(pu8Data);
6883
6884 return S_OK;
6885}
6886
6887STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6888{
6889 LogFlowThisFunc(("\n"));
6890
6891 CheckComArgNotNull(aWidth);
6892 CheckComArgNotNull(aHeight);
6893 CheckComArgOutSafeArrayPointerValid(aData);
6894
6895 if (aScreenId != 0)
6896 return E_NOTIMPL;
6897
6898 AutoCaller autoCaller(this);
6899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6900
6901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6902
6903 uint8_t *pu8Data = NULL;
6904 uint32_t cbData = 0;
6905 uint32_t u32Width = 0;
6906 uint32_t u32Height = 0;
6907
6908 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6909
6910 if (RT_FAILURE(vrc))
6911 return setError(VBOX_E_IPRT_ERROR,
6912 tr("Saved screenshot data is not available (%Rrc)"),
6913 vrc);
6914
6915 *aWidth = u32Width;
6916 *aHeight = u32Height;
6917
6918 com::SafeArray<BYTE> bitmap(cbData);
6919 /* Convert pixels to format expected by the API caller. */
6920 if (aBGR)
6921 {
6922 /* [0] B, [1] G, [2] R, [3] A. */
6923 for (unsigned i = 0; i < cbData; i += 4)
6924 {
6925 bitmap[i] = pu8Data[i];
6926 bitmap[i + 1] = pu8Data[i + 1];
6927 bitmap[i + 2] = pu8Data[i + 2];
6928 bitmap[i + 3] = 0xff;
6929 }
6930 }
6931 else
6932 {
6933 /* [0] R, [1] G, [2] B, [3] A. */
6934 for (unsigned i = 0; i < cbData; i += 4)
6935 {
6936 bitmap[i] = pu8Data[i + 2];
6937 bitmap[i + 1] = pu8Data[i + 1];
6938 bitmap[i + 2] = pu8Data[i];
6939 bitmap[i + 3] = 0xff;
6940 }
6941 }
6942 bitmap.detachTo(ComSafeArrayOutArg(aData));
6943
6944 freeSavedDisplayScreenshot(pu8Data);
6945
6946 return S_OK;
6947}
6948
6949
6950STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6951{
6952 LogFlowThisFunc(("\n"));
6953
6954 CheckComArgNotNull(aWidth);
6955 CheckComArgNotNull(aHeight);
6956 CheckComArgOutSafeArrayPointerValid(aData);
6957
6958 if (aScreenId != 0)
6959 return E_NOTIMPL;
6960
6961 AutoCaller autoCaller(this);
6962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6963
6964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6965
6966 uint8_t *pu8Data = NULL;
6967 uint32_t cbData = 0;
6968 uint32_t u32Width = 0;
6969 uint32_t u32Height = 0;
6970
6971 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6972
6973 if (RT_FAILURE(vrc))
6974 return setError(VBOX_E_IPRT_ERROR,
6975 tr("Saved screenshot data is not available (%Rrc)"),
6976 vrc);
6977
6978 *aWidth = u32Width;
6979 *aHeight = u32Height;
6980
6981 HRESULT rc = S_OK;
6982 uint8_t *pu8PNG = NULL;
6983 uint32_t cbPNG = 0;
6984 uint32_t cxPNG = 0;
6985 uint32_t cyPNG = 0;
6986
6987 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6988
6989 if (RT_SUCCESS(vrc))
6990 {
6991 com::SafeArray<BYTE> screenData(cbPNG);
6992 screenData.initFrom(pu8PNG, cbPNG);
6993 if (pu8PNG)
6994 RTMemFree(pu8PNG);
6995 screenData.detachTo(ComSafeArrayOutArg(aData));
6996 }
6997 else
6998 {
6999 if (pu8PNG)
7000 RTMemFree(pu8PNG);
7001 return setError(VBOX_E_IPRT_ERROR,
7002 tr("Could not convert screenshot to PNG (%Rrc)"),
7003 vrc);
7004 }
7005
7006 freeSavedDisplayScreenshot(pu8Data);
7007
7008 return rc;
7009}
7010
7011STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
7012{
7013 LogFlowThisFunc(("\n"));
7014
7015 CheckComArgNotNull(aSize);
7016 CheckComArgNotNull(aWidth);
7017 CheckComArgNotNull(aHeight);
7018
7019 if (aScreenId != 0)
7020 return E_NOTIMPL;
7021
7022 AutoCaller autoCaller(this);
7023 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7024
7025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7026
7027 uint8_t *pu8Data = NULL;
7028 uint32_t cbData = 0;
7029 uint32_t u32Width = 0;
7030 uint32_t u32Height = 0;
7031
7032 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
7033
7034 if (RT_FAILURE(vrc))
7035 return setError(VBOX_E_IPRT_ERROR,
7036 tr("Saved screenshot data is not available (%Rrc)"),
7037 vrc);
7038
7039 *aSize = cbData;
7040 *aWidth = u32Width;
7041 *aHeight = u32Height;
7042
7043 freeSavedDisplayScreenshot(pu8Data);
7044
7045 return S_OK;
7046}
7047
7048STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
7049{
7050 LogFlowThisFunc(("\n"));
7051
7052 CheckComArgNotNull(aWidth);
7053 CheckComArgNotNull(aHeight);
7054 CheckComArgOutSafeArrayPointerValid(aData);
7055
7056 if (aScreenId != 0)
7057 return E_NOTIMPL;
7058
7059 AutoCaller autoCaller(this);
7060 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7061
7062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7063
7064 uint8_t *pu8Data = NULL;
7065 uint32_t cbData = 0;
7066 uint32_t u32Width = 0;
7067 uint32_t u32Height = 0;
7068
7069 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
7070
7071 if (RT_FAILURE(vrc))
7072 return setError(VBOX_E_IPRT_ERROR,
7073 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
7074 vrc);
7075
7076 *aWidth = u32Width;
7077 *aHeight = u32Height;
7078
7079 com::SafeArray<BYTE> png(cbData);
7080 png.initFrom(pu8Data, cbData);
7081 png.detachTo(ComSafeArrayOutArg(aData));
7082
7083 freeSavedDisplayScreenshot(pu8Data);
7084
7085 return S_OK;
7086}
7087
7088STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
7089{
7090 HRESULT rc = S_OK;
7091 LogFlowThisFunc(("\n"));
7092
7093 AutoCaller autoCaller(this);
7094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7095
7096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 if (!mHWData->mCPUHotPlugEnabled)
7099 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7100
7101 if (aCpu >= mHWData->mCPUCount)
7102 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
7103
7104 if (mHWData->mCPUAttached[aCpu])
7105 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
7106
7107 alock.release();
7108 rc = onCPUChange(aCpu, false);
7109 alock.acquire();
7110 if (FAILED(rc)) return rc;
7111
7112 setModified(IsModified_MachineData);
7113 mHWData.backup();
7114 mHWData->mCPUAttached[aCpu] = true;
7115
7116 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7117 if (Global::IsOnline(mData->mMachineState))
7118 saveSettings(NULL);
7119
7120 return S_OK;
7121}
7122
7123STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7124{
7125 HRESULT rc = S_OK;
7126 LogFlowThisFunc(("\n"));
7127
7128 AutoCaller autoCaller(this);
7129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7130
7131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7132
7133 if (!mHWData->mCPUHotPlugEnabled)
7134 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7135
7136 if (aCpu >= SchemaDefs::MaxCPUCount)
7137 return setError(E_INVALIDARG,
7138 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7139 SchemaDefs::MaxCPUCount);
7140
7141 if (!mHWData->mCPUAttached[aCpu])
7142 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7143
7144 /* CPU 0 can't be detached */
7145 if (aCpu == 0)
7146 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7147
7148 alock.release();
7149 rc = onCPUChange(aCpu, true);
7150 alock.acquire();
7151 if (FAILED(rc)) return rc;
7152
7153 setModified(IsModified_MachineData);
7154 mHWData.backup();
7155 mHWData->mCPUAttached[aCpu] = false;
7156
7157 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7158 if (Global::IsOnline(mData->mMachineState))
7159 saveSettings(NULL);
7160
7161 return S_OK;
7162}
7163
7164STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7165{
7166 LogFlowThisFunc(("\n"));
7167
7168 CheckComArgNotNull(aCpuAttached);
7169
7170 *aCpuAttached = false;
7171
7172 AutoCaller autoCaller(this);
7173 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7174
7175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7176
7177 /* If hotplug is enabled the CPU is always enabled. */
7178 if (!mHWData->mCPUHotPlugEnabled)
7179 {
7180 if (aCpu < mHWData->mCPUCount)
7181 *aCpuAttached = true;
7182 }
7183 else
7184 {
7185 if (aCpu < SchemaDefs::MaxCPUCount)
7186 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7187 }
7188
7189 return S_OK;
7190}
7191
7192STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7193{
7194 CheckComArgOutPointerValid(aName);
7195
7196 AutoCaller autoCaller(this);
7197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7198
7199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7200
7201 Utf8Str log = queryLogFilename(aIdx);
7202 if (!RTFileExists(log.c_str()))
7203 log.setNull();
7204 log.cloneTo(aName);
7205
7206 return S_OK;
7207}
7208
7209STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7210{
7211 LogFlowThisFunc(("\n"));
7212 CheckComArgOutSafeArrayPointerValid(aData);
7213 if (aSize < 0)
7214 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7215
7216 AutoCaller autoCaller(this);
7217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7218
7219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7220
7221 HRESULT rc = S_OK;
7222 Utf8Str log = queryLogFilename(aIdx);
7223
7224 /* do not unnecessarily hold the lock while doing something which does
7225 * not need the lock and potentially takes a long time. */
7226 alock.release();
7227
7228 /* Limit the chunk size to 32K for now, as that gives better performance
7229 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7230 * One byte expands to approx. 25 bytes of breathtaking XML. */
7231 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7232 com::SafeArray<BYTE> logData(cbData);
7233
7234 RTFILE LogFile;
7235 int vrc = RTFileOpen(&LogFile, log.c_str(),
7236 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7237 if (RT_SUCCESS(vrc))
7238 {
7239 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7240 if (RT_SUCCESS(vrc))
7241 logData.resize(cbData);
7242 else
7243 rc = setError(VBOX_E_IPRT_ERROR,
7244 tr("Could not read log file '%s' (%Rrc)"),
7245 log.c_str(), vrc);
7246 RTFileClose(LogFile);
7247 }
7248 else
7249 rc = setError(VBOX_E_IPRT_ERROR,
7250 tr("Could not open log file '%s' (%Rrc)"),
7251 log.c_str(), vrc);
7252
7253 if (FAILED(rc))
7254 logData.resize(0);
7255 logData.detachTo(ComSafeArrayOutArg(aData));
7256
7257 return rc;
7258}
7259
7260
7261/**
7262 * Currently this method doesn't attach device to the running VM,
7263 * just makes sure it's plugged on next VM start.
7264 */
7265STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7266{
7267 AutoCaller autoCaller(this);
7268 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7269
7270 // lock scope
7271 {
7272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7273
7274 HRESULT rc = checkStateDependency(MutableStateDep);
7275 if (FAILED(rc)) return rc;
7276
7277 ChipsetType_T aChipset = ChipsetType_PIIX3;
7278 COMGETTER(ChipsetType)(&aChipset);
7279
7280 if (aChipset != ChipsetType_ICH9)
7281 {
7282 return setError(E_INVALIDARG,
7283 tr("Host PCI attachment only supported with ICH9 chipset"));
7284 }
7285
7286 // check if device with this host PCI address already attached
7287 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7288 it != mHWData->mPCIDeviceAssignments.end();
7289 ++it)
7290 {
7291 LONG iHostAddress = -1;
7292 ComPtr<PCIDeviceAttachment> pAttach;
7293 pAttach = *it;
7294 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7295 if (iHostAddress == hostAddress)
7296 return setError(E_INVALIDARG,
7297 tr("Device with host PCI address already attached to this VM"));
7298 }
7299
7300 ComObjPtr<PCIDeviceAttachment> pda;
7301 char name[32];
7302
7303 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7304 Bstr bname(name);
7305 pda.createObject();
7306 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7307 setModified(IsModified_MachineData);
7308 mHWData.backup();
7309 mHWData->mPCIDeviceAssignments.push_back(pda);
7310 }
7311
7312 return S_OK;
7313}
7314
7315/**
7316 * Currently this method doesn't detach device from the running VM,
7317 * just makes sure it's not plugged on next VM start.
7318 */
7319STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7320{
7321 AutoCaller autoCaller(this);
7322 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7323
7324 ComObjPtr<PCIDeviceAttachment> pAttach;
7325 bool fRemoved = false;
7326 HRESULT rc;
7327
7328 // lock scope
7329 {
7330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7331
7332 rc = checkStateDependency(MutableStateDep);
7333 if (FAILED(rc)) return rc;
7334
7335 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7336 it != mHWData->mPCIDeviceAssignments.end();
7337 ++it)
7338 {
7339 LONG iHostAddress = -1;
7340 pAttach = *it;
7341 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7342 if (iHostAddress != -1 && iHostAddress == hostAddress)
7343 {
7344 setModified(IsModified_MachineData);
7345 mHWData.backup();
7346 mHWData->mPCIDeviceAssignments.remove(pAttach);
7347 fRemoved = true;
7348 break;
7349 }
7350 }
7351 }
7352
7353
7354 /* Fire event outside of the lock */
7355 if (fRemoved)
7356 {
7357 Assert(!pAttach.isNull());
7358 ComPtr<IEventSource> es;
7359 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7360 Assert(SUCCEEDED(rc));
7361 Bstr mid;
7362 rc = this->COMGETTER(Id)(mid.asOutParam());
7363 Assert(SUCCEEDED(rc));
7364 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7365 }
7366
7367 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7368 tr("No host PCI device %08x attached"),
7369 hostAddress
7370 );
7371}
7372
7373STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7374{
7375 CheckComArgOutSafeArrayPointerValid(aAssignments);
7376
7377 AutoCaller autoCaller(this);
7378 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7379
7380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7381
7382 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7383 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7384
7385 return S_OK;
7386}
7387
7388STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7389{
7390 CheckComArgOutPointerValid(aBandwidthControl);
7391
7392 AutoCaller autoCaller(this);
7393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7394
7395 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7396
7397 return S_OK;
7398}
7399
7400STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7401{
7402 CheckComArgOutPointerValid(pfEnabled);
7403 AutoCaller autoCaller(this);
7404 HRESULT hrc = autoCaller.rc();
7405 if (SUCCEEDED(hrc))
7406 {
7407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7408 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7409 }
7410 return hrc;
7411}
7412
7413STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7414{
7415 AutoCaller autoCaller(this);
7416 HRESULT hrc = autoCaller.rc();
7417 if (SUCCEEDED(hrc))
7418 {
7419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7420 hrc = checkStateDependency(MutableStateDep);
7421 if (SUCCEEDED(hrc))
7422 {
7423 hrc = mHWData.backupEx();
7424 if (SUCCEEDED(hrc))
7425 {
7426 setModified(IsModified_MachineData);
7427 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7428 }
7429 }
7430 }
7431 return hrc;
7432}
7433
7434STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7435{
7436 CheckComArgOutPointerValid(pbstrConfig);
7437 AutoCaller autoCaller(this);
7438 HRESULT hrc = autoCaller.rc();
7439 if (SUCCEEDED(hrc))
7440 {
7441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7442 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7443 }
7444 return hrc;
7445}
7446
7447STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7448{
7449 CheckComArgStr(bstrConfig);
7450 AutoCaller autoCaller(this);
7451 HRESULT hrc = autoCaller.rc();
7452 if (SUCCEEDED(hrc))
7453 {
7454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7455 hrc = checkStateDependency(MutableStateDep);
7456 if (SUCCEEDED(hrc))
7457 {
7458 hrc = mHWData.backupEx();
7459 if (SUCCEEDED(hrc))
7460 {
7461 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7462 if (SUCCEEDED(hrc))
7463 setModified(IsModified_MachineData);
7464 }
7465 }
7466 }
7467 return hrc;
7468
7469}
7470
7471STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7472{
7473 CheckComArgOutPointerValid(pfAllow);
7474 AutoCaller autoCaller(this);
7475 HRESULT hrc = autoCaller.rc();
7476 if (SUCCEEDED(hrc))
7477 {
7478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7479 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7480 }
7481 return hrc;
7482}
7483
7484STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7485{
7486 AutoCaller autoCaller(this);
7487 HRESULT hrc = autoCaller.rc();
7488 if (SUCCEEDED(hrc))
7489 {
7490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7491 hrc = checkStateDependency(MutableStateDep);
7492 if (SUCCEEDED(hrc))
7493 {
7494 hrc = mHWData.backupEx();
7495 if (SUCCEEDED(hrc))
7496 {
7497 setModified(IsModified_MachineData);
7498 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7499 }
7500 }
7501 }
7502 return hrc;
7503}
7504
7505STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7506{
7507 CheckComArgOutPointerValid(pfEnabled);
7508 AutoCaller autoCaller(this);
7509 HRESULT hrc = autoCaller.rc();
7510 if (SUCCEEDED(hrc))
7511 {
7512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7513 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7514 }
7515 return hrc;
7516}
7517
7518STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7519{
7520 AutoCaller autoCaller(this);
7521 HRESULT hrc = autoCaller.rc();
7522 if (SUCCEEDED(hrc))
7523 {
7524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7525 hrc = checkStateDependency(MutableStateDep);
7526 if ( SUCCEEDED(hrc)
7527 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7528 {
7529 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7530 int vrc;
7531
7532 if (fEnabled)
7533 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7534 else
7535 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7536
7537 if (RT_SUCCESS(vrc))
7538 {
7539 hrc = mHWData.backupEx();
7540 if (SUCCEEDED(hrc))
7541 {
7542 setModified(IsModified_MachineData);
7543 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7544 }
7545 }
7546 else if (vrc == VERR_NOT_SUPPORTED)
7547 hrc = setError(VBOX_E_NOT_SUPPORTED,
7548 tr("The VM autostart feature is not supported on this platform"));
7549 else if (vrc == VERR_PATH_NOT_FOUND)
7550 hrc = setError(E_FAIL,
7551 tr("The path to the autostart database is not set"));
7552 else
7553 hrc = setError(E_UNEXPECTED,
7554 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7555 fEnabled ? "Adding" : "Removing",
7556 mUserData->s.strName.c_str(), vrc);
7557 }
7558 }
7559 return hrc;
7560}
7561
7562STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7563{
7564 CheckComArgOutPointerValid(puDelay);
7565 AutoCaller autoCaller(this);
7566 HRESULT hrc = autoCaller.rc();
7567 if (SUCCEEDED(hrc))
7568 {
7569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7570 *puDelay = mHWData->mAutostart.uAutostartDelay;
7571 }
7572 return hrc;
7573}
7574
7575STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7576{
7577 AutoCaller autoCaller(this);
7578 HRESULT hrc = autoCaller.rc();
7579 if (SUCCEEDED(hrc))
7580 {
7581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7582 hrc = checkStateDependency(MutableStateDep);
7583 if (SUCCEEDED(hrc))
7584 {
7585 hrc = mHWData.backupEx();
7586 if (SUCCEEDED(hrc))
7587 {
7588 setModified(IsModified_MachineData);
7589 mHWData->mAutostart.uAutostartDelay = uDelay;
7590 }
7591 }
7592 }
7593 return hrc;
7594}
7595
7596STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7597{
7598 CheckComArgOutPointerValid(penmAutostopType);
7599 AutoCaller autoCaller(this);
7600 HRESULT hrc = autoCaller.rc();
7601 if (SUCCEEDED(hrc))
7602 {
7603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7604 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7605 }
7606 return hrc;
7607}
7608
7609STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7610{
7611 AutoCaller autoCaller(this);
7612 HRESULT hrc = autoCaller.rc();
7613 if (SUCCEEDED(hrc))
7614 {
7615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7616 hrc = checkStateDependency(MutableStateDep);
7617 if ( SUCCEEDED(hrc)
7618 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7619 {
7620 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7621 int vrc;
7622
7623 if (enmAutostopType != AutostopType_Disabled)
7624 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7625 else
7626 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7627
7628 if (RT_SUCCESS(vrc))
7629 {
7630 hrc = mHWData.backupEx();
7631 if (SUCCEEDED(hrc))
7632 {
7633 setModified(IsModified_MachineData);
7634 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7635 }
7636 }
7637 else if (vrc == VERR_NOT_SUPPORTED)
7638 hrc = setError(VBOX_E_NOT_SUPPORTED,
7639 tr("The VM autostop feature is not supported on this platform"));
7640 else if (vrc == VERR_PATH_NOT_FOUND)
7641 hrc = setError(E_FAIL,
7642 tr("The path to the autostart database is not set"));
7643 else
7644 hrc = setError(E_UNEXPECTED,
7645 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7646 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7647 mUserData->s.strName.c_str(), vrc);
7648 }
7649 }
7650 return hrc;
7651}
7652
7653STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7654{
7655 CheckComArgOutPointerValid(aDefaultFrontend);
7656 AutoCaller autoCaller(this);
7657 HRESULT hrc = autoCaller.rc();
7658 if (SUCCEEDED(hrc))
7659 {
7660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7661 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7662 }
7663 return hrc;
7664}
7665
7666STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7667{
7668 CheckComArgStr(aDefaultFrontend);
7669 AutoCaller autoCaller(this);
7670 HRESULT hrc = autoCaller.rc();
7671 if (SUCCEEDED(hrc))
7672 {
7673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7674 hrc = checkStateDependency(MutableOrSavedStateDep);
7675 if (SUCCEEDED(hrc))
7676 {
7677 hrc = mHWData.backupEx();
7678 if (SUCCEEDED(hrc))
7679 {
7680 setModified(IsModified_MachineData);
7681 mHWData->mDefaultFrontend = aDefaultFrontend;
7682 }
7683 }
7684 }
7685 return hrc;
7686}
7687
7688STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7689{
7690 CheckComArgSafeArrayNotNull(aIcon);
7691 CheckComArgOutSafeArrayPointerValid(aIcon);
7692 AutoCaller autoCaller(this);
7693 HRESULT hrc = autoCaller.rc();
7694 if (SUCCEEDED(hrc))
7695 {
7696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7697 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7698 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7699 icon.detachTo(ComSafeArrayOutArg(aIcon));
7700 }
7701 return hrc;
7702}
7703
7704STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7705{
7706 CheckComArgSafeArrayNotNull(aIcon);
7707 AutoCaller autoCaller(this);
7708 HRESULT hrc = autoCaller.rc();
7709 if (SUCCEEDED(hrc))
7710 {
7711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7712 hrc = checkStateDependency(MutableOrSavedStateDep);
7713 if (SUCCEEDED(hrc))
7714 {
7715 setModified(IsModified_MachineData);
7716 mUserData.backup();
7717 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7718 mUserData->mIcon.resize(icon.size());
7719 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7720 }
7721 }
7722 return hrc;
7723}
7724
7725STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7726{
7727 CheckComArgOutPointerValid(aAvailable);
7728
7729 AutoCaller autoCaller(this);
7730 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7731
7732#ifdef VBOX_WITH_USB
7733 *aAvailable = true;
7734#else
7735 *aAvailable = false;
7736#endif
7737 return S_OK;
7738}
7739
7740STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7741{
7742 LogFlowFuncEnter();
7743
7744 CheckComArgNotNull(pTarget);
7745 CheckComArgOutPointerValid(pProgress);
7746
7747 /* Convert the options. */
7748 RTCList<CloneOptions_T> optList;
7749 if (options != NULL)
7750 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7751
7752 if (optList.contains(CloneOptions_Link))
7753 {
7754 if (!isSnapshotMachine())
7755 return setError(E_INVALIDARG,
7756 tr("Linked clone can only be created from a snapshot"));
7757 if (mode != CloneMode_MachineState)
7758 return setError(E_INVALIDARG,
7759 tr("Linked clone can only be created for a single machine state"));
7760 }
7761 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7762
7763 AutoCaller autoCaller(this);
7764 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7765
7766
7767 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7768
7769 HRESULT rc = pWorker->start(pProgress);
7770
7771 LogFlowFuncLeave();
7772
7773 return rc;
7774}
7775
7776// public methods for internal purposes
7777/////////////////////////////////////////////////////////////////////////////
7778
7779/**
7780 * Adds the given IsModified_* flag to the dirty flags of the machine.
7781 * This must be called either during loadSettings or under the machine write lock.
7782 * @param fl
7783 */
7784void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7785{
7786 mData->flModifications |= fl;
7787 if (fAllowStateModification && isStateModificationAllowed())
7788 mData->mCurrentStateModified = true;
7789}
7790
7791/**
7792 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7793 * care of the write locking.
7794 *
7795 * @param fModifications The flag to add.
7796 */
7797void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7798{
7799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7800 setModified(fModification, fAllowStateModification);
7801}
7802
7803/**
7804 * Saves the registry entry of this machine to the given configuration node.
7805 *
7806 * @param aEntryNode Node to save the registry entry to.
7807 *
7808 * @note locks this object for reading.
7809 */
7810HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7811{
7812 AutoLimitedCaller autoCaller(this);
7813 AssertComRCReturnRC(autoCaller.rc());
7814
7815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7816
7817 data.uuid = mData->mUuid;
7818 data.strSettingsFile = mData->m_strConfigFile;
7819
7820 return S_OK;
7821}
7822
7823/**
7824 * Calculates the absolute path of the given path taking the directory of the
7825 * machine settings file as the current directory.
7826 *
7827 * @param aPath Path to calculate the absolute path for.
7828 * @param aResult Where to put the result (used only on success, can be the
7829 * same Utf8Str instance as passed in @a aPath).
7830 * @return IPRT result.
7831 *
7832 * @note Locks this object for reading.
7833 */
7834int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7835{
7836 AutoCaller autoCaller(this);
7837 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7838
7839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7840
7841 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7842
7843 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7844
7845 strSettingsDir.stripFilename();
7846 char folder[RTPATH_MAX];
7847 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7848 if (RT_SUCCESS(vrc))
7849 aResult = folder;
7850
7851 return vrc;
7852}
7853
7854/**
7855 * Copies strSource to strTarget, making it relative to the machine folder
7856 * if it is a subdirectory thereof, or simply copying it otherwise.
7857 *
7858 * @param strSource Path to evaluate and copy.
7859 * @param strTarget Buffer to receive target path.
7860 *
7861 * @note Locks this object for reading.
7862 */
7863void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7864 Utf8Str &strTarget)
7865{
7866 AutoCaller autoCaller(this);
7867 AssertComRCReturn(autoCaller.rc(), (void)0);
7868
7869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7870
7871 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7872 // use strTarget as a temporary buffer to hold the machine settings dir
7873 strTarget = mData->m_strConfigFileFull;
7874 strTarget.stripFilename();
7875 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7876 {
7877 // is relative: then append what's left
7878 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7879 // for empty paths (only possible for subdirs) use "." to avoid
7880 // triggering default settings for not present config attributes.
7881 if (strTarget.isEmpty())
7882 strTarget = ".";
7883 }
7884 else
7885 // is not relative: then overwrite
7886 strTarget = strSource;
7887}
7888
7889/**
7890 * Returns the full path to the machine's log folder in the
7891 * \a aLogFolder argument.
7892 */
7893void Machine::getLogFolder(Utf8Str &aLogFolder)
7894{
7895 AutoCaller autoCaller(this);
7896 AssertComRCReturnVoid(autoCaller.rc());
7897
7898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7899
7900 char szTmp[RTPATH_MAX];
7901 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7902 if (RT_SUCCESS(vrc))
7903 {
7904 if (szTmp[0] && !mUserData.isNull())
7905 {
7906 char szTmp2[RTPATH_MAX];
7907 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7908 if (RT_SUCCESS(vrc))
7909 aLogFolder = BstrFmt("%s%c%s",
7910 szTmp2,
7911 RTPATH_DELIMITER,
7912 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7913 }
7914 else
7915 vrc = VERR_PATH_IS_RELATIVE;
7916 }
7917
7918 if (RT_FAILURE(vrc))
7919 {
7920 // fallback if VBOX_USER_LOGHOME is not set or invalid
7921 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7922 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7923 aLogFolder.append(RTPATH_DELIMITER);
7924 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7925 }
7926}
7927
7928/**
7929 * Returns the full path to the machine's log file for an given index.
7930 */
7931Utf8Str Machine::queryLogFilename(ULONG idx)
7932{
7933 Utf8Str logFolder;
7934 getLogFolder(logFolder);
7935 Assert(logFolder.length());
7936 Utf8Str log;
7937 if (idx == 0)
7938 log = Utf8StrFmt("%s%cVBox.log",
7939 logFolder.c_str(), RTPATH_DELIMITER);
7940 else
7941 log = Utf8StrFmt("%s%cVBox.log.%d",
7942 logFolder.c_str(), RTPATH_DELIMITER, idx);
7943 return log;
7944}
7945
7946/**
7947 * Composes a unique saved state filename based on the current system time. The filename is
7948 * granular to the second so this will work so long as no more than one snapshot is taken on
7949 * a machine per second.
7950 *
7951 * Before version 4.1, we used this formula for saved state files:
7952 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7953 * which no longer works because saved state files can now be shared between the saved state of the
7954 * "saved" machine and an online snapshot, and the following would cause problems:
7955 * 1) save machine
7956 * 2) create online snapshot from that machine state --> reusing saved state file
7957 * 3) save machine again --> filename would be reused, breaking the online snapshot
7958 *
7959 * So instead we now use a timestamp.
7960 *
7961 * @param str
7962 */
7963void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7964{
7965 AutoCaller autoCaller(this);
7966 AssertComRCReturnVoid(autoCaller.rc());
7967
7968 {
7969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7970 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7971 }
7972
7973 RTTIMESPEC ts;
7974 RTTimeNow(&ts);
7975 RTTIME time;
7976 RTTimeExplode(&time, &ts);
7977
7978 strStateFilePath += RTPATH_DELIMITER;
7979 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7980 time.i32Year, time.u8Month, time.u8MonthDay,
7981 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7982}
7983
7984/**
7985 * Returns the full path to the default video capture file.
7986 */
7987void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7988{
7989 AutoCaller autoCaller(this);
7990 AssertComRCReturnVoid(autoCaller.rc());
7991
7992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7993
7994 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7995 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7996 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7997}
7998
7999/**
8000 * Returns whether at least one USB controller is present for the VM.
8001 */
8002bool Machine::isUSBControllerPresent()
8003{
8004 AutoCaller autoCaller(this);
8005 AssertComRCReturn(autoCaller.rc(), false);
8006
8007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8008
8009 return (mUSBControllers->size() > 0);
8010}
8011
8012/**
8013 * @note Locks this object for writing, calls the client process
8014 * (inside the lock).
8015 */
8016HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
8017 const Utf8Str &strFrontend,
8018 const Utf8Str &strEnvironment,
8019 ProgressProxy *aProgress)
8020{
8021 LogFlowThisFuncEnter();
8022
8023 AssertReturn(aControl, E_FAIL);
8024 AssertReturn(aProgress, E_FAIL);
8025 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
8026
8027 AutoCaller autoCaller(this);
8028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
8029
8030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8031
8032 if (!mData->mRegistered)
8033 return setError(E_UNEXPECTED,
8034 tr("The machine '%s' is not registered"),
8035 mUserData->s.strName.c_str());
8036
8037 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
8038
8039 if ( mData->mSession.mState == SessionState_Locked
8040 || mData->mSession.mState == SessionState_Spawning
8041 || mData->mSession.mState == SessionState_Unlocking)
8042 return setError(VBOX_E_INVALID_OBJECT_STATE,
8043 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
8044 mUserData->s.strName.c_str());
8045
8046 /* may not be busy */
8047 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
8048
8049 /* get the path to the executable */
8050 char szPath[RTPATH_MAX];
8051 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
8052 size_t sz = strlen(szPath);
8053 szPath[sz++] = RTPATH_DELIMITER;
8054 szPath[sz] = 0;
8055 char *cmd = szPath + sz;
8056 sz = sizeof(szPath) - sz;
8057
8058 int vrc = VINF_SUCCESS;
8059 RTPROCESS pid = NIL_RTPROCESS;
8060
8061 RTENV env = RTENV_DEFAULT;
8062
8063 if (!strEnvironment.isEmpty())
8064 {
8065 char *newEnvStr = NULL;
8066
8067 do
8068 {
8069 /* clone the current environment */
8070 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
8071 AssertRCBreakStmt(vrc2, vrc = vrc2);
8072
8073 newEnvStr = RTStrDup(strEnvironment.c_str());
8074 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
8075
8076 /* put new variables to the environment
8077 * (ignore empty variable names here since RTEnv API
8078 * intentionally doesn't do that) */
8079 char *var = newEnvStr;
8080 for (char *p = newEnvStr; *p; ++p)
8081 {
8082 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
8083 {
8084 *p = '\0';
8085 if (*var)
8086 {
8087 char *val = strchr(var, '=');
8088 if (val)
8089 {
8090 *val++ = '\0';
8091 vrc2 = RTEnvSetEx(env, var, val);
8092 }
8093 else
8094 vrc2 = RTEnvUnsetEx(env, var);
8095 if (RT_FAILURE(vrc2))
8096 break;
8097 }
8098 var = p + 1;
8099 }
8100 }
8101 if (RT_SUCCESS(vrc2) && *var)
8102 vrc2 = RTEnvPutEx(env, var);
8103
8104 AssertRCBreakStmt(vrc2, vrc = vrc2);
8105 }
8106 while (0);
8107
8108 if (newEnvStr != NULL)
8109 RTStrFree(newEnvStr);
8110 }
8111
8112#ifdef VBOX_WITH_QTGUI
8113 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
8114 {
8115# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
8116 /* Modify the base path so that we don't need to use ".." below. */
8117 RTPathStripTrailingSlash(szPath);
8118 RTPathStripFilename(szPath);
8119 sz = strlen(szPath);
8120 cmd = szPath + sz;
8121 sz = sizeof(szPath) - sz;
8122
8123#define OSX_APP_NAME "VirtualBoxVM"
8124#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
8125
8126 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
8127 if ( strAppOverride.contains(".")
8128 || strAppOverride.contains("/")
8129 || strAppOverride.contains("\\")
8130 || strAppOverride.contains(":"))
8131 strAppOverride.setNull();
8132 Utf8Str strAppPath;
8133 if (!strAppOverride.isEmpty())
8134 {
8135 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8136 Utf8Str strFullPath(szPath);
8137 strFullPath.append(strAppPath);
8138 /* there is a race, but people using this deserve the failure */
8139 if (!RTFileExists(strFullPath.c_str()))
8140 strAppOverride.setNull();
8141 }
8142 if (strAppOverride.isEmpty())
8143 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8144 const char *VirtualBox_exe = strAppPath.c_str();
8145 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8146# else
8147 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8148 Assert(sz >= sizeof(VirtualBox_exe));
8149# endif
8150 strcpy(cmd, VirtualBox_exe);
8151
8152 Utf8Str idStr = mData->mUuid.toString();
8153 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8154 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8155 }
8156#else /* !VBOX_WITH_QTGUI */
8157 if (0)
8158 ;
8159#endif /* VBOX_WITH_QTGUI */
8160
8161 else
8162
8163#ifdef VBOX_WITH_VBOXSDL
8164 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8165 {
8166 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8167 Assert(sz >= sizeof(VBoxSDL_exe));
8168 strcpy(cmd, VBoxSDL_exe);
8169
8170 Utf8Str idStr = mData->mUuid.toString();
8171 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8172 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8173 }
8174#else /* !VBOX_WITH_VBOXSDL */
8175 if (0)
8176 ;
8177#endif /* !VBOX_WITH_VBOXSDL */
8178
8179 else
8180
8181#ifdef VBOX_WITH_HEADLESS
8182 if ( strFrontend == "headless"
8183 || strFrontend == "capture"
8184 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8185 )
8186 {
8187 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8188 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8189 * and a VM works even if the server has not been installed.
8190 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8191 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8192 * differently in 4.0 and 3.x.
8193 */
8194 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8195 Assert(sz >= sizeof(VBoxHeadless_exe));
8196 strcpy(cmd, VBoxHeadless_exe);
8197
8198 Utf8Str idStr = mData->mUuid.toString();
8199 /* Leave space for "--capture" arg. */
8200 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8201 "--startvm", idStr.c_str(),
8202 "--vrde", "config",
8203 0, /* For "--capture". */
8204 0 };
8205 if (strFrontend == "capture")
8206 {
8207 unsigned pos = RT_ELEMENTS(args) - 2;
8208 args[pos] = "--capture";
8209 }
8210 vrc = RTProcCreate(szPath, args, env,
8211#ifdef RT_OS_WINDOWS
8212 RTPROC_FLAGS_NO_WINDOW
8213#else
8214 0
8215#endif
8216 , &pid);
8217 }
8218#else /* !VBOX_WITH_HEADLESS */
8219 if (0)
8220 ;
8221#endif /* !VBOX_WITH_HEADLESS */
8222 else
8223 {
8224 RTEnvDestroy(env);
8225 return setError(E_INVALIDARG,
8226 tr("Invalid frontend name: '%s'"),
8227 strFrontend.c_str());
8228 }
8229
8230 RTEnvDestroy(env);
8231
8232 if (RT_FAILURE(vrc))
8233 return setError(VBOX_E_IPRT_ERROR,
8234 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8235 mUserData->s.strName.c_str(), vrc);
8236
8237 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8238
8239 /*
8240 * Note that we don't release the lock here before calling the client,
8241 * because it doesn't need to call us back if called with a NULL argument.
8242 * Releasing the lock here is dangerous because we didn't prepare the
8243 * launch data yet, but the client we've just started may happen to be
8244 * too fast and call LockMachine() that will fail (because of PID, etc.),
8245 * so that the Machine will never get out of the Spawning session state.
8246 */
8247
8248 /* inform the session that it will be a remote one */
8249 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8250#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8251 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8252#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8253 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8254#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8255 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8256
8257 if (FAILED(rc))
8258 {
8259 /* restore the session state */
8260 mData->mSession.mState = SessionState_Unlocked;
8261 alock.release();
8262 mParent->i_addProcessToReap(pid);
8263 /* The failure may occur w/o any error info (from RPC), so provide one */
8264 return setError(VBOX_E_VM_ERROR,
8265 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8266 }
8267
8268 /* attach launch data to the machine */
8269 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8270 mData->mSession.mRemoteControls.push_back(aControl);
8271 mData->mSession.mProgress = aProgress;
8272 mData->mSession.mPID = pid;
8273 mData->mSession.mState = SessionState_Spawning;
8274 mData->mSession.mType = strFrontend;
8275
8276 alock.release();
8277 mParent->i_addProcessToReap(pid);
8278
8279 LogFlowThisFuncLeave();
8280 return S_OK;
8281}
8282
8283/**
8284 * Returns @c true if the given session machine instance has an open direct
8285 * session (and optionally also for direct sessions which are closing) and
8286 * returns the session control machine instance if so.
8287 *
8288 * Note that when the method returns @c false, the arguments remain unchanged.
8289 *
8290 * @param aMachine Session machine object.
8291 * @param aControl Direct session control object (optional).
8292 * @param aAllowClosing If true then additionally a session which is currently
8293 * being closed will also be allowed.
8294 *
8295 * @note locks this object for reading.
8296 */
8297bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8298 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8299 bool aAllowClosing /*= false*/)
8300{
8301 AutoLimitedCaller autoCaller(this);
8302 AssertComRCReturn(autoCaller.rc(), false);
8303
8304 /* just return false for inaccessible machines */
8305 if (autoCaller.state() != Ready)
8306 return false;
8307
8308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8309
8310 if ( mData->mSession.mState == SessionState_Locked
8311 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8312 )
8313 {
8314 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8315
8316 aMachine = mData->mSession.mMachine;
8317
8318 if (aControl != NULL)
8319 *aControl = mData->mSession.mDirectControl;
8320
8321 return true;
8322 }
8323
8324 return false;
8325}
8326
8327/**
8328 * Returns @c true if the given machine has an spawning direct session.
8329 *
8330 * @note locks this object for reading.
8331 */
8332bool Machine::isSessionSpawning()
8333{
8334 AutoLimitedCaller autoCaller(this);
8335 AssertComRCReturn(autoCaller.rc(), false);
8336
8337 /* just return false for inaccessible machines */
8338 if (autoCaller.state() != Ready)
8339 return false;
8340
8341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8342
8343 if (mData->mSession.mState == SessionState_Spawning)
8344 return true;
8345
8346 return false;
8347}
8348
8349/**
8350 * Called from the client watcher thread to check for unexpected client process
8351 * death during Session_Spawning state (e.g. before it successfully opened a
8352 * direct session).
8353 *
8354 * On Win32 and on OS/2, this method is called only when we've got the
8355 * direct client's process termination notification, so it always returns @c
8356 * true.
8357 *
8358 * On other platforms, this method returns @c true if the client process is
8359 * terminated and @c false if it's still alive.
8360 *
8361 * @note Locks this object for writing.
8362 */
8363bool Machine::checkForSpawnFailure()
8364{
8365 AutoCaller autoCaller(this);
8366 if (!autoCaller.isOk())
8367 {
8368 /* nothing to do */
8369 LogFlowThisFunc(("Already uninitialized!\n"));
8370 return true;
8371 }
8372
8373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8374
8375 if (mData->mSession.mState != SessionState_Spawning)
8376 {
8377 /* nothing to do */
8378 LogFlowThisFunc(("Not spawning any more!\n"));
8379 return true;
8380 }
8381
8382 HRESULT rc = S_OK;
8383
8384 /* PID not yet initialized, skip check. */
8385 if (mData->mSession.mPID == NIL_RTPROCESS)
8386 return false;
8387
8388 RTPROCSTATUS status;
8389 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8390
8391 if (vrc != VERR_PROCESS_RUNNING)
8392 {
8393 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8394 rc = setError(E_FAIL,
8395 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8396 getName().c_str(), status.iStatus);
8397 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8398 rc = setError(E_FAIL,
8399 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8400 getName().c_str(), status.iStatus);
8401 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8402 rc = setError(E_FAIL,
8403 tr("The virtual machine '%s' has terminated abnormally"),
8404 getName().c_str(), status.iStatus);
8405 else
8406 rc = setError(E_FAIL,
8407 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8408 getName().c_str(), vrc);
8409 }
8410
8411 if (FAILED(rc))
8412 {
8413 /* Close the remote session, remove the remote control from the list
8414 * and reset session state to Closed (@note keep the code in sync with
8415 * the relevant part in LockMachine()). */
8416
8417 Assert(mData->mSession.mRemoteControls.size() == 1);
8418 if (mData->mSession.mRemoteControls.size() == 1)
8419 {
8420 ErrorInfoKeeper eik;
8421 mData->mSession.mRemoteControls.front()->Uninitialize();
8422 }
8423
8424 mData->mSession.mRemoteControls.clear();
8425 mData->mSession.mState = SessionState_Unlocked;
8426
8427 /* finalize the progress after setting the state */
8428 if (!mData->mSession.mProgress.isNull())
8429 {
8430 mData->mSession.mProgress->i_notifyComplete(rc);
8431 mData->mSession.mProgress.setNull();
8432 }
8433
8434 mData->mSession.mPID = NIL_RTPROCESS;
8435
8436 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8437 return true;
8438 }
8439
8440 return false;
8441}
8442
8443/**
8444 * Checks whether the machine can be registered. If so, commits and saves
8445 * all settings.
8446 *
8447 * @note Must be called from mParent's write lock. Locks this object and
8448 * children for writing.
8449 */
8450HRESULT Machine::prepareRegister()
8451{
8452 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8453
8454 AutoLimitedCaller autoCaller(this);
8455 AssertComRCReturnRC(autoCaller.rc());
8456
8457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8458
8459 /* wait for state dependents to drop to zero */
8460 ensureNoStateDependencies();
8461
8462 if (!mData->mAccessible)
8463 return setError(VBOX_E_INVALID_OBJECT_STATE,
8464 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8465 mUserData->s.strName.c_str(),
8466 mData->mUuid.toString().c_str());
8467
8468 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8469
8470 if (mData->mRegistered)
8471 return setError(VBOX_E_INVALID_OBJECT_STATE,
8472 tr("The machine '%s' with UUID {%s} is already registered"),
8473 mUserData->s.strName.c_str(),
8474 mData->mUuid.toString().c_str());
8475
8476 HRESULT rc = S_OK;
8477
8478 // Ensure the settings are saved. If we are going to be registered and
8479 // no config file exists yet, create it by calling saveSettings() too.
8480 if ( (mData->flModifications)
8481 || (!mData->pMachineConfigFile->fileExists())
8482 )
8483 {
8484 rc = saveSettings(NULL);
8485 // no need to check whether VirtualBox.xml needs saving too since
8486 // we can't have a machine XML file rename pending
8487 if (FAILED(rc)) return rc;
8488 }
8489
8490 /* more config checking goes here */
8491
8492 if (SUCCEEDED(rc))
8493 {
8494 /* we may have had implicit modifications we want to fix on success */
8495 commit();
8496
8497 mData->mRegistered = true;
8498 }
8499 else
8500 {
8501 /* we may have had implicit modifications we want to cancel on failure*/
8502 rollback(false /* aNotify */);
8503 }
8504
8505 return rc;
8506}
8507
8508/**
8509 * Increases the number of objects dependent on the machine state or on the
8510 * registered state. Guarantees that these two states will not change at least
8511 * until #releaseStateDependency() is called.
8512 *
8513 * Depending on the @a aDepType value, additional state checks may be made.
8514 * These checks will set extended error info on failure. See
8515 * #checkStateDependency() for more info.
8516 *
8517 * If this method returns a failure, the dependency is not added and the caller
8518 * is not allowed to rely on any particular machine state or registration state
8519 * value and may return the failed result code to the upper level.
8520 *
8521 * @param aDepType Dependency type to add.
8522 * @param aState Current machine state (NULL if not interested).
8523 * @param aRegistered Current registered state (NULL if not interested).
8524 *
8525 * @note Locks this object for writing.
8526 */
8527HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8528 MachineState_T *aState /* = NULL */,
8529 BOOL *aRegistered /* = NULL */)
8530{
8531 AutoCaller autoCaller(this);
8532 AssertComRCReturnRC(autoCaller.rc());
8533
8534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8535
8536 HRESULT rc = checkStateDependency(aDepType);
8537 if (FAILED(rc)) return rc;
8538
8539 {
8540 if (mData->mMachineStateChangePending != 0)
8541 {
8542 /* ensureNoStateDependencies() is waiting for state dependencies to
8543 * drop to zero so don't add more. It may make sense to wait a bit
8544 * and retry before reporting an error (since the pending state
8545 * transition should be really quick) but let's just assert for
8546 * now to see if it ever happens on practice. */
8547
8548 AssertFailed();
8549
8550 return setError(E_ACCESSDENIED,
8551 tr("Machine state change is in progress. Please retry the operation later."));
8552 }
8553
8554 ++mData->mMachineStateDeps;
8555 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8556 }
8557
8558 if (aState)
8559 *aState = mData->mMachineState;
8560 if (aRegistered)
8561 *aRegistered = mData->mRegistered;
8562
8563 return S_OK;
8564}
8565
8566/**
8567 * Decreases the number of objects dependent on the machine state.
8568 * Must always complete the #addStateDependency() call after the state
8569 * dependency is no more necessary.
8570 */
8571void Machine::releaseStateDependency()
8572{
8573 AutoCaller autoCaller(this);
8574 AssertComRCReturnVoid(autoCaller.rc());
8575
8576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8577
8578 /* releaseStateDependency() w/o addStateDependency()? */
8579 AssertReturnVoid(mData->mMachineStateDeps != 0);
8580 -- mData->mMachineStateDeps;
8581
8582 if (mData->mMachineStateDeps == 0)
8583 {
8584 /* inform ensureNoStateDependencies() that there are no more deps */
8585 if (mData->mMachineStateChangePending != 0)
8586 {
8587 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8588 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8589 }
8590 }
8591}
8592
8593Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8594{
8595 /* start with nothing found */
8596 Utf8Str strResult("");
8597
8598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8599
8600 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8601 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8602 // found:
8603 strResult = it->second; // source is a Utf8Str
8604
8605 return strResult;
8606}
8607
8608// protected methods
8609/////////////////////////////////////////////////////////////////////////////
8610
8611/**
8612 * Performs machine state checks based on the @a aDepType value. If a check
8613 * fails, this method will set extended error info, otherwise it will return
8614 * S_OK. It is supposed, that on failure, the caller will immediately return
8615 * the return value of this method to the upper level.
8616 *
8617 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8618 *
8619 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8620 * current state of this machine object allows to change settings of the
8621 * machine (i.e. the machine is not registered, or registered but not running
8622 * and not saved). It is useful to call this method from Machine setters
8623 * before performing any change.
8624 *
8625 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8626 * as for MutableStateDep except that if the machine is saved, S_OK is also
8627 * returned. This is useful in setters which allow changing machine
8628 * properties when it is in the saved state.
8629 *
8630 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8631 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8632 * Aborted).
8633 *
8634 * @param aDepType Dependency type to check.
8635 *
8636 * @note Non Machine based classes should use #addStateDependency() and
8637 * #releaseStateDependency() methods or the smart AutoStateDependency
8638 * template.
8639 *
8640 * @note This method must be called from under this object's read or write
8641 * lock.
8642 */
8643HRESULT Machine::checkStateDependency(StateDependency aDepType)
8644{
8645 switch (aDepType)
8646 {
8647 case AnyStateDep:
8648 {
8649 break;
8650 }
8651 case MutableStateDep:
8652 {
8653 if ( mData->mRegistered
8654 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8655 || ( mData->mMachineState != MachineState_Paused
8656 && mData->mMachineState != MachineState_Running
8657 && mData->mMachineState != MachineState_Aborted
8658 && mData->mMachineState != MachineState_Teleported
8659 && mData->mMachineState != MachineState_PoweredOff
8660 )
8661 )
8662 )
8663 return setError(VBOX_E_INVALID_VM_STATE,
8664 tr("The machine is not mutable (state is %s)"),
8665 Global::stringifyMachineState(mData->mMachineState));
8666 break;
8667 }
8668 case MutableOrSavedStateDep:
8669 {
8670 if ( mData->mRegistered
8671 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8672 || ( mData->mMachineState != MachineState_Paused
8673 && mData->mMachineState != MachineState_Running
8674 && mData->mMachineState != MachineState_Aborted
8675 && mData->mMachineState != MachineState_Teleported
8676 && mData->mMachineState != MachineState_Saved
8677 && mData->mMachineState != MachineState_PoweredOff
8678 )
8679 )
8680 )
8681 return setError(VBOX_E_INVALID_VM_STATE,
8682 tr("The machine is not mutable (state is %s)"),
8683 Global::stringifyMachineState(mData->mMachineState));
8684 break;
8685 }
8686 case OfflineStateDep:
8687 {
8688 if ( mData->mRegistered
8689 && ( !isSessionMachine()
8690 || ( mData->mMachineState != MachineState_PoweredOff
8691 && mData->mMachineState != MachineState_Saved
8692 && mData->mMachineState != MachineState_Aborted
8693 && mData->mMachineState != MachineState_Teleported
8694 )
8695 )
8696 )
8697 return setError(VBOX_E_INVALID_VM_STATE,
8698 tr("The machine is not offline (state is %s)"),
8699 Global::stringifyMachineState(mData->mMachineState));
8700 break;
8701 }
8702 }
8703
8704 return S_OK;
8705}
8706
8707/**
8708 * Helper to initialize all associated child objects and allocate data
8709 * structures.
8710 *
8711 * This method must be called as a part of the object's initialization procedure
8712 * (usually done in the #init() method).
8713 *
8714 * @note Must be called only from #init() or from #registeredInit().
8715 */
8716HRESULT Machine::initDataAndChildObjects()
8717{
8718 AutoCaller autoCaller(this);
8719 AssertComRCReturnRC(autoCaller.rc());
8720 AssertComRCReturn(autoCaller.state() == InInit ||
8721 autoCaller.state() == Limited, E_FAIL);
8722
8723 AssertReturn(!mData->mAccessible, E_FAIL);
8724
8725 /* allocate data structures */
8726 mSSData.allocate();
8727 mUserData.allocate();
8728 mHWData.allocate();
8729 mMediaData.allocate();
8730 mStorageControllers.allocate();
8731 mUSBControllers.allocate();
8732
8733 /* initialize mOSTypeId */
8734 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8735
8736 /* create associated BIOS settings object */
8737 unconst(mBIOSSettings).createObject();
8738 mBIOSSettings->init(this);
8739
8740 /* create an associated VRDE object (default is disabled) */
8741 unconst(mVRDEServer).createObject();
8742 mVRDEServer->init(this);
8743
8744 /* create associated serial port objects */
8745 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8746 {
8747 unconst(mSerialPorts[slot]).createObject();
8748 mSerialPorts[slot]->init(this, slot);
8749 }
8750
8751 /* create associated parallel port objects */
8752 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8753 {
8754 unconst(mParallelPorts[slot]).createObject();
8755 mParallelPorts[slot]->init(this, slot);
8756 }
8757
8758 /* create the audio adapter object (always present, default is disabled) */
8759 unconst(mAudioAdapter).createObject();
8760 mAudioAdapter->init(this);
8761
8762 /* create the USB device filters object (always present) */
8763 unconst(mUSBDeviceFilters).createObject();
8764 mUSBDeviceFilters->init(this);
8765
8766 /* create associated network adapter objects */
8767 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8768 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8769 {
8770 unconst(mNetworkAdapters[slot]).createObject();
8771 mNetworkAdapters[slot]->init(this, slot);
8772 }
8773
8774 /* create the bandwidth control */
8775 unconst(mBandwidthControl).createObject();
8776 mBandwidthControl->init(this);
8777
8778 return S_OK;
8779}
8780
8781/**
8782 * Helper to uninitialize all associated child objects and to free all data
8783 * structures.
8784 *
8785 * This method must be called as a part of the object's uninitialization
8786 * procedure (usually done in the #uninit() method).
8787 *
8788 * @note Must be called only from #uninit() or from #registeredInit().
8789 */
8790void Machine::uninitDataAndChildObjects()
8791{
8792 AutoCaller autoCaller(this);
8793 AssertComRCReturnVoid(autoCaller.rc());
8794 AssertComRCReturnVoid( autoCaller.state() == InUninit
8795 || autoCaller.state() == Limited);
8796
8797 /* tell all our other child objects we've been uninitialized */
8798 if (mBandwidthControl)
8799 {
8800 mBandwidthControl->uninit();
8801 unconst(mBandwidthControl).setNull();
8802 }
8803
8804 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8805 {
8806 if (mNetworkAdapters[slot])
8807 {
8808 mNetworkAdapters[slot]->uninit();
8809 unconst(mNetworkAdapters[slot]).setNull();
8810 }
8811 }
8812
8813 if (mUSBDeviceFilters)
8814 {
8815 mUSBDeviceFilters->uninit();
8816 unconst(mUSBDeviceFilters).setNull();
8817 }
8818
8819 if (mAudioAdapter)
8820 {
8821 mAudioAdapter->uninit();
8822 unconst(mAudioAdapter).setNull();
8823 }
8824
8825 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8826 {
8827 if (mParallelPorts[slot])
8828 {
8829 mParallelPorts[slot]->uninit();
8830 unconst(mParallelPorts[slot]).setNull();
8831 }
8832 }
8833
8834 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8835 {
8836 if (mSerialPorts[slot])
8837 {
8838 mSerialPorts[slot]->uninit();
8839 unconst(mSerialPorts[slot]).setNull();
8840 }
8841 }
8842
8843 if (mVRDEServer)
8844 {
8845 mVRDEServer->uninit();
8846 unconst(mVRDEServer).setNull();
8847 }
8848
8849 if (mBIOSSettings)
8850 {
8851 mBIOSSettings->uninit();
8852 unconst(mBIOSSettings).setNull();
8853 }
8854
8855 /* Deassociate media (only when a real Machine or a SnapshotMachine
8856 * instance is uninitialized; SessionMachine instances refer to real
8857 * Machine media). This is necessary for a clean re-initialization of
8858 * the VM after successfully re-checking the accessibility state. Note
8859 * that in case of normal Machine or SnapshotMachine uninitialization (as
8860 * a result of unregistering or deleting the snapshot), outdated media
8861 * attachments will already be uninitialized and deleted, so this
8862 * code will not affect them. */
8863 if ( !!mMediaData
8864 && (!isSessionMachine())
8865 )
8866 {
8867 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8868 it != mMediaData->mAttachments.end();
8869 ++it)
8870 {
8871 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8872 if (pMedium.isNull())
8873 continue;
8874 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, getSnapshotId());
8875 AssertComRC(rc);
8876 }
8877 }
8878
8879 if (!isSessionMachine() && !isSnapshotMachine())
8880 {
8881 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8882 if (mData->mFirstSnapshot)
8883 {
8884 // snapshots tree is protected by machine write lock; strictly
8885 // this isn't necessary here since we're deleting the entire
8886 // machine, but otherwise we assert in Snapshot::uninit()
8887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8888 mData->mFirstSnapshot->uninit();
8889 mData->mFirstSnapshot.setNull();
8890 }
8891
8892 mData->mCurrentSnapshot.setNull();
8893 }
8894
8895 /* free data structures (the essential mData structure is not freed here
8896 * since it may be still in use) */
8897 mMediaData.free();
8898 mStorageControllers.free();
8899 mUSBControllers.free();
8900 mHWData.free();
8901 mUserData.free();
8902 mSSData.free();
8903}
8904
8905/**
8906 * Returns a pointer to the Machine object for this machine that acts like a
8907 * parent for complex machine data objects such as shared folders, etc.
8908 *
8909 * For primary Machine objects and for SnapshotMachine objects, returns this
8910 * object's pointer itself. For SessionMachine objects, returns the peer
8911 * (primary) machine pointer.
8912 */
8913Machine* Machine::getMachine()
8914{
8915 if (isSessionMachine())
8916 return (Machine*)mPeer;
8917 return this;
8918}
8919
8920/**
8921 * Makes sure that there are no machine state dependents. If necessary, waits
8922 * for the number of dependents to drop to zero.
8923 *
8924 * Make sure this method is called from under this object's write lock to
8925 * guarantee that no new dependents may be added when this method returns
8926 * control to the caller.
8927 *
8928 * @note Locks this object for writing. The lock will be released while waiting
8929 * (if necessary).
8930 *
8931 * @warning To be used only in methods that change the machine state!
8932 */
8933void Machine::ensureNoStateDependencies()
8934{
8935 AssertReturnVoid(isWriteLockOnCurrentThread());
8936
8937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8938
8939 /* Wait for all state dependents if necessary */
8940 if (mData->mMachineStateDeps != 0)
8941 {
8942 /* lazy semaphore creation */
8943 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8944 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8945
8946 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8947 mData->mMachineStateDeps));
8948
8949 ++mData->mMachineStateChangePending;
8950
8951 /* reset the semaphore before waiting, the last dependent will signal
8952 * it */
8953 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8954
8955 alock.release();
8956
8957 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8958
8959 alock.acquire();
8960
8961 -- mData->mMachineStateChangePending;
8962 }
8963}
8964
8965/**
8966 * Changes the machine state and informs callbacks.
8967 *
8968 * This method is not intended to fail so it either returns S_OK or asserts (and
8969 * returns a failure).
8970 *
8971 * @note Locks this object for writing.
8972 */
8973HRESULT Machine::setMachineState(MachineState_T aMachineState)
8974{
8975 LogFlowThisFuncEnter();
8976 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8977
8978 AutoCaller autoCaller(this);
8979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8980
8981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8982
8983 /* wait for state dependents to drop to zero */
8984 ensureNoStateDependencies();
8985
8986 if (mData->mMachineState != aMachineState)
8987 {
8988 mData->mMachineState = aMachineState;
8989
8990 RTTimeNow(&mData->mLastStateChange);
8991
8992 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8993 }
8994
8995 LogFlowThisFuncLeave();
8996 return S_OK;
8997}
8998
8999/**
9000 * Searches for a shared folder with the given logical name
9001 * in the collection of shared folders.
9002 *
9003 * @param aName logical name of the shared folder
9004 * @param aSharedFolder where to return the found object
9005 * @param aSetError whether to set the error info if the folder is
9006 * not found
9007 * @return
9008 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
9009 *
9010 * @note
9011 * must be called from under the object's lock!
9012 */
9013HRESULT Machine::findSharedFolder(const Utf8Str &aName,
9014 ComObjPtr<SharedFolder> &aSharedFolder,
9015 bool aSetError /* = false */)
9016{
9017 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
9018 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9019 it != mHWData->mSharedFolders.end();
9020 ++it)
9021 {
9022 SharedFolder *pSF = *it;
9023 AutoCaller autoCaller(pSF);
9024 if (pSF->i_getName() == aName)
9025 {
9026 aSharedFolder = pSF;
9027 rc = S_OK;
9028 break;
9029 }
9030 }
9031
9032 if (aSetError && FAILED(rc))
9033 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
9034
9035 return rc;
9036}
9037
9038/**
9039 * Initializes all machine instance data from the given settings structures
9040 * from XML. The exception is the machine UUID which needs special handling
9041 * depending on the caller's use case, so the caller needs to set that herself.
9042 *
9043 * This gets called in several contexts during machine initialization:
9044 *
9045 * -- When machine XML exists on disk already and needs to be loaded into memory,
9046 * for example, from registeredInit() to load all registered machines on
9047 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
9048 * attached to the machine should be part of some media registry already.
9049 *
9050 * -- During OVF import, when a machine config has been constructed from an
9051 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9052 * ensure that the media listed as attachments in the config (which have
9053 * been imported from the OVF) receive the correct registry ID.
9054 *
9055 * -- During VM cloning.
9056 *
9057 * @param config Machine settings from XML.
9058 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
9059 * @return
9060 */
9061HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9062 const Guid *puuidRegistry)
9063{
9064 // copy name, description, OS type, teleporter, UTC etc.
9065 mUserData->s = config.machineUserData;
9066
9067 // Decode the Icon overide data from config userdata and set onto Machine.
9068 #define DECODE_STR_MAX _1M
9069 const char* pszStr = config.machineUserData.ovIcon.c_str();
9070 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
9071 if (cbOut > DECODE_STR_MAX)
9072 return setError(E_FAIL,
9073 tr("Icon Data too long.'%d' > '%d'"),
9074 cbOut,
9075 DECODE_STR_MAX);
9076 com::SafeArray<BYTE> iconByte(cbOut);
9077 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
9078 if (FAILED(rc))
9079 return setError(E_FAIL,
9080 tr("Failure to Decode Icon Data. '%s' (%d)"),
9081 pszStr,
9082 rc);
9083 mUserData->mIcon.resize(iconByte.size());
9084 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
9085
9086 // look up the object by Id to check it is valid
9087 ComPtr<IGuestOSType> guestOSType;
9088 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9089 guestOSType.asOutParam());
9090 if (FAILED(rc)) return rc;
9091
9092 // stateFile (optional)
9093 if (config.strStateFile.isEmpty())
9094 mSSData->strStateFilePath.setNull();
9095 else
9096 {
9097 Utf8Str stateFilePathFull(config.strStateFile);
9098 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9099 if (RT_FAILURE(vrc))
9100 return setError(E_FAIL,
9101 tr("Invalid saved state file path '%s' (%Rrc)"),
9102 config.strStateFile.c_str(),
9103 vrc);
9104 mSSData->strStateFilePath = stateFilePathFull;
9105 }
9106
9107 // snapshot folder needs special processing so set it again
9108 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9109 if (FAILED(rc)) return rc;
9110
9111 /* Copy the extra data items (Not in any case config is already the same as
9112 * mData->pMachineConfigFile, like when the xml files are read from disk. So
9113 * make sure the extra data map is copied). */
9114 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9115
9116 /* currentStateModified (optional, default is true) */
9117 mData->mCurrentStateModified = config.fCurrentStateModified;
9118
9119 mData->mLastStateChange = config.timeLastStateChange;
9120
9121 /*
9122 * note: all mUserData members must be assigned prior this point because
9123 * we need to commit changes in order to let mUserData be shared by all
9124 * snapshot machine instances.
9125 */
9126 mUserData.commitCopy();
9127
9128 // machine registry, if present (must be loaded before snapshots)
9129 if (config.canHaveOwnMediaRegistry())
9130 {
9131 // determine machine folder
9132 Utf8Str strMachineFolder = getSettingsFileFull();
9133 strMachineFolder.stripFilename();
9134 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9135 config.mediaRegistry,
9136 strMachineFolder);
9137 if (FAILED(rc)) return rc;
9138 }
9139
9140 /* Snapshot node (optional) */
9141 size_t cRootSnapshots;
9142 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9143 {
9144 // there must be only one root snapshot
9145 Assert(cRootSnapshots == 1);
9146
9147 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9148
9149 rc = loadSnapshot(snap,
9150 config.uuidCurrentSnapshot,
9151 NULL); // no parent == first snapshot
9152 if (FAILED(rc)) return rc;
9153 }
9154
9155 // hardware data
9156 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9157 if (FAILED(rc)) return rc;
9158
9159 // load storage controllers
9160 rc = loadStorageControllers(config.storageMachine,
9161 puuidRegistry,
9162 NULL /* puuidSnapshot */);
9163 if (FAILED(rc)) return rc;
9164
9165 /*
9166 * NOTE: the assignment below must be the last thing to do,
9167 * otherwise it will be not possible to change the settings
9168 * somewhere in the code above because all setters will be
9169 * blocked by checkStateDependency(MutableStateDep).
9170 */
9171
9172 /* set the machine state to Aborted or Saved when appropriate */
9173 if (config.fAborted)
9174 {
9175 mSSData->strStateFilePath.setNull();
9176
9177 /* no need to use setMachineState() during init() */
9178 mData->mMachineState = MachineState_Aborted;
9179 }
9180 else if (!mSSData->strStateFilePath.isEmpty())
9181 {
9182 /* no need to use setMachineState() during init() */
9183 mData->mMachineState = MachineState_Saved;
9184 }
9185
9186 // after loading settings, we are no longer different from the XML on disk
9187 mData->flModifications = 0;
9188
9189 return S_OK;
9190}
9191
9192/**
9193 * Recursively loads all snapshots starting from the given.
9194 *
9195 * @param aNode <Snapshot> node.
9196 * @param aCurSnapshotId Current snapshot ID from the settings file.
9197 * @param aParentSnapshot Parent snapshot.
9198 */
9199HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9200 const Guid &aCurSnapshotId,
9201 Snapshot *aParentSnapshot)
9202{
9203 AssertReturn(!isSnapshotMachine(), E_FAIL);
9204 AssertReturn(!isSessionMachine(), E_FAIL);
9205
9206 HRESULT rc = S_OK;
9207
9208 Utf8Str strStateFile;
9209 if (!data.strStateFile.isEmpty())
9210 {
9211 /* optional */
9212 strStateFile = data.strStateFile;
9213 int vrc = calculateFullPath(strStateFile, strStateFile);
9214 if (RT_FAILURE(vrc))
9215 return setError(E_FAIL,
9216 tr("Invalid saved state file path '%s' (%Rrc)"),
9217 strStateFile.c_str(),
9218 vrc);
9219 }
9220
9221 /* create a snapshot machine object */
9222 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9223 pSnapshotMachine.createObject();
9224 rc = pSnapshotMachine->initFromSettings(this,
9225 data.hardware,
9226 &data.debugging,
9227 &data.autostart,
9228 data.storage,
9229 data.uuid.ref(),
9230 strStateFile);
9231 if (FAILED(rc)) return rc;
9232
9233 /* create a snapshot object */
9234 ComObjPtr<Snapshot> pSnapshot;
9235 pSnapshot.createObject();
9236 /* initialize the snapshot */
9237 rc = pSnapshot->init(mParent, // VirtualBox object
9238 data.uuid,
9239 data.strName,
9240 data.strDescription,
9241 data.timestamp,
9242 pSnapshotMachine,
9243 aParentSnapshot);
9244 if (FAILED(rc)) return rc;
9245
9246 /* memorize the first snapshot if necessary */
9247 if (!mData->mFirstSnapshot)
9248 mData->mFirstSnapshot = pSnapshot;
9249
9250 /* memorize the current snapshot when appropriate */
9251 if ( !mData->mCurrentSnapshot
9252 && pSnapshot->i_getId() == aCurSnapshotId
9253 )
9254 mData->mCurrentSnapshot = pSnapshot;
9255
9256 // now create the children
9257 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9258 it != data.llChildSnapshots.end();
9259 ++it)
9260 {
9261 const settings::Snapshot &childData = *it;
9262 // recurse
9263 rc = loadSnapshot(childData,
9264 aCurSnapshotId,
9265 pSnapshot); // parent = the one we created above
9266 if (FAILED(rc)) return rc;
9267 }
9268
9269 return rc;
9270}
9271
9272/**
9273 * Loads settings into mHWData.
9274 *
9275 * @param data Reference to the hardware settings.
9276 * @param pDbg Pointer to the debugging settings.
9277 * @param pAutostart Pointer to the autostart settings.
9278 */
9279HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9280 const settings::Autostart *pAutostart)
9281{
9282 AssertReturn(!isSessionMachine(), E_FAIL);
9283
9284 HRESULT rc = S_OK;
9285
9286 try
9287 {
9288 /* The hardware version attribute (optional). */
9289 mHWData->mHWVersion = data.strVersion;
9290 mHWData->mHardwareUUID = data.uuid;
9291
9292 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9293 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9294 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9295 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9296 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9297 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9298 mHWData->mPAEEnabled = data.fPAE;
9299 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9300 mHWData->mLongMode = data.enmLongMode;
9301 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9302 mHWData->mCPUCount = data.cCPUs;
9303 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9304 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9305
9306 // cpu
9307 if (mHWData->mCPUHotPlugEnabled)
9308 {
9309 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9310 it != data.llCpus.end();
9311 ++it)
9312 {
9313 const settings::Cpu &cpu = *it;
9314
9315 mHWData->mCPUAttached[cpu.ulId] = true;
9316 }
9317 }
9318
9319 // cpuid leafs
9320 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9321 it != data.llCpuIdLeafs.end();
9322 ++it)
9323 {
9324 const settings::CpuIdLeaf &leaf = *it;
9325
9326 switch (leaf.ulId)
9327 {
9328 case 0x0:
9329 case 0x1:
9330 case 0x2:
9331 case 0x3:
9332 case 0x4:
9333 case 0x5:
9334 case 0x6:
9335 case 0x7:
9336 case 0x8:
9337 case 0x9:
9338 case 0xA:
9339 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9340 break;
9341
9342 case 0x80000000:
9343 case 0x80000001:
9344 case 0x80000002:
9345 case 0x80000003:
9346 case 0x80000004:
9347 case 0x80000005:
9348 case 0x80000006:
9349 case 0x80000007:
9350 case 0x80000008:
9351 case 0x80000009:
9352 case 0x8000000A:
9353 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9354 break;
9355
9356 default:
9357 /* just ignore */
9358 break;
9359 }
9360 }
9361
9362 mHWData->mMemorySize = data.ulMemorySizeMB;
9363 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9364
9365 // boot order
9366 for (size_t i = 0;
9367 i < RT_ELEMENTS(mHWData->mBootOrder);
9368 i++)
9369 {
9370 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9371 if (it == data.mapBootOrder.end())
9372 mHWData->mBootOrder[i] = DeviceType_Null;
9373 else
9374 mHWData->mBootOrder[i] = it->second;
9375 }
9376
9377 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9378 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9379 mHWData->mMonitorCount = data.cMonitors;
9380 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9381 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9382 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9383 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9384 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9385 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9386 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9387 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9388 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9389 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9390 if (!data.strVideoCaptureFile.isEmpty())
9391 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9392 else
9393 mHWData->mVideoCaptureFile.setNull();
9394 mHWData->mFirmwareType = data.firmwareType;
9395 mHWData->mPointingHIDType = data.pointingHIDType;
9396 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9397 mHWData->mChipsetType = data.chipsetType;
9398 mHWData->mParavirtProvider = data.paravirtProvider;
9399 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9400 mHWData->mHPETEnabled = data.fHPETEnabled;
9401
9402 /* VRDEServer */
9403 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9404 if (FAILED(rc)) return rc;
9405
9406 /* BIOS */
9407 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9408 if (FAILED(rc)) return rc;
9409
9410 // Bandwidth control (must come before network adapters)
9411 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9412 if (FAILED(rc)) return rc;
9413
9414 /* Shared folders */
9415 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9416 it != data.usbSettings.llUSBControllers.end();
9417 ++it)
9418 {
9419 const settings::USBController &settingsCtrl = *it;
9420 ComObjPtr<USBController> newCtrl;
9421
9422 newCtrl.createObject();
9423 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9424 mUSBControllers->push_back(newCtrl);
9425 }
9426
9427 /* USB device filters */
9428 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9429 if (FAILED(rc)) return rc;
9430
9431 // network adapters
9432 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9433 uint32_t oldCount = mNetworkAdapters.size();
9434 if (newCount > oldCount)
9435 {
9436 mNetworkAdapters.resize(newCount);
9437 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9438 {
9439 unconst(mNetworkAdapters[slot]).createObject();
9440 mNetworkAdapters[slot]->init(this, slot);
9441 }
9442 }
9443 else if (newCount < oldCount)
9444 mNetworkAdapters.resize(newCount);
9445 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9446 it != data.llNetworkAdapters.end();
9447 ++it)
9448 {
9449 const settings::NetworkAdapter &nic = *it;
9450
9451 /* slot unicity is guaranteed by XML Schema */
9452 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9453 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9454 if (FAILED(rc)) return rc;
9455 }
9456
9457 // serial ports
9458 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9459 it != data.llSerialPorts.end();
9460 ++it)
9461 {
9462 const settings::SerialPort &s = *it;
9463
9464 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9465 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9466 if (FAILED(rc)) return rc;
9467 }
9468
9469 // parallel ports (optional)
9470 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9471 it != data.llParallelPorts.end();
9472 ++it)
9473 {
9474 const settings::ParallelPort &p = *it;
9475
9476 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9477 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9478 if (FAILED(rc)) return rc;
9479 }
9480
9481 /* AudioAdapter */
9482 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9483 if (FAILED(rc)) return rc;
9484
9485 /* Shared folders */
9486 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9487 it != data.llSharedFolders.end();
9488 ++it)
9489 {
9490 const settings::SharedFolder &sf = *it;
9491
9492 ComObjPtr<SharedFolder> sharedFolder;
9493 /* Check for double entries. Not allowed! */
9494 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9495 if (SUCCEEDED(rc))
9496 return setError(VBOX_E_OBJECT_IN_USE,
9497 tr("Shared folder named '%s' already exists"),
9498 sf.strName.c_str());
9499
9500 /* Create the new shared folder. Don't break on error. This will be
9501 * reported when the machine starts. */
9502 sharedFolder.createObject();
9503 rc = sharedFolder->init(getMachine(),
9504 sf.strName,
9505 sf.strHostPath,
9506 RT_BOOL(sf.fWritable),
9507 RT_BOOL(sf.fAutoMount),
9508 false /* fFailOnError */);
9509 if (FAILED(rc)) return rc;
9510 mHWData->mSharedFolders.push_back(sharedFolder);
9511 }
9512
9513 // Clipboard
9514 mHWData->mClipboardMode = data.clipboardMode;
9515
9516 // drag'n'drop
9517 mHWData->mDragAndDropMode = data.dragAndDropMode;
9518
9519 // guest settings
9520 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9521
9522 // IO settings
9523 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9524 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9525
9526 // Host PCI devices
9527 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9528 it != data.pciAttachments.end();
9529 ++it)
9530 {
9531 const settings::HostPCIDeviceAttachment &hpda = *it;
9532 ComObjPtr<PCIDeviceAttachment> pda;
9533
9534 pda.createObject();
9535 pda->i_loadSettings(this, hpda);
9536 mHWData->mPCIDeviceAssignments.push_back(pda);
9537 }
9538
9539 /*
9540 * (The following isn't really real hardware, but it lives in HWData
9541 * for reasons of convenience.)
9542 */
9543
9544#ifdef VBOX_WITH_GUEST_PROPS
9545 /* Guest properties (optional) */
9546 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9547 it != data.llGuestProperties.end();
9548 ++it)
9549 {
9550 const settings::GuestProperty &prop = *it;
9551 uint32_t fFlags = guestProp::NILFLAG;
9552 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9553 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9554 mHWData->mGuestProperties[prop.strName] = property;
9555 }
9556
9557 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9558#endif /* VBOX_WITH_GUEST_PROPS defined */
9559
9560 rc = loadDebugging(pDbg);
9561 if (FAILED(rc))
9562 return rc;
9563
9564 mHWData->mAutostart = *pAutostart;
9565
9566 /* default frontend */
9567 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9568 }
9569 catch(std::bad_alloc &)
9570 {
9571 return E_OUTOFMEMORY;
9572 }
9573
9574 AssertComRC(rc);
9575 return rc;
9576}
9577
9578/**
9579 * Called from Machine::loadHardware() to load the debugging settings of the
9580 * machine.
9581 *
9582 * @param pDbg Pointer to the settings.
9583 */
9584HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9585{
9586 mHWData->mDebugging = *pDbg;
9587 /* no more processing currently required, this will probably change. */
9588 return S_OK;
9589}
9590
9591/**
9592 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9593 *
9594 * @param data
9595 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9596 * @param puuidSnapshot
9597 * @return
9598 */
9599HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9600 const Guid *puuidRegistry,
9601 const Guid *puuidSnapshot)
9602{
9603 AssertReturn(!isSessionMachine(), E_FAIL);
9604
9605 HRESULT rc = S_OK;
9606
9607 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9608 it != data.llStorageControllers.end();
9609 ++it)
9610 {
9611 const settings::StorageController &ctlData = *it;
9612
9613 ComObjPtr<StorageController> pCtl;
9614 /* Try to find one with the name first. */
9615 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9616 if (SUCCEEDED(rc))
9617 return setError(VBOX_E_OBJECT_IN_USE,
9618 tr("Storage controller named '%s' already exists"),
9619 ctlData.strName.c_str());
9620
9621 pCtl.createObject();
9622 rc = pCtl->init(this,
9623 ctlData.strName,
9624 ctlData.storageBus,
9625 ctlData.ulInstance,
9626 ctlData.fBootable);
9627 if (FAILED(rc)) return rc;
9628
9629 mStorageControllers->push_back(pCtl);
9630
9631 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9632 if (FAILED(rc)) return rc;
9633
9634 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9635 if (FAILED(rc)) return rc;
9636
9637 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9638 if (FAILED(rc)) return rc;
9639
9640 /* Set IDE emulation settings (only for AHCI controller). */
9641 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9642 {
9643 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9644 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9645 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9646 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9647 )
9648 return rc;
9649 }
9650
9651 /* Load the attached devices now. */
9652 rc = loadStorageDevices(pCtl,
9653 ctlData,
9654 puuidRegistry,
9655 puuidSnapshot);
9656 if (FAILED(rc)) return rc;
9657 }
9658
9659 return S_OK;
9660}
9661
9662/**
9663 * Called from loadStorageControllers for a controller's devices.
9664 *
9665 * @param aStorageController
9666 * @param data
9667 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9668 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9669 * @return
9670 */
9671HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9672 const settings::StorageController &data,
9673 const Guid *puuidRegistry,
9674 const Guid *puuidSnapshot)
9675{
9676 HRESULT rc = S_OK;
9677
9678 /* paranoia: detect duplicate attachments */
9679 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9680 it != data.llAttachedDevices.end();
9681 ++it)
9682 {
9683 const settings::AttachedDevice &ad = *it;
9684
9685 for (settings::AttachedDevicesList::const_iterator it2 = it;
9686 it2 != data.llAttachedDevices.end();
9687 ++it2)
9688 {
9689 if (it == it2)
9690 continue;
9691
9692 const settings::AttachedDevice &ad2 = *it2;
9693
9694 if ( ad.lPort == ad2.lPort
9695 && ad.lDevice == ad2.lDevice)
9696 {
9697 return setError(E_FAIL,
9698 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9699 aStorageController->i_getName().c_str(),
9700 ad.lPort,
9701 ad.lDevice,
9702 mUserData->s.strName.c_str());
9703 }
9704 }
9705 }
9706
9707 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9708 it != data.llAttachedDevices.end();
9709 ++it)
9710 {
9711 const settings::AttachedDevice &dev = *it;
9712 ComObjPtr<Medium> medium;
9713
9714 switch (dev.deviceType)
9715 {
9716 case DeviceType_Floppy:
9717 case DeviceType_DVD:
9718 if (dev.strHostDriveSrc.isNotEmpty())
9719 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9720 else
9721 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9722 dev.uuid,
9723 false /* fRefresh */,
9724 false /* aSetError */,
9725 medium);
9726 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9727 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9728 rc = S_OK;
9729 break;
9730
9731 case DeviceType_HardDisk:
9732 {
9733 /* find a hard disk by UUID */
9734 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9735 if (FAILED(rc))
9736 {
9737 if (isSnapshotMachine())
9738 {
9739 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9740 // so the user knows that the bad disk is in a snapshot somewhere
9741 com::ErrorInfo info;
9742 return setError(E_FAIL,
9743 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9744 puuidSnapshot->raw(),
9745 info.getText().raw());
9746 }
9747 else
9748 return rc;
9749 }
9750
9751 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9752
9753 if (medium->i_getType() == MediumType_Immutable)
9754 {
9755 if (isSnapshotMachine())
9756 return setError(E_FAIL,
9757 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9758 "of the virtual machine '%s' ('%s')"),
9759 medium->i_getLocationFull().c_str(),
9760 dev.uuid.raw(),
9761 puuidSnapshot->raw(),
9762 mUserData->s.strName.c_str(),
9763 mData->m_strConfigFileFull.c_str());
9764
9765 return setError(E_FAIL,
9766 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9767 medium->i_getLocationFull().c_str(),
9768 dev.uuid.raw(),
9769 mUserData->s.strName.c_str(),
9770 mData->m_strConfigFileFull.c_str());
9771 }
9772
9773 if (medium->i_getType() == MediumType_MultiAttach)
9774 {
9775 if (isSnapshotMachine())
9776 return setError(E_FAIL,
9777 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9778 "of the virtual machine '%s' ('%s')"),
9779 medium->i_getLocationFull().c_str(),
9780 dev.uuid.raw(),
9781 puuidSnapshot->raw(),
9782 mUserData->s.strName.c_str(),
9783 mData->m_strConfigFileFull.c_str());
9784
9785 return setError(E_FAIL,
9786 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9787 medium->i_getLocationFull().c_str(),
9788 dev.uuid.raw(),
9789 mUserData->s.strName.c_str(),
9790 mData->m_strConfigFileFull.c_str());
9791 }
9792
9793 if ( !isSnapshotMachine()
9794 && medium->i_getChildren().size() != 0
9795 )
9796 return setError(E_FAIL,
9797 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9798 "because it has %d differencing child hard disks"),
9799 medium->i_getLocationFull().c_str(),
9800 dev.uuid.raw(),
9801 mUserData->s.strName.c_str(),
9802 mData->m_strConfigFileFull.c_str(),
9803 medium->i_getChildren().size());
9804
9805 if (findAttachment(mMediaData->mAttachments,
9806 medium))
9807 return setError(E_FAIL,
9808 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9809 medium->i_getLocationFull().c_str(),
9810 dev.uuid.raw(),
9811 mUserData->s.strName.c_str(),
9812 mData->m_strConfigFileFull.c_str());
9813
9814 break;
9815 }
9816
9817 default:
9818 return setError(E_FAIL,
9819 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9820 medium->i_getLocationFull().c_str(),
9821 mUserData->s.strName.c_str(),
9822 mData->m_strConfigFileFull.c_str());
9823 }
9824
9825 if (FAILED(rc))
9826 break;
9827
9828 /* Bandwidth groups are loaded at this point. */
9829 ComObjPtr<BandwidthGroup> pBwGroup;
9830
9831 if (!dev.strBwGroup.isEmpty())
9832 {
9833 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9834 if (FAILED(rc))
9835 return setError(E_FAIL,
9836 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9837 medium->i_getLocationFull().c_str(),
9838 dev.strBwGroup.c_str(),
9839 mUserData->s.strName.c_str(),
9840 mData->m_strConfigFileFull.c_str());
9841 pBwGroup->i_reference();
9842 }
9843
9844 const Bstr controllerName = aStorageController->i_getName();
9845 ComObjPtr<MediumAttachment> pAttachment;
9846 pAttachment.createObject();
9847 rc = pAttachment->init(this,
9848 medium,
9849 controllerName,
9850 dev.lPort,
9851 dev.lDevice,
9852 dev.deviceType,
9853 false,
9854 dev.fPassThrough,
9855 dev.fTempEject,
9856 dev.fNonRotational,
9857 dev.fDiscard,
9858 dev.fHotPluggable,
9859 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9860 if (FAILED(rc)) break;
9861
9862 /* associate the medium with this machine and snapshot */
9863 if (!medium.isNull())
9864 {
9865 AutoCaller medCaller(medium);
9866 if (FAILED(medCaller.rc())) return medCaller.rc();
9867 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9868
9869 if (isSnapshotMachine())
9870 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9871 else
9872 rc = medium->i_addBackReference(mData->mUuid);
9873 /* If the medium->addBackReference fails it sets an appropriate
9874 * error message, so no need to do any guesswork here. */
9875
9876 if (puuidRegistry)
9877 // caller wants registry ID to be set on all attached media (OVF import case)
9878 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9879 }
9880
9881 if (FAILED(rc))
9882 break;
9883
9884 /* back up mMediaData to let registeredInit() properly rollback on failure
9885 * (= limited accessibility) */
9886 setModified(IsModified_Storage);
9887 mMediaData.backup();
9888 mMediaData->mAttachments.push_back(pAttachment);
9889 }
9890
9891 return rc;
9892}
9893
9894/**
9895 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9896 *
9897 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9898 * @param aSnapshot where to return the found snapshot
9899 * @param aSetError true to set extended error info on failure
9900 */
9901HRESULT Machine::findSnapshotById(const Guid &aId,
9902 ComObjPtr<Snapshot> &aSnapshot,
9903 bool aSetError /* = false */)
9904{
9905 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9906
9907 if (!mData->mFirstSnapshot)
9908 {
9909 if (aSetError)
9910 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9911 return E_FAIL;
9912 }
9913
9914 if (aId.isZero())
9915 aSnapshot = mData->mFirstSnapshot;
9916 else
9917 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9918
9919 if (!aSnapshot)
9920 {
9921 if (aSetError)
9922 return setError(E_FAIL,
9923 tr("Could not find a snapshot with UUID {%s}"),
9924 aId.toString().c_str());
9925 return E_FAIL;
9926 }
9927
9928 return S_OK;
9929}
9930
9931/**
9932 * Returns the snapshot with the given name or fails of no such snapshot.
9933 *
9934 * @param aName snapshot name to find
9935 * @param aSnapshot where to return the found snapshot
9936 * @param aSetError true to set extended error info on failure
9937 */
9938HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9939 ComObjPtr<Snapshot> &aSnapshot,
9940 bool aSetError /* = false */)
9941{
9942 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9943
9944 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9945
9946 if (!mData->mFirstSnapshot)
9947 {
9948 if (aSetError)
9949 return setError(VBOX_E_OBJECT_NOT_FOUND,
9950 tr("This machine does not have any snapshots"));
9951 return VBOX_E_OBJECT_NOT_FOUND;
9952 }
9953
9954 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9955
9956 if (!aSnapshot)
9957 {
9958 if (aSetError)
9959 return setError(VBOX_E_OBJECT_NOT_FOUND,
9960 tr("Could not find a snapshot named '%s'"), strName.c_str());
9961 return VBOX_E_OBJECT_NOT_FOUND;
9962 }
9963
9964 return S_OK;
9965}
9966
9967/**
9968 * Returns a storage controller object with the given name.
9969 *
9970 * @param aName storage controller name to find
9971 * @param aStorageController where to return the found storage controller
9972 * @param aSetError true to set extended error info on failure
9973 */
9974HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9975 ComObjPtr<StorageController> &aStorageController,
9976 bool aSetError /* = false */)
9977{
9978 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9979
9980 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9981 it != mStorageControllers->end();
9982 ++it)
9983 {
9984 if ((*it)->i_getName() == aName)
9985 {
9986 aStorageController = (*it);
9987 return S_OK;
9988 }
9989 }
9990
9991 if (aSetError)
9992 return setError(VBOX_E_OBJECT_NOT_FOUND,
9993 tr("Could not find a storage controller named '%s'"),
9994 aName.c_str());
9995 return VBOX_E_OBJECT_NOT_FOUND;
9996}
9997
9998/**
9999 * Returns a USB controller object with the given name.
10000 *
10001 * @param aName USB controller name to find
10002 * @param aUSBController where to return the found USB controller
10003 * @param aSetError true to set extended error info on failure
10004 */
10005HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
10006 ComObjPtr<USBController> &aUSBController,
10007 bool aSetError /* = false */)
10008{
10009 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
10010
10011 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10012 it != mUSBControllers->end();
10013 ++it)
10014 {
10015 if ((*it)->i_getName() == aName)
10016 {
10017 aUSBController = (*it);
10018 return S_OK;
10019 }
10020 }
10021
10022 if (aSetError)
10023 return setError(VBOX_E_OBJECT_NOT_FOUND,
10024 tr("Could not find a storage controller named '%s'"),
10025 aName.c_str());
10026 return VBOX_E_OBJECT_NOT_FOUND;
10027}
10028
10029/**
10030 * Returns the number of USB controller instance of the given type.
10031 *
10032 * @param enmType USB controller type.
10033 */
10034ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
10035{
10036 ULONG cCtrls = 0;
10037
10038 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10039 it != mUSBControllers->end();
10040 ++it)
10041 {
10042 if ((*it)->i_getControllerType() == enmType)
10043 cCtrls++;
10044 }
10045
10046 return cCtrls;
10047}
10048
10049HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
10050 MediaData::AttachmentList &atts)
10051{
10052 AutoCaller autoCaller(this);
10053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10054
10055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10056
10057 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
10058 it != mMediaData->mAttachments.end();
10059 ++it)
10060 {
10061 const ComObjPtr<MediumAttachment> &pAtt = *it;
10062
10063 // should never happen, but deal with NULL pointers in the list.
10064 AssertStmt(!pAtt.isNull(), continue);
10065
10066 // getControllerName() needs caller+read lock
10067 AutoCaller autoAttCaller(pAtt);
10068 if (FAILED(autoAttCaller.rc()))
10069 {
10070 atts.clear();
10071 return autoAttCaller.rc();
10072 }
10073 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10074
10075 if (pAtt->i_getControllerName() == aName)
10076 atts.push_back(pAtt);
10077 }
10078
10079 return S_OK;
10080}
10081
10082/**
10083 * Helper for #saveSettings. Cares about renaming the settings directory and
10084 * file if the machine name was changed and about creating a new settings file
10085 * if this is a new machine.
10086 *
10087 * @note Must be never called directly but only from #saveSettings().
10088 */
10089HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
10090{
10091 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10092
10093 HRESULT rc = S_OK;
10094
10095 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10096
10097 /// @todo need to handle primary group change, too
10098
10099 /* attempt to rename the settings file if machine name is changed */
10100 if ( mUserData->s.fNameSync
10101 && mUserData.isBackedUp()
10102 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10103 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10104 )
10105 {
10106 bool dirRenamed = false;
10107 bool fileRenamed = false;
10108
10109 Utf8Str configFile, newConfigFile;
10110 Utf8Str configFilePrev, newConfigFilePrev;
10111 Utf8Str configDir, newConfigDir;
10112
10113 do
10114 {
10115 int vrc = VINF_SUCCESS;
10116
10117 Utf8Str name = mUserData.backedUpData()->s.strName;
10118 Utf8Str newName = mUserData->s.strName;
10119 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10120 if (group == "/")
10121 group.setNull();
10122 Utf8Str newGroup = mUserData->s.llGroups.front();
10123 if (newGroup == "/")
10124 newGroup.setNull();
10125
10126 configFile = mData->m_strConfigFileFull;
10127
10128 /* first, rename the directory if it matches the group and machine name */
10129 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
10130 group.c_str(), RTPATH_DELIMITER, name.c_str());
10131 /** @todo hack, make somehow use of ComposeMachineFilename */
10132 if (mUserData->s.fDirectoryIncludesUUID)
10133 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10134 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10135 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10136 /** @todo hack, make somehow use of ComposeMachineFilename */
10137 if (mUserData->s.fDirectoryIncludesUUID)
10138 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10139 configDir = configFile;
10140 configDir.stripFilename();
10141 newConfigDir = configDir;
10142 if ( configDir.length() >= groupPlusName.length()
10143 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10144 {
10145 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10146 Utf8Str newConfigBaseDir(newConfigDir);
10147 newConfigDir.append(newGroupPlusName);
10148 /* consistency: use \ if appropriate on the platform */
10149 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10150 /* new dir and old dir cannot be equal here because of 'if'
10151 * above and because name != newName */
10152 Assert(configDir != newConfigDir);
10153 if (!fSettingsFileIsNew)
10154 {
10155 /* perform real rename only if the machine is not new */
10156 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10157 if ( vrc == VERR_FILE_NOT_FOUND
10158 || vrc == VERR_PATH_NOT_FOUND)
10159 {
10160 /* create the parent directory, then retry renaming */
10161 Utf8Str parent(newConfigDir);
10162 parent.stripFilename();
10163 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10164 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10165 }
10166 if (RT_FAILURE(vrc))
10167 {
10168 rc = setError(E_FAIL,
10169 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10170 configDir.c_str(),
10171 newConfigDir.c_str(),
10172 vrc);
10173 break;
10174 }
10175 /* delete subdirectories which are no longer needed */
10176 Utf8Str dir(configDir);
10177 dir.stripFilename();
10178 while (dir != newConfigBaseDir && dir != ".")
10179 {
10180 vrc = RTDirRemove(dir.c_str());
10181 if (RT_FAILURE(vrc))
10182 break;
10183 dir.stripFilename();
10184 }
10185 dirRenamed = true;
10186 }
10187 }
10188
10189 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10190 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10191
10192 /* then try to rename the settings file itself */
10193 if (newConfigFile != configFile)
10194 {
10195 /* get the path to old settings file in renamed directory */
10196 configFile = Utf8StrFmt("%s%c%s",
10197 newConfigDir.c_str(),
10198 RTPATH_DELIMITER,
10199 RTPathFilename(configFile.c_str()));
10200 if (!fSettingsFileIsNew)
10201 {
10202 /* perform real rename only if the machine is not new */
10203 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10204 if (RT_FAILURE(vrc))
10205 {
10206 rc = setError(E_FAIL,
10207 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10208 configFile.c_str(),
10209 newConfigFile.c_str(),
10210 vrc);
10211 break;
10212 }
10213 fileRenamed = true;
10214 configFilePrev = configFile;
10215 configFilePrev += "-prev";
10216 newConfigFilePrev = newConfigFile;
10217 newConfigFilePrev += "-prev";
10218 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10219 }
10220 }
10221
10222 // update m_strConfigFileFull amd mConfigFile
10223 mData->m_strConfigFileFull = newConfigFile;
10224 // compute the relative path too
10225 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10226
10227 // store the old and new so that VirtualBox::saveSettings() can update
10228 // the media registry
10229 if ( mData->mRegistered
10230 && (configDir != newConfigDir || configFile != newConfigFile))
10231 {
10232 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10233
10234 if (pfNeedsGlobalSaveSettings)
10235 *pfNeedsGlobalSaveSettings = true;
10236 }
10237
10238 // in the saved state file path, replace the old directory with the new directory
10239 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10240 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10241
10242 // and do the same thing for the saved state file paths of all the online snapshots
10243 if (mData->mFirstSnapshot)
10244 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10245 newConfigDir.c_str());
10246 }
10247 while (0);
10248
10249 if (FAILED(rc))
10250 {
10251 /* silently try to rename everything back */
10252 if (fileRenamed)
10253 {
10254 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10255 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10256 }
10257 if (dirRenamed)
10258 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10259 }
10260
10261 if (FAILED(rc)) return rc;
10262 }
10263
10264 if (fSettingsFileIsNew)
10265 {
10266 /* create a virgin config file */
10267 int vrc = VINF_SUCCESS;
10268
10269 /* ensure the settings directory exists */
10270 Utf8Str path(mData->m_strConfigFileFull);
10271 path.stripFilename();
10272 if (!RTDirExists(path.c_str()))
10273 {
10274 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10275 if (RT_FAILURE(vrc))
10276 {
10277 return setError(E_FAIL,
10278 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10279 path.c_str(),
10280 vrc);
10281 }
10282 }
10283
10284 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10285 path = Utf8Str(mData->m_strConfigFileFull);
10286 RTFILE f = NIL_RTFILE;
10287 vrc = RTFileOpen(&f, path.c_str(),
10288 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10289 if (RT_FAILURE(vrc))
10290 return setError(E_FAIL,
10291 tr("Could not create the settings file '%s' (%Rrc)"),
10292 path.c_str(),
10293 vrc);
10294 RTFileClose(f);
10295 }
10296
10297 return rc;
10298}
10299
10300/**
10301 * Saves and commits machine data, user data and hardware data.
10302 *
10303 * Note that on failure, the data remains uncommitted.
10304 *
10305 * @a aFlags may combine the following flags:
10306 *
10307 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10308 * Used when saving settings after an operation that makes them 100%
10309 * correspond to the settings from the current snapshot.
10310 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10311 * #isReallyModified() returns false. This is necessary for cases when we
10312 * change machine data directly, not through the backup()/commit() mechanism.
10313 * - SaveS_Force: settings will be saved without doing a deep compare of the
10314 * settings structures. This is used when this is called because snapshots
10315 * have changed to avoid the overhead of the deep compare.
10316 *
10317 * @note Must be called from under this object's write lock. Locks children for
10318 * writing.
10319 *
10320 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10321 * initialized to false and that will be set to true by this function if
10322 * the caller must invoke VirtualBox::saveSettings() because the global
10323 * settings have changed. This will happen if a machine rename has been
10324 * saved and the global machine and media registries will therefore need
10325 * updating.
10326 */
10327HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10328 int aFlags /*= 0*/)
10329{
10330 LogFlowThisFuncEnter();
10331
10332 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10333
10334 /* make sure child objects are unable to modify the settings while we are
10335 * saving them */
10336 ensureNoStateDependencies();
10337
10338 AssertReturn(!isSnapshotMachine(),
10339 E_FAIL);
10340
10341 HRESULT rc = S_OK;
10342 bool fNeedsWrite = false;
10343
10344 /* First, prepare to save settings. It will care about renaming the
10345 * settings directory and file if the machine name was changed and about
10346 * creating a new settings file if this is a new machine. */
10347 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10348 if (FAILED(rc)) return rc;
10349
10350 // keep a pointer to the current settings structures
10351 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10352 settings::MachineConfigFile *pNewConfig = NULL;
10353
10354 try
10355 {
10356 // make a fresh one to have everyone write stuff into
10357 pNewConfig = new settings::MachineConfigFile(NULL);
10358 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10359
10360 // now go and copy all the settings data from COM to the settings structures
10361 // (this calles saveSettings() on all the COM objects in the machine)
10362 copyMachineDataToSettings(*pNewConfig);
10363
10364 if (aFlags & SaveS_ResetCurStateModified)
10365 {
10366 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10367 mData->mCurrentStateModified = FALSE;
10368 fNeedsWrite = true; // always, no need to compare
10369 }
10370 else if (aFlags & SaveS_Force)
10371 {
10372 fNeedsWrite = true; // always, no need to compare
10373 }
10374 else
10375 {
10376 if (!mData->mCurrentStateModified)
10377 {
10378 // do a deep compare of the settings that we just saved with the settings
10379 // previously stored in the config file; this invokes MachineConfigFile::operator==
10380 // which does a deep compare of all the settings, which is expensive but less expensive
10381 // than writing out XML in vain
10382 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10383
10384 // could still be modified if any settings changed
10385 mData->mCurrentStateModified = fAnySettingsChanged;
10386
10387 fNeedsWrite = fAnySettingsChanged;
10388 }
10389 else
10390 fNeedsWrite = true;
10391 }
10392
10393 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10394
10395 if (fNeedsWrite)
10396 // now spit it all out!
10397 pNewConfig->write(mData->m_strConfigFileFull);
10398
10399 mData->pMachineConfigFile = pNewConfig;
10400 delete pOldConfig;
10401 commit();
10402
10403 // after saving settings, we are no longer different from the XML on disk
10404 mData->flModifications = 0;
10405 }
10406 catch (HRESULT err)
10407 {
10408 // we assume that error info is set by the thrower
10409 rc = err;
10410
10411 // restore old config
10412 delete pNewConfig;
10413 mData->pMachineConfigFile = pOldConfig;
10414 }
10415 catch (...)
10416 {
10417 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10418 }
10419
10420 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10421 {
10422 /* Fire the data change event, even on failure (since we've already
10423 * committed all data). This is done only for SessionMachines because
10424 * mutable Machine instances are always not registered (i.e. private
10425 * to the client process that creates them) and thus don't need to
10426 * inform callbacks. */
10427 if (isSessionMachine())
10428 mParent->i_onMachineDataChange(mData->mUuid);
10429 }
10430
10431 LogFlowThisFunc(("rc=%08X\n", rc));
10432 LogFlowThisFuncLeave();
10433 return rc;
10434}
10435
10436/**
10437 * Implementation for saving the machine settings into the given
10438 * settings::MachineConfigFile instance. This copies machine extradata
10439 * from the previous machine config file in the instance data, if any.
10440 *
10441 * This gets called from two locations:
10442 *
10443 * -- Machine::saveSettings(), during the regular XML writing;
10444 *
10445 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10446 * exported to OVF and we write the VirtualBox proprietary XML
10447 * into a <vbox:Machine> tag.
10448 *
10449 * This routine fills all the fields in there, including snapshots, *except*
10450 * for the following:
10451 *
10452 * -- fCurrentStateModified. There is some special logic associated with that.
10453 *
10454 * The caller can then call MachineConfigFile::write() or do something else
10455 * with it.
10456 *
10457 * Caller must hold the machine lock!
10458 *
10459 * This throws XML errors and HRESULT, so the caller must have a catch block!
10460 */
10461void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10462{
10463 // deep copy extradata
10464 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10465
10466 config.uuid = mData->mUuid;
10467
10468 // copy name, description, OS type, teleport, UTC etc.
10469 config.machineUserData = mUserData->s;
10470
10471 // Encode the Icon Override data from Machine and store on config userdata.
10472 com::SafeArray<BYTE> iconByte;
10473 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10474 ssize_t cbData = iconByte.size();
10475 if (cbData > 0)
10476 {
10477 ssize_t cchOut = RTBase64EncodedLength(cbData);
10478 Utf8Str strIconData;
10479 strIconData.reserve(cchOut+1);
10480 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10481 strIconData.mutableRaw(), strIconData.capacity(),
10482 NULL);
10483 if (RT_FAILURE(vrc))
10484 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10485 strIconData.jolt();
10486 config.machineUserData.ovIcon = strIconData;
10487 }
10488 else
10489 config.machineUserData.ovIcon.setNull();
10490
10491 if ( mData->mMachineState == MachineState_Saved
10492 || mData->mMachineState == MachineState_Restoring
10493 // when deleting a snapshot we may or may not have a saved state in the current state,
10494 // so let's not assert here please
10495 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10496 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10497 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10498 && (!mSSData->strStateFilePath.isEmpty())
10499 )
10500 )
10501 {
10502 Assert(!mSSData->strStateFilePath.isEmpty());
10503 /* try to make the file name relative to the settings file dir */
10504 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10505 }
10506 else
10507 {
10508 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10509 config.strStateFile.setNull();
10510 }
10511
10512 if (mData->mCurrentSnapshot)
10513 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10514 else
10515 config.uuidCurrentSnapshot.clear();
10516
10517 config.timeLastStateChange = mData->mLastStateChange;
10518 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10519 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10520
10521 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10522 if (FAILED(rc)) throw rc;
10523
10524 rc = saveStorageControllers(config.storageMachine);
10525 if (FAILED(rc)) throw rc;
10526
10527 // save machine's media registry if this is VirtualBox 4.0 or later
10528 if (config.canHaveOwnMediaRegistry())
10529 {
10530 // determine machine folder
10531 Utf8Str strMachineFolder = getSettingsFileFull();
10532 strMachineFolder.stripFilename();
10533 mParent->i_saveMediaRegistry(config.mediaRegistry,
10534 getId(), // only media with registry ID == machine UUID
10535 strMachineFolder);
10536 // this throws HRESULT
10537 }
10538
10539 // save snapshots
10540 rc = saveAllSnapshots(config);
10541 if (FAILED(rc)) throw rc;
10542}
10543
10544/**
10545 * Saves all snapshots of the machine into the given machine config file. Called
10546 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10547 * @param config
10548 * @return
10549 */
10550HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10551{
10552 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10553
10554 HRESULT rc = S_OK;
10555
10556 try
10557 {
10558 config.llFirstSnapshot.clear();
10559
10560 if (mData->mFirstSnapshot)
10561 {
10562 settings::Snapshot snapNew;
10563 config.llFirstSnapshot.push_back(snapNew);
10564
10565 // get reference to the fresh copy of the snapshot on the list and
10566 // work on that copy directly to avoid excessive copying later
10567 settings::Snapshot &snap = config.llFirstSnapshot.front();
10568
10569 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
10570 if (FAILED(rc)) throw rc;
10571 }
10572
10573// if (mType == IsSessionMachine)
10574// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10575
10576 }
10577 catch (HRESULT err)
10578 {
10579 /* we assume that error info is set by the thrower */
10580 rc = err;
10581 }
10582 catch (...)
10583 {
10584 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10585 }
10586
10587 return rc;
10588}
10589
10590/**
10591 * Saves the VM hardware configuration. It is assumed that the
10592 * given node is empty.
10593 *
10594 * @param data Reference to the settings object for the hardware config.
10595 * @param pDbg Pointer to the settings object for the debugging config
10596 * which happens to live in mHWData.
10597 * @param pAutostart Pointer to the settings object for the autostart config
10598 * which happens to live in mHWData.
10599 */
10600HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10601 settings::Autostart *pAutostart)
10602{
10603 HRESULT rc = S_OK;
10604
10605 try
10606 {
10607 /* The hardware version attribute (optional).
10608 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10609 if ( mHWData->mHWVersion == "1"
10610 && mSSData->strStateFilePath.isEmpty()
10611 )
10612 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
10613
10614 data.strVersion = mHWData->mHWVersion;
10615 data.uuid = mHWData->mHardwareUUID;
10616
10617 // CPU
10618 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10619 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10620 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10621 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10622 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10623 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10624 data.fPAE = !!mHWData->mPAEEnabled;
10625 data.enmLongMode = mHWData->mLongMode;
10626 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10627 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10628
10629 /* Standard and Extended CPUID leafs. */
10630 data.llCpuIdLeafs.clear();
10631 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10632 {
10633 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10634 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10635 }
10636 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10637 {
10638 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10639 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10640 }
10641
10642 data.cCPUs = mHWData->mCPUCount;
10643 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10644 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10645
10646 data.llCpus.clear();
10647 if (data.fCpuHotPlug)
10648 {
10649 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10650 {
10651 if (mHWData->mCPUAttached[idx])
10652 {
10653 settings::Cpu cpu;
10654 cpu.ulId = idx;
10655 data.llCpus.push_back(cpu);
10656 }
10657 }
10658 }
10659
10660 // memory
10661 data.ulMemorySizeMB = mHWData->mMemorySize;
10662 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10663
10664 // firmware
10665 data.firmwareType = mHWData->mFirmwareType;
10666
10667 // HID
10668 data.pointingHIDType = mHWData->mPointingHIDType;
10669 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10670
10671 // chipset
10672 data.chipsetType = mHWData->mChipsetType;
10673
10674 // paravirt
10675 data.paravirtProvider = mHWData->mParavirtProvider;
10676
10677 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10678
10679 // HPET
10680 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10681
10682 // boot order
10683 data.mapBootOrder.clear();
10684 for (size_t i = 0;
10685 i < RT_ELEMENTS(mHWData->mBootOrder);
10686 ++i)
10687 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10688
10689 // display
10690 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10691 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10692 data.cMonitors = mHWData->mMonitorCount;
10693 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10694 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10695 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10696 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10697 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10698 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10699 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10700 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10701 {
10702 if (mHWData->maVideoCaptureScreens[i])
10703 ASMBitSet(&data.u64VideoCaptureScreens, i);
10704 else
10705 ASMBitClear(&data.u64VideoCaptureScreens, i);
10706 }
10707 /* store relative video capture file if possible */
10708 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10709
10710 /* VRDEServer settings (optional) */
10711 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10712 if (FAILED(rc)) throw rc;
10713
10714 /* BIOS (required) */
10715 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10716 if (FAILED(rc)) throw rc;
10717
10718 /* USB Controller (required) */
10719 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10720 it != mUSBControllers->end();
10721 ++it)
10722 {
10723 ComObjPtr<USBController> ctrl = *it;
10724 settings::USBController settingsCtrl;
10725
10726 settingsCtrl.strName = ctrl->i_getName();
10727 settingsCtrl.enmType = ctrl->i_getControllerType();
10728
10729 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10730 }
10731
10732 /* USB device filters (required) */
10733 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10734 if (FAILED(rc)) throw rc;
10735
10736 /* Network adapters (required) */
10737 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10738 data.llNetworkAdapters.clear();
10739 /* Write out only the nominal number of network adapters for this
10740 * chipset type. Since Machine::commit() hasn't been called there
10741 * may be extra NIC settings in the vector. */
10742 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10743 {
10744 settings::NetworkAdapter nic;
10745 nic.ulSlot = slot;
10746 /* paranoia check... must not be NULL, but must not crash either. */
10747 if (mNetworkAdapters[slot])
10748 {
10749 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10750 if (FAILED(rc)) throw rc;
10751
10752 data.llNetworkAdapters.push_back(nic);
10753 }
10754 }
10755
10756 /* Serial ports */
10757 data.llSerialPorts.clear();
10758 for (ULONG slot = 0;
10759 slot < RT_ELEMENTS(mSerialPorts);
10760 ++slot)
10761 {
10762 settings::SerialPort s;
10763 s.ulSlot = slot;
10764 rc = mSerialPorts[slot]->i_saveSettings(s);
10765 if (FAILED(rc)) return rc;
10766
10767 data.llSerialPorts.push_back(s);
10768 }
10769
10770 /* Parallel ports */
10771 data.llParallelPorts.clear();
10772 for (ULONG slot = 0;
10773 slot < RT_ELEMENTS(mParallelPorts);
10774 ++slot)
10775 {
10776 settings::ParallelPort p;
10777 p.ulSlot = slot;
10778 rc = mParallelPorts[slot]->i_saveSettings(p);
10779 if (FAILED(rc)) return rc;
10780
10781 data.llParallelPorts.push_back(p);
10782 }
10783
10784 /* Audio adapter */
10785 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10786 if (FAILED(rc)) return rc;
10787
10788 /* Shared folders */
10789 data.llSharedFolders.clear();
10790 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10791 it != mHWData->mSharedFolders.end();
10792 ++it)
10793 {
10794 SharedFolder *pSF = *it;
10795 AutoCaller sfCaller(pSF);
10796 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10797 settings::SharedFolder sf;
10798 sf.strName = pSF->i_getName();
10799 sf.strHostPath = pSF->i_getHostPath();
10800 sf.fWritable = !!pSF->i_isWritable();
10801 sf.fAutoMount = !!pSF->i_isAutoMounted();
10802
10803 data.llSharedFolders.push_back(sf);
10804 }
10805
10806 // clipboard
10807 data.clipboardMode = mHWData->mClipboardMode;
10808
10809 // drag'n'drop
10810 data.dragAndDropMode = mHWData->mDragAndDropMode;
10811
10812 /* Guest */
10813 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10814
10815 // IO settings
10816 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10817 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10818
10819 /* BandwidthControl (required) */
10820 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10821 if (FAILED(rc)) throw rc;
10822
10823 /* Host PCI devices */
10824 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10825 it != mHWData->mPCIDeviceAssignments.end();
10826 ++it)
10827 {
10828 ComObjPtr<PCIDeviceAttachment> pda = *it;
10829 settings::HostPCIDeviceAttachment hpda;
10830
10831 rc = pda->i_saveSettings(hpda);
10832 if (FAILED(rc)) throw rc;
10833
10834 data.pciAttachments.push_back(hpda);
10835 }
10836
10837
10838 // guest properties
10839 data.llGuestProperties.clear();
10840#ifdef VBOX_WITH_GUEST_PROPS
10841 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10842 it != mHWData->mGuestProperties.end();
10843 ++it)
10844 {
10845 HWData::GuestProperty property = it->second;
10846
10847 /* Remove transient guest properties at shutdown unless we
10848 * are saving state */
10849 if ( ( mData->mMachineState == MachineState_PoweredOff
10850 || mData->mMachineState == MachineState_Aborted
10851 || mData->mMachineState == MachineState_Teleported)
10852 && ( property.mFlags & guestProp::TRANSIENT
10853 || property.mFlags & guestProp::TRANSRESET))
10854 continue;
10855 settings::GuestProperty prop;
10856 prop.strName = it->first;
10857 prop.strValue = property.strValue;
10858 prop.timestamp = property.mTimestamp;
10859 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10860 guestProp::writeFlags(property.mFlags, szFlags);
10861 prop.strFlags = szFlags;
10862
10863 data.llGuestProperties.push_back(prop);
10864 }
10865
10866 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10867 /* I presume this doesn't require a backup(). */
10868 mData->mGuestPropertiesModified = FALSE;
10869#endif /* VBOX_WITH_GUEST_PROPS defined */
10870
10871 *pDbg = mHWData->mDebugging;
10872 *pAutostart = mHWData->mAutostart;
10873
10874 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10875 }
10876 catch(std::bad_alloc &)
10877 {
10878 return E_OUTOFMEMORY;
10879 }
10880
10881 AssertComRC(rc);
10882 return rc;
10883}
10884
10885/**
10886 * Saves the storage controller configuration.
10887 *
10888 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10889 */
10890HRESULT Machine::saveStorageControllers(settings::Storage &data)
10891{
10892 data.llStorageControllers.clear();
10893
10894 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10895 it != mStorageControllers->end();
10896 ++it)
10897 {
10898 HRESULT rc;
10899 ComObjPtr<StorageController> pCtl = *it;
10900
10901 settings::StorageController ctl;
10902 ctl.strName = pCtl->i_getName();
10903 ctl.controllerType = pCtl->i_getControllerType();
10904 ctl.storageBus = pCtl->i_getStorageBus();
10905 ctl.ulInstance = pCtl->i_getInstance();
10906 ctl.fBootable = pCtl->i_getBootable();
10907
10908 /* Save the port count. */
10909 ULONG portCount;
10910 rc = pCtl->COMGETTER(PortCount)(&portCount);
10911 ComAssertComRCRet(rc, rc);
10912 ctl.ulPortCount = portCount;
10913
10914 /* Save fUseHostIOCache */
10915 BOOL fUseHostIOCache;
10916 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10917 ComAssertComRCRet(rc, rc);
10918 ctl.fUseHostIOCache = !!fUseHostIOCache;
10919
10920 /* Save IDE emulation settings. */
10921 if (ctl.controllerType == StorageControllerType_IntelAhci)
10922 {
10923 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10924 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10925 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10926 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10927 )
10928 ComAssertComRCRet(rc, rc);
10929 }
10930
10931 /* save the devices now. */
10932 rc = saveStorageDevices(pCtl, ctl);
10933 ComAssertComRCRet(rc, rc);
10934
10935 data.llStorageControllers.push_back(ctl);
10936 }
10937
10938 return S_OK;
10939}
10940
10941/**
10942 * Saves the hard disk configuration.
10943 */
10944HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10945 settings::StorageController &data)
10946{
10947 MediaData::AttachmentList atts;
10948
10949 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->i_getName()).raw(), atts);
10950 if (FAILED(rc)) return rc;
10951
10952 data.llAttachedDevices.clear();
10953 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10954 it != atts.end();
10955 ++it)
10956 {
10957 settings::AttachedDevice dev;
10958
10959 MediumAttachment *pAttach = *it;
10960 Medium *pMedium = pAttach->i_getMedium();
10961
10962 dev.deviceType = pAttach->i_getType();
10963 dev.lPort = pAttach->i_getPort();
10964 dev.lDevice = pAttach->i_getDevice();
10965 dev.fPassThrough = pAttach->i_getPassthrough();
10966 dev.fHotPluggable = pAttach->i_getHotPluggable();
10967 if (pMedium)
10968 {
10969 if (pMedium->i_isHostDrive())
10970 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10971 else
10972 dev.uuid = pMedium->i_getId();
10973 dev.fTempEject = pAttach->i_getTempEject();
10974 dev.fNonRotational = pAttach->i_getNonRotational();
10975 dev.fDiscard = pAttach->i_getDiscard();
10976 }
10977
10978 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10979
10980 data.llAttachedDevices.push_back(dev);
10981 }
10982
10983 return S_OK;
10984}
10985
10986/**
10987 * Saves machine state settings as defined by aFlags
10988 * (SaveSTS_* values).
10989 *
10990 * @param aFlags Combination of SaveSTS_* flags.
10991 *
10992 * @note Locks objects for writing.
10993 */
10994HRESULT Machine::saveStateSettings(int aFlags)
10995{
10996 if (aFlags == 0)
10997 return S_OK;
10998
10999 AutoCaller autoCaller(this);
11000 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11001
11002 /* This object's write lock is also necessary to serialize file access
11003 * (prevent concurrent reads and writes) */
11004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11005
11006 HRESULT rc = S_OK;
11007
11008 Assert(mData->pMachineConfigFile);
11009
11010 try
11011 {
11012 if (aFlags & SaveSTS_CurStateModified)
11013 mData->pMachineConfigFile->fCurrentStateModified = true;
11014
11015 if (aFlags & SaveSTS_StateFilePath)
11016 {
11017 if (!mSSData->strStateFilePath.isEmpty())
11018 /* try to make the file name relative to the settings file dir */
11019 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11020 else
11021 mData->pMachineConfigFile->strStateFile.setNull();
11022 }
11023
11024 if (aFlags & SaveSTS_StateTimeStamp)
11025 {
11026 Assert( mData->mMachineState != MachineState_Aborted
11027 || mSSData->strStateFilePath.isEmpty());
11028
11029 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11030
11031 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
11032//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11033 }
11034
11035 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11036 }
11037 catch (...)
11038 {
11039 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11040 }
11041
11042 return rc;
11043}
11044
11045/**
11046 * Ensures that the given medium is added to a media registry. If this machine
11047 * was created with 4.0 or later, then the machine registry is used. Otherwise
11048 * the global VirtualBox media registry is used.
11049 *
11050 * Caller must NOT hold machine lock, media tree or any medium locks!
11051 *
11052 * @param pMedium
11053 */
11054void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11055{
11056 /* Paranoia checks: do not hold machine or media tree locks. */
11057 AssertReturnVoid(!isWriteLockOnCurrentThread());
11058 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11059
11060 ComObjPtr<Medium> pBase;
11061 {
11062 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11063 pBase = pMedium->i_getBase();
11064 }
11065
11066 /* Paranoia checks: do not hold medium locks. */
11067 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11068 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11069
11070 // decide which medium registry to use now that the medium is attached:
11071 Guid uuid;
11072 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
11073 // machine XML is VirtualBox 4.0 or higher:
11074 uuid = getId(); // machine UUID
11075 else
11076 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11077
11078 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
11079 mParent->i_markRegistryModified(uuid);
11080
11081 /* For more complex hard disk structures it can happen that the base
11082 * medium isn't yet associated with any medium registry. Do that now. */
11083 if (pMedium != pBase)
11084 {
11085 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
11086 mParent->i_markRegistryModified(uuid);
11087 }
11088}
11089
11090/**
11091 * Creates differencing hard disks for all normal hard disks attached to this
11092 * machine and a new set of attachments to refer to created disks.
11093 *
11094 * Used when taking a snapshot or when deleting the current state. Gets called
11095 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11096 *
11097 * This method assumes that mMediaData contains the original hard disk attachments
11098 * it needs to create diffs for. On success, these attachments will be replaced
11099 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
11100 * called to delete created diffs which will also rollback mMediaData and restore
11101 * whatever was backed up before calling this method.
11102 *
11103 * Attachments with non-normal hard disks are left as is.
11104 *
11105 * If @a aOnline is @c false then the original hard disks that require implicit
11106 * diffs will be locked for reading. Otherwise it is assumed that they are
11107 * already locked for writing (when the VM was started). Note that in the latter
11108 * case it is responsibility of the caller to lock the newly created diffs for
11109 * writing if this method succeeds.
11110 *
11111 * @param aProgress Progress object to run (must contain at least as
11112 * many operations left as the number of hard disks
11113 * attached).
11114 * @param aOnline Whether the VM was online prior to this operation.
11115 *
11116 * @note The progress object is not marked as completed, neither on success nor
11117 * on failure. This is a responsibility of the caller.
11118 *
11119 * @note Locks this object and the media tree for writing.
11120 */
11121HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
11122 ULONG aWeight,
11123 bool aOnline)
11124{
11125 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11126
11127 AutoCaller autoCaller(this);
11128 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11129
11130 AutoMultiWriteLock2 alock(this->lockHandle(),
11131 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11132
11133 /* must be in a protective state because we release the lock below */
11134 AssertReturn( mData->mMachineState == MachineState_Saving
11135 || mData->mMachineState == MachineState_LiveSnapshotting
11136 || mData->mMachineState == MachineState_RestoringSnapshot
11137 || mData->mMachineState == MachineState_DeletingSnapshot
11138 , E_FAIL);
11139
11140 HRESULT rc = S_OK;
11141
11142 // use appropriate locked media map (online or offline)
11143 MediumLockListMap lockedMediaOffline;
11144 MediumLockListMap *lockedMediaMap;
11145 if (aOnline)
11146 lockedMediaMap = &mData->mSession.mLockedMedia;
11147 else
11148 lockedMediaMap = &lockedMediaOffline;
11149
11150 try
11151 {
11152 if (!aOnline)
11153 {
11154 /* lock all attached hard disks early to detect "in use"
11155 * situations before creating actual diffs */
11156 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11157 it != mMediaData->mAttachments.end();
11158 ++it)
11159 {
11160 MediumAttachment* pAtt = *it;
11161 if (pAtt->i_getType() == DeviceType_HardDisk)
11162 {
11163 Medium* pMedium = pAtt->i_getMedium();
11164 Assert(pMedium);
11165
11166 MediumLockList *pMediumLockList(new MediumLockList());
11167 alock.release();
11168 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11169 false /* fMediumLockWrite */,
11170 NULL,
11171 *pMediumLockList);
11172 alock.acquire();
11173 if (FAILED(rc))
11174 {
11175 delete pMediumLockList;
11176 throw rc;
11177 }
11178 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11179 if (FAILED(rc))
11180 {
11181 throw setError(rc,
11182 tr("Collecting locking information for all attached media failed"));
11183 }
11184 }
11185 }
11186
11187 /* Now lock all media. If this fails, nothing is locked. */
11188 alock.release();
11189 rc = lockedMediaMap->Lock();
11190 alock.acquire();
11191 if (FAILED(rc))
11192 {
11193 throw setError(rc,
11194 tr("Locking of attached media failed"));
11195 }
11196 }
11197
11198 /* remember the current list (note that we don't use backup() since
11199 * mMediaData may be already backed up) */
11200 MediaData::AttachmentList atts = mMediaData->mAttachments;
11201
11202 /* start from scratch */
11203 mMediaData->mAttachments.clear();
11204
11205 /* go through remembered attachments and create diffs for normal hard
11206 * disks and attach them */
11207 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11208 it != atts.end();
11209 ++it)
11210 {
11211 MediumAttachment* pAtt = *it;
11212
11213 DeviceType_T devType = pAtt->i_getType();
11214 Medium* pMedium = pAtt->i_getMedium();
11215
11216 if ( devType != DeviceType_HardDisk
11217 || pMedium == NULL
11218 || pMedium->i_getType() != MediumType_Normal)
11219 {
11220 /* copy the attachment as is */
11221
11222 /** @todo the progress object created in Console::TakeSnaphot
11223 * only expects operations for hard disks. Later other
11224 * device types need to show up in the progress as well. */
11225 if (devType == DeviceType_HardDisk)
11226 {
11227 if (pMedium == NULL)
11228 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11229 aWeight); // weight
11230 else
11231 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11232 pMedium->i_getBase()->i_getName().c_str()).raw(),
11233 aWeight); // weight
11234 }
11235
11236 mMediaData->mAttachments.push_back(pAtt);
11237 continue;
11238 }
11239
11240 /* need a diff */
11241 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11242 pMedium->i_getBase()->i_getName().c_str()).raw(),
11243 aWeight); // weight
11244
11245 Utf8Str strFullSnapshotFolder;
11246 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11247
11248 ComObjPtr<Medium> diff;
11249 diff.createObject();
11250 // store the diff in the same registry as the parent
11251 // (this cannot fail here because we can't create implicit diffs for
11252 // unregistered images)
11253 Guid uuidRegistryParent;
11254 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11255 Assert(fInRegistry); NOREF(fInRegistry);
11256 rc = diff->init(mParent,
11257 pMedium->i_getPreferredDiffFormat(),
11258 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11259 uuidRegistryParent);
11260 if (FAILED(rc)) throw rc;
11261
11262 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11263 * the push_back? Looks like we're going to release medium with the
11264 * wrong kind of lock (general issue with if we fail anywhere at all)
11265 * and an orphaned VDI in the snapshots folder. */
11266
11267 /* update the appropriate lock list */
11268 MediumLockList *pMediumLockList;
11269 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11270 AssertComRCThrowRC(rc);
11271 if (aOnline)
11272 {
11273 alock.release();
11274 /* The currently attached medium will be read-only, change
11275 * the lock type to read. */
11276 rc = pMediumLockList->Update(pMedium, false);
11277 alock.acquire();
11278 AssertComRCThrowRC(rc);
11279 }
11280
11281 /* release the locks before the potentially lengthy operation */
11282 alock.release();
11283 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
11284 pMediumLockList,
11285 NULL /* aProgress */,
11286 true /* aWait */);
11287 alock.acquire();
11288 if (FAILED(rc)) throw rc;
11289
11290 /* actual lock list update is done in Medium::commitMedia */
11291
11292 rc = diff->i_addBackReference(mData->mUuid);
11293 AssertComRCThrowRC(rc);
11294
11295 /* add a new attachment */
11296 ComObjPtr<MediumAttachment> attachment;
11297 attachment.createObject();
11298 rc = attachment->init(this,
11299 diff,
11300 pAtt->i_getControllerName(),
11301 pAtt->i_getPort(),
11302 pAtt->i_getDevice(),
11303 DeviceType_HardDisk,
11304 true /* aImplicit */,
11305 false /* aPassthrough */,
11306 false /* aTempEject */,
11307 pAtt->i_getNonRotational(),
11308 pAtt->i_getDiscard(),
11309 pAtt->i_getHotPluggable(),
11310 pAtt->i_getBandwidthGroup());
11311 if (FAILED(rc)) throw rc;
11312
11313 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11314 AssertComRCThrowRC(rc);
11315 mMediaData->mAttachments.push_back(attachment);
11316 }
11317 }
11318 catch (HRESULT aRC) { rc = aRC; }
11319
11320 /* unlock all hard disks we locked when there is no VM */
11321 if (!aOnline)
11322 {
11323 ErrorInfoKeeper eik;
11324
11325 HRESULT rc1 = lockedMediaMap->Clear();
11326 AssertComRC(rc1);
11327 }
11328
11329 return rc;
11330}
11331
11332/**
11333 * Deletes implicit differencing hard disks created either by
11334 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11335 *
11336 * Note that to delete hard disks created by #AttachDevice() this method is
11337 * called from #fixupMedia() when the changes are rolled back.
11338 *
11339 * @note Locks this object and the media tree for writing.
11340 */
11341HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11342{
11343 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11344
11345 AutoCaller autoCaller(this);
11346 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11347
11348 AutoMultiWriteLock2 alock(this->lockHandle(),
11349 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11350
11351 /* We absolutely must have backed up state. */
11352 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11353
11354 /* Check if there are any implicitly created diff images. */
11355 bool fImplicitDiffs = false;
11356 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11357 it != mMediaData->mAttachments.end();
11358 ++it)
11359 {
11360 const ComObjPtr<MediumAttachment> &pAtt = *it;
11361 if (pAtt->i_isImplicit())
11362 {
11363 fImplicitDiffs = true;
11364 break;
11365 }
11366 }
11367 /* If there is nothing to do, leave early. This saves lots of image locking
11368 * effort. It also avoids a MachineStateChanged event without real reason.
11369 * This is important e.g. when loading a VM config, because there should be
11370 * no events. Otherwise API clients can become thoroughly confused for
11371 * inaccessible VMs (the code for loading VM configs uses this method for
11372 * cleanup if the config makes no sense), as they take such events as an
11373 * indication that the VM is alive, and they would force the VM config to
11374 * be reread, leading to an endless loop. */
11375 if (!fImplicitDiffs)
11376 return S_OK;
11377
11378 HRESULT rc = S_OK;
11379 MachineState_T oldState = mData->mMachineState;
11380
11381 /* will release the lock before the potentially lengthy operation,
11382 * so protect with the special state (unless already protected) */
11383 if ( oldState != MachineState_Saving
11384 && oldState != MachineState_LiveSnapshotting
11385 && oldState != MachineState_RestoringSnapshot
11386 && oldState != MachineState_DeletingSnapshot
11387 && oldState != MachineState_DeletingSnapshotOnline
11388 && oldState != MachineState_DeletingSnapshotPaused
11389 )
11390 setMachineState(MachineState_SettingUp);
11391
11392 // use appropriate locked media map (online or offline)
11393 MediumLockListMap lockedMediaOffline;
11394 MediumLockListMap *lockedMediaMap;
11395 if (aOnline)
11396 lockedMediaMap = &mData->mSession.mLockedMedia;
11397 else
11398 lockedMediaMap = &lockedMediaOffline;
11399
11400 try
11401 {
11402 if (!aOnline)
11403 {
11404 /* lock all attached hard disks early to detect "in use"
11405 * situations before deleting actual diffs */
11406 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11407 it != mMediaData->mAttachments.end();
11408 ++it)
11409 {
11410 MediumAttachment* pAtt = *it;
11411 if (pAtt->i_getType() == DeviceType_HardDisk)
11412 {
11413 Medium* pMedium = pAtt->i_getMedium();
11414 Assert(pMedium);
11415
11416 MediumLockList *pMediumLockList(new MediumLockList());
11417 alock.release();
11418 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11419 false /* fMediumLockWrite */,
11420 NULL,
11421 *pMediumLockList);
11422 alock.acquire();
11423
11424 if (FAILED(rc))
11425 {
11426 delete pMediumLockList;
11427 throw rc;
11428 }
11429
11430 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11431 if (FAILED(rc))
11432 throw rc;
11433 }
11434 }
11435
11436 if (FAILED(rc))
11437 throw rc;
11438 } // end of offline
11439
11440 /* Lock lists are now up to date and include implicitly created media */
11441
11442 /* Go through remembered attachments and delete all implicitly created
11443 * diffs and fix up the attachment information */
11444 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11445 MediaData::AttachmentList implicitAtts;
11446 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11447 it != mMediaData->mAttachments.end();
11448 ++it)
11449 {
11450 ComObjPtr<MediumAttachment> pAtt = *it;
11451 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11452 if (pMedium.isNull())
11453 continue;
11454
11455 // Implicit attachments go on the list for deletion and back references are removed.
11456 if (pAtt->i_isImplicit())
11457 {
11458 /* Deassociate and mark for deletion */
11459 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11460 rc = pMedium->i_removeBackReference(mData->mUuid);
11461 if (FAILED(rc))
11462 throw rc;
11463 implicitAtts.push_back(pAtt);
11464 continue;
11465 }
11466
11467 /* Was this medium attached before? */
11468 if (!findAttachment(oldAtts, pMedium))
11469 {
11470 /* no: de-associate */
11471 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11472 rc = pMedium->i_removeBackReference(mData->mUuid);
11473 if (FAILED(rc))
11474 throw rc;
11475 continue;
11476 }
11477 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11478 }
11479
11480 /* If there are implicit attachments to delete, throw away the lock
11481 * map contents (which will unlock all media) since the medium
11482 * attachments will be rolled back. Below we need to completely
11483 * recreate the lock map anyway since it is infinitely complex to
11484 * do this incrementally (would need reconstructing each attachment
11485 * change, which would be extremely hairy). */
11486 if (implicitAtts.size() != 0)
11487 {
11488 ErrorInfoKeeper eik;
11489
11490 HRESULT rc1 = lockedMediaMap->Clear();
11491 AssertComRC(rc1);
11492 }
11493
11494 /* rollback hard disk changes */
11495 mMediaData.rollback();
11496
11497 MultiResult mrc(S_OK);
11498
11499 // Delete unused implicit diffs.
11500 if (implicitAtts.size() != 0)
11501 {
11502 alock.release();
11503
11504 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11505 it != implicitAtts.end();
11506 ++it)
11507 {
11508 // Remove medium associated with this attachment.
11509 ComObjPtr<MediumAttachment> pAtt = *it;
11510 Assert(pAtt);
11511 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11512 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11513 Assert(pMedium);
11514
11515 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11516 // continue on delete failure, just collect error messages
11517 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(), pMedium->i_getLocationFull().c_str() ));
11518 mrc = rc;
11519 }
11520
11521 alock.acquire();
11522
11523 /* if there is a VM recreate media lock map as mentioned above,
11524 * otherwise it is a waste of time and we leave things unlocked */
11525 if (aOnline)
11526 {
11527 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11528 /* must never be NULL, but better safe than sorry */
11529 if (!pMachine.isNull())
11530 {
11531 alock.release();
11532 rc = mData->mSession.mMachine->lockMedia();
11533 alock.acquire();
11534 if (FAILED(rc))
11535 throw rc;
11536 }
11537 }
11538 }
11539 }
11540 catch (HRESULT aRC) {rc = aRC;}
11541
11542 if (mData->mMachineState == MachineState_SettingUp)
11543 setMachineState(oldState);
11544
11545 /* unlock all hard disks we locked when there is no VM */
11546 if (!aOnline)
11547 {
11548 ErrorInfoKeeper eik;
11549
11550 HRESULT rc1 = lockedMediaMap->Clear();
11551 AssertComRC(rc1);
11552 }
11553
11554 return rc;
11555}
11556
11557
11558/**
11559 * Looks through the given list of media attachments for one with the given parameters
11560 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11561 * can be searched as well if needed.
11562 *
11563 * @param list
11564 * @param aControllerName
11565 * @param aControllerPort
11566 * @param aDevice
11567 * @return
11568 */
11569MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11570 IN_BSTR aControllerName,
11571 LONG aControllerPort,
11572 LONG aDevice)
11573{
11574 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11575 it != ll.end();
11576 ++it)
11577 {
11578 MediumAttachment *pAttach = *it;
11579 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11580 return pAttach;
11581 }
11582
11583 return NULL;
11584}
11585
11586/**
11587 * Looks through the given list of media attachments for one with the given parameters
11588 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11589 * can be searched as well if needed.
11590 *
11591 * @param list
11592 * @param aControllerName
11593 * @param aControllerPort
11594 * @param aDevice
11595 * @return
11596 */
11597MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11598 ComObjPtr<Medium> pMedium)
11599{
11600 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11601 it != ll.end();
11602 ++it)
11603 {
11604 MediumAttachment *pAttach = *it;
11605 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11606 if (pMediumThis == pMedium)
11607 return pAttach;
11608 }
11609
11610 return NULL;
11611}
11612
11613/**
11614 * Looks through the given list of media attachments for one with the given parameters
11615 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11616 * can be searched as well if needed.
11617 *
11618 * @param list
11619 * @param aControllerName
11620 * @param aControllerPort
11621 * @param aDevice
11622 * @return
11623 */
11624MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11625 Guid &id)
11626{
11627 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11628 it != ll.end();
11629 ++it)
11630 {
11631 MediumAttachment *pAttach = *it;
11632 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11633 if (pMediumThis->i_getId() == id)
11634 return pAttach;
11635 }
11636
11637 return NULL;
11638}
11639
11640/**
11641 * Main implementation for Machine::DetachDevice. This also gets called
11642 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11643 *
11644 * @param pAttach Medium attachment to detach.
11645 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11646 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11647 * @return
11648 */
11649HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11650 AutoWriteLock &writeLock,
11651 Snapshot *pSnapshot)
11652{
11653 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11654 DeviceType_T mediumType = pAttach->i_getType();
11655
11656 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11657
11658 if (pAttach->i_isImplicit())
11659 {
11660 /* attempt to implicitly delete the implicitly created diff */
11661
11662 /// @todo move the implicit flag from MediumAttachment to Medium
11663 /// and forbid any hard disk operation when it is implicit. Or maybe
11664 /// a special media state for it to make it even more simple.
11665
11666 Assert(mMediaData.isBackedUp());
11667
11668 /* will release the lock before the potentially lengthy operation, so
11669 * protect with the special state */
11670 MachineState_T oldState = mData->mMachineState;
11671 setMachineState(MachineState_SettingUp);
11672
11673 writeLock.release();
11674
11675 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11676 true /*aWait*/);
11677
11678 writeLock.acquire();
11679
11680 setMachineState(oldState);
11681
11682 if (FAILED(rc)) return rc;
11683 }
11684
11685 setModified(IsModified_Storage);
11686 mMediaData.backup();
11687 mMediaData->mAttachments.remove(pAttach);
11688
11689 if (!oldmedium.isNull())
11690 {
11691 // if this is from a snapshot, do not defer detachment to commitMedia()
11692 if (pSnapshot)
11693 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11694 // else if non-hard disk media, do not defer detachment to commitMedia() either
11695 else if (mediumType != DeviceType_HardDisk)
11696 oldmedium->i_removeBackReference(mData->mUuid);
11697 }
11698
11699 return S_OK;
11700}
11701
11702/**
11703 * Goes thru all media of the given list and
11704 *
11705 * 1) calls detachDevice() on each of them for this machine and
11706 * 2) adds all Medium objects found in the process to the given list,
11707 * depending on cleanupMode.
11708 *
11709 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11710 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11711 * media to the list.
11712 *
11713 * This gets called from Machine::Unregister, both for the actual Machine and
11714 * the SnapshotMachine objects that might be found in the snapshots.
11715 *
11716 * Requires caller and locking. The machine lock must be passed in because it
11717 * will be passed on to detachDevice which needs it for temporary unlocking.
11718 *
11719 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11720 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11721 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11722 * otherwise no media get added.
11723 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11724 * @return
11725 */
11726HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11727 Snapshot *pSnapshot,
11728 CleanupMode_T cleanupMode,
11729 MediaList &llMedia)
11730{
11731 Assert(isWriteLockOnCurrentThread());
11732
11733 HRESULT rc;
11734
11735 // make a temporary list because detachDevice invalidates iterators into
11736 // mMediaData->mAttachments
11737 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11738
11739 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11740 it != llAttachments2.end();
11741 ++it)
11742 {
11743 ComObjPtr<MediumAttachment> &pAttach = *it;
11744 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11745
11746 if (!pMedium.isNull())
11747 {
11748 AutoCaller mac(pMedium);
11749 if (FAILED(mac.rc())) return mac.rc();
11750 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11751 DeviceType_T devType = pMedium->i_getDeviceType();
11752 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11753 && devType == DeviceType_HardDisk)
11754 || (cleanupMode == CleanupMode_Full)
11755 )
11756 {
11757 llMedia.push_back(pMedium);
11758 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11759 /*
11760 * Search for medias which are not attached to any machine, but
11761 * in the chain to an attached disk. Mediums are only consided
11762 * if they are:
11763 * - have only one child
11764 * - no references to any machines
11765 * - are of normal medium type
11766 */
11767 while (!pParent.isNull())
11768 {
11769 AutoCaller mac1(pParent);
11770 if (FAILED(mac1.rc())) return mac1.rc();
11771 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11772 if (pParent->i_getChildren().size() == 1)
11773 {
11774 if ( pParent->i_getMachineBackRefCount() == 0
11775 && pParent->i_getType() == MediumType_Normal
11776 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11777 llMedia.push_back(pParent);
11778 }
11779 else
11780 break;
11781 pParent = pParent->i_getParent();
11782 }
11783 }
11784 }
11785
11786 // real machine: then we need to use the proper method
11787 rc = detachDevice(pAttach, writeLock, pSnapshot);
11788
11789 if (FAILED(rc))
11790 return rc;
11791 }
11792
11793 return S_OK;
11794}
11795
11796/**
11797 * Perform deferred hard disk detachments.
11798 *
11799 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11800 * backed up).
11801 *
11802 * If @a aOnline is @c true then this method will also unlock the old hard disks
11803 * for which the new implicit diffs were created and will lock these new diffs for
11804 * writing.
11805 *
11806 * @param aOnline Whether the VM was online prior to this operation.
11807 *
11808 * @note Locks this object for writing!
11809 */
11810void Machine::commitMedia(bool aOnline /*= false*/)
11811{
11812 AutoCaller autoCaller(this);
11813 AssertComRCReturnVoid(autoCaller.rc());
11814
11815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11816
11817 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11818
11819 HRESULT rc = S_OK;
11820
11821 /* no attach/detach operations -- nothing to do */
11822 if (!mMediaData.isBackedUp())
11823 return;
11824
11825 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11826 bool fMediaNeedsLocking = false;
11827
11828 /* enumerate new attachments */
11829 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11830 it != mMediaData->mAttachments.end();
11831 ++it)
11832 {
11833 MediumAttachment *pAttach = *it;
11834
11835 pAttach->i_commit();
11836
11837 Medium* pMedium = pAttach->i_getMedium();
11838 bool fImplicit = pAttach->i_isImplicit();
11839
11840 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11841 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11842 fImplicit));
11843
11844 /** @todo convert all this Machine-based voodoo to MediumAttachment
11845 * based commit logic. */
11846 if (fImplicit)
11847 {
11848 /* convert implicit attachment to normal */
11849 pAttach->i_setImplicit(false);
11850
11851 if ( aOnline
11852 && pMedium
11853 && pAttach->i_getType() == DeviceType_HardDisk
11854 )
11855 {
11856 ComObjPtr<Medium> parent = pMedium->i_getParent();
11857 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11858
11859 /* update the appropriate lock list */
11860 MediumLockList *pMediumLockList;
11861 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11862 AssertComRC(rc);
11863 if (pMediumLockList)
11864 {
11865 /* unlock if there's a need to change the locking */
11866 if (!fMediaNeedsLocking)
11867 {
11868 rc = mData->mSession.mLockedMedia.Unlock();
11869 AssertComRC(rc);
11870 fMediaNeedsLocking = true;
11871 }
11872 rc = pMediumLockList->Update(parent, false);
11873 AssertComRC(rc);
11874 rc = pMediumLockList->Append(pMedium, true);
11875 AssertComRC(rc);
11876 }
11877 }
11878
11879 continue;
11880 }
11881
11882 if (pMedium)
11883 {
11884 /* was this medium attached before? */
11885 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11886 oldIt != oldAtts.end();
11887 ++oldIt)
11888 {
11889 MediumAttachment *pOldAttach = *oldIt;
11890 if (pOldAttach->i_getMedium() == pMedium)
11891 {
11892 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11893
11894 /* yes: remove from old to avoid de-association */
11895 oldAtts.erase(oldIt);
11896 break;
11897 }
11898 }
11899 }
11900 }
11901
11902 /* enumerate remaining old attachments and de-associate from the
11903 * current machine state */
11904 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11905 it != oldAtts.end();
11906 ++it)
11907 {
11908 MediumAttachment *pAttach = *it;
11909 Medium* pMedium = pAttach->i_getMedium();
11910
11911 /* Detach only hard disks, since DVD/floppy media is detached
11912 * instantly in MountMedium. */
11913 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11914 {
11915 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11916
11917 /* now de-associate from the current machine state */
11918 rc = pMedium->i_removeBackReference(mData->mUuid);
11919 AssertComRC(rc);
11920
11921 if (aOnline)
11922 {
11923 /* unlock since medium is not used anymore */
11924 MediumLockList *pMediumLockList;
11925 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11926 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11927 {
11928 /* this happens for online snapshots, there the attachment
11929 * is changing, but only to a diff image created under
11930 * the old one, so there is no separate lock list */
11931 Assert(!pMediumLockList);
11932 }
11933 else
11934 {
11935 AssertComRC(rc);
11936 if (pMediumLockList)
11937 {
11938 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11939 AssertComRC(rc);
11940 }
11941 }
11942 }
11943 }
11944 }
11945
11946 /* take media locks again so that the locking state is consistent */
11947 if (fMediaNeedsLocking)
11948 {
11949 Assert(aOnline);
11950 rc = mData->mSession.mLockedMedia.Lock();
11951 AssertComRC(rc);
11952 }
11953
11954 /* commit the hard disk changes */
11955 mMediaData.commit();
11956
11957 if (isSessionMachine())
11958 {
11959 /*
11960 * Update the parent machine to point to the new owner.
11961 * This is necessary because the stored parent will point to the
11962 * session machine otherwise and cause crashes or errors later
11963 * when the session machine gets invalid.
11964 */
11965 /** @todo Change the MediumAttachment class to behave like any other
11966 * class in this regard by creating peer MediumAttachment
11967 * objects for session machines and share the data with the peer
11968 * machine.
11969 */
11970 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11971 it != mMediaData->mAttachments.end();
11972 ++it)
11973 {
11974 (*it)->i_updateParentMachine(mPeer);
11975 }
11976
11977 /* attach new data to the primary machine and reshare it */
11978 mPeer->mMediaData.attach(mMediaData);
11979 }
11980
11981 return;
11982}
11983
11984/**
11985 * Perform deferred deletion of implicitly created diffs.
11986 *
11987 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11988 * backed up).
11989 *
11990 * @note Locks this object for writing!
11991 */
11992void Machine::rollbackMedia()
11993{
11994 AutoCaller autoCaller(this);
11995 AssertComRCReturnVoid(autoCaller.rc());
11996
11997 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11998 LogFlowThisFunc(("Entering rollbackMedia\n"));
11999
12000 HRESULT rc = S_OK;
12001
12002 /* no attach/detach operations -- nothing to do */
12003 if (!mMediaData.isBackedUp())
12004 return;
12005
12006 /* enumerate new attachments */
12007 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12008 it != mMediaData->mAttachments.end();
12009 ++it)
12010 {
12011 MediumAttachment *pAttach = *it;
12012 /* Fix up the backrefs for DVD/floppy media. */
12013 if (pAttach->i_getType() != DeviceType_HardDisk)
12014 {
12015 Medium* pMedium = pAttach->i_getMedium();
12016 if (pMedium)
12017 {
12018 rc = pMedium->i_removeBackReference(mData->mUuid);
12019 AssertComRC(rc);
12020 }
12021 }
12022
12023 (*it)->i_rollback();
12024
12025 pAttach = *it;
12026 /* Fix up the backrefs for DVD/floppy media. */
12027 if (pAttach->i_getType() != DeviceType_HardDisk)
12028 {
12029 Medium* pMedium = pAttach->i_getMedium();
12030 if (pMedium)
12031 {
12032 rc = pMedium->i_addBackReference(mData->mUuid);
12033 AssertComRC(rc);
12034 }
12035 }
12036 }
12037
12038 /** @todo convert all this Machine-based voodoo to MediumAttachment
12039 * based rollback logic. */
12040 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12041
12042 return;
12043}
12044
12045/**
12046 * Returns true if the settings file is located in the directory named exactly
12047 * as the machine; this means, among other things, that the machine directory
12048 * should be auto-renamed.
12049 *
12050 * @param aSettingsDir if not NULL, the full machine settings file directory
12051 * name will be assigned there.
12052 *
12053 * @note Doesn't lock anything.
12054 * @note Not thread safe (must be called from this object's lock).
12055 */
12056bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12057{
12058 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12059 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12060 if (aSettingsDir)
12061 *aSettingsDir = strMachineDirName;
12062 strMachineDirName.stripPath(); // vmname
12063 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12064 strConfigFileOnly.stripPath() // vmname.vbox
12065 .stripSuffix(); // vmname
12066 /** @todo hack, make somehow use of ComposeMachineFilename */
12067 if (mUserData->s.fDirectoryIncludesUUID)
12068 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
12069
12070 AssertReturn(!strMachineDirName.isEmpty(), false);
12071 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12072
12073 return strMachineDirName == strConfigFileOnly;
12074}
12075
12076/**
12077 * Discards all changes to machine settings.
12078 *
12079 * @param aNotify Whether to notify the direct session about changes or not.
12080 *
12081 * @note Locks objects for writing!
12082 */
12083void Machine::rollback(bool aNotify)
12084{
12085 AutoCaller autoCaller(this);
12086 AssertComRCReturn(autoCaller.rc(), (void)0);
12087
12088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12089
12090 if (!mStorageControllers.isNull())
12091 {
12092 if (mStorageControllers.isBackedUp())
12093 {
12094 /* unitialize all new devices (absent in the backed up list). */
12095 StorageControllerList::const_iterator it = mStorageControllers->begin();
12096 StorageControllerList *backedList = mStorageControllers.backedUpData();
12097 while (it != mStorageControllers->end())
12098 {
12099 if ( std::find(backedList->begin(), backedList->end(), *it)
12100 == backedList->end()
12101 )
12102 {
12103 (*it)->uninit();
12104 }
12105 ++it;
12106 }
12107
12108 /* restore the list */
12109 mStorageControllers.rollback();
12110 }
12111
12112 /* rollback any changes to devices after restoring the list */
12113 if (mData->flModifications & IsModified_Storage)
12114 {
12115 StorageControllerList::const_iterator it = mStorageControllers->begin();
12116 while (it != mStorageControllers->end())
12117 {
12118 (*it)->i_rollback();
12119 ++it;
12120 }
12121 }
12122 }
12123
12124 if (!mUSBControllers.isNull())
12125 {
12126 if (mUSBControllers.isBackedUp())
12127 {
12128 /* unitialize all new devices (absent in the backed up list). */
12129 USBControllerList::const_iterator it = mUSBControllers->begin();
12130 USBControllerList *backedList = mUSBControllers.backedUpData();
12131 while (it != mUSBControllers->end())
12132 {
12133 if ( std::find(backedList->begin(), backedList->end(), *it)
12134 == backedList->end()
12135 )
12136 {
12137 (*it)->uninit();
12138 }
12139 ++it;
12140 }
12141
12142 /* restore the list */
12143 mUSBControllers.rollback();
12144 }
12145
12146 /* rollback any changes to devices after restoring the list */
12147 if (mData->flModifications & IsModified_USB)
12148 {
12149 USBControllerList::const_iterator it = mUSBControllers->begin();
12150 while (it != mUSBControllers->end())
12151 {
12152 (*it)->i_rollback();
12153 ++it;
12154 }
12155 }
12156 }
12157
12158 mUserData.rollback();
12159
12160 mHWData.rollback();
12161
12162 if (mData->flModifications & IsModified_Storage)
12163 rollbackMedia();
12164
12165 if (mBIOSSettings)
12166 mBIOSSettings->i_rollback();
12167
12168 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12169 mVRDEServer->i_rollback();
12170
12171 if (mAudioAdapter)
12172 mAudioAdapter->i_rollback();
12173
12174 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12175 mUSBDeviceFilters->i_rollback();
12176
12177 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12178 mBandwidthControl->i_rollback();
12179
12180 if (!mHWData.isNull())
12181 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12182 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12183 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12184 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12185
12186 if (mData->flModifications & IsModified_NetworkAdapters)
12187 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12188 if ( mNetworkAdapters[slot]
12189 && mNetworkAdapters[slot]->i_isModified())
12190 {
12191 mNetworkAdapters[slot]->i_rollback();
12192 networkAdapters[slot] = mNetworkAdapters[slot];
12193 }
12194
12195 if (mData->flModifications & IsModified_SerialPorts)
12196 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12197 if ( mSerialPorts[slot]
12198 && mSerialPorts[slot]->i_isModified())
12199 {
12200 mSerialPorts[slot]->i_rollback();
12201 serialPorts[slot] = mSerialPorts[slot];
12202 }
12203
12204 if (mData->flModifications & IsModified_ParallelPorts)
12205 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12206 if ( mParallelPorts[slot]
12207 && mParallelPorts[slot]->i_isModified())
12208 {
12209 mParallelPorts[slot]->i_rollback();
12210 parallelPorts[slot] = mParallelPorts[slot];
12211 }
12212
12213 if (aNotify)
12214 {
12215 /* inform the direct session about changes */
12216
12217 ComObjPtr<Machine> that = this;
12218 uint32_t flModifications = mData->flModifications;
12219 alock.release();
12220
12221 if (flModifications & IsModified_SharedFolders)
12222 that->onSharedFolderChange();
12223
12224 if (flModifications & IsModified_VRDEServer)
12225 that->onVRDEServerChange(/* aRestart */ TRUE);
12226 if (flModifications & IsModified_USB)
12227 that->onUSBControllerChange();
12228
12229 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12230 if (networkAdapters[slot])
12231 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12232 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12233 if (serialPorts[slot])
12234 that->onSerialPortChange(serialPorts[slot]);
12235 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12236 if (parallelPorts[slot])
12237 that->onParallelPortChange(parallelPorts[slot]);
12238
12239 if (flModifications & IsModified_Storage)
12240 that->onStorageControllerChange();
12241
12242#if 0
12243 if (flModifications & IsModified_BandwidthControl)
12244 that->onBandwidthControlChange();
12245#endif
12246 }
12247}
12248
12249/**
12250 * Commits all the changes to machine settings.
12251 *
12252 * Note that this operation is supposed to never fail.
12253 *
12254 * @note Locks this object and children for writing.
12255 */
12256void Machine::commit()
12257{
12258 AutoCaller autoCaller(this);
12259 AssertComRCReturnVoid(autoCaller.rc());
12260
12261 AutoCaller peerCaller(mPeer);
12262 AssertComRCReturnVoid(peerCaller.rc());
12263
12264 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12265
12266 /*
12267 * use safe commit to ensure Snapshot machines (that share mUserData)
12268 * will still refer to a valid memory location
12269 */
12270 mUserData.commitCopy();
12271
12272 mHWData.commit();
12273
12274 if (mMediaData.isBackedUp())
12275 commitMedia(Global::IsOnline(mData->mMachineState));
12276
12277 mBIOSSettings->i_commit();
12278 mVRDEServer->i_commit();
12279 mAudioAdapter->i_commit();
12280 mUSBDeviceFilters->i_commit();
12281 mBandwidthControl->i_commit();
12282
12283 /* Since mNetworkAdapters is a list which might have been changed (resized)
12284 * without using the Backupable<> template we need to handle the copying
12285 * of the list entries manually, including the creation of peers for the
12286 * new objects. */
12287 bool commitNetworkAdapters = false;
12288 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12289 if (mPeer)
12290 {
12291 /* commit everything, even the ones which will go away */
12292 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12293 mNetworkAdapters[slot]->i_commit();
12294 /* copy over the new entries, creating a peer and uninit the original */
12295 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12296 for (size_t slot = 0; slot < newSize; slot++)
12297 {
12298 /* look if this adapter has a peer device */
12299 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12300 if (!peer)
12301 {
12302 /* no peer means the adapter is a newly created one;
12303 * create a peer owning data this data share it with */
12304 peer.createObject();
12305 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12306 }
12307 mPeer->mNetworkAdapters[slot] = peer;
12308 }
12309 /* uninit any no longer needed network adapters */
12310 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12311 mNetworkAdapters[slot]->uninit();
12312 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12313 {
12314 if (mPeer->mNetworkAdapters[slot])
12315 mPeer->mNetworkAdapters[slot]->uninit();
12316 }
12317 /* Keep the original network adapter count until this point, so that
12318 * discarding a chipset type change will not lose settings. */
12319 mNetworkAdapters.resize(newSize);
12320 mPeer->mNetworkAdapters.resize(newSize);
12321 }
12322 else
12323 {
12324 /* we have no peer (our parent is the newly created machine);
12325 * just commit changes to the network adapters */
12326 commitNetworkAdapters = true;
12327 }
12328 if (commitNetworkAdapters)
12329 {
12330 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12331 mNetworkAdapters[slot]->i_commit();
12332 }
12333
12334 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12335 mSerialPorts[slot]->i_commit();
12336 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12337 mParallelPorts[slot]->i_commit();
12338
12339 bool commitStorageControllers = false;
12340
12341 if (mStorageControllers.isBackedUp())
12342 {
12343 mStorageControllers.commit();
12344
12345 if (mPeer)
12346 {
12347 /* Commit all changes to new controllers (this will reshare data with
12348 * peers for those who have peers) */
12349 StorageControllerList *newList = new StorageControllerList();
12350 StorageControllerList::const_iterator it = mStorageControllers->begin();
12351 while (it != mStorageControllers->end())
12352 {
12353 (*it)->i_commit();
12354
12355 /* look if this controller has a peer device */
12356 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12357 if (!peer)
12358 {
12359 /* no peer means the device is a newly created one;
12360 * create a peer owning data this device share it with */
12361 peer.createObject();
12362 peer->init(mPeer, *it, true /* aReshare */);
12363 }
12364 else
12365 {
12366 /* remove peer from the old list */
12367 mPeer->mStorageControllers->remove(peer);
12368 }
12369 /* and add it to the new list */
12370 newList->push_back(peer);
12371
12372 ++it;
12373 }
12374
12375 /* uninit old peer's controllers that are left */
12376 it = mPeer->mStorageControllers->begin();
12377 while (it != mPeer->mStorageControllers->end())
12378 {
12379 (*it)->uninit();
12380 ++it;
12381 }
12382
12383 /* attach new list of controllers to our peer */
12384 mPeer->mStorageControllers.attach(newList);
12385 }
12386 else
12387 {
12388 /* we have no peer (our parent is the newly created machine);
12389 * just commit changes to devices */
12390 commitStorageControllers = true;
12391 }
12392 }
12393 else
12394 {
12395 /* the list of controllers itself is not changed,
12396 * just commit changes to controllers themselves */
12397 commitStorageControllers = true;
12398 }
12399
12400 if (commitStorageControllers)
12401 {
12402 StorageControllerList::const_iterator it = mStorageControllers->begin();
12403 while (it != mStorageControllers->end())
12404 {
12405 (*it)->i_commit();
12406 ++it;
12407 }
12408 }
12409
12410 bool commitUSBControllers = false;
12411
12412 if (mUSBControllers.isBackedUp())
12413 {
12414 mUSBControllers.commit();
12415
12416 if (mPeer)
12417 {
12418 /* Commit all changes to new controllers (this will reshare data with
12419 * peers for those who have peers) */
12420 USBControllerList *newList = new USBControllerList();
12421 USBControllerList::const_iterator it = mUSBControllers->begin();
12422 while (it != mUSBControllers->end())
12423 {
12424 (*it)->i_commit();
12425
12426 /* look if this controller has a peer device */
12427 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12428 if (!peer)
12429 {
12430 /* no peer means the device is a newly created one;
12431 * create a peer owning data this device share it with */
12432 peer.createObject();
12433 peer->init(mPeer, *it, true /* aReshare */);
12434 }
12435 else
12436 {
12437 /* remove peer from the old list */
12438 mPeer->mUSBControllers->remove(peer);
12439 }
12440 /* and add it to the new list */
12441 newList->push_back(peer);
12442
12443 ++it;
12444 }
12445
12446 /* uninit old peer's controllers that are left */
12447 it = mPeer->mUSBControllers->begin();
12448 while (it != mPeer->mUSBControllers->end())
12449 {
12450 (*it)->uninit();
12451 ++it;
12452 }
12453
12454 /* attach new list of controllers to our peer */
12455 mPeer->mUSBControllers.attach(newList);
12456 }
12457 else
12458 {
12459 /* we have no peer (our parent is the newly created machine);
12460 * just commit changes to devices */
12461 commitUSBControllers = true;
12462 }
12463 }
12464 else
12465 {
12466 /* the list of controllers itself is not changed,
12467 * just commit changes to controllers themselves */
12468 commitUSBControllers = true;
12469 }
12470
12471 if (commitUSBControllers)
12472 {
12473 USBControllerList::const_iterator it = mUSBControllers->begin();
12474 while (it != mUSBControllers->end())
12475 {
12476 (*it)->i_commit();
12477 ++it;
12478 }
12479 }
12480
12481 if (isSessionMachine())
12482 {
12483 /* attach new data to the primary machine and reshare it */
12484 mPeer->mUserData.attach(mUserData);
12485 mPeer->mHWData.attach(mHWData);
12486 /* mMediaData is reshared by fixupMedia */
12487 // mPeer->mMediaData.attach(mMediaData);
12488 Assert(mPeer->mMediaData.data() == mMediaData.data());
12489 }
12490}
12491
12492/**
12493 * Copies all the hardware data from the given machine.
12494 *
12495 * Currently, only called when the VM is being restored from a snapshot. In
12496 * particular, this implies that the VM is not running during this method's
12497 * call.
12498 *
12499 * @note This method must be called from under this object's lock.
12500 *
12501 * @note This method doesn't call #commit(), so all data remains backed up and
12502 * unsaved.
12503 */
12504void Machine::copyFrom(Machine *aThat)
12505{
12506 AssertReturnVoid(!isSnapshotMachine());
12507 AssertReturnVoid(aThat->isSnapshotMachine());
12508
12509 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12510
12511 mHWData.assignCopy(aThat->mHWData);
12512
12513 // create copies of all shared folders (mHWData after attaching a copy
12514 // contains just references to original objects)
12515 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12516 it != mHWData->mSharedFolders.end();
12517 ++it)
12518 {
12519 ComObjPtr<SharedFolder> folder;
12520 folder.createObject();
12521 HRESULT rc = folder->initCopy(getMachine(), *it);
12522 AssertComRC(rc);
12523 *it = folder;
12524 }
12525
12526 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12527 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12528 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12529 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12530 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12531
12532 /* create private copies of all controllers */
12533 mStorageControllers.backup();
12534 mStorageControllers->clear();
12535 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12536 it != aThat->mStorageControllers->end();
12537 ++it)
12538 {
12539 ComObjPtr<StorageController> ctrl;
12540 ctrl.createObject();
12541 ctrl->initCopy(this, *it);
12542 mStorageControllers->push_back(ctrl);
12543 }
12544
12545 /* create private copies of all USB controllers */
12546 mUSBControllers.backup();
12547 mUSBControllers->clear();
12548 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12549 it != aThat->mUSBControllers->end();
12550 ++it)
12551 {
12552 ComObjPtr<USBController> ctrl;
12553 ctrl.createObject();
12554 ctrl->initCopy(this, *it);
12555 mUSBControllers->push_back(ctrl);
12556 }
12557
12558 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12559 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12560 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12561 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12562 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12563 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12564 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12565}
12566
12567/**
12568 * Returns whether the given storage controller is hotplug capable.
12569 *
12570 * @returns true if the controller supports hotplugging
12571 * false otherwise.
12572 * @param enmCtrlType The controller type to check for.
12573 */
12574bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12575{
12576 ComPtr<ISystemProperties> systemProperties;
12577 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12578 if (FAILED(rc))
12579 return false;
12580
12581 BOOL aHotplugCapable = FALSE;
12582 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12583
12584 return RT_BOOL(aHotplugCapable);
12585}
12586
12587#ifdef VBOX_WITH_RESOURCE_USAGE_API
12588
12589void Machine::getDiskList(MediaList &list)
12590{
12591 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12592 it != mMediaData->mAttachments.end();
12593 ++it)
12594 {
12595 MediumAttachment* pAttach = *it;
12596 /* just in case */
12597 AssertStmt(pAttach, continue);
12598
12599 AutoCaller localAutoCallerA(pAttach);
12600 if (FAILED(localAutoCallerA.rc())) continue;
12601
12602 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12603
12604 if (pAttach->i_getType() == DeviceType_HardDisk)
12605 list.push_back(pAttach->i_getMedium());
12606 }
12607}
12608
12609void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12610{
12611 AssertReturnVoid(isWriteLockOnCurrentThread());
12612 AssertPtrReturnVoid(aCollector);
12613
12614 pm::CollectorHAL *hal = aCollector->getHAL();
12615 /* Create sub metrics */
12616 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12617 "Percentage of processor time spent in user mode by the VM process.");
12618 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12619 "Percentage of processor time spent in kernel mode by the VM process.");
12620 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12621 "Size of resident portion of VM process in memory.");
12622 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12623 "Actual size of all VM disks combined.");
12624 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12625 "Network receive rate.");
12626 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12627 "Network transmit rate.");
12628 /* Create and register base metrics */
12629 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12630 cpuLoadUser, cpuLoadKernel);
12631 aCollector->registerBaseMetric(cpuLoad);
12632 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12633 ramUsageUsed);
12634 aCollector->registerBaseMetric(ramUsage);
12635 MediaList disks;
12636 getDiskList(disks);
12637 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12638 diskUsageUsed);
12639 aCollector->registerBaseMetric(diskUsage);
12640
12641 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12642 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12643 new pm::AggregateAvg()));
12644 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12645 new pm::AggregateMin()));
12646 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12647 new pm::AggregateMax()));
12648 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12649 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12650 new pm::AggregateAvg()));
12651 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12652 new pm::AggregateMin()));
12653 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12654 new pm::AggregateMax()));
12655
12656 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12657 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12658 new pm::AggregateAvg()));
12659 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12660 new pm::AggregateMin()));
12661 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12662 new pm::AggregateMax()));
12663
12664 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12665 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12666 new pm::AggregateAvg()));
12667 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12668 new pm::AggregateMin()));
12669 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12670 new pm::AggregateMax()));
12671
12672
12673 /* Guest metrics collector */
12674 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12675 aCollector->registerGuest(mCollectorGuest);
12676 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12677 this, __PRETTY_FUNCTION__, mCollectorGuest));
12678
12679 /* Create sub metrics */
12680 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12681 "Percentage of processor time spent in user mode as seen by the guest.");
12682 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12683 "Percentage of processor time spent in kernel mode as seen by the guest.");
12684 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12685 "Percentage of processor time spent idling as seen by the guest.");
12686
12687 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12688 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12689 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12690 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12691 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12692 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12693
12694 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12695
12696 /* Create and register base metrics */
12697 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12698 machineNetRx, machineNetTx);
12699 aCollector->registerBaseMetric(machineNetRate);
12700
12701 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12702 guestLoadUser, guestLoadKernel, guestLoadIdle);
12703 aCollector->registerBaseMetric(guestCpuLoad);
12704
12705 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12706 guestMemTotal, guestMemFree,
12707 guestMemBalloon, guestMemShared,
12708 guestMemCache, guestPagedTotal);
12709 aCollector->registerBaseMetric(guestCpuMem);
12710
12711 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12712 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12713 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12714 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12715
12716 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12717 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12718 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12719 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12720
12721 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12722 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12723 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12724 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12725
12726 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12727 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12728 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12729 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12730
12731 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12732 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12733 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12734 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12735
12736 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12737 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12738 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12739 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12740
12741 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12742 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12743 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12744 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12745
12746 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12747 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12748 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12749 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12750
12751 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12752 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12753 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12754 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12755
12756 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12757 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12758 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12759 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12760
12761 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12762 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12763 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12764 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12765}
12766
12767void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12768{
12769 AssertReturnVoid(isWriteLockOnCurrentThread());
12770
12771 if (aCollector)
12772 {
12773 aCollector->unregisterMetricsFor(aMachine);
12774 aCollector->unregisterBaseMetricsFor(aMachine);
12775 }
12776}
12777
12778#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12779
12780
12781////////////////////////////////////////////////////////////////////////////////
12782
12783DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12784
12785HRESULT SessionMachine::FinalConstruct()
12786{
12787 LogFlowThisFunc(("\n"));
12788
12789 mClientToken = NULL;
12790
12791 return BaseFinalConstruct();
12792}
12793
12794void SessionMachine::FinalRelease()
12795{
12796 LogFlowThisFunc(("\n"));
12797
12798 Assert(!mClientToken);
12799 /* paranoia, should not hang around any more */
12800 if (mClientToken)
12801 {
12802 delete mClientToken;
12803 mClientToken = NULL;
12804 }
12805
12806 uninit(Uninit::Unexpected);
12807
12808 BaseFinalRelease();
12809}
12810
12811/**
12812 * @note Must be called only by Machine::LockMachine() from its own write lock.
12813 */
12814HRESULT SessionMachine::init(Machine *aMachine)
12815{
12816 LogFlowThisFuncEnter();
12817 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12818
12819 AssertReturn(aMachine, E_INVALIDARG);
12820
12821 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12822
12823 /* Enclose the state transition NotReady->InInit->Ready */
12824 AutoInitSpan autoInitSpan(this);
12825 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12826
12827 HRESULT rc = S_OK;
12828
12829 /* create the machine client token */
12830 try
12831 {
12832 mClientToken = new ClientToken(aMachine, this);
12833 if (!mClientToken->isReady())
12834 {
12835 delete mClientToken;
12836 mClientToken = NULL;
12837 rc = E_FAIL;
12838 }
12839 }
12840 catch (std::bad_alloc &)
12841 {
12842 rc = E_OUTOFMEMORY;
12843 }
12844 if (FAILED(rc))
12845 return rc;
12846
12847 /* memorize the peer Machine */
12848 unconst(mPeer) = aMachine;
12849 /* share the parent pointer */
12850 unconst(mParent) = aMachine->mParent;
12851
12852 /* take the pointers to data to share */
12853 mData.share(aMachine->mData);
12854 mSSData.share(aMachine->mSSData);
12855
12856 mUserData.share(aMachine->mUserData);
12857 mHWData.share(aMachine->mHWData);
12858 mMediaData.share(aMachine->mMediaData);
12859
12860 mStorageControllers.allocate();
12861 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12862 it != aMachine->mStorageControllers->end();
12863 ++it)
12864 {
12865 ComObjPtr<StorageController> ctl;
12866 ctl.createObject();
12867 ctl->init(this, *it);
12868 mStorageControllers->push_back(ctl);
12869 }
12870
12871 mUSBControllers.allocate();
12872 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12873 it != aMachine->mUSBControllers->end();
12874 ++it)
12875 {
12876 ComObjPtr<USBController> ctl;
12877 ctl.createObject();
12878 ctl->init(this, *it);
12879 mUSBControllers->push_back(ctl);
12880 }
12881
12882 unconst(mBIOSSettings).createObject();
12883 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12884 /* create another VRDEServer object that will be mutable */
12885 unconst(mVRDEServer).createObject();
12886 mVRDEServer->init(this, aMachine->mVRDEServer);
12887 /* create another audio adapter object that will be mutable */
12888 unconst(mAudioAdapter).createObject();
12889 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12890 /* create a list of serial ports that will be mutable */
12891 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12892 {
12893 unconst(mSerialPorts[slot]).createObject();
12894 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12895 }
12896 /* create a list of parallel ports that will be mutable */
12897 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12898 {
12899 unconst(mParallelPorts[slot]).createObject();
12900 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12901 }
12902
12903 /* create another USB device filters object that will be mutable */
12904 unconst(mUSBDeviceFilters).createObject();
12905 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12906
12907 /* create a list of network adapters that will be mutable */
12908 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12909 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12910 {
12911 unconst(mNetworkAdapters[slot]).createObject();
12912 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12913 }
12914
12915 /* create another bandwidth control object that will be mutable */
12916 unconst(mBandwidthControl).createObject();
12917 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12918
12919 /* default is to delete saved state on Saved -> PoweredOff transition */
12920 mRemoveSavedState = true;
12921
12922 /* Confirm a successful initialization when it's the case */
12923 autoInitSpan.setSucceeded();
12924
12925 miNATNetworksStarted = 0;
12926
12927 LogFlowThisFuncLeave();
12928 return rc;
12929}
12930
12931/**
12932 * Uninitializes this session object. If the reason is other than
12933 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12934 * or the client watcher code.
12935 *
12936 * @param aReason uninitialization reason
12937 *
12938 * @note Locks mParent + this object for writing.
12939 */
12940void SessionMachine::uninit(Uninit::Reason aReason)
12941{
12942 LogFlowThisFuncEnter();
12943 LogFlowThisFunc(("reason=%d\n", aReason));
12944
12945 /*
12946 * Strongly reference ourselves to prevent this object deletion after
12947 * mData->mSession.mMachine.setNull() below (which can release the last
12948 * reference and call the destructor). Important: this must be done before
12949 * accessing any members (and before AutoUninitSpan that does it as well).
12950 * This self reference will be released as the very last step on return.
12951 */
12952 ComObjPtr<SessionMachine> selfRef = this;
12953
12954 /* Enclose the state transition Ready->InUninit->NotReady */
12955 AutoUninitSpan autoUninitSpan(this);
12956 if (autoUninitSpan.uninitDone())
12957 {
12958 LogFlowThisFunc(("Already uninitialized\n"));
12959 LogFlowThisFuncLeave();
12960 return;
12961 }
12962
12963 if (autoUninitSpan.initFailed())
12964 {
12965 /* We've been called by init() because it's failed. It's not really
12966 * necessary (nor it's safe) to perform the regular uninit sequence
12967 * below, the following is enough.
12968 */
12969 LogFlowThisFunc(("Initialization failed.\n"));
12970 /* destroy the machine client token */
12971 if (mClientToken)
12972 {
12973 delete mClientToken;
12974 mClientToken = NULL;
12975 }
12976 uninitDataAndChildObjects();
12977 mData.free();
12978 unconst(mParent) = NULL;
12979 unconst(mPeer) = NULL;
12980 LogFlowThisFuncLeave();
12981 return;
12982 }
12983
12984 MachineState_T lastState;
12985 {
12986 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12987 lastState = mData->mMachineState;
12988 }
12989 NOREF(lastState);
12990
12991#ifdef VBOX_WITH_USB
12992 // release all captured USB devices, but do this before requesting the locks below
12993 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12994 {
12995 /* Console::captureUSBDevices() is called in the VM process only after
12996 * setting the machine state to Starting or Restoring.
12997 * Console::detachAllUSBDevices() will be called upon successful
12998 * termination. So, we need to release USB devices only if there was
12999 * an abnormal termination of a running VM.
13000 *
13001 * This is identical to SessionMachine::DetachAllUSBDevices except
13002 * for the aAbnormal argument. */
13003 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13004 AssertComRC(rc);
13005 NOREF(rc);
13006
13007 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13008 if (service)
13009 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13010 }
13011#endif /* VBOX_WITH_USB */
13012
13013 // we need to lock this object in uninit() because the lock is shared
13014 // with mPeer (as well as data we modify below). mParent lock is needed
13015 // by several calls to it, and USB needs host lock.
13016 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
13017
13018#ifdef VBOX_WITH_RESOURCE_USAGE_API
13019 /*
13020 * It is safe to call Machine::unregisterMetrics() here because
13021 * PerformanceCollector::samplerCallback no longer accesses guest methods
13022 * holding the lock.
13023 */
13024 unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13025 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13026 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
13027 this, __PRETTY_FUNCTION__, mCollectorGuest));
13028 if (mCollectorGuest)
13029 {
13030 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13031 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13032 mCollectorGuest = NULL;
13033 }
13034#endif
13035
13036 if (aReason == Uninit::Abnormal)
13037 {
13038 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
13039 Global::IsOnlineOrTransient(lastState)));
13040
13041 /* reset the state to Aborted */
13042 if (mData->mMachineState != MachineState_Aborted)
13043 setMachineState(MachineState_Aborted);
13044 }
13045
13046 // any machine settings modified?
13047 if (mData->flModifications)
13048 {
13049 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
13050 rollback(false /* aNotify */);
13051 }
13052
13053 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
13054 || !mConsoleTaskData.mSnapshot);
13055 if (!mConsoleTaskData.strStateFilePath.isEmpty())
13056 {
13057 LogWarningThisFunc(("canceling failed save state request!\n"));
13058 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
13059 }
13060 else if (!mConsoleTaskData.mSnapshot.isNull())
13061 {
13062 LogWarningThisFunc(("canceling untaken snapshot!\n"));
13063
13064 /* delete all differencing hard disks created (this will also attach
13065 * their parents back by rolling back mMediaData) */
13066 rollbackMedia();
13067
13068 // delete the saved state file (it might have been already created)
13069 // AFTER killing the snapshot so that releaseSavedStateFile() won't
13070 // think it's still in use
13071 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
13072 mConsoleTaskData.mSnapshot->uninit();
13073 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
13074 }
13075
13076 mData->mSession.mPID = NIL_RTPROCESS;
13077
13078 if (aReason == Uninit::Unexpected)
13079 {
13080 /* Uninitialization didn't come from #checkForDeath(), so tell the
13081 * client watcher thread to update the set of machines that have open
13082 * sessions. */
13083 mParent->i_updateClientWatcher();
13084 }
13085
13086 /* uninitialize all remote controls */
13087 if (mData->mSession.mRemoteControls.size())
13088 {
13089 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13090 mData->mSession.mRemoteControls.size()));
13091
13092 Data::Session::RemoteControlList::iterator it =
13093 mData->mSession.mRemoteControls.begin();
13094 while (it != mData->mSession.mRemoteControls.end())
13095 {
13096 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13097 HRESULT rc = (*it)->Uninitialize();
13098 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13099 if (FAILED(rc))
13100 LogWarningThisFunc(("Forgot to close the remote session?\n"));
13101 ++it;
13102 }
13103 mData->mSession.mRemoteControls.clear();
13104 }
13105
13106 /* Remove all references to the NAT network service. The service will stop
13107 * if all references (also from other VMs) are removed. */
13108 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13109 {
13110 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
13111 {
13112 NetworkAttachmentType_T type;
13113 HRESULT hrc;
13114
13115 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13116 if ( SUCCEEDED(hrc)
13117 && type == NetworkAttachmentType_NATNetwork)
13118 {
13119 Bstr name;
13120 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13121 if (SUCCEEDED(hrc))
13122 {
13123 multilock.release();
13124 LogRel(("VM '%s' stops using NAT network '%ls'\n",
13125 mUserData->s.strName.c_str(), name.raw()));
13126 mParent->i_natNetworkRefDec(name.raw());
13127 multilock.acquire();
13128 }
13129 }
13130 }
13131 }
13132
13133 /*
13134 * An expected uninitialization can come only from #checkForDeath().
13135 * Otherwise it means that something's gone really wrong (for example,
13136 * the Session implementation has released the VirtualBox reference
13137 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13138 * etc). However, it's also possible, that the client releases the IPC
13139 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13140 * but the VirtualBox release event comes first to the server process.
13141 * This case is practically possible, so we should not assert on an
13142 * unexpected uninit, just log a warning.
13143 */
13144
13145 if ((aReason == Uninit::Unexpected))
13146 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13147
13148 if (aReason != Uninit::Normal)
13149 {
13150 mData->mSession.mDirectControl.setNull();
13151 }
13152 else
13153 {
13154 /* this must be null here (see #OnSessionEnd()) */
13155 Assert(mData->mSession.mDirectControl.isNull());
13156 Assert(mData->mSession.mState == SessionState_Unlocking);
13157 Assert(!mData->mSession.mProgress.isNull());
13158 }
13159 if (mData->mSession.mProgress)
13160 {
13161 if (aReason == Uninit::Normal)
13162 mData->mSession.mProgress->i_notifyComplete(S_OK);
13163 else
13164 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13165 COM_IIDOF(ISession),
13166 getComponentName(),
13167 tr("The VM session was aborted"));
13168 mData->mSession.mProgress.setNull();
13169 }
13170
13171 /* remove the association between the peer machine and this session machine */
13172 Assert( (SessionMachine*)mData->mSession.mMachine == this
13173 || aReason == Uninit::Unexpected);
13174
13175 /* reset the rest of session data */
13176 mData->mSession.mMachine.setNull();
13177 mData->mSession.mState = SessionState_Unlocked;
13178 mData->mSession.mType.setNull();
13179
13180 /* destroy the machine client token before leaving the exclusive lock */
13181 if (mClientToken)
13182 {
13183 delete mClientToken;
13184 mClientToken = NULL;
13185 }
13186
13187 /* fire an event */
13188 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13189
13190 uninitDataAndChildObjects();
13191
13192 /* free the essential data structure last */
13193 mData.free();
13194
13195 /* release the exclusive lock before setting the below two to NULL */
13196 multilock.release();
13197
13198 unconst(mParent) = NULL;
13199 unconst(mPeer) = NULL;
13200
13201 LogFlowThisFuncLeave();
13202}
13203
13204// util::Lockable interface
13205////////////////////////////////////////////////////////////////////////////////
13206
13207/**
13208 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13209 * with the primary Machine instance (mPeer).
13210 */
13211RWLockHandle *SessionMachine::lockHandle() const
13212{
13213 AssertReturn(mPeer != NULL, NULL);
13214 return mPeer->lockHandle();
13215}
13216
13217// IInternalMachineControl methods
13218////////////////////////////////////////////////////////////////////////////////
13219
13220/**
13221 * Passes collected guest statistics to performance collector object
13222 */
13223STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13224 ULONG aCpuKernel, ULONG aCpuIdle,
13225 ULONG aMemTotal, ULONG aMemFree,
13226 ULONG aMemBalloon, ULONG aMemShared,
13227 ULONG aMemCache, ULONG aPageTotal,
13228 ULONG aAllocVMM, ULONG aFreeVMM,
13229 ULONG aBalloonedVMM, ULONG aSharedVMM,
13230 ULONG aVmNetRx, ULONG aVmNetTx)
13231{
13232#ifdef VBOX_WITH_RESOURCE_USAGE_API
13233 if (mCollectorGuest)
13234 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13235 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13236 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13237 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13238
13239 return S_OK;
13240#else
13241 NOREF(aValidStats);
13242 NOREF(aCpuUser);
13243 NOREF(aCpuKernel);
13244 NOREF(aCpuIdle);
13245 NOREF(aMemTotal);
13246 NOREF(aMemFree);
13247 NOREF(aMemBalloon);
13248 NOREF(aMemShared);
13249 NOREF(aMemCache);
13250 NOREF(aPageTotal);
13251 NOREF(aAllocVMM);
13252 NOREF(aFreeVMM);
13253 NOREF(aBalloonedVMM);
13254 NOREF(aSharedVMM);
13255 NOREF(aVmNetRx);
13256 NOREF(aVmNetTx);
13257 return E_NOTIMPL;
13258#endif
13259}
13260
13261/**
13262 * @note Locks this object for writing.
13263 */
13264STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13265{
13266 AutoCaller autoCaller(this);
13267 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13268
13269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13270
13271 mRemoveSavedState = aRemove;
13272
13273 return S_OK;
13274}
13275
13276/**
13277 * @note Locks the same as #setMachineState() does.
13278 */
13279STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13280{
13281 return setMachineState(aMachineState);
13282}
13283
13284/**
13285 * @note Locks this object for writing.
13286 */
13287STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13288{
13289 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13290 AutoCaller autoCaller(this);
13291 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13292
13293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13294
13295 if (mData->mSession.mState != SessionState_Locked)
13296 return VBOX_E_INVALID_OBJECT_STATE;
13297
13298 if (!mData->mSession.mProgress.isNull())
13299 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13300
13301 /* If we didn't reference the NAT network service yet, add a reference to
13302 * force a start */
13303 if (miNATNetworksStarted < 1)
13304 {
13305 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
13306 {
13307 NetworkAttachmentType_T type;
13308 HRESULT hrc;
13309 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13310 if ( SUCCEEDED(hrc)
13311 && type == NetworkAttachmentType_NATNetwork)
13312 {
13313 Bstr name;
13314 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13315 if (SUCCEEDED(hrc))
13316 {
13317 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13318 mUserData->s.strName.c_str(), name.raw()));
13319 mPeer->lockHandle()->unlockWrite();
13320 mParent->i_natNetworkRefInc(name.raw());
13321#ifdef RT_LOCK_STRICT
13322 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13323#else
13324 mPeer->lockHandle()->lockWrite();
13325#endif
13326 }
13327 }
13328 }
13329 miNATNetworksStarted++;
13330 }
13331
13332 LogFlowThisFunc(("returns S_OK.\n"));
13333 return S_OK;
13334}
13335
13336/**
13337 * @note Locks this object for writing.
13338 */
13339STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13340{
13341 AutoCaller autoCaller(this);
13342 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13343
13344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13345
13346 if (mData->mSession.mState != SessionState_Locked)
13347 return VBOX_E_INVALID_OBJECT_STATE;
13348
13349 /* Finalize the LaunchVMProcess progress object. */
13350 if (mData->mSession.mProgress)
13351 {
13352 mData->mSession.mProgress->i_notifyComplete((HRESULT)iResult);
13353 mData->mSession.mProgress.setNull();
13354 }
13355
13356 if (SUCCEEDED((HRESULT)iResult))
13357 {
13358#ifdef VBOX_WITH_RESOURCE_USAGE_API
13359 /* The VM has been powered up successfully, so it makes sense
13360 * now to offer the performance metrics for a running machine
13361 * object. Doing it earlier wouldn't be safe. */
13362 registerMetrics(mParent->i_performanceCollector(), mPeer,
13363 mData->mSession.mPID);
13364#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13365 }
13366
13367 return S_OK;
13368}
13369
13370/**
13371 * @note Locks this object for writing.
13372 */
13373STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13374{
13375 LogFlowThisFuncEnter();
13376
13377 CheckComArgOutPointerValid(aProgress);
13378
13379 AutoCaller autoCaller(this);
13380 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13381
13382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13383
13384 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13385 E_FAIL);
13386
13387 /* create a progress object to track operation completion */
13388 ComObjPtr<Progress> pProgress;
13389 pProgress.createObject();
13390 pProgress->init(getVirtualBox(),
13391 static_cast<IMachine *>(this) /* aInitiator */,
13392 Bstr(tr("Stopping the virtual machine")).raw(),
13393 FALSE /* aCancelable */);
13394
13395 /* fill in the console task data */
13396 mConsoleTaskData.mLastState = mData->mMachineState;
13397 mConsoleTaskData.mProgress = pProgress;
13398
13399 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13400 setMachineState(MachineState_Stopping);
13401
13402 pProgress.queryInterfaceTo(aProgress);
13403
13404 return S_OK;
13405}
13406
13407/**
13408 * @note Locks this object for writing.
13409 */
13410STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13411{
13412 LogFlowThisFuncEnter();
13413
13414 AutoCaller autoCaller(this);
13415 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13416
13417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13418
13419 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13420 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13421 && mConsoleTaskData.mLastState != MachineState_Null,
13422 E_FAIL);
13423
13424 /*
13425 * On failure, set the state to the state we had when BeginPoweringDown()
13426 * was called (this is expected by Console::PowerDown() and the associated
13427 * task). On success the VM process already changed the state to
13428 * MachineState_PoweredOff, so no need to do anything.
13429 */
13430 if (FAILED(iResult))
13431 setMachineState(mConsoleTaskData.mLastState);
13432
13433 /* notify the progress object about operation completion */
13434 Assert(mConsoleTaskData.mProgress);
13435 if (SUCCEEDED(iResult))
13436 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13437 else
13438 {
13439 Utf8Str strErrMsg(aErrMsg);
13440 if (strErrMsg.length())
13441 mConsoleTaskData.mProgress->i_notifyComplete(iResult,
13442 COM_IIDOF(ISession),
13443 getComponentName(),
13444 strErrMsg.c_str());
13445 else
13446 mConsoleTaskData.mProgress->i_notifyComplete(iResult);
13447 }
13448
13449 /* clear out the temporary saved state data */
13450 mConsoleTaskData.mLastState = MachineState_Null;
13451 mConsoleTaskData.mProgress.setNull();
13452
13453 LogFlowThisFuncLeave();
13454 return S_OK;
13455}
13456
13457
13458/**
13459 * Goes through the USB filters of the given machine to see if the given
13460 * device matches any filter or not.
13461 *
13462 * @note Locks the same as USBController::hasMatchingFilter() does.
13463 */
13464STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13465 BOOL *aMatched,
13466 ULONG *aMaskedIfs)
13467{
13468 LogFlowThisFunc(("\n"));
13469
13470 CheckComArgNotNull(aUSBDevice);
13471 CheckComArgOutPointerValid(aMatched);
13472
13473 AutoCaller autoCaller(this);
13474 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13475
13476#ifdef VBOX_WITH_USB
13477 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
13478#else
13479 NOREF(aUSBDevice);
13480 NOREF(aMaskedIfs);
13481 *aMatched = FALSE;
13482#endif
13483
13484 return S_OK;
13485}
13486
13487/**
13488 * @note Locks the same as Host::captureUSBDevice() does.
13489 */
13490STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13491{
13492 LogFlowThisFunc(("\n"));
13493
13494 AutoCaller autoCaller(this);
13495 AssertComRCReturnRC(autoCaller.rc());
13496
13497#ifdef VBOX_WITH_USB
13498 /* if captureDeviceForVM() fails, it must have set extended error info */
13499 clearError();
13500 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13501 if (FAILED(rc)) return rc;
13502
13503 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13504 AssertReturn(service, E_FAIL);
13505 return service->captureDeviceForVM(this, Guid(aId).ref());
13506#else
13507 NOREF(aId);
13508 return E_NOTIMPL;
13509#endif
13510}
13511
13512/**
13513 * @note Locks the same as Host::detachUSBDevice() does.
13514 */
13515STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13516{
13517 LogFlowThisFunc(("\n"));
13518
13519 AutoCaller autoCaller(this);
13520 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13521
13522#ifdef VBOX_WITH_USB
13523 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13524 AssertReturn(service, E_FAIL);
13525 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13526#else
13527 NOREF(aId);
13528 NOREF(aDone);
13529 return E_NOTIMPL;
13530#endif
13531}
13532
13533/**
13534 * Inserts all machine filters to the USB proxy service and then calls
13535 * Host::autoCaptureUSBDevices().
13536 *
13537 * Called by Console from the VM process upon VM startup.
13538 *
13539 * @note Locks what called methods lock.
13540 */
13541STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13542{
13543 LogFlowThisFunc(("\n"));
13544
13545 AutoCaller autoCaller(this);
13546 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13547
13548#ifdef VBOX_WITH_USB
13549 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13550 AssertComRC(rc);
13551 NOREF(rc);
13552
13553 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13554 AssertReturn(service, E_FAIL);
13555 return service->autoCaptureDevicesForVM(this);
13556#else
13557 return S_OK;
13558#endif
13559}
13560
13561/**
13562 * Removes all machine filters from the USB proxy service and then calls
13563 * Host::detachAllUSBDevices().
13564 *
13565 * Called by Console from the VM process upon normal VM termination or by
13566 * SessionMachine::uninit() upon abnormal VM termination (from under the
13567 * Machine/SessionMachine lock).
13568 *
13569 * @note Locks what called methods lock.
13570 */
13571STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13572{
13573 LogFlowThisFunc(("\n"));
13574
13575 AutoCaller autoCaller(this);
13576 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13577
13578#ifdef VBOX_WITH_USB
13579 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13580 AssertComRC(rc);
13581 NOREF(rc);
13582
13583 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13584 AssertReturn(service, E_FAIL);
13585 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13586#else
13587 NOREF(aDone);
13588 return S_OK;
13589#endif
13590}
13591
13592/**
13593 * @note Locks this object for writing.
13594 */
13595STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13596 IProgress **aProgress)
13597{
13598 LogFlowThisFuncEnter();
13599
13600 AssertReturn(aSession, E_INVALIDARG);
13601 AssertReturn(aProgress, E_INVALIDARG);
13602
13603 AutoCaller autoCaller(this);
13604
13605 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13606 /*
13607 * We don't assert below because it might happen that a non-direct session
13608 * informs us it is closed right after we've been uninitialized -- it's ok.
13609 */
13610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13611
13612 /* get IInternalSessionControl interface */
13613 ComPtr<IInternalSessionControl> control(aSession);
13614
13615 ComAssertRet(!control.isNull(), E_INVALIDARG);
13616
13617 /* Creating a Progress object requires the VirtualBox lock, and
13618 * thus locking it here is required by the lock order rules. */
13619 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13620
13621 if (control == mData->mSession.mDirectControl)
13622 {
13623 ComAssertRet(aProgress, E_POINTER);
13624
13625 /* The direct session is being normally closed by the client process
13626 * ----------------------------------------------------------------- */
13627
13628 /* go to the closing state (essential for all open*Session() calls and
13629 * for #checkForDeath()) */
13630 Assert(mData->mSession.mState == SessionState_Locked);
13631 mData->mSession.mState = SessionState_Unlocking;
13632
13633 /* set direct control to NULL to release the remote instance */
13634 mData->mSession.mDirectControl.setNull();
13635 LogFlowThisFunc(("Direct control is set to NULL\n"));
13636
13637 if (mData->mSession.mProgress)
13638 {
13639 /* finalize the progress, someone might wait if a frontend
13640 * closes the session before powering on the VM. */
13641 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13642 COM_IIDOF(ISession),
13643 getComponentName(),
13644 tr("The VM session was closed before any attempt to power it on"));
13645 mData->mSession.mProgress.setNull();
13646 }
13647
13648 /* Create the progress object the client will use to wait until
13649 * #checkForDeath() is called to uninitialize this session object after
13650 * it releases the IPC semaphore.
13651 * Note! Because we're "reusing" mProgress here, this must be a proxy
13652 * object just like for LaunchVMProcess. */
13653 Assert(mData->mSession.mProgress.isNull());
13654 ComObjPtr<ProgressProxy> progress;
13655 progress.createObject();
13656 ComPtr<IUnknown> pPeer(mPeer);
13657 progress->init(mParent, pPeer,
13658 Bstr(tr("Closing session")).raw(),
13659 FALSE /* aCancelable */);
13660 progress.queryInterfaceTo(aProgress);
13661 mData->mSession.mProgress = progress;
13662 }
13663 else
13664 {
13665 /* the remote session is being normally closed */
13666 Data::Session::RemoteControlList::iterator it =
13667 mData->mSession.mRemoteControls.begin();
13668 while (it != mData->mSession.mRemoteControls.end())
13669 {
13670 if (control == *it)
13671 break;
13672 ++it;
13673 }
13674 BOOL found = it != mData->mSession.mRemoteControls.end();
13675 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13676 E_INVALIDARG);
13677 // This MUST be erase(it), not remove(*it) as the latter triggers a
13678 // very nasty use after free due to the place where the value "lives".
13679 mData->mSession.mRemoteControls.erase(it);
13680 }
13681
13682 /* signal the client watcher thread, because the client is going away */
13683 mParent->i_updateClientWatcher();
13684
13685 LogFlowThisFuncLeave();
13686 return S_OK;
13687}
13688
13689/**
13690 * @note Locks this object for writing.
13691 */
13692STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13693{
13694 LogFlowThisFuncEnter();
13695
13696 CheckComArgOutPointerValid(aProgress);
13697 CheckComArgOutPointerValid(aStateFilePath);
13698
13699 AutoCaller autoCaller(this);
13700 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13701
13702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13703
13704 AssertReturn( mData->mMachineState == MachineState_Paused
13705 && mConsoleTaskData.mLastState == MachineState_Null
13706 && mConsoleTaskData.strStateFilePath.isEmpty(),
13707 E_FAIL);
13708
13709 /* create a progress object to track operation completion */
13710 ComObjPtr<Progress> pProgress;
13711 pProgress.createObject();
13712 pProgress->init(getVirtualBox(),
13713 static_cast<IMachine *>(this) /* aInitiator */,
13714 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13715 FALSE /* aCancelable */);
13716
13717 Utf8Str strStateFilePath;
13718 /* stateFilePath is null when the machine is not running */
13719 if (mData->mMachineState == MachineState_Paused)
13720 composeSavedStateFilename(strStateFilePath);
13721
13722 /* fill in the console task data */
13723 mConsoleTaskData.mLastState = mData->mMachineState;
13724 mConsoleTaskData.strStateFilePath = strStateFilePath;
13725 mConsoleTaskData.mProgress = pProgress;
13726
13727 /* set the state to Saving (this is expected by Console::SaveState()) */
13728 setMachineState(MachineState_Saving);
13729
13730 strStateFilePath.cloneTo(aStateFilePath);
13731 pProgress.queryInterfaceTo(aProgress);
13732
13733 return S_OK;
13734}
13735
13736/**
13737 * @note Locks mParent + this object for writing.
13738 */
13739STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13740{
13741 LogFlowThisFunc(("\n"));
13742
13743 AutoCaller autoCaller(this);
13744 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13745
13746 /* endSavingState() need mParent lock */
13747 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13748
13749 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13750 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13751 && mConsoleTaskData.mLastState != MachineState_Null
13752 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13753 E_FAIL);
13754
13755 /*
13756 * On failure, set the state to the state we had when BeginSavingState()
13757 * was called (this is expected by Console::SaveState() and the associated
13758 * task). On success the VM process already changed the state to
13759 * MachineState_Saved, so no need to do anything.
13760 */
13761 if (FAILED(iResult))
13762 setMachineState(mConsoleTaskData.mLastState);
13763
13764 return endSavingState(iResult, aErrMsg);
13765}
13766
13767/**
13768 * @note Locks this object for writing.
13769 */
13770STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13771{
13772 LogFlowThisFunc(("\n"));
13773
13774 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13775
13776 AutoCaller autoCaller(this);
13777 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13778
13779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13780
13781 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13782 || mData->mMachineState == MachineState_Teleported
13783 || mData->mMachineState == MachineState_Aborted
13784 , E_FAIL); /** @todo setError. */
13785
13786 Utf8Str stateFilePathFull = aSavedStateFile;
13787 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13788 if (RT_FAILURE(vrc))
13789 return setError(VBOX_E_FILE_ERROR,
13790 tr("Invalid saved state file path '%ls' (%Rrc)"),
13791 aSavedStateFile,
13792 vrc);
13793
13794 mSSData->strStateFilePath = stateFilePathFull;
13795
13796 /* The below setMachineState() will detect the state transition and will
13797 * update the settings file */
13798
13799 return setMachineState(MachineState_Saved);
13800}
13801
13802STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13803 ComSafeArrayOut(BSTR, aValues),
13804 ComSafeArrayOut(LONG64, aTimestamps),
13805 ComSafeArrayOut(BSTR, aFlags))
13806{
13807 LogFlowThisFunc(("\n"));
13808
13809#ifdef VBOX_WITH_GUEST_PROPS
13810 using namespace guestProp;
13811
13812 AutoCaller autoCaller(this);
13813 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13814
13815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13816
13817 CheckComArgOutSafeArrayPointerValid(aNames);
13818 CheckComArgOutSafeArrayPointerValid(aValues);
13819 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13820 CheckComArgOutSafeArrayPointerValid(aFlags);
13821
13822 size_t cEntries = mHWData->mGuestProperties.size();
13823 com::SafeArray<BSTR> names(cEntries);
13824 com::SafeArray<BSTR> values(cEntries);
13825 com::SafeArray<LONG64> timestamps(cEntries);
13826 com::SafeArray<BSTR> flags(cEntries);
13827 unsigned i = 0;
13828 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13829 it != mHWData->mGuestProperties.end();
13830 ++it)
13831 {
13832 char szFlags[MAX_FLAGS_LEN + 1];
13833 it->first.cloneTo(&names[i]);
13834 it->second.strValue.cloneTo(&values[i]);
13835 timestamps[i] = it->second.mTimestamp;
13836 /* If it is NULL, keep it NULL. */
13837 if (it->second.mFlags)
13838 {
13839 writeFlags(it->second.mFlags, szFlags);
13840 Bstr(szFlags).cloneTo(&flags[i]);
13841 }
13842 else
13843 flags[i] = NULL;
13844 ++i;
13845 }
13846 names.detachTo(ComSafeArrayOutArg(aNames));
13847 values.detachTo(ComSafeArrayOutArg(aValues));
13848 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13849 flags.detachTo(ComSafeArrayOutArg(aFlags));
13850 return S_OK;
13851#else
13852 ReturnComNotImplemented();
13853#endif
13854}
13855
13856STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13857 IN_BSTR aValue,
13858 LONG64 aTimestamp,
13859 IN_BSTR aFlags)
13860{
13861 LogFlowThisFunc(("\n"));
13862
13863#ifdef VBOX_WITH_GUEST_PROPS
13864 using namespace guestProp;
13865
13866 CheckComArgStrNotEmptyOrNull(aName);
13867 CheckComArgNotNull(aValue);
13868 CheckComArgNotNull(aFlags);
13869
13870 try
13871 {
13872 /*
13873 * Convert input up front.
13874 */
13875 Utf8Str utf8Name(aName);
13876 uint32_t fFlags = NILFLAG;
13877 if (aFlags)
13878 {
13879 Utf8Str utf8Flags(aFlags);
13880 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13881 AssertRCReturn(vrc, E_INVALIDARG);
13882 }
13883
13884 /*
13885 * Now grab the object lock, validate the state and do the update.
13886 */
13887 AutoCaller autoCaller(this);
13888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13889
13890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13891
13892 switch (mData->mMachineState)
13893 {
13894 case MachineState_Paused:
13895 case MachineState_Running:
13896 case MachineState_Teleporting:
13897 case MachineState_TeleportingPausedVM:
13898 case MachineState_LiveSnapshotting:
13899 case MachineState_DeletingSnapshotOnline:
13900 case MachineState_DeletingSnapshotPaused:
13901 case MachineState_Saving:
13902 case MachineState_Stopping:
13903 break;
13904
13905 default:
13906 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13907 VBOX_E_INVALID_VM_STATE);
13908 }
13909
13910 setModified(IsModified_MachineData);
13911 mHWData.backup();
13912
13913 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13914 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13915 if (it != mHWData->mGuestProperties.end())
13916 {
13917 if (!fDelete)
13918 {
13919 it->second.strValue = aValue;
13920 it->second.mTimestamp = aTimestamp;
13921 it->second.mFlags = fFlags;
13922 }
13923 else
13924 mHWData->mGuestProperties.erase(it);
13925
13926 mData->mGuestPropertiesModified = TRUE;
13927 }
13928 else if (!fDelete)
13929 {
13930 HWData::GuestProperty prop;
13931 prop.strValue = aValue;
13932 prop.mTimestamp = aTimestamp;
13933 prop.mFlags = fFlags;
13934
13935 mHWData->mGuestProperties[utf8Name] = prop;
13936 mData->mGuestPropertiesModified = TRUE;
13937 }
13938
13939 /*
13940 * Send a callback notification if appropriate
13941 */
13942 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13943 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13944 RTSTR_MAX,
13945 utf8Name.c_str(),
13946 RTSTR_MAX, NULL)
13947 )
13948 {
13949 alock.release();
13950
13951 mParent->i_onGuestPropertyChange(mData->mUuid,
13952 aName,
13953 aValue,
13954 aFlags);
13955 }
13956 }
13957 catch (...)
13958 {
13959 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13960 }
13961 return S_OK;
13962#else
13963 ReturnComNotImplemented();
13964#endif
13965}
13966
13967STDMETHODIMP SessionMachine::LockMedia()
13968{
13969 AutoCaller autoCaller(this);
13970 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13971
13972 AutoMultiWriteLock2 alock(this->lockHandle(),
13973 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13974
13975 AssertReturn( mData->mMachineState == MachineState_Starting
13976 || mData->mMachineState == MachineState_Restoring
13977 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13978
13979 clearError();
13980 alock.release();
13981 return lockMedia();
13982}
13983
13984STDMETHODIMP SessionMachine::UnlockMedia()
13985{
13986 unlockMedia();
13987 return S_OK;
13988}
13989
13990STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13991 IMediumAttachment **aNewAttachment)
13992{
13993 CheckComArgNotNull(aAttachment);
13994 CheckComArgOutPointerValid(aNewAttachment);
13995
13996 AutoCaller autoCaller(this);
13997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13998
13999 // request the host lock first, since might be calling Host methods for getting host drives;
14000 // next, protect the media tree all the while we're in here, as well as our member variables
14001 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14002 this->lockHandle(),
14003 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14004
14005 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
14006
14007 Bstr ctrlName;
14008 LONG lPort;
14009 LONG lDevice;
14010 bool fTempEject;
14011 {
14012 AutoCaller autoAttachCaller(this);
14013 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14014
14015 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14016
14017 /* Need to query the details first, as the IMediumAttachment reference
14018 * might be to the original settings, which we are going to change. */
14019 ctrlName = pAttach->i_getControllerName();
14020 lPort = pAttach->i_getPort();
14021 lDevice = pAttach->i_getDevice();
14022 fTempEject = pAttach->i_getTempEject();
14023 }
14024
14025 if (!fTempEject)
14026 {
14027 /* Remember previously mounted medium. The medium before taking the
14028 * backup is not necessarily the same thing. */
14029 ComObjPtr<Medium> oldmedium;
14030 oldmedium = pAttach->i_getMedium();
14031
14032 setModified(IsModified_Storage);
14033 mMediaData.backup();
14034
14035 // The backup operation makes the pAttach reference point to the
14036 // old settings. Re-get the correct reference.
14037 pAttach = findAttachment(mMediaData->mAttachments,
14038 ctrlName.raw(),
14039 lPort,
14040 lDevice);
14041
14042 {
14043 AutoCaller autoAttachCaller(this);
14044 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14045
14046 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14047 if (!oldmedium.isNull())
14048 oldmedium->i_removeBackReference(mData->mUuid);
14049
14050 pAttach->i_updateMedium(NULL);
14051 pAttach->i_updateEjected();
14052 }
14053
14054 setModified(IsModified_Storage);
14055 }
14056 else
14057 {
14058 {
14059 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14060 pAttach->i_updateEjected();
14061 }
14062 }
14063
14064 pAttach.queryInterfaceTo(aNewAttachment);
14065
14066 return S_OK;
14067}
14068
14069// public methods only for internal purposes
14070/////////////////////////////////////////////////////////////////////////////
14071
14072#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14073/**
14074 * Called from the client watcher thread to check for expected or unexpected
14075 * death of the client process that has a direct session to this machine.
14076 *
14077 * On Win32 and on OS/2, this method is called only when we've got the
14078 * mutex (i.e. the client has either died or terminated normally) so it always
14079 * returns @c true (the client is terminated, the session machine is
14080 * uninitialized).
14081 *
14082 * On other platforms, the method returns @c true if the client process has
14083 * terminated normally or abnormally and the session machine was uninitialized,
14084 * and @c false if the client process is still alive.
14085 *
14086 * @note Locks this object for writing.
14087 */
14088bool SessionMachine::checkForDeath()
14089{
14090 Uninit::Reason reason;
14091 bool terminated = false;
14092
14093 /* Enclose autoCaller with a block because calling uninit() from under it
14094 * will deadlock. */
14095 {
14096 AutoCaller autoCaller(this);
14097 if (!autoCaller.isOk())
14098 {
14099 /* return true if not ready, to cause the client watcher to exclude
14100 * the corresponding session from watching */
14101 LogFlowThisFunc(("Already uninitialized!\n"));
14102 return true;
14103 }
14104
14105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14106
14107 /* Determine the reason of death: if the session state is Closing here,
14108 * everything is fine. Otherwise it means that the client did not call
14109 * OnSessionEnd() before it released the IPC semaphore. This may happen
14110 * either because the client process has abnormally terminated, or
14111 * because it simply forgot to call ISession::Close() before exiting. We
14112 * threat the latter also as an abnormal termination (see
14113 * Session::uninit() for details). */
14114 reason = mData->mSession.mState == SessionState_Unlocking ?
14115 Uninit::Normal :
14116 Uninit::Abnormal;
14117
14118 if (mClientToken)
14119 terminated = mClientToken->release();
14120 } /* AutoCaller block */
14121
14122 if (terminated)
14123 uninit(reason);
14124
14125 return terminated;
14126}
14127
14128void SessionMachine::getTokenId(Utf8Str &strTokenId)
14129{
14130 LogFlowThisFunc(("\n"));
14131
14132 strTokenId.setNull();
14133
14134 AutoCaller autoCaller(this);
14135 AssertComRCReturnVoid(autoCaller.rc());
14136
14137 Assert(mClientToken);
14138 if (mClientToken)
14139 mClientToken->getId(strTokenId);
14140}
14141#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14142IToken *SessionMachine::getToken()
14143{
14144 LogFlowThisFunc(("\n"));
14145
14146 AutoCaller autoCaller(this);
14147 AssertComRCReturn(autoCaller.rc(), NULL);
14148
14149 Assert(mClientToken);
14150 if (mClientToken)
14151 return mClientToken->getToken();
14152 else
14153 return NULL;
14154}
14155#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14156
14157Machine::ClientToken *SessionMachine::getClientToken()
14158{
14159 LogFlowThisFunc(("\n"));
14160
14161 AutoCaller autoCaller(this);
14162 AssertComRCReturn(autoCaller.rc(), NULL);
14163
14164 return mClientToken;
14165}
14166
14167
14168/**
14169 * @note Locks this object for reading.
14170 */
14171HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14172{
14173 LogFlowThisFunc(("\n"));
14174
14175 AutoCaller autoCaller(this);
14176 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14177
14178 ComPtr<IInternalSessionControl> directControl;
14179 {
14180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14181 directControl = mData->mSession.mDirectControl;
14182 }
14183
14184 /* ignore notifications sent after #OnSessionEnd() is called */
14185 if (!directControl)
14186 return S_OK;
14187
14188 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14189}
14190
14191/**
14192 * @note Locks this object for reading.
14193 */
14194HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14195 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14196{
14197 LogFlowThisFunc(("\n"));
14198
14199 AutoCaller autoCaller(this);
14200 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14201
14202 ComPtr<IInternalSessionControl> directControl;
14203 {
14204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14205 directControl = mData->mSession.mDirectControl;
14206 }
14207
14208 /* ignore notifications sent after #OnSessionEnd() is called */
14209 if (!directControl)
14210 return S_OK;
14211 /*
14212 * instead acting like callback we ask IVirtualBox deliver corresponding event
14213 */
14214
14215 mParent->i_onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14216 return S_OK;
14217}
14218
14219/**
14220 * @note Locks this object for reading.
14221 */
14222HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14223{
14224 LogFlowThisFunc(("\n"));
14225
14226 AutoCaller autoCaller(this);
14227 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14228
14229 ComPtr<IInternalSessionControl> directControl;
14230 {
14231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14232 directControl = mData->mSession.mDirectControl;
14233 }
14234
14235 /* ignore notifications sent after #OnSessionEnd() is called */
14236 if (!directControl)
14237 return S_OK;
14238
14239 return directControl->OnSerialPortChange(serialPort);
14240}
14241
14242/**
14243 * @note Locks this object for reading.
14244 */
14245HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14246{
14247 LogFlowThisFunc(("\n"));
14248
14249 AutoCaller autoCaller(this);
14250 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14251
14252 ComPtr<IInternalSessionControl> directControl;
14253 {
14254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14255 directControl = mData->mSession.mDirectControl;
14256 }
14257
14258 /* ignore notifications sent after #OnSessionEnd() is called */
14259 if (!directControl)
14260 return S_OK;
14261
14262 return directControl->OnParallelPortChange(parallelPort);
14263}
14264
14265/**
14266 * @note Locks this object for reading.
14267 */
14268HRESULT SessionMachine::onStorageControllerChange()
14269{
14270 LogFlowThisFunc(("\n"));
14271
14272 AutoCaller autoCaller(this);
14273 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14274
14275 ComPtr<IInternalSessionControl> directControl;
14276 {
14277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14278 directControl = mData->mSession.mDirectControl;
14279 }
14280
14281 /* ignore notifications sent after #OnSessionEnd() is called */
14282 if (!directControl)
14283 return S_OK;
14284
14285 return directControl->OnStorageControllerChange();
14286}
14287
14288/**
14289 * @note Locks this object for reading.
14290 */
14291HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14292{
14293 LogFlowThisFunc(("\n"));
14294
14295 AutoCaller autoCaller(this);
14296 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14297
14298 ComPtr<IInternalSessionControl> directControl;
14299 {
14300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14301 directControl = mData->mSession.mDirectControl;
14302 }
14303
14304 /* ignore notifications sent after #OnSessionEnd() is called */
14305 if (!directControl)
14306 return S_OK;
14307
14308 return directControl->OnMediumChange(aAttachment, aForce);
14309}
14310
14311/**
14312 * @note Locks this object for reading.
14313 */
14314HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14315{
14316 LogFlowThisFunc(("\n"));
14317
14318 AutoCaller autoCaller(this);
14319 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14320
14321 ComPtr<IInternalSessionControl> directControl;
14322 {
14323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14324 directControl = mData->mSession.mDirectControl;
14325 }
14326
14327 /* ignore notifications sent after #OnSessionEnd() is called */
14328 if (!directControl)
14329 return S_OK;
14330
14331 return directControl->OnCPUChange(aCPU, aRemove);
14332}
14333
14334HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14335{
14336 LogFlowThisFunc(("\n"));
14337
14338 AutoCaller autoCaller(this);
14339 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14340
14341 ComPtr<IInternalSessionControl> directControl;
14342 {
14343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14344 directControl = mData->mSession.mDirectControl;
14345 }
14346
14347 /* ignore notifications sent after #OnSessionEnd() is called */
14348 if (!directControl)
14349 return S_OK;
14350
14351 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14352}
14353
14354/**
14355 * @note Locks this object for reading.
14356 */
14357HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14358{
14359 LogFlowThisFunc(("\n"));
14360
14361 AutoCaller autoCaller(this);
14362 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14363
14364 ComPtr<IInternalSessionControl> directControl;
14365 {
14366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14367 directControl = mData->mSession.mDirectControl;
14368 }
14369
14370 /* ignore notifications sent after #OnSessionEnd() is called */
14371 if (!directControl)
14372 return S_OK;
14373
14374 return directControl->OnVRDEServerChange(aRestart);
14375}
14376
14377/**
14378 * @note Locks this object for reading.
14379 */
14380HRESULT SessionMachine::onVideoCaptureChange()
14381{
14382 LogFlowThisFunc(("\n"));
14383
14384 AutoCaller autoCaller(this);
14385 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14386
14387 ComPtr<IInternalSessionControl> directControl;
14388 {
14389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14390 directControl = mData->mSession.mDirectControl;
14391 }
14392
14393 /* ignore notifications sent after #OnSessionEnd() is called */
14394 if (!directControl)
14395 return S_OK;
14396
14397 return directControl->OnVideoCaptureChange();
14398}
14399
14400/**
14401 * @note Locks this object for reading.
14402 */
14403HRESULT SessionMachine::onUSBControllerChange()
14404{
14405 LogFlowThisFunc(("\n"));
14406
14407 AutoCaller autoCaller(this);
14408 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14409
14410 ComPtr<IInternalSessionControl> directControl;
14411 {
14412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14413 directControl = mData->mSession.mDirectControl;
14414 }
14415
14416 /* ignore notifications sent after #OnSessionEnd() is called */
14417 if (!directControl)
14418 return S_OK;
14419
14420 return directControl->OnUSBControllerChange();
14421}
14422
14423/**
14424 * @note Locks this object for reading.
14425 */
14426HRESULT SessionMachine::onSharedFolderChange()
14427{
14428 LogFlowThisFunc(("\n"));
14429
14430 AutoCaller autoCaller(this);
14431 AssertComRCReturnRC(autoCaller.rc());
14432
14433 ComPtr<IInternalSessionControl> directControl;
14434 {
14435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14436 directControl = mData->mSession.mDirectControl;
14437 }
14438
14439 /* ignore notifications sent after #OnSessionEnd() is called */
14440 if (!directControl)
14441 return S_OK;
14442
14443 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14444}
14445
14446/**
14447 * @note Locks this object for reading.
14448 */
14449HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14450{
14451 LogFlowThisFunc(("\n"));
14452
14453 AutoCaller autoCaller(this);
14454 AssertComRCReturnRC(autoCaller.rc());
14455
14456 ComPtr<IInternalSessionControl> directControl;
14457 {
14458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14459 directControl = mData->mSession.mDirectControl;
14460 }
14461
14462 /* ignore notifications sent after #OnSessionEnd() is called */
14463 if (!directControl)
14464 return S_OK;
14465
14466 return directControl->OnClipboardModeChange(aClipboardMode);
14467}
14468
14469/**
14470 * @note Locks this object for reading.
14471 */
14472HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14473{
14474 LogFlowThisFunc(("\n"));
14475
14476 AutoCaller autoCaller(this);
14477 AssertComRCReturnRC(autoCaller.rc());
14478
14479 ComPtr<IInternalSessionControl> directControl;
14480 {
14481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14482 directControl = mData->mSession.mDirectControl;
14483 }
14484
14485 /* ignore notifications sent after #OnSessionEnd() is called */
14486 if (!directControl)
14487 return S_OK;
14488
14489 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14490}
14491
14492/**
14493 * @note Locks this object for reading.
14494 */
14495HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14496{
14497 LogFlowThisFunc(("\n"));
14498
14499 AutoCaller autoCaller(this);
14500 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14501
14502 ComPtr<IInternalSessionControl> directControl;
14503 {
14504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14505 directControl = mData->mSession.mDirectControl;
14506 }
14507
14508 /* ignore notifications sent after #OnSessionEnd() is called */
14509 if (!directControl)
14510 return S_OK;
14511
14512 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14513}
14514
14515/**
14516 * @note Locks this object for reading.
14517 */
14518HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14519{
14520 LogFlowThisFunc(("\n"));
14521
14522 AutoCaller autoCaller(this);
14523 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14524
14525 ComPtr<IInternalSessionControl> directControl;
14526 {
14527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14528 directControl = mData->mSession.mDirectControl;
14529 }
14530
14531 /* ignore notifications sent after #OnSessionEnd() is called */
14532 if (!directControl)
14533 return S_OK;
14534
14535 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14536}
14537
14538/**
14539 * Returns @c true if this machine's USB controller reports it has a matching
14540 * filter for the given USB device and @c false otherwise.
14541 *
14542 * @note locks this object for reading.
14543 */
14544bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14545{
14546 AutoCaller autoCaller(this);
14547 /* silently return if not ready -- this method may be called after the
14548 * direct machine session has been called */
14549 if (!autoCaller.isOk())
14550 return false;
14551
14552#ifdef VBOX_WITH_USB
14553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14554
14555 switch (mData->mMachineState)
14556 {
14557 case MachineState_Starting:
14558 case MachineState_Restoring:
14559 case MachineState_TeleportingIn:
14560 case MachineState_Paused:
14561 case MachineState_Running:
14562 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14563 * elsewhere... */
14564 alock.release();
14565 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14566 default: break;
14567 }
14568#else
14569 NOREF(aDevice);
14570 NOREF(aMaskedIfs);
14571#endif
14572 return false;
14573}
14574
14575/**
14576 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14577 */
14578HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14579 IVirtualBoxErrorInfo *aError,
14580 ULONG aMaskedIfs)
14581{
14582 LogFlowThisFunc(("\n"));
14583
14584 AutoCaller autoCaller(this);
14585
14586 /* This notification may happen after the machine object has been
14587 * uninitialized (the session was closed), so don't assert. */
14588 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14589
14590 ComPtr<IInternalSessionControl> directControl;
14591 {
14592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14593 directControl = mData->mSession.mDirectControl;
14594 }
14595
14596 /* fail on notifications sent after #OnSessionEnd() is called, it is
14597 * expected by the caller */
14598 if (!directControl)
14599 return E_FAIL;
14600
14601 /* No locks should be held at this point. */
14602 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14603 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14604
14605 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14606}
14607
14608/**
14609 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14610 */
14611HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14612 IVirtualBoxErrorInfo *aError)
14613{
14614 LogFlowThisFunc(("\n"));
14615
14616 AutoCaller autoCaller(this);
14617
14618 /* This notification may happen after the machine object has been
14619 * uninitialized (the session was closed), so don't assert. */
14620 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14621
14622 ComPtr<IInternalSessionControl> directControl;
14623 {
14624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14625 directControl = mData->mSession.mDirectControl;
14626 }
14627
14628 /* fail on notifications sent after #OnSessionEnd() is called, it is
14629 * expected by the caller */
14630 if (!directControl)
14631 return E_FAIL;
14632
14633 /* No locks should be held at this point. */
14634 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14635 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14636
14637 return directControl->OnUSBDeviceDetach(aId, aError);
14638}
14639
14640// protected methods
14641/////////////////////////////////////////////////////////////////////////////
14642
14643/**
14644 * Helper method to finalize saving the state.
14645 *
14646 * @note Must be called from under this object's lock.
14647 *
14648 * @param aRc S_OK if the snapshot has been taken successfully
14649 * @param aErrMsg human readable error message for failure
14650 *
14651 * @note Locks mParent + this objects for writing.
14652 */
14653HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14654{
14655 LogFlowThisFuncEnter();
14656
14657 AutoCaller autoCaller(this);
14658 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14659
14660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14661
14662 HRESULT rc = S_OK;
14663
14664 if (SUCCEEDED(aRc))
14665 {
14666 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14667
14668 /* save all VM settings */
14669 rc = saveSettings(NULL);
14670 // no need to check whether VirtualBox.xml needs saving also since
14671 // we can't have a name change pending at this point
14672 }
14673 else
14674 {
14675 // delete the saved state file (it might have been already created);
14676 // we need not check whether this is shared with a snapshot here because
14677 // we certainly created this saved state file here anew
14678 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14679 }
14680
14681 /* notify the progress object about operation completion */
14682 Assert(mConsoleTaskData.mProgress);
14683 if (SUCCEEDED(aRc))
14684 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
14685 else
14686 {
14687 if (aErrMsg.length())
14688 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
14689 COM_IIDOF(ISession),
14690 getComponentName(),
14691 aErrMsg.c_str());
14692 else
14693 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
14694 }
14695
14696 /* clear out the temporary saved state data */
14697 mConsoleTaskData.mLastState = MachineState_Null;
14698 mConsoleTaskData.strStateFilePath.setNull();
14699 mConsoleTaskData.mProgress.setNull();
14700
14701 LogFlowThisFuncLeave();
14702 return rc;
14703}
14704
14705/**
14706 * Deletes the given file if it is no longer in use by either the current machine state
14707 * (if the machine is "saved") or any of the machine's snapshots.
14708 *
14709 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14710 * but is different for each SnapshotMachine. When calling this, the order of calling this
14711 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14712 * is therefore critical. I know, it's all rather messy.
14713 *
14714 * @param strStateFile
14715 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14716 */
14717void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14718 Snapshot *pSnapshotToIgnore)
14719{
14720 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14721 if ( (strStateFile.isNotEmpty())
14722 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14723 )
14724 // ... and it must also not be shared with other snapshots
14725 if ( !mData->mFirstSnapshot
14726 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14727 // this checks the SnapshotMachine's state file paths
14728 )
14729 RTFileDelete(strStateFile.c_str());
14730}
14731
14732/**
14733 * Locks the attached media.
14734 *
14735 * All attached hard disks are locked for writing and DVD/floppy are locked for
14736 * reading. Parents of attached hard disks (if any) are locked for reading.
14737 *
14738 * This method also performs accessibility check of all media it locks: if some
14739 * media is inaccessible, the method will return a failure and a bunch of
14740 * extended error info objects per each inaccessible medium.
14741 *
14742 * Note that this method is atomic: if it returns a success, all media are
14743 * locked as described above; on failure no media is locked at all (all
14744 * succeeded individual locks will be undone).
14745 *
14746 * The caller is responsible for doing the necessary state sanity checks.
14747 *
14748 * The locks made by this method must be undone by calling #unlockMedia() when
14749 * no more needed.
14750 */
14751HRESULT SessionMachine::lockMedia()
14752{
14753 AutoCaller autoCaller(this);
14754 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14755
14756 AutoMultiWriteLock2 alock(this->lockHandle(),
14757 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14758
14759 /* bail out if trying to lock things with already set up locking */
14760 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14761
14762 MultiResult mrc(S_OK);
14763
14764 /* Collect locking information for all medium objects attached to the VM. */
14765 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14766 it != mMediaData->mAttachments.end();
14767 ++it)
14768 {
14769 MediumAttachment* pAtt = *it;
14770 DeviceType_T devType = pAtt->i_getType();
14771 Medium *pMedium = pAtt->i_getMedium();
14772
14773 MediumLockList *pMediumLockList(new MediumLockList());
14774 // There can be attachments without a medium (floppy/dvd), and thus
14775 // it's impossible to create a medium lock list. It still makes sense
14776 // to have the empty medium lock list in the map in case a medium is
14777 // attached later.
14778 if (pMedium != NULL)
14779 {
14780 MediumType_T mediumType = pMedium->i_getType();
14781 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14782 || mediumType == MediumType_Shareable;
14783 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14784
14785 alock.release();
14786 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14787 !fIsReadOnlyLock /* fMediumLockWrite */,
14788 NULL,
14789 *pMediumLockList);
14790 alock.acquire();
14791 if (FAILED(mrc))
14792 {
14793 delete pMediumLockList;
14794 mData->mSession.mLockedMedia.Clear();
14795 break;
14796 }
14797 }
14798
14799 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14800 if (FAILED(rc))
14801 {
14802 mData->mSession.mLockedMedia.Clear();
14803 mrc = setError(rc,
14804 tr("Collecting locking information for all attached media failed"));
14805 break;
14806 }
14807 }
14808
14809 if (SUCCEEDED(mrc))
14810 {
14811 /* Now lock all media. If this fails, nothing is locked. */
14812 alock.release();
14813 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14814 alock.acquire();
14815 if (FAILED(rc))
14816 {
14817 mrc = setError(rc,
14818 tr("Locking of attached media failed"));
14819 }
14820 }
14821
14822 return mrc;
14823}
14824
14825/**
14826 * Undoes the locks made by by #lockMedia().
14827 */
14828void SessionMachine::unlockMedia()
14829{
14830 AutoCaller autoCaller(this);
14831 AssertComRCReturnVoid(autoCaller.rc());
14832
14833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14834
14835 /* we may be holding important error info on the current thread;
14836 * preserve it */
14837 ErrorInfoKeeper eik;
14838
14839 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14840 AssertComRC(rc);
14841}
14842
14843/**
14844 * Helper to change the machine state (reimplementation).
14845 *
14846 * @note Locks this object for writing.
14847 * @note This method must not call saveSettings or SaveSettings, otherwise
14848 * it can cause crashes in random places due to unexpectedly committing
14849 * the current settings. The caller is responsible for that. The call
14850 * to saveStateSettings is fine, because this method does not commit.
14851 */
14852HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14853{
14854 LogFlowThisFuncEnter();
14855 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14856
14857 AutoCaller autoCaller(this);
14858 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14859
14860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14861
14862 MachineState_T oldMachineState = mData->mMachineState;
14863
14864 AssertMsgReturn(oldMachineState != aMachineState,
14865 ("oldMachineState=%s, aMachineState=%s\n",
14866 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14867 E_FAIL);
14868
14869 HRESULT rc = S_OK;
14870
14871 int stsFlags = 0;
14872 bool deleteSavedState = false;
14873
14874 /* detect some state transitions */
14875
14876 if ( ( oldMachineState == MachineState_Saved
14877 && aMachineState == MachineState_Restoring)
14878 || ( ( oldMachineState == MachineState_PoweredOff
14879 || oldMachineState == MachineState_Teleported
14880 || oldMachineState == MachineState_Aborted
14881 )
14882 && ( aMachineState == MachineState_TeleportingIn
14883 || aMachineState == MachineState_Starting
14884 )
14885 )
14886 )
14887 {
14888 /* The EMT thread is about to start */
14889
14890 /* Nothing to do here for now... */
14891
14892 /// @todo NEWMEDIA don't let mDVDDrive and other children
14893 /// change anything when in the Starting/Restoring state
14894 }
14895 else if ( ( oldMachineState == MachineState_Running
14896 || oldMachineState == MachineState_Paused
14897 || oldMachineState == MachineState_Teleporting
14898 || oldMachineState == MachineState_LiveSnapshotting
14899 || oldMachineState == MachineState_Stuck
14900 || oldMachineState == MachineState_Starting
14901 || oldMachineState == MachineState_Stopping
14902 || oldMachineState == MachineState_Saving
14903 || oldMachineState == MachineState_Restoring
14904 || oldMachineState == MachineState_TeleportingPausedVM
14905 || oldMachineState == MachineState_TeleportingIn
14906 )
14907 && ( aMachineState == MachineState_PoweredOff
14908 || aMachineState == MachineState_Saved
14909 || aMachineState == MachineState_Teleported
14910 || aMachineState == MachineState_Aborted
14911 )
14912 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14913 * snapshot */
14914 && ( mConsoleTaskData.mSnapshot.isNull()
14915 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14916 )
14917 )
14918 {
14919 /* The EMT thread has just stopped, unlock attached media. Note that as
14920 * opposed to locking that is done from Console, we do unlocking here
14921 * because the VM process may have aborted before having a chance to
14922 * properly unlock all media it locked. */
14923
14924 unlockMedia();
14925 }
14926
14927 if (oldMachineState == MachineState_Restoring)
14928 {
14929 if (aMachineState != MachineState_Saved)
14930 {
14931 /*
14932 * delete the saved state file once the machine has finished
14933 * restoring from it (note that Console sets the state from
14934 * Restoring to Saved if the VM couldn't restore successfully,
14935 * to give the user an ability to fix an error and retry --
14936 * we keep the saved state file in this case)
14937 */
14938 deleteSavedState = true;
14939 }
14940 }
14941 else if ( oldMachineState == MachineState_Saved
14942 && ( aMachineState == MachineState_PoweredOff
14943 || aMachineState == MachineState_Aborted
14944 || aMachineState == MachineState_Teleported
14945 )
14946 )
14947 {
14948 /*
14949 * delete the saved state after Console::ForgetSavedState() is called
14950 * or if the VM process (owning a direct VM session) crashed while the
14951 * VM was Saved
14952 */
14953
14954 /// @todo (dmik)
14955 // Not sure that deleting the saved state file just because of the
14956 // client death before it attempted to restore the VM is a good
14957 // thing. But when it crashes we need to go to the Aborted state
14958 // which cannot have the saved state file associated... The only
14959 // way to fix this is to make the Aborted condition not a VM state
14960 // but a bool flag: i.e., when a crash occurs, set it to true and
14961 // change the state to PoweredOff or Saved depending on the
14962 // saved state presence.
14963
14964 deleteSavedState = true;
14965 mData->mCurrentStateModified = TRUE;
14966 stsFlags |= SaveSTS_CurStateModified;
14967 }
14968
14969 if ( aMachineState == MachineState_Starting
14970 || aMachineState == MachineState_Restoring
14971 || aMachineState == MachineState_TeleportingIn
14972 )
14973 {
14974 /* set the current state modified flag to indicate that the current
14975 * state is no more identical to the state in the
14976 * current snapshot */
14977 if (!mData->mCurrentSnapshot.isNull())
14978 {
14979 mData->mCurrentStateModified = TRUE;
14980 stsFlags |= SaveSTS_CurStateModified;
14981 }
14982 }
14983
14984 if (deleteSavedState)
14985 {
14986 if (mRemoveSavedState)
14987 {
14988 Assert(!mSSData->strStateFilePath.isEmpty());
14989
14990 // it is safe to delete the saved state file if ...
14991 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14992 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14993 // ... none of the snapshots share the saved state file
14994 )
14995 RTFileDelete(mSSData->strStateFilePath.c_str());
14996 }
14997
14998 mSSData->strStateFilePath.setNull();
14999 stsFlags |= SaveSTS_StateFilePath;
15000 }
15001
15002 /* redirect to the underlying peer machine */
15003 mPeer->setMachineState(aMachineState);
15004
15005 if ( aMachineState == MachineState_PoweredOff
15006 || aMachineState == MachineState_Teleported
15007 || aMachineState == MachineState_Aborted
15008 || aMachineState == MachineState_Saved)
15009 {
15010 /* the machine has stopped execution
15011 * (or the saved state file was adopted) */
15012 stsFlags |= SaveSTS_StateTimeStamp;
15013 }
15014
15015 if ( ( oldMachineState == MachineState_PoweredOff
15016 || oldMachineState == MachineState_Aborted
15017 || oldMachineState == MachineState_Teleported
15018 )
15019 && aMachineState == MachineState_Saved)
15020 {
15021 /* the saved state file was adopted */
15022 Assert(!mSSData->strStateFilePath.isEmpty());
15023 stsFlags |= SaveSTS_StateFilePath;
15024 }
15025
15026#ifdef VBOX_WITH_GUEST_PROPS
15027 if ( aMachineState == MachineState_PoweredOff
15028 || aMachineState == MachineState_Aborted
15029 || aMachineState == MachineState_Teleported)
15030 {
15031 /* Make sure any transient guest properties get removed from the
15032 * property store on shutdown. */
15033
15034 HWData::GuestPropertyMap::const_iterator it;
15035 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15036 if (!fNeedsSaving)
15037 for (it = mHWData->mGuestProperties.begin();
15038 it != mHWData->mGuestProperties.end(); ++it)
15039 if ( (it->second.mFlags & guestProp::TRANSIENT)
15040 || (it->second.mFlags & guestProp::TRANSRESET))
15041 {
15042 fNeedsSaving = true;
15043 break;
15044 }
15045 if (fNeedsSaving)
15046 {
15047 mData->mCurrentStateModified = TRUE;
15048 stsFlags |= SaveSTS_CurStateModified;
15049 }
15050 }
15051#endif
15052
15053 rc = saveStateSettings(stsFlags);
15054
15055 if ( ( oldMachineState != MachineState_PoweredOff
15056 && oldMachineState != MachineState_Aborted
15057 && oldMachineState != MachineState_Teleported
15058 )
15059 && ( aMachineState == MachineState_PoweredOff
15060 || aMachineState == MachineState_Aborted
15061 || aMachineState == MachineState_Teleported
15062 )
15063 )
15064 {
15065 /* we've been shut down for any reason */
15066 /* no special action so far */
15067 }
15068
15069 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15070 LogFlowThisFuncLeave();
15071 return rc;
15072}
15073
15074/**
15075 * Sends the current machine state value to the VM process.
15076 *
15077 * @note Locks this object for reading, then calls a client process.
15078 */
15079HRESULT SessionMachine::updateMachineStateOnClient()
15080{
15081 AutoCaller autoCaller(this);
15082 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15083
15084 ComPtr<IInternalSessionControl> directControl;
15085 {
15086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15087 AssertReturn(!!mData, E_FAIL);
15088 directControl = mData->mSession.mDirectControl;
15089
15090 /* directControl may be already set to NULL here in #OnSessionEnd()
15091 * called too early by the direct session process while there is still
15092 * some operation (like deleting the snapshot) in progress. The client
15093 * process in this case is waiting inside Session::close() for the
15094 * "end session" process object to complete, while #uninit() called by
15095 * #checkForDeath() on the Watcher thread is waiting for the pending
15096 * operation to complete. For now, we accept this inconsistent behavior
15097 * and simply do nothing here. */
15098
15099 if (mData->mSession.mState == SessionState_Unlocking)
15100 return S_OK;
15101
15102 AssertReturn(!directControl.isNull(), E_FAIL);
15103 }
15104
15105 return directControl->UpdateMachineState(mData->mMachineState);
15106}
Note: See TracBrowser for help on using the repository browser.

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