VirtualBox

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

Last change on this file since 50213 was 49983, checked in by vboxsync, 11 years ago

Devices/Graphics: VMware SVGA II compatible graphics emulation (2D only), including the associated small API and VBoxManage changes, contributed by trivirt AG.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 507.1 KB
Line 
1/* $Id: MachineImpl.cpp 49983 2013-12-19 12:23:17Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 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 mEmulatedUSBCardReaderEnabled = FALSE;
209
210 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
211 mCPUAttached[i] = false;
212
213 mIOCacheEnabled = true;
214 mIOCacheSize = 5; /* 5MB */
215
216 /* Maximum CPU execution cap by default. */
217 mCpuExecutionCap = 100;
218}
219
220Machine::HWData::~HWData()
221{
222}
223
224/////////////////////////////////////////////////////////////////////////////
225// Machine::HDData structure
226/////////////////////////////////////////////////////////////////////////////
227
228Machine::MediaData::MediaData()
229{
230}
231
232Machine::MediaData::~MediaData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param aOsType OS Type of this machine or NULL.
280 * @param aId UUID for the new machine.
281 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
282 *
283 * @return Success indicator. if not S_OK, the machine object is invalid
284 */
285HRESULT Machine::init(VirtualBox *aParent,
286 const Utf8Str &strConfigFile,
287 const Utf8Str &strName,
288 const StringsList &llGroups,
289 GuestOSType *aOsType,
290 const Guid &aId,
291 bool fForceOverwrite,
292 bool fDirectoryIncludesUUID)
293{
294 LogFlowThisFuncEnter();
295 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
296
297 /* Enclose the state transition NotReady->InInit->Ready */
298 AutoInitSpan autoInitSpan(this);
299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
300
301 HRESULT rc = initImpl(aParent, strConfigFile);
302 if (FAILED(rc)) return rc;
303
304 rc = tryCreateMachineConfigFile(fForceOverwrite);
305 if (FAILED(rc)) return rc;
306
307 if (SUCCEEDED(rc))
308 {
309 // create an empty machine config
310 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
311
312 rc = initDataAndChildObjects();
313 }
314
315 if (SUCCEEDED(rc))
316 {
317 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
318 mData->mAccessible = TRUE;
319
320 unconst(mData->mUuid) = aId;
321
322 mUserData->s.strName = strName;
323
324 mUserData->s.llGroups = llGroups;
325
326 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
327 // the "name sync" flag determines whether the machine directory gets renamed along
328 // with the machine file; say so if the settings file name is the same as the
329 // settings file parent directory (machine directory)
330 mUserData->s.fNameSync = isInOwnDir();
331
332 // initialize the default snapshots folder
333 rc = COMSETTER(SnapshotFolder)(NULL);
334 AssertComRC(rc);
335
336 if (aOsType)
337 {
338 /* Store OS type */
339 mUserData->s.strOsType = aOsType->i_id();
340
341 /* Apply BIOS defaults */
342 mBIOSSettings->i_applyDefaults(aOsType);
343
344 /* Apply network adapters defaults */
345 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
346 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
347
348 /* Apply serial port defaults */
349 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
350 mSerialPorts[slot]->i_applyDefaults(aOsType);
351
352 /* Let the OS type select 64-bit ness. */
353 mHWData->mLongMode = aOsType->i_is64Bit()
354 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
355 }
356
357 /* At this point the changing of the current state modification
358 * flag is allowed. */
359 allowStateModification();
360
361 /* commit all changes made during the initialization */
362 commit();
363 }
364
365 /* Confirm a successful initialization when it's the case */
366 if (SUCCEEDED(rc))
367 {
368 if (mData->mAccessible)
369 autoInitSpan.setSucceeded();
370 else
371 autoInitSpan.setLimited();
372 }
373
374 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
375 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
376 mData->mRegistered,
377 mData->mAccessible,
378 rc));
379
380 LogFlowThisFuncLeave();
381
382 return rc;
383}
384
385/**
386 * Initializes a new instance with data from machine XML (formerly Init_Registered).
387 * Gets called in two modes:
388 *
389 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
390 * UUID is specified and we mark the machine as "registered";
391 *
392 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
393 * and the machine remains unregistered until RegisterMachine() is called.
394 *
395 * @param aParent Associated parent object
396 * @param aConfigFile Local file system path to the VM settings file (can
397 * be relative to the VirtualBox config directory).
398 * @param aId UUID of the machine or NULL (see above).
399 *
400 * @return Success indicator. if not S_OK, the machine object is invalid
401 */
402HRESULT Machine::initFromSettings(VirtualBox *aParent,
403 const Utf8Str &strConfigFile,
404 const Guid *aId)
405{
406 LogFlowThisFuncEnter();
407 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
408
409 /* Enclose the state transition NotReady->InInit->Ready */
410 AutoInitSpan autoInitSpan(this);
411 AssertReturn(autoInitSpan.isOk(), E_FAIL);
412
413 HRESULT rc = initImpl(aParent, strConfigFile);
414 if (FAILED(rc)) return rc;
415
416 if (aId)
417 {
418 // loading a registered VM:
419 unconst(mData->mUuid) = *aId;
420 mData->mRegistered = TRUE;
421 // now load the settings from XML:
422 rc = registeredInit();
423 // this calls initDataAndChildObjects() and loadSettings()
424 }
425 else
426 {
427 // opening an unregistered VM (VirtualBox::OpenMachine()):
428 rc = initDataAndChildObjects();
429
430 if (SUCCEEDED(rc))
431 {
432 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
433 mData->mAccessible = TRUE;
434
435 try
436 {
437 // load and parse machine XML; this will throw on XML or logic errors
438 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
439
440 // reject VM UUID duplicates, they can happen if someone
441 // tries to register an already known VM config again
442 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
443 true /* fPermitInaccessible */,
444 false /* aDoSetError */,
445 NULL) != VBOX_E_OBJECT_NOT_FOUND)
446 {
447 throw setError(E_FAIL,
448 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
449 mData->m_strConfigFile.c_str());
450 }
451
452 // use UUID from machine config
453 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
454
455 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
456 NULL /* puuidRegistry */);
457 if (FAILED(rc)) throw rc;
458
459 /* At this point the changing of the current state modification
460 * flag is allowed. */
461 allowStateModification();
462
463 commit();
464 }
465 catch (HRESULT err)
466 {
467 /* we assume that error info is set by the thrower */
468 rc = err;
469 }
470 catch (...)
471 {
472 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
473 }
474 }
475 }
476
477 /* Confirm a successful initialization when it's the case */
478 if (SUCCEEDED(rc))
479 {
480 if (mData->mAccessible)
481 autoInitSpan.setSucceeded();
482 else
483 {
484 autoInitSpan.setLimited();
485
486 // uninit media from this machine's media registry, or else
487 // reloading the settings will fail
488 mParent->unregisterMachineMedia(getId());
489 }
490 }
491
492 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
493 "rc=%08X\n",
494 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
495 mData->mRegistered, mData->mAccessible, rc));
496
497 LogFlowThisFuncLeave();
498
499 return rc;
500}
501
502/**
503 * Initializes a new instance from a machine config that is already in memory
504 * (import OVF case). Since we are importing, the UUID in the machine
505 * config is ignored and we always generate a fresh one.
506 *
507 * @param strName Name for the new machine; this overrides what is specified in config and is used
508 * for the settings file as well.
509 * @param config Machine configuration loaded and parsed from XML.
510 *
511 * @return Success indicator. if not S_OK, the machine object is invalid
512 */
513HRESULT Machine::init(VirtualBox *aParent,
514 const Utf8Str &strName,
515 const settings::MachineConfigFile &config)
516{
517 LogFlowThisFuncEnter();
518
519 /* Enclose the state transition NotReady->InInit->Ready */
520 AutoInitSpan autoInitSpan(this);
521 AssertReturn(autoInitSpan.isOk(), E_FAIL);
522
523 Utf8Str strConfigFile;
524 aParent->getDefaultMachineFolder(strConfigFile);
525 strConfigFile.append(RTPATH_DELIMITER);
526 strConfigFile.append(strName);
527 strConfigFile.append(RTPATH_DELIMITER);
528 strConfigFile.append(strName);
529 strConfigFile.append(".vbox");
530
531 HRESULT rc = initImpl(aParent, strConfigFile);
532 if (FAILED(rc)) return rc;
533
534 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
535 if (FAILED(rc)) return rc;
536
537 rc = initDataAndChildObjects();
538
539 if (SUCCEEDED(rc))
540 {
541 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
542 mData->mAccessible = TRUE;
543
544 // create empty machine config for instance data
545 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
546
547 // generate fresh UUID, ignore machine config
548 unconst(mData->mUuid).create();
549
550 rc = loadMachineDataFromSettings(config,
551 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
552
553 // override VM name as well, it may be different
554 mUserData->s.strName = strName;
555
556 if (SUCCEEDED(rc))
557 {
558 /* At this point the changing of the current state modification
559 * flag is allowed. */
560 allowStateModification();
561
562 /* commit all changes made during the initialization */
563 commit();
564 }
565 }
566
567 /* Confirm a successful initialization when it's the case */
568 if (SUCCEEDED(rc))
569 {
570 if (mData->mAccessible)
571 autoInitSpan.setSucceeded();
572 else
573 {
574 autoInitSpan.setLimited();
575
576 // uninit media from this machine's media registry, or else
577 // reloading the settings will fail
578 mParent->unregisterMachineMedia(getId());
579 }
580 }
581
582 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
583 "rc=%08X\n",
584 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
585 mData->mRegistered, mData->mAccessible, rc));
586
587 LogFlowThisFuncLeave();
588
589 return rc;
590}
591
592/**
593 * Shared code between the various init() implementations.
594 * @param aParent
595 * @return
596 */
597HRESULT Machine::initImpl(VirtualBox *aParent,
598 const Utf8Str &strConfigFile)
599{
600 LogFlowThisFuncEnter();
601
602 AssertReturn(aParent, E_INVALIDARG);
603 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
604
605 HRESULT rc = S_OK;
606
607 /* share the parent weakly */
608 unconst(mParent) = aParent;
609
610 /* allocate the essential machine data structure (the rest will be
611 * allocated later by initDataAndChildObjects() */
612 mData.allocate();
613
614 /* memorize the config file name (as provided) */
615 mData->m_strConfigFile = strConfigFile;
616
617 /* get the full file name */
618 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
619 if (RT_FAILURE(vrc1))
620 return setError(VBOX_E_FILE_ERROR,
621 tr("Invalid machine settings file name '%s' (%Rrc)"),
622 strConfigFile.c_str(),
623 vrc1);
624
625 LogFlowThisFuncLeave();
626
627 return rc;
628}
629
630/**
631 * Tries to create a machine settings file in the path stored in the machine
632 * instance data. Used when a new machine is created to fail gracefully if
633 * the settings file could not be written (e.g. because machine dir is read-only).
634 * @return
635 */
636HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
637{
638 HRESULT rc = S_OK;
639
640 // when we create a new machine, we must be able to create the settings file
641 RTFILE f = NIL_RTFILE;
642 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
643 if ( RT_SUCCESS(vrc)
644 || vrc == VERR_SHARING_VIOLATION
645 )
646 {
647 if (RT_SUCCESS(vrc))
648 RTFileClose(f);
649 if (!fForceOverwrite)
650 rc = setError(VBOX_E_FILE_ERROR,
651 tr("Machine settings file '%s' already exists"),
652 mData->m_strConfigFileFull.c_str());
653 else
654 {
655 /* try to delete the config file, as otherwise the creation
656 * of a new settings file will fail. */
657 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
658 if (RT_FAILURE(vrc2))
659 rc = setError(VBOX_E_FILE_ERROR,
660 tr("Could not delete the existing settings file '%s' (%Rrc)"),
661 mData->m_strConfigFileFull.c_str(), vrc2);
662 }
663 }
664 else if ( vrc != VERR_FILE_NOT_FOUND
665 && vrc != VERR_PATH_NOT_FOUND
666 )
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Invalid machine settings file name '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(),
670 vrc);
671 return rc;
672}
673
674/**
675 * Initializes the registered machine by loading the settings file.
676 * This method is separated from #init() in order to make it possible to
677 * retry the operation after VirtualBox startup instead of refusing to
678 * startup the whole VirtualBox server in case if the settings file of some
679 * registered VM is invalid or inaccessible.
680 *
681 * @note Must be always called from this object's write lock
682 * (unless called from #init() that doesn't need any locking).
683 * @note Locks the mUSBController method for writing.
684 * @note Subclasses must not call this method.
685 */
686HRESULT Machine::registeredInit()
687{
688 AssertReturn(!isSessionMachine(), E_FAIL);
689 AssertReturn(!isSnapshotMachine(), E_FAIL);
690 AssertReturn(mData->mUuid.isValid(), E_FAIL);
691 AssertReturn(!mData->mAccessible, E_FAIL);
692
693 HRESULT rc = initDataAndChildObjects();
694
695 if (SUCCEEDED(rc))
696 {
697 /* Temporarily reset the registered flag in order to let setters
698 * potentially called from loadSettings() succeed (isMutable() used in
699 * all setters will return FALSE for a Machine instance if mRegistered
700 * is TRUE). */
701 mData->mRegistered = FALSE;
702
703 try
704 {
705 // load and parse machine XML; this will throw on XML or logic errors
706 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
707
708 if (mData->mUuid != mData->pMachineConfigFile->uuid)
709 throw setError(E_FAIL,
710 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
711 mData->pMachineConfigFile->uuid.raw(),
712 mData->m_strConfigFileFull.c_str(),
713 mData->mUuid.toString().c_str(),
714 mParent->settingsFilePath().c_str());
715
716 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
717 NULL /* const Guid *puuidRegistry */);
718 if (FAILED(rc)) throw rc;
719 }
720 catch (HRESULT err)
721 {
722 /* we assume that error info is set by the thrower */
723 rc = err;
724 }
725 catch (...)
726 {
727 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
728 }
729
730 /* Restore the registered flag (even on failure) */
731 mData->mRegistered = TRUE;
732 }
733
734 if (SUCCEEDED(rc))
735 {
736 /* Set mAccessible to TRUE only if we successfully locked and loaded
737 * the settings file */
738 mData->mAccessible = TRUE;
739
740 /* commit all changes made during loading the settings file */
741 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
742 /// @todo r=klaus for some reason the settings loading logic backs up
743 // the settings, and therefore a commit is needed. Should probably be changed.
744 }
745 else
746 {
747 /* If the machine is registered, then, instead of returning a
748 * failure, we mark it as inaccessible and set the result to
749 * success to give it a try later */
750
751 /* fetch the current error info */
752 mData->mAccessError = com::ErrorInfo();
753 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
754 mData->mUuid.raw(),
755 mData->mAccessError.getText().raw()));
756
757 /* rollback all changes */
758 rollback(false /* aNotify */);
759
760 // uninit media from this machine's media registry, or else
761 // reloading the settings will fail
762 mParent->unregisterMachineMedia(getId());
763
764 /* uninitialize the common part to make sure all data is reset to
765 * default (null) values */
766 uninitDataAndChildObjects();
767
768 rc = S_OK;
769 }
770
771 return rc;
772}
773
774/**
775 * Uninitializes the instance.
776 * Called either from FinalRelease() or by the parent when it gets destroyed.
777 *
778 * @note The caller of this method must make sure that this object
779 * a) doesn't have active callers on the current thread and b) is not locked
780 * by the current thread; otherwise uninit() will hang either a) due to
781 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
782 * a dead-lock caused by this thread waiting for all callers on the other
783 * threads are done but preventing them from doing so by holding a lock.
784 */
785void Machine::uninit()
786{
787 LogFlowThisFuncEnter();
788
789 Assert(!isWriteLockOnCurrentThread());
790
791 Assert(!uRegistryNeedsSaving);
792 if (uRegistryNeedsSaving)
793 {
794 AutoCaller autoCaller(this);
795 if (SUCCEEDED(autoCaller.rc()))
796 {
797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
798 saveSettings(NULL, Machine::SaveS_Force);
799 }
800 }
801
802 /* Enclose the state transition Ready->InUninit->NotReady */
803 AutoUninitSpan autoUninitSpan(this);
804 if (autoUninitSpan.uninitDone())
805 return;
806
807 Assert(!isSnapshotMachine());
808 Assert(!isSessionMachine());
809 Assert(!!mData);
810
811 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
812 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
813
814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
815
816 if (!mData->mSession.mMachine.isNull())
817 {
818 /* Theoretically, this can only happen if the VirtualBox server has been
819 * terminated while there were clients running that owned open direct
820 * sessions. Since in this case we are definitely called by
821 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
822 * won't happen on the client watcher thread (because it does
823 * VirtualBox::addCaller() for the duration of the
824 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
825 * cannot happen until the VirtualBox caller is released). This is
826 * important, because SessionMachine::uninit() cannot correctly operate
827 * after we return from this method (it expects the Machine instance is
828 * still valid). We'll call it ourselves below.
829 */
830 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
831 (SessionMachine*)mData->mSession.mMachine));
832
833 if (Global::IsOnlineOrTransient(mData->mMachineState))
834 {
835 LogWarningThisFunc(("Setting state to Aborted!\n"));
836 /* set machine state using SessionMachine reimplementation */
837 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
838 }
839
840 /*
841 * Uninitialize SessionMachine using public uninit() to indicate
842 * an unexpected uninitialization.
843 */
844 mData->mSession.mMachine->uninit();
845 /* SessionMachine::uninit() must set mSession.mMachine to null */
846 Assert(mData->mSession.mMachine.isNull());
847 }
848
849 // uninit media from this machine's media registry, if they're still there
850 Guid uuidMachine(getId());
851
852 /* the lock is no more necessary (SessionMachine is uninitialized) */
853 alock.release();
854
855 /* XXX This will fail with
856 * "cannot be closed because it is still attached to 1 virtual machines"
857 * because at this point we did not call uninitDataAndChildObjects() yet
858 * and therefore also removeBackReference() for all these mediums was not called! */
859
860 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
861 mParent->unregisterMachineMedia(uuidMachine);
862
863 // has machine been modified?
864 if (mData->flModifications)
865 {
866 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
867 rollback(false /* aNotify */);
868 }
869
870 if (mData->mAccessible)
871 uninitDataAndChildObjects();
872
873 /* free the essential data structure last */
874 mData.free();
875
876 LogFlowThisFuncLeave();
877}
878
879// IMachine properties
880/////////////////////////////////////////////////////////////////////////////
881
882STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
883{
884 CheckComArgOutPointerValid(aParent);
885
886 AutoLimitedCaller autoCaller(this);
887 if (FAILED(autoCaller.rc())) return autoCaller.rc();
888
889 /* mParent is constant during life time, no need to lock */
890 ComObjPtr<VirtualBox> pVirtualBox(mParent);
891 pVirtualBox.queryInterfaceTo(aParent);
892
893 return S_OK;
894}
895
896STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
897{
898 CheckComArgOutPointerValid(aAccessible);
899
900 AutoLimitedCaller autoCaller(this);
901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
902
903 LogFlowThisFunc(("ENTER\n"));
904
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
955{
956 CheckComArgOutPointerValid(aAccessError);
957
958 AutoLimitedCaller autoCaller(this);
959 if (FAILED(autoCaller.rc())) return autoCaller.rc();
960
961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
962
963 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
964 {
965 /* return shortly */
966 aAccessError = NULL;
967 return S_OK;
968 }
969
970 HRESULT rc = S_OK;
971
972 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
973 rc = errorInfo.createObject();
974 if (SUCCEEDED(rc))
975 {
976 errorInfo->init(mData->mAccessError.getResultCode(),
977 mData->mAccessError.getInterfaceID().ref(),
978 Utf8Str(mData->mAccessError.getComponent()).c_str(),
979 Utf8Str(mData->mAccessError.getText()));
980 rc = errorInfo.queryInterfaceTo(aAccessError);
981 }
982
983 return rc;
984}
985
986STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
987{
988 CheckComArgOutPointerValid(aName);
989
990 AutoCaller autoCaller(this);
991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
992
993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 mUserData->s.strName.cloneTo(aName);
996
997 return S_OK;
998}
999
1000STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1001{
1002 CheckComArgStrNotEmptyOrNull(aName);
1003
1004 AutoCaller autoCaller(this);
1005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1006
1007 // prohibit setting a UUID only as the machine name, or else it can
1008 // never be found by findMachine()
1009 Guid test(aName);
1010
1011 if (test.isValid())
1012 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1013
1014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 HRESULT rc = checkStateDependency(MutableStateDep);
1017 if (FAILED(rc)) return rc;
1018
1019 setModified(IsModified_MachineData);
1020 mUserData.backup();
1021 mUserData->s.strName = aName;
1022
1023 return S_OK;
1024}
1025
1026STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1027{
1028 CheckComArgOutPointerValid(aDescription);
1029
1030 AutoCaller autoCaller(this);
1031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1032
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 mUserData->s.strDescription.cloneTo(aDescription);
1036
1037 return S_OK;
1038}
1039
1040STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1041{
1042 AutoCaller autoCaller(this);
1043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1044
1045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1046
1047 // this can be done in principle in any state as it doesn't affect the VM
1048 // significantly, but play safe by not messing around while complex
1049 // activities are going on
1050 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1051 if (FAILED(rc)) return rc;
1052
1053 setModified(IsModified_MachineData);
1054 mUserData.backup();
1055 mUserData->s.strDescription = aDescription;
1056
1057 return S_OK;
1058}
1059
1060STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1061{
1062 CheckComArgOutPointerValid(aId);
1063
1064 AutoLimitedCaller autoCaller(this);
1065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1066
1067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1068
1069 mData->mUuid.toUtf16().cloneTo(aId);
1070
1071 return S_OK;
1072}
1073
1074STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1075{
1076 CheckComArgOutSafeArrayPointerValid(aGroups);
1077
1078 AutoCaller autoCaller(this);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1082 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1083 size_t i = 0;
1084 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1085 it != mUserData->s.llGroups.end();
1086 ++it, i++)
1087 {
1088 Bstr tmp = *it;
1089 tmp.cloneTo(&groups[i]);
1090 }
1091 groups.detachTo(ComSafeArrayOutArg(aGroups));
1092
1093 return S_OK;
1094}
1095
1096STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1097{
1098 AutoCaller autoCaller(this);
1099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1100
1101 StringsList llGroups;
1102 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1103 if (FAILED(rc))
1104 return rc;
1105
1106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1107
1108 // changing machine groups is possible while the VM is offline
1109 rc = checkStateDependency(OfflineStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.llGroups = llGroups;
1115
1116 return S_OK;
1117}
1118
1119STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1120{
1121 CheckComArgOutPointerValid(aOSTypeId);
1122
1123 AutoCaller autoCaller(this);
1124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1125
1126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 mUserData->s.strOsType.cloneTo(aOSTypeId);
1129
1130 return S_OK;
1131}
1132
1133STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1134{
1135 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1136
1137 AutoCaller autoCaller(this);
1138 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1139
1140 /* look up the object by Id to check it is valid */
1141 ComPtr<IGuestOSType> guestOSType;
1142 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1143 if (FAILED(rc)) return rc;
1144
1145 /* when setting, always use the "etalon" value for consistency -- lookup
1146 * by ID is case-insensitive and the input value may have different case */
1147 Bstr osTypeId;
1148 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1152
1153 rc = checkStateDependency(MutableStateDep);
1154 if (FAILED(rc)) return rc;
1155
1156 setModified(IsModified_MachineData);
1157 mUserData.backup();
1158 mUserData->s.strOsType = osTypeId;
1159
1160 return S_OK;
1161}
1162
1163
1164STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1165{
1166 CheckComArgOutPointerValid(aFirmwareType);
1167
1168 AutoCaller autoCaller(this);
1169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1170
1171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 *aFirmwareType = mHWData->mFirmwareType;
1174
1175 return S_OK;
1176}
1177
1178STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1179{
1180 AutoCaller autoCaller(this);
1181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 HRESULT rc = checkStateDependency(MutableStateDep);
1185 if (FAILED(rc)) return rc;
1186
1187 setModified(IsModified_MachineData);
1188 mHWData.backup();
1189 mHWData->mFirmwareType = aFirmwareType;
1190
1191 return S_OK;
1192}
1193
1194STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1195{
1196 CheckComArgOutPointerValid(aKeyboardHIDType);
1197
1198 AutoCaller autoCaller(this);
1199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1200
1201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1204
1205 return S_OK;
1206}
1207
1208STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1209{
1210 AutoCaller autoCaller(this);
1211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1213
1214 HRESULT rc = checkStateDependency(MutableStateDep);
1215 if (FAILED(rc)) return rc;
1216
1217 setModified(IsModified_MachineData);
1218 mHWData.backup();
1219 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1220
1221 return S_OK;
1222}
1223
1224STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1225{
1226 CheckComArgOutPointerValid(aPointingHIDType);
1227
1228 AutoCaller autoCaller(this);
1229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1230
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 *aPointingHIDType = mHWData->mPointingHIDType;
1234
1235 return S_OK;
1236}
1237
1238STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1239{
1240 AutoCaller autoCaller(this);
1241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1243
1244 HRESULT rc = checkStateDependency(MutableStateDep);
1245 if (FAILED(rc)) return rc;
1246
1247 setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mPointingHIDType = aPointingHIDType;
1250
1251 return S_OK;
1252}
1253
1254STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1255{
1256 CheckComArgOutPointerValid(aChipsetType);
1257
1258 AutoCaller autoCaller(this);
1259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1260
1261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 *aChipsetType = mHWData->mChipsetType;
1264
1265 return S_OK;
1266}
1267
1268STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1269{
1270 AutoCaller autoCaller(this);
1271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1273
1274 HRESULT rc = checkStateDependency(MutableStateDep);
1275 if (FAILED(rc)) return rc;
1276
1277 if (aChipsetType != mHWData->mChipsetType)
1278 {
1279 setModified(IsModified_MachineData);
1280 mHWData.backup();
1281 mHWData->mChipsetType = aChipsetType;
1282
1283 // Resize network adapter array, to be finalized on commit/rollback.
1284 // We must not throw away entries yet, otherwise settings are lost
1285 // without a way to roll back.
1286 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1287 size_t oldCount = mNetworkAdapters.size();
1288 if (newCount > oldCount)
1289 {
1290 mNetworkAdapters.resize(newCount);
1291 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1292 {
1293 unconst(mNetworkAdapters[slot]).createObject();
1294 mNetworkAdapters[slot]->init(this, slot);
1295 }
1296 }
1297 }
1298
1299 return S_OK;
1300}
1301
1302STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1303{
1304 CheckComArgOutPointerValid(aHWVersion);
1305
1306 AutoCaller autoCaller(this);
1307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1308
1309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1310
1311 mHWData->mHWVersion.cloneTo(aHWVersion);
1312
1313 return S_OK;
1314}
1315
1316STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1317{
1318 /* check known version */
1319 Utf8Str hwVersion = aHWVersion;
1320 if ( hwVersion.compare("1") != 0
1321 && hwVersion.compare("2") != 0)
1322 return setError(E_INVALIDARG,
1323 tr("Invalid hardware version: %ls\n"), aHWVersion);
1324
1325 AutoCaller autoCaller(this);
1326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1327
1328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1329
1330 HRESULT rc = checkStateDependency(MutableStateDep);
1331 if (FAILED(rc)) return rc;
1332
1333 setModified(IsModified_MachineData);
1334 mHWData.backup();
1335 mHWData->mHWVersion = hwVersion;
1336
1337 return S_OK;
1338}
1339
1340STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1341{
1342 CheckComArgOutPointerValid(aUUID);
1343
1344 AutoCaller autoCaller(this);
1345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1346
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 if (!mHWData->mHardwareUUID.isZero())
1350 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1351 else
1352 mData->mUuid.toUtf16().cloneTo(aUUID);
1353
1354 return S_OK;
1355}
1356
1357STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1358{
1359 Guid hardwareUUID(aUUID);
1360 if (!hardwareUUID.isValid())
1361 return E_INVALIDARG;
1362
1363 AutoCaller autoCaller(this);
1364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT rc = checkStateDependency(MutableStateDep);
1369 if (FAILED(rc)) return rc;
1370
1371 setModified(IsModified_MachineData);
1372 mHWData.backup();
1373 if (hardwareUUID == mData->mUuid)
1374 mHWData->mHardwareUUID.clear();
1375 else
1376 mHWData->mHardwareUUID = hardwareUUID;
1377
1378 return S_OK;
1379}
1380
1381STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1382{
1383 CheckComArgOutPointerValid(memorySize);
1384
1385 AutoCaller autoCaller(this);
1386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1387
1388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1389
1390 *memorySize = mHWData->mMemorySize;
1391
1392 return S_OK;
1393}
1394
1395STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1396{
1397 /* check RAM limits */
1398 if ( memorySize < MM_RAM_MIN_IN_MB
1399 || memorySize > MM_RAM_MAX_IN_MB
1400 )
1401 return setError(E_INVALIDARG,
1402 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1403 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1404
1405 AutoCaller autoCaller(this);
1406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mMemorySize = memorySize;
1416
1417 return S_OK;
1418}
1419
1420STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1421{
1422 CheckComArgOutPointerValid(CPUCount);
1423
1424 AutoCaller autoCaller(this);
1425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1426
1427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 *CPUCount = mHWData->mCPUCount;
1430
1431 return S_OK;
1432}
1433
1434STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1435{
1436 /* check CPU limits */
1437 if ( CPUCount < SchemaDefs::MinCPUCount
1438 || CPUCount > SchemaDefs::MaxCPUCount
1439 )
1440 return setError(E_INVALIDARG,
1441 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1442 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1443
1444 AutoCaller autoCaller(this);
1445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1446
1447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1450 if (mHWData->mCPUHotPlugEnabled)
1451 {
1452 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1453 {
1454 if (mHWData->mCPUAttached[idx])
1455 return setError(E_INVALIDARG,
1456 tr("There is still a CPU attached to socket %lu."
1457 "Detach the CPU before removing the socket"),
1458 CPUCount, idx+1);
1459 }
1460 }
1461
1462 HRESULT rc = checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mCPUCount = CPUCount;
1468
1469 return S_OK;
1470}
1471
1472STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1473{
1474 CheckComArgOutPointerValid(aExecutionCap);
1475
1476 AutoCaller autoCaller(this);
1477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1478
1479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 *aExecutionCap = mHWData->mCpuExecutionCap;
1482
1483 return S_OK;
1484}
1485
1486STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1487{
1488 HRESULT rc = S_OK;
1489
1490 /* check throttle limits */
1491 if ( aExecutionCap < 1
1492 || aExecutionCap > 100
1493 )
1494 return setError(E_INVALIDARG,
1495 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1496 aExecutionCap, 1, 100);
1497
1498 AutoCaller autoCaller(this);
1499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1500
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 alock.release();
1504 rc = onCPUExecutionCapChange(aExecutionCap);
1505 alock.acquire();
1506 if (FAILED(rc)) return rc;
1507
1508 setModified(IsModified_MachineData);
1509 mHWData.backup();
1510 mHWData->mCpuExecutionCap = aExecutionCap;
1511
1512 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1513 if (Global::IsOnline(mData->mMachineState))
1514 saveSettings(NULL);
1515
1516 return S_OK;
1517}
1518
1519
1520STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1521{
1522 CheckComArgOutPointerValid(aEnabled);
1523
1524 AutoCaller autoCaller(this);
1525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1526
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aEnabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoCaller autoCaller(this);
1539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1540
1541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 rc = checkStateDependency(MutableStateDep);
1544 if (FAILED(rc)) return rc;
1545
1546 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1547 {
1548 if (aEnabled)
1549 {
1550 setModified(IsModified_MachineData);
1551 mHWData.backup();
1552
1553 /* Add the amount of CPUs currently attached */
1554 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1555 {
1556 mHWData->mCPUAttached[i] = true;
1557 }
1558 }
1559 else
1560 {
1561 /*
1562 * We can disable hotplug only if the amount of maximum CPUs is equal
1563 * to the amount of attached CPUs
1564 */
1565 unsigned cCpusAttached = 0;
1566 unsigned iHighestId = 0;
1567
1568 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1569 {
1570 if (mHWData->mCPUAttached[i])
1571 {
1572 cCpusAttached++;
1573 iHighestId = i;
1574 }
1575 }
1576
1577 if ( (cCpusAttached != mHWData->mCPUCount)
1578 || (iHighestId >= mHWData->mCPUCount))
1579 return setError(E_INVALIDARG,
1580 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1581
1582 setModified(IsModified_MachineData);
1583 mHWData.backup();
1584 }
1585 }
1586
1587 mHWData->mCPUHotPlugEnabled = aEnabled;
1588
1589 return rc;
1590}
1591
1592STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1593{
1594#ifdef VBOX_WITH_USB_CARDREADER
1595 CheckComArgOutPointerValid(aEnabled);
1596
1597 AutoCaller autoCaller(this);
1598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1599
1600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1603
1604 return S_OK;
1605#else
1606 NOREF(aEnabled);
1607 return E_NOTIMPL;
1608#endif
1609}
1610
1611STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1612{
1613#ifdef VBOX_WITH_USB_CARDREADER
1614 AutoCaller autoCaller(this);
1615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 HRESULT rc = checkStateDependency(MutableStateDep);
1619 if (FAILED(rc)) return rc;
1620
1621 setModified(IsModified_MachineData);
1622 mHWData.backup();
1623 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1624
1625 return S_OK;
1626#else
1627 NOREF(aEnabled);
1628 return E_NOTIMPL;
1629#endif
1630}
1631
1632STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1633{
1634 CheckComArgOutPointerValid(aEnabled);
1635
1636 AutoCaller autoCaller(this);
1637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1639
1640 *aEnabled = mHWData->mHPETEnabled;
1641
1642 return S_OK;
1643}
1644
1645STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1646{
1647 HRESULT rc = S_OK;
1648
1649 AutoCaller autoCaller(this);
1650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1652
1653 rc = checkStateDependency(MutableStateDep);
1654 if (FAILED(rc)) return rc;
1655
1656 setModified(IsModified_MachineData);
1657 mHWData.backup();
1658
1659 mHWData->mHPETEnabled = aEnabled;
1660
1661 return rc;
1662}
1663
1664STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1665{
1666 AutoCaller autoCaller(this);
1667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1668
1669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 *fEnabled = mHWData->mVideoCaptureEnabled;
1672 return S_OK;
1673}
1674
1675STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1676{
1677 HRESULT rc = S_OK;
1678
1679 AutoCaller autoCaller(this);
1680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 setModified(IsModified_MachineData);
1684 mHWData.backup();
1685 mHWData->mVideoCaptureEnabled = fEnabled;
1686
1687 alock.release();
1688 rc = onVideoCaptureChange();
1689 alock.acquire();
1690 if (FAILED(rc))
1691 {
1692 /*
1693 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1694 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1695 * determine if it should start or stop capturing. Therefore we need to manually
1696 * undo change.
1697 */
1698 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1699 return rc;
1700 }
1701
1702 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1703 if (Global::IsOnline(mData->mMachineState))
1704 saveSettings(NULL);
1705
1706 return rc;
1707}
1708
1709STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1710{
1711 CheckComArgOutSafeArrayPointerValid(aScreens);
1712
1713 AutoCaller autoCaller(this);
1714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1715
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1719 for (unsigned i = 0; i < screens.size(); i++)
1720 screens[i] = mHWData->maVideoCaptureScreens[i];
1721 screens.detachTo(ComSafeArrayOutArg(aScreens));
1722 return S_OK;
1723}
1724
1725STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1726{
1727 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1728 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1729 bool fChanged = false;
1730
1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1732
1733 for (unsigned i = 0; i < screens.size(); i++)
1734 {
1735 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1736 {
1737 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1738 fChanged = true;
1739 }
1740 }
1741 if (fChanged)
1742 {
1743 alock.release();
1744 HRESULT rc = onVideoCaptureChange();
1745 alock.acquire();
1746 if (FAILED(rc)) return rc;
1747 setModified(IsModified_MachineData);
1748
1749 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1750 if (Global::IsOnline(mData->mMachineState))
1751 saveSettings(NULL);
1752 }
1753
1754 return S_OK;
1755}
1756
1757STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1758{
1759 AutoCaller autoCaller(this);
1760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1761
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 if (mHWData->mVideoCaptureFile.isEmpty())
1764 {
1765 Utf8Str defaultFile;
1766 getDefaultVideoCaptureFile(defaultFile);
1767 defaultFile.cloneTo(apFile);
1768 }
1769 else
1770 mHWData->mVideoCaptureFile.cloneTo(apFile);
1771 return S_OK;
1772}
1773
1774STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1775{
1776 Utf8Str strFile(aFile);
1777 AutoCaller autoCaller(this);
1778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1779
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 if ( Global::IsOnline(mData->mMachineState)
1783 && mHWData->mVideoCaptureEnabled)
1784 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1785
1786 if (!RTPathStartsWithRoot(strFile.c_str()))
1787 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1788
1789 if (!strFile.isEmpty())
1790 {
1791 Utf8Str defaultFile;
1792 getDefaultVideoCaptureFile(defaultFile);
1793 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1794 strFile.setNull();
1795 }
1796
1797 setModified(IsModified_MachineData);
1798 mHWData.backup();
1799 mHWData->mVideoCaptureFile = strFile;
1800
1801 return S_OK;
1802}
1803
1804STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1805{
1806 AutoCaller autoCaller(this);
1807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1808
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aHorzRes = mHWData->mVideoCaptureWidth;
1811 return S_OK;
1812}
1813
1814STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1815{
1816 AutoCaller autoCaller(this);
1817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1818
1819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1820
1821 if ( Global::IsOnline(mData->mMachineState)
1822 && mHWData->mVideoCaptureEnabled)
1823 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1824
1825 setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mVideoCaptureWidth = aHorzRes;
1828
1829 return S_OK;
1830}
1831
1832STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1833{
1834 AutoCaller autoCaller(this);
1835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1836
1837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1838 *aVertRes = mHWData->mVideoCaptureHeight;
1839 return S_OK;
1840}
1841
1842STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1843{
1844 AutoCaller autoCaller(this);
1845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1846
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 if ( Global::IsOnline(mData->mMachineState)
1850 && mHWData->mVideoCaptureEnabled)
1851 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1852
1853 setModified(IsModified_MachineData);
1854 mHWData.backup();
1855 mHWData->mVideoCaptureHeight = aVertRes;
1856
1857 return S_OK;
1858}
1859
1860STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1861{
1862 AutoCaller autoCaller(this);
1863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1864
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866 *aRate = mHWData->mVideoCaptureRate;
1867 return S_OK;
1868}
1869
1870STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1871{
1872 AutoCaller autoCaller(this);
1873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1874
1875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 if ( Global::IsOnline(mData->mMachineState)
1878 && mHWData->mVideoCaptureEnabled)
1879 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1880
1881 setModified(IsModified_MachineData);
1882 mHWData.backup();
1883 mHWData->mVideoCaptureRate = aRate;
1884
1885 return S_OK;
1886}
1887
1888STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1889{
1890 AutoCaller autoCaller(this);
1891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1892
1893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1894 *aFPS = mHWData->mVideoCaptureFPS;
1895 return S_OK;
1896}
1897
1898STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1899{
1900 AutoCaller autoCaller(this);
1901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1902
1903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1904
1905 if ( Global::IsOnline(mData->mMachineState)
1906 && mHWData->mVideoCaptureEnabled)
1907 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1908
1909 setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mVideoCaptureFPS = aFPS;
1912
1913 return S_OK;
1914}
1915
1916STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1917{
1918 CheckComArgOutPointerValid(aGraphicsControllerType);
1919
1920 AutoCaller autoCaller(this);
1921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1922
1923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1926
1927 return S_OK;
1928}
1929
1930STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1931{
1932 switch (aGraphicsControllerType)
1933 {
1934 case GraphicsControllerType_Null:
1935 case GraphicsControllerType_VBoxVGA:
1936#ifdef VBOX_WITH_VMSVGA
1937 case GraphicsControllerType_VMSVGA:
1938#endif
1939 break;
1940 default:
1941 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1942 }
1943
1944 AutoCaller autoCaller(this);
1945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1946
1947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1948
1949 HRESULT rc = checkStateDependency(MutableStateDep);
1950 if (FAILED(rc)) return rc;
1951
1952 setModified(IsModified_MachineData);
1953 mHWData.backup();
1954 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1955
1956 return S_OK;
1957}
1958
1959STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1960{
1961 CheckComArgOutPointerValid(memorySize);
1962
1963 AutoCaller autoCaller(this);
1964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1965
1966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1967
1968 *memorySize = mHWData->mVRAMSize;
1969
1970 return S_OK;
1971}
1972
1973STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1974{
1975 /* check VRAM limits */
1976 if (memorySize < SchemaDefs::MinGuestVRAM ||
1977 memorySize > SchemaDefs::MaxGuestVRAM)
1978 return setError(E_INVALIDARG,
1979 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1980 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1981
1982 AutoCaller autoCaller(this);
1983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1984
1985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1986
1987 HRESULT rc = checkStateDependency(MutableStateDep);
1988 if (FAILED(rc)) return rc;
1989
1990 setModified(IsModified_MachineData);
1991 mHWData.backup();
1992 mHWData->mVRAMSize = memorySize;
1993
1994 return S_OK;
1995}
1996
1997/** @todo this method should not be public */
1998STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1999{
2000 CheckComArgOutPointerValid(memoryBalloonSize);
2001
2002 AutoCaller autoCaller(this);
2003 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2004
2005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2006
2007 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2008
2009 return S_OK;
2010}
2011
2012/**
2013 * Set the memory balloon size.
2014 *
2015 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2016 * we have to make sure that we never call IGuest from here.
2017 */
2018STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2019{
2020 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2021#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2022 /* check limits */
2023 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2024 return setError(E_INVALIDARG,
2025 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2026 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2027
2028 AutoCaller autoCaller(this);
2029 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2030
2031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2032
2033 setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2036
2037 return S_OK;
2038#else
2039 NOREF(memoryBalloonSize);
2040 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2041#endif
2042}
2043
2044STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2045{
2046 CheckComArgOutPointerValid(aEnabled);
2047
2048 AutoCaller autoCaller(this);
2049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2050
2051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2052
2053 *aEnabled = mHWData->mPageFusionEnabled;
2054 return S_OK;
2055}
2056
2057STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2058{
2059#ifdef VBOX_WITH_PAGE_SHARING
2060 AutoCaller autoCaller(this);
2061 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2062
2063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2066 setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mPageFusionEnabled = aEnabled;
2069 return S_OK;
2070#else
2071 NOREF(aEnabled);
2072 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2073#endif
2074}
2075
2076STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2077{
2078 CheckComArgOutPointerValid(aEnabled);
2079
2080 AutoCaller autoCaller(this);
2081 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2082
2083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 *aEnabled = mHWData->mAccelerate3DEnabled;
2086
2087 return S_OK;
2088}
2089
2090STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2091{
2092 AutoCaller autoCaller(this);
2093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2094
2095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 HRESULT rc = checkStateDependency(MutableStateDep);
2098 if (FAILED(rc)) return rc;
2099
2100 /** @todo check validity! */
2101
2102 setModified(IsModified_MachineData);
2103 mHWData.backup();
2104 mHWData->mAccelerate3DEnabled = enable;
2105
2106 return S_OK;
2107}
2108
2109
2110STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2111{
2112 CheckComArgOutPointerValid(aEnabled);
2113
2114 AutoCaller autoCaller(this);
2115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2116
2117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2118
2119 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2120
2121 return S_OK;
2122}
2123
2124STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2125{
2126 AutoCaller autoCaller(this);
2127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2128
2129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2130
2131 HRESULT rc = checkStateDependency(MutableStateDep);
2132 if (FAILED(rc)) return rc;
2133
2134 /** @todo check validity! */
2135
2136 setModified(IsModified_MachineData);
2137 mHWData.backup();
2138 mHWData->mAccelerate2DVideoEnabled = enable;
2139
2140 return S_OK;
2141}
2142
2143STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2144{
2145 CheckComArgOutPointerValid(monitorCount);
2146
2147 AutoCaller autoCaller(this);
2148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2149
2150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2151
2152 *monitorCount = mHWData->mMonitorCount;
2153
2154 return S_OK;
2155}
2156
2157STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2158{
2159 /* make sure monitor count is a sensible number */
2160 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2161 return setError(E_INVALIDARG,
2162 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2163 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2164
2165 AutoCaller autoCaller(this);
2166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2167
2168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 HRESULT rc = checkStateDependency(MutableStateDep);
2171 if (FAILED(rc)) return rc;
2172
2173 setModified(IsModified_MachineData);
2174 mHWData.backup();
2175 mHWData->mMonitorCount = monitorCount;
2176
2177 return S_OK;
2178}
2179
2180STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2181{
2182 CheckComArgOutPointerValid(biosSettings);
2183
2184 AutoCaller autoCaller(this);
2185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2186
2187 /* mBIOSSettings is constant during life time, no need to lock */
2188 mBIOSSettings.queryInterfaceTo(biosSettings);
2189
2190 return S_OK;
2191}
2192
2193STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2194{
2195 CheckComArgOutPointerValid(aVal);
2196
2197 AutoCaller autoCaller(this);
2198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2199
2200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2201
2202 switch (property)
2203 {
2204 case CPUPropertyType_PAE:
2205 *aVal = mHWData->mPAEEnabled;
2206 break;
2207
2208 case CPUPropertyType_Synthetic:
2209 *aVal = mHWData->mSyntheticCpu;
2210 break;
2211
2212 case CPUPropertyType_LongMode:
2213 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2214 *aVal = TRUE;
2215 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2216 *aVal = FALSE;
2217#if HC_ARCH_BITS == 64
2218 else
2219 *aVal = TRUE;
2220#else
2221 else
2222 {
2223 *aVal = FALSE;
2224
2225 ComPtr<IGuestOSType> ptrGuestOSType;
2226 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2227 if (SUCCEEDED(hrc2))
2228 {
2229 BOOL fIs64Bit = FALSE;
2230 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2231 if (SUCCEEDED(hrc2) && fIs64Bit)
2232 {
2233 ComObjPtr<Host> ptrHost = mParent->host();
2234 alock.release();
2235
2236 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2237 if (FAILED(hrc2))
2238 *aVal = FALSE;
2239 }
2240 }
2241 }
2242#endif
2243 break;
2244
2245 case CPUPropertyType_TripleFaultReset:
2246 *aVal = mHWData->mTripleFaultReset;
2247 break;
2248
2249 default:
2250 return E_INVALIDARG;
2251 }
2252 return S_OK;
2253}
2254
2255STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2256{
2257 AutoCaller autoCaller(this);
2258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2259
2260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2261
2262 HRESULT rc = checkStateDependency(MutableStateDep);
2263 if (FAILED(rc)) return rc;
2264
2265 switch (property)
2266 {
2267 case CPUPropertyType_PAE:
2268 setModified(IsModified_MachineData);
2269 mHWData.backup();
2270 mHWData->mPAEEnabled = !!aVal;
2271 break;
2272
2273 case CPUPropertyType_Synthetic:
2274 setModified(IsModified_MachineData);
2275 mHWData.backup();
2276 mHWData->mSyntheticCpu = !!aVal;
2277 break;
2278
2279 case CPUPropertyType_LongMode:
2280 setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2283 break;
2284
2285 case CPUPropertyType_TripleFaultReset:
2286 setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 mHWData->mTripleFaultReset = !!aVal;
2289 break;
2290
2291 default:
2292 return E_INVALIDARG;
2293 }
2294 return S_OK;
2295}
2296
2297STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2298{
2299 CheckComArgOutPointerValid(aValEax);
2300 CheckComArgOutPointerValid(aValEbx);
2301 CheckComArgOutPointerValid(aValEcx);
2302 CheckComArgOutPointerValid(aValEdx);
2303
2304 AutoCaller autoCaller(this);
2305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2306
2307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 switch(aId)
2310 {
2311 case 0x0:
2312 case 0x1:
2313 case 0x2:
2314 case 0x3:
2315 case 0x4:
2316 case 0x5:
2317 case 0x6:
2318 case 0x7:
2319 case 0x8:
2320 case 0x9:
2321 case 0xA:
2322 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2323 return E_INVALIDARG;
2324
2325 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2326 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2327 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2328 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2329 break;
2330
2331 case 0x80000000:
2332 case 0x80000001:
2333 case 0x80000002:
2334 case 0x80000003:
2335 case 0x80000004:
2336 case 0x80000005:
2337 case 0x80000006:
2338 case 0x80000007:
2339 case 0x80000008:
2340 case 0x80000009:
2341 case 0x8000000A:
2342 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2343 return E_INVALIDARG;
2344
2345 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2346 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2347 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2348 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2349 break;
2350
2351 default:
2352 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2353 }
2354 return S_OK;
2355}
2356
2357STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2358{
2359 AutoCaller autoCaller(this);
2360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2361
2362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 HRESULT rc = checkStateDependency(MutableStateDep);
2365 if (FAILED(rc)) return rc;
2366
2367 switch(aId)
2368 {
2369 case 0x0:
2370 case 0x1:
2371 case 0x2:
2372 case 0x3:
2373 case 0x4:
2374 case 0x5:
2375 case 0x6:
2376 case 0x7:
2377 case 0x8:
2378 case 0x9:
2379 case 0xA:
2380 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2381 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2382 setModified(IsModified_MachineData);
2383 mHWData.backup();
2384 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2385 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2386 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2387 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2388 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2389 break;
2390
2391 case 0x80000000:
2392 case 0x80000001:
2393 case 0x80000002:
2394 case 0x80000003:
2395 case 0x80000004:
2396 case 0x80000005:
2397 case 0x80000006:
2398 case 0x80000007:
2399 case 0x80000008:
2400 case 0x80000009:
2401 case 0x8000000A:
2402 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2403 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2404 setModified(IsModified_MachineData);
2405 mHWData.backup();
2406 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2407 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2408 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2409 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2410 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2411 break;
2412
2413 default:
2414 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2415 }
2416 return S_OK;
2417}
2418
2419STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2420{
2421 AutoCaller autoCaller(this);
2422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2423
2424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2425
2426 HRESULT rc = checkStateDependency(MutableStateDep);
2427 if (FAILED(rc)) return rc;
2428
2429 switch(aId)
2430 {
2431 case 0x0:
2432 case 0x1:
2433 case 0x2:
2434 case 0x3:
2435 case 0x4:
2436 case 0x5:
2437 case 0x6:
2438 case 0x7:
2439 case 0x8:
2440 case 0x9:
2441 case 0xA:
2442 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2443 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2444 setModified(IsModified_MachineData);
2445 mHWData.backup();
2446 /* Invalidate leaf. */
2447 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2448 break;
2449
2450 case 0x80000000:
2451 case 0x80000001:
2452 case 0x80000002:
2453 case 0x80000003:
2454 case 0x80000004:
2455 case 0x80000005:
2456 case 0x80000006:
2457 case 0x80000007:
2458 case 0x80000008:
2459 case 0x80000009:
2460 case 0x8000000A:
2461 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2462 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2463 setModified(IsModified_MachineData);
2464 mHWData.backup();
2465 /* Invalidate leaf. */
2466 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2467 break;
2468
2469 default:
2470 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2471 }
2472 return S_OK;
2473}
2474
2475STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2476{
2477 AutoCaller autoCaller(this);
2478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2479
2480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 HRESULT rc = checkStateDependency(MutableStateDep);
2483 if (FAILED(rc)) return rc;
2484
2485 setModified(IsModified_MachineData);
2486 mHWData.backup();
2487
2488 /* Invalidate all standard leafs. */
2489 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2490 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2491
2492 /* Invalidate all extended leafs. */
2493 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2494 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2495
2496 return S_OK;
2497}
2498
2499STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2500{
2501 CheckComArgOutPointerValid(aVal);
2502
2503 AutoCaller autoCaller(this);
2504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2505
2506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 switch(property)
2509 {
2510 case HWVirtExPropertyType_Enabled:
2511 *aVal = mHWData->mHWVirtExEnabled;
2512 break;
2513
2514 case HWVirtExPropertyType_VPID:
2515 *aVal = mHWData->mHWVirtExVPIDEnabled;
2516 break;
2517
2518 case HWVirtExPropertyType_NestedPaging:
2519 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2520 break;
2521
2522 case HWVirtExPropertyType_UnrestrictedExecution:
2523 *aVal = mHWData->mHWVirtExUXEnabled;
2524 break;
2525
2526 case HWVirtExPropertyType_LargePages:
2527 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2528#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2529 *aVal = FALSE;
2530#endif
2531 break;
2532
2533 case HWVirtExPropertyType_Force:
2534 *aVal = mHWData->mHWVirtExForceEnabled;
2535 break;
2536
2537 default:
2538 return E_INVALIDARG;
2539 }
2540 return S_OK;
2541}
2542
2543STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2544{
2545 AutoCaller autoCaller(this);
2546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2547
2548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 HRESULT rc = checkStateDependency(MutableStateDep);
2551 if (FAILED(rc)) return rc;
2552
2553 switch(property)
2554 {
2555 case HWVirtExPropertyType_Enabled:
2556 setModified(IsModified_MachineData);
2557 mHWData.backup();
2558 mHWData->mHWVirtExEnabled = !!aVal;
2559 break;
2560
2561 case HWVirtExPropertyType_VPID:
2562 setModified(IsModified_MachineData);
2563 mHWData.backup();
2564 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2565 break;
2566
2567 case HWVirtExPropertyType_NestedPaging:
2568 setModified(IsModified_MachineData);
2569 mHWData.backup();
2570 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2571 break;
2572
2573 case HWVirtExPropertyType_UnrestrictedExecution:
2574 setModified(IsModified_MachineData);
2575 mHWData.backup();
2576 mHWData->mHWVirtExUXEnabled = !!aVal;
2577 break;
2578
2579 case HWVirtExPropertyType_LargePages:
2580 setModified(IsModified_MachineData);
2581 mHWData.backup();
2582 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2583 break;
2584
2585 case HWVirtExPropertyType_Force:
2586 setModified(IsModified_MachineData);
2587 mHWData.backup();
2588 mHWData->mHWVirtExForceEnabled = !!aVal;
2589 break;
2590
2591 default:
2592 return E_INVALIDARG;
2593 }
2594
2595 return S_OK;
2596}
2597
2598STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2599{
2600 CheckComArgOutPointerValid(aSnapshotFolder);
2601
2602 AutoCaller autoCaller(this);
2603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2604
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 Utf8Str strFullSnapshotFolder;
2608 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2609 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2610
2611 return S_OK;
2612}
2613
2614STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2615{
2616 /* @todo (r=dmik):
2617 * 1. Allow to change the name of the snapshot folder containing snapshots
2618 * 2. Rename the folder on disk instead of just changing the property
2619 * value (to be smart and not to leave garbage). Note that it cannot be
2620 * done here because the change may be rolled back. Thus, the right
2621 * place is #saveSettings().
2622 */
2623
2624 AutoCaller autoCaller(this);
2625 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2626
2627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2628
2629 HRESULT rc = checkStateDependency(MutableStateDep);
2630 if (FAILED(rc)) return rc;
2631
2632 if (!mData->mCurrentSnapshot.isNull())
2633 return setError(E_FAIL,
2634 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2635
2636 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2637
2638 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2639 if (strSnapshotFolder.isEmpty())
2640 strSnapshotFolder = "Snapshots";
2641 int vrc = calculateFullPath(strSnapshotFolder,
2642 strSnapshotFolder);
2643 if (RT_FAILURE(vrc))
2644 return setError(E_FAIL,
2645 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2646 aSnapshotFolder, vrc);
2647
2648 setModified(IsModified_MachineData);
2649 mUserData.backup();
2650
2651 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2652
2653 return S_OK;
2654}
2655
2656STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2657{
2658 CheckComArgOutSafeArrayPointerValid(aAttachments);
2659
2660 AutoCaller autoCaller(this);
2661 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2662
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2666 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2667
2668 return S_OK;
2669}
2670
2671STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2672{
2673 CheckComArgOutPointerValid(vrdeServer);
2674
2675 AutoCaller autoCaller(this);
2676 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2677
2678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2679
2680 Assert(!!mVRDEServer);
2681 mVRDEServer.queryInterfaceTo(vrdeServer);
2682
2683 return S_OK;
2684}
2685
2686STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2687{
2688 CheckComArgOutPointerValid(audioAdapter);
2689
2690 AutoCaller autoCaller(this);
2691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2692
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 mAudioAdapter.queryInterfaceTo(audioAdapter);
2696 return S_OK;
2697}
2698
2699STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2700{
2701#ifdef VBOX_WITH_VUSB
2702 CheckComArgOutPointerValid(aUSBControllers);
2703
2704 AutoCaller autoCaller(this);
2705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2706
2707 clearError();
2708 MultiResult rc(S_OK);
2709
2710# ifdef VBOX_WITH_USB
2711 rc = mParent->host()->i_checkUSBProxyService();
2712 if (FAILED(rc)) return rc;
2713# endif
2714
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2718 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2719 return S_OK;
2720#else
2721 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2722 * extended error info to indicate that USB is simply not available
2723 * (w/o treating it as a failure), for example, as in OSE */
2724 NOREF(aUSBControllers);
2725 ReturnComNotImplemented();
2726#endif /* VBOX_WITH_VUSB */
2727}
2728
2729STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2730{
2731#ifdef VBOX_WITH_VUSB
2732 CheckComArgOutPointerValid(aUSBDeviceFilters);
2733
2734 AutoCaller autoCaller(this);
2735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2736
2737 clearError();
2738 MultiResult rc(S_OK);
2739
2740# ifdef VBOX_WITH_USB
2741 rc = mParent->host()->i_checkUSBProxyService();
2742 if (FAILED(rc)) return rc;
2743# endif
2744
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2748#else
2749 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2750 * extended error info to indicate that USB is simply not available
2751 * (w/o treating it as a failure), for example, as in OSE */
2752 NOREF(aUSBDeviceFilters);
2753 ReturnComNotImplemented();
2754#endif /* VBOX_WITH_VUSB */
2755}
2756
2757STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2758{
2759 CheckComArgOutPointerValid(aFilePath);
2760
2761 AutoLimitedCaller autoCaller(this);
2762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2763
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 mData->m_strConfigFileFull.cloneTo(aFilePath);
2767 return S_OK;
2768}
2769
2770STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2771{
2772 CheckComArgOutPointerValid(aModified);
2773
2774 AutoCaller autoCaller(this);
2775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2776
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 HRESULT rc = checkStateDependency(MutableStateDep);
2780 if (FAILED(rc)) return rc;
2781
2782 if (!mData->pMachineConfigFile->fileExists())
2783 // this is a new machine, and no config file exists yet:
2784 *aModified = TRUE;
2785 else
2786 *aModified = (mData->flModifications != 0);
2787
2788 return S_OK;
2789}
2790
2791STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2792{
2793 CheckComArgOutPointerValid(aSessionState);
2794
2795 AutoCaller autoCaller(this);
2796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2797
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 *aSessionState = mData->mSession.mState;
2801
2802 return S_OK;
2803}
2804
2805STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2806{
2807 CheckComArgOutPointerValid(aSessionType);
2808
2809 AutoCaller autoCaller(this);
2810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2811
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 mData->mSession.mType.cloneTo(aSessionType);
2815
2816 return S_OK;
2817}
2818
2819STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2820{
2821 CheckComArgOutPointerValid(aSessionPID);
2822
2823 AutoCaller autoCaller(this);
2824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2825
2826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 *aSessionPID = mData->mSession.mPID;
2829
2830 return S_OK;
2831}
2832
2833STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2834{
2835 CheckComArgOutPointerValid(machineState);
2836
2837 AutoCaller autoCaller(this);
2838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2839
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *machineState = mData->mMachineState;
2843
2844 return S_OK;
2845}
2846
2847STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2848{
2849 CheckComArgOutPointerValid(aLastStateChange);
2850
2851 AutoCaller autoCaller(this);
2852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2853
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2857
2858 return S_OK;
2859}
2860
2861STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2862{
2863 CheckComArgOutPointerValid(aStateFilePath);
2864
2865 AutoCaller autoCaller(this);
2866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2867
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2871
2872 return S_OK;
2873}
2874
2875STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2876{
2877 CheckComArgOutPointerValid(aLogFolder);
2878
2879 AutoCaller autoCaller(this);
2880 AssertComRCReturnRC(autoCaller.rc());
2881
2882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 Utf8Str logFolder;
2885 getLogFolder(logFolder);
2886 logFolder.cloneTo(aLogFolder);
2887
2888 return S_OK;
2889}
2890
2891STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2892{
2893 CheckComArgOutPointerValid(aCurrentSnapshot);
2894
2895 AutoCaller autoCaller(this);
2896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2897
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2901
2902 return S_OK;
2903}
2904
2905STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2906{
2907 CheckComArgOutPointerValid(aSnapshotCount);
2908
2909 AutoCaller autoCaller(this);
2910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2911
2912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2915 ? 0
2916 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2917
2918 return S_OK;
2919}
2920
2921STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2922{
2923 CheckComArgOutPointerValid(aCurrentStateModified);
2924
2925 AutoCaller autoCaller(this);
2926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2927
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 /* Note: for machines with no snapshots, we always return FALSE
2931 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2932 * reasons :) */
2933
2934 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2935 ? FALSE
2936 : mData->mCurrentStateModified;
2937
2938 return S_OK;
2939}
2940
2941STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2942{
2943 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2944
2945 AutoCaller autoCaller(this);
2946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2947
2948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2951 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2952
2953 return S_OK;
2954}
2955
2956STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2957{
2958 CheckComArgOutPointerValid(aClipboardMode);
2959
2960 AutoCaller autoCaller(this);
2961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2962
2963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2964
2965 *aClipboardMode = mHWData->mClipboardMode;
2966
2967 return S_OK;
2968}
2969
2970STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2971{
2972 HRESULT rc = S_OK;
2973
2974 AutoCaller autoCaller(this);
2975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2976
2977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2978
2979 alock.release();
2980 rc = onClipboardModeChange(aClipboardMode);
2981 alock.acquire();
2982 if (FAILED(rc)) return rc;
2983
2984 setModified(IsModified_MachineData);
2985 mHWData.backup();
2986 mHWData->mClipboardMode = aClipboardMode;
2987
2988 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2989 if (Global::IsOnline(mData->mMachineState))
2990 saveSettings(NULL);
2991
2992 return S_OK;
2993}
2994
2995STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2996{
2997 CheckComArgOutPointerValid(aDragAndDropMode);
2998
2999 AutoCaller autoCaller(this);
3000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3001
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 *aDragAndDropMode = mHWData->mDragAndDropMode;
3005
3006 return S_OK;
3007}
3008
3009STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3010{
3011 HRESULT rc = S_OK;
3012
3013 AutoCaller autoCaller(this);
3014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3015
3016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3017
3018 alock.release();
3019 rc = onDragAndDropModeChange(aDragAndDropMode);
3020 alock.acquire();
3021 if (FAILED(rc)) return rc;
3022
3023 setModified(IsModified_MachineData);
3024 mHWData.backup();
3025 mHWData->mDragAndDropMode = aDragAndDropMode;
3026
3027 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3028 if (Global::IsOnline(mData->mMachineState))
3029 saveSettings(NULL);
3030
3031 return S_OK;
3032}
3033
3034STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3035{
3036 CheckComArgOutPointerValid(aPatterns);
3037
3038 AutoCaller autoCaller(this);
3039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3040
3041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 try
3044 {
3045 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3046 }
3047 catch (...)
3048 {
3049 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3050 }
3051
3052 return S_OK;
3053}
3054
3055STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3056{
3057 AutoCaller autoCaller(this);
3058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3059
3060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3061
3062 HRESULT rc = checkStateDependency(MutableStateDep);
3063 if (FAILED(rc)) return rc;
3064
3065 setModified(IsModified_MachineData);
3066 mHWData.backup();
3067 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3068 return rc;
3069}
3070
3071STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3072{
3073 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3074
3075 AutoCaller autoCaller(this);
3076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3077
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3081 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3082
3083 return S_OK;
3084}
3085
3086STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3087{
3088 CheckComArgOutPointerValid(aEnabled);
3089
3090 AutoCaller autoCaller(this);
3091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3092
3093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 *aEnabled = mUserData->s.fTeleporterEnabled;
3096
3097 return S_OK;
3098}
3099
3100STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3101{
3102 AutoCaller autoCaller(this);
3103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3104
3105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3106
3107 /* Only allow it to be set to true when PoweredOff or Aborted.
3108 (Clearing it is always permitted.) */
3109 if ( aEnabled
3110 && mData->mRegistered
3111 && ( !isSessionMachine()
3112 || ( mData->mMachineState != MachineState_PoweredOff
3113 && mData->mMachineState != MachineState_Teleported
3114 && mData->mMachineState != MachineState_Aborted
3115 )
3116 )
3117 )
3118 return setError(VBOX_E_INVALID_VM_STATE,
3119 tr("The machine is not powered off (state is %s)"),
3120 Global::stringifyMachineState(mData->mMachineState));
3121
3122 setModified(IsModified_MachineData);
3123 mUserData.backup();
3124 mUserData->s.fTeleporterEnabled = !!aEnabled;
3125
3126 return S_OK;
3127}
3128
3129STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3130{
3131 CheckComArgOutPointerValid(aPort);
3132
3133 AutoCaller autoCaller(this);
3134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3135
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3139
3140 return S_OK;
3141}
3142
3143STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3144{
3145 if (aPort >= _64K)
3146 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3147
3148 AutoCaller autoCaller(this);
3149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3150
3151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3152
3153 HRESULT rc = checkStateDependency(MutableStateDep);
3154 if (FAILED(rc)) return rc;
3155
3156 setModified(IsModified_MachineData);
3157 mUserData.backup();
3158 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3164{
3165 CheckComArgOutPointerValid(aAddress);
3166
3167 AutoCaller autoCaller(this);
3168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3169
3170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3171
3172 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3173
3174 return S_OK;
3175}
3176
3177STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3178{
3179 AutoCaller autoCaller(this);
3180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3181
3182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 HRESULT rc = checkStateDependency(MutableStateDep);
3185 if (FAILED(rc)) return rc;
3186
3187 setModified(IsModified_MachineData);
3188 mUserData.backup();
3189 mUserData->s.strTeleporterAddress = aAddress;
3190
3191 return S_OK;
3192}
3193
3194STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3195{
3196 CheckComArgOutPointerValid(aPassword);
3197
3198 AutoCaller autoCaller(this);
3199 HRESULT hrc = autoCaller.rc();
3200 if (SUCCEEDED(hrc))
3201 {
3202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3203 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3204 }
3205
3206 return hrc;
3207}
3208
3209STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3210{
3211 /*
3212 * Hash the password first.
3213 */
3214 Utf8Str strPassword(aPassword);
3215 if (!strPassword.isEmpty())
3216 {
3217 if (VBoxIsPasswordHashed(&strPassword))
3218 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3219 VBoxHashPassword(&strPassword);
3220 }
3221
3222 /*
3223 * Do the update.
3224 */
3225 AutoCaller autoCaller(this);
3226 HRESULT hrc = autoCaller.rc();
3227 if (SUCCEEDED(hrc))
3228 {
3229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3230 hrc = checkStateDependency(MutableStateDep);
3231 if (SUCCEEDED(hrc))
3232 {
3233 setModified(IsModified_MachineData);
3234 mUserData.backup();
3235 mUserData->s.strTeleporterPassword = strPassword;
3236 }
3237 }
3238
3239 return hrc;
3240}
3241
3242STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3243{
3244 CheckComArgOutPointerValid(aState);
3245
3246 AutoCaller autoCaller(this);
3247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3248
3249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251 *aState = mUserData->s.enmFaultToleranceState;
3252 return S_OK;
3253}
3254
3255STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3256{
3257 AutoCaller autoCaller(this);
3258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3259
3260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3261
3262 /* @todo deal with running state change. */
3263 HRESULT rc = checkStateDependency(MutableStateDep);
3264 if (FAILED(rc)) return rc;
3265
3266 setModified(IsModified_MachineData);
3267 mUserData.backup();
3268 mUserData->s.enmFaultToleranceState = aState;
3269 return S_OK;
3270}
3271
3272STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3273{
3274 CheckComArgOutPointerValid(aAddress);
3275
3276 AutoCaller autoCaller(this);
3277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3278
3279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3280
3281 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3282 return S_OK;
3283}
3284
3285STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3286{
3287 AutoCaller autoCaller(this);
3288 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3289
3290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3291
3292 /* @todo deal with running state change. */
3293 HRESULT rc = checkStateDependency(MutableStateDep);
3294 if (FAILED(rc)) return rc;
3295
3296 setModified(IsModified_MachineData);
3297 mUserData.backup();
3298 mUserData->s.strFaultToleranceAddress = aAddress;
3299 return S_OK;
3300}
3301
3302STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3303{
3304 CheckComArgOutPointerValid(aPort);
3305
3306 AutoCaller autoCaller(this);
3307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3308
3309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3310
3311 *aPort = mUserData->s.uFaultTolerancePort;
3312 return S_OK;
3313}
3314
3315STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3316{
3317 AutoCaller autoCaller(this);
3318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3319
3320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3321
3322 /* @todo deal with running state change. */
3323 HRESULT rc = checkStateDependency(MutableStateDep);
3324 if (FAILED(rc)) return rc;
3325
3326 setModified(IsModified_MachineData);
3327 mUserData.backup();
3328 mUserData->s.uFaultTolerancePort = aPort;
3329 return S_OK;
3330}
3331
3332STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3333{
3334 CheckComArgOutPointerValid(aPassword);
3335
3336 AutoCaller autoCaller(this);
3337 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3338
3339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3340
3341 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3342
3343 return S_OK;
3344}
3345
3346STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3347{
3348 AutoCaller autoCaller(this);
3349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3350
3351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3352
3353 /* @todo deal with running state change. */
3354 HRESULT rc = checkStateDependency(MutableStateDep);
3355 if (FAILED(rc)) return rc;
3356
3357 setModified(IsModified_MachineData);
3358 mUserData.backup();
3359 mUserData->s.strFaultTolerancePassword = aPassword;
3360
3361 return S_OK;
3362}
3363
3364STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3365{
3366 CheckComArgOutPointerValid(aInterval);
3367
3368 AutoCaller autoCaller(this);
3369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3370
3371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3372
3373 *aInterval = mUserData->s.uFaultToleranceInterval;
3374 return S_OK;
3375}
3376
3377STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3378{
3379 AutoCaller autoCaller(this);
3380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3381
3382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3383
3384 /* @todo deal with running state change. */
3385 HRESULT rc = checkStateDependency(MutableStateDep);
3386 if (FAILED(rc)) return rc;
3387
3388 setModified(IsModified_MachineData);
3389 mUserData.backup();
3390 mUserData->s.uFaultToleranceInterval = aInterval;
3391 return S_OK;
3392}
3393
3394STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3395{
3396 CheckComArgOutPointerValid(aEnabled);
3397
3398 AutoCaller autoCaller(this);
3399 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3400
3401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3402
3403 *aEnabled = mUserData->s.fRTCUseUTC;
3404
3405 return S_OK;
3406}
3407
3408STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3409{
3410 AutoCaller autoCaller(this);
3411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3412
3413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3414
3415 /* Only allow it to be set to true when PoweredOff or Aborted.
3416 (Clearing it is always permitted.) */
3417 if ( aEnabled
3418 && mData->mRegistered
3419 && ( !isSessionMachine()
3420 || ( mData->mMachineState != MachineState_PoweredOff
3421 && mData->mMachineState != MachineState_Teleported
3422 && mData->mMachineState != MachineState_Aborted
3423 )
3424 )
3425 )
3426 return setError(VBOX_E_INVALID_VM_STATE,
3427 tr("The machine is not powered off (state is %s)"),
3428 Global::stringifyMachineState(mData->mMachineState));
3429
3430 setModified(IsModified_MachineData);
3431 mUserData.backup();
3432 mUserData->s.fRTCUseUTC = !!aEnabled;
3433
3434 return S_OK;
3435}
3436
3437STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(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 = mHWData->mIOCacheEnabled;
3447
3448 return S_OK;
3449}
3450
3451STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(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 HRESULT rc = checkStateDependency(MutableStateDep);
3459 if (FAILED(rc)) return rc;
3460
3461 setModified(IsModified_MachineData);
3462 mHWData.backup();
3463 mHWData->mIOCacheEnabled = aEnabled;
3464
3465 return S_OK;
3466}
3467
3468STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3469{
3470 CheckComArgOutPointerValid(aIOCacheSize);
3471
3472 AutoCaller autoCaller(this);
3473 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3474
3475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3476
3477 *aIOCacheSize = mHWData->mIOCacheSize;
3478
3479 return S_OK;
3480}
3481
3482STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3483{
3484 AutoCaller autoCaller(this);
3485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3486
3487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3488
3489 HRESULT rc = checkStateDependency(MutableStateDep);
3490 if (FAILED(rc)) return rc;
3491
3492 setModified(IsModified_MachineData);
3493 mHWData.backup();
3494 mHWData->mIOCacheSize = aIOCacheSize;
3495
3496 return S_OK;
3497}
3498
3499
3500/**
3501 * @note Locks objects!
3502 */
3503STDMETHODIMP Machine::LockMachine(ISession *aSession,
3504 LockType_T lockType)
3505{
3506 CheckComArgNotNull(aSession);
3507
3508 AutoCaller autoCaller(this);
3509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3510
3511 /* check the session state */
3512 SessionState_T state;
3513 HRESULT rc = aSession->COMGETTER(State)(&state);
3514 if (FAILED(rc)) return rc;
3515
3516 if (state != SessionState_Unlocked)
3517 return setError(VBOX_E_INVALID_OBJECT_STATE,
3518 tr("The given session is busy"));
3519
3520 // get the client's IInternalSessionControl interface
3521 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3522 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3523 E_INVALIDARG);
3524
3525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3526
3527 if (!mData->mRegistered)
3528 return setError(E_UNEXPECTED,
3529 tr("The machine '%s' is not registered"),
3530 mUserData->s.strName.c_str());
3531
3532 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3533
3534 SessionState_T oldState = mData->mSession.mState;
3535 /* Hack: in case the session is closing and there is a progress object
3536 * which allows waiting for the session to be closed, take the opportunity
3537 * and do a limited wait (max. 1 second). This helps a lot when the system
3538 * is busy and thus session closing can take a little while. */
3539 if ( mData->mSession.mState == SessionState_Unlocking
3540 && mData->mSession.mProgress)
3541 {
3542 alock.release();
3543 mData->mSession.mProgress->WaitForCompletion(1000);
3544 alock.acquire();
3545 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3546 }
3547
3548 // try again now
3549 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3550 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3551 )
3552 {
3553 // OK, share the session... we are now dealing with three processes:
3554 // 1) VBoxSVC (where this code runs);
3555 // 2) process C: the caller's client process (who wants a shared session);
3556 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3557
3558 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3559 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3560 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3561 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3562 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3563
3564 /*
3565 * Release the lock before calling the client process. It's safe here
3566 * since the only thing to do after we get the lock again is to add
3567 * the remote control to the list (which doesn't directly influence
3568 * anything).
3569 */
3570 alock.release();
3571
3572 // get the console of the session holding the write lock (this is a remote call)
3573 ComPtr<IConsole> pConsoleW;
3574 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3575 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3576 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3577 if (FAILED(rc))
3578 // the failure may occur w/o any error info (from RPC), so provide one
3579 return setError(VBOX_E_VM_ERROR,
3580 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3581
3582 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3583
3584 // share the session machine and W's console with the caller's session
3585 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3586 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3587 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3588
3589 if (FAILED(rc))
3590 // the failure may occur w/o any error info (from RPC), so provide one
3591 return setError(VBOX_E_VM_ERROR,
3592 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3593 alock.acquire();
3594
3595 // need to revalidate the state after acquiring the lock again
3596 if (mData->mSession.mState != SessionState_Locked)
3597 {
3598 pSessionControl->Uninitialize();
3599 return setError(VBOX_E_INVALID_SESSION_STATE,
3600 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3601 mUserData->s.strName.c_str());
3602 }
3603
3604 // add the caller's session to the list
3605 mData->mSession.mRemoteControls.push_back(pSessionControl);
3606 }
3607 else if ( mData->mSession.mState == SessionState_Locked
3608 || mData->mSession.mState == SessionState_Unlocking
3609 )
3610 {
3611 // sharing not permitted, or machine still unlocking:
3612 return setError(VBOX_E_INVALID_OBJECT_STATE,
3613 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3614 mUserData->s.strName.c_str());
3615 }
3616 else
3617 {
3618 // machine is not locked: then write-lock the machine (create the session machine)
3619
3620 // must not be busy
3621 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3622
3623 // get the caller's session PID
3624 RTPROCESS pid = NIL_RTPROCESS;
3625 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3626 pSessionControl->GetPID((ULONG*)&pid);
3627 Assert(pid != NIL_RTPROCESS);
3628
3629 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3630
3631 if (fLaunchingVMProcess)
3632 {
3633 if (mData->mSession.mPID == NIL_RTPROCESS)
3634 {
3635 // two or more clients racing for a lock, the one which set the
3636 // session state to Spawning will win, the others will get an
3637 // error as we can't decide here if waiting a little would help
3638 // (only for shared locks this would avoid an error)
3639 return setError(VBOX_E_INVALID_OBJECT_STATE,
3640 tr("The machine '%s' already has a lock request pending"),
3641 mUserData->s.strName.c_str());
3642 }
3643
3644 // this machine is awaiting for a spawning session to be opened:
3645 // then the calling process must be the one that got started by
3646 // LaunchVMProcess()
3647
3648 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3649 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3650
3651 if (mData->mSession.mPID != pid)
3652 return setError(E_ACCESSDENIED,
3653 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3654 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3655 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3656 }
3657
3658 // create the mutable SessionMachine from the current machine
3659 ComObjPtr<SessionMachine> sessionMachine;
3660 sessionMachine.createObject();
3661 rc = sessionMachine->init(this);
3662 AssertComRC(rc);
3663
3664 /* NOTE: doing return from this function after this point but
3665 * before the end is forbidden since it may call SessionMachine::uninit()
3666 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3667 * lock while still holding the Machine lock in alock so that a deadlock
3668 * is possible due to the wrong lock order. */
3669
3670 if (SUCCEEDED(rc))
3671 {
3672 /*
3673 * Set the session state to Spawning to protect against subsequent
3674 * attempts to open a session and to unregister the machine after
3675 * we release the lock.
3676 */
3677 SessionState_T origState = mData->mSession.mState;
3678 mData->mSession.mState = SessionState_Spawning;
3679
3680#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3681 /* Get the client token ID to be passed to the client process */
3682 Utf8Str strTokenId;
3683 sessionMachine->getTokenId(strTokenId);
3684 Assert(!strTokenId.isEmpty());
3685#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3686 /* Get the client token to be passed to the client process */
3687 ComPtr<IToken> pToken(sessionMachine->getToken());
3688 /* The token is now "owned" by pToken, fix refcount */
3689 if (!pToken.isNull())
3690 pToken->Release();
3691#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3692
3693 /*
3694 * Release the lock before calling the client process -- it will call
3695 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3696 * because the state is Spawning, so that LaunchVMProcess() and
3697 * LockMachine() calls will fail. This method, called before we
3698 * acquire the lock again, will fail because of the wrong PID.
3699 *
3700 * Note that mData->mSession.mRemoteControls accessed outside
3701 * the lock may not be modified when state is Spawning, so it's safe.
3702 */
3703 alock.release();
3704
3705 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3706#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3707 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3708#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3709 rc = pSessionControl->AssignMachine(sessionMachine, lockType, pToken);
3710 /* Now the token is owned by the client process. */
3711 pToken.setNull();
3712#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3713 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3714
3715 /* The failure may occur w/o any error info (from RPC), so provide one */
3716 if (FAILED(rc))
3717 setError(VBOX_E_VM_ERROR,
3718 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3719
3720 if ( SUCCEEDED(rc)
3721 && fLaunchingVMProcess
3722 )
3723 {
3724 /* complete the remote session initialization */
3725
3726 /* get the console from the direct session */
3727 ComPtr<IConsole> console;
3728 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3729 ComAssertComRC(rc);
3730
3731 if (SUCCEEDED(rc) && !console)
3732 {
3733 ComAssert(!!console);
3734 rc = E_FAIL;
3735 }
3736
3737 /* assign machine & console to the remote session */
3738 if (SUCCEEDED(rc))
3739 {
3740 /*
3741 * after LaunchVMProcess(), the first and the only
3742 * entry in remoteControls is that remote session
3743 */
3744 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3745 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3746 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3747
3748 /* The failure may occur w/o any error info (from RPC), so provide one */
3749 if (FAILED(rc))
3750 setError(VBOX_E_VM_ERROR,
3751 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3752 }
3753
3754 if (FAILED(rc))
3755 pSessionControl->Uninitialize();
3756 }
3757
3758 /* acquire the lock again */
3759 alock.acquire();
3760
3761 /* Restore the session state */
3762 mData->mSession.mState = origState;
3763 }
3764
3765 // finalize spawning anyway (this is why we don't return on errors above)
3766 if (fLaunchingVMProcess)
3767 {
3768 /* Note that the progress object is finalized later */
3769 /** @todo Consider checking mData->mSession.mProgress for cancellation
3770 * around here. */
3771
3772 /* We don't reset mSession.mPID here because it is necessary for
3773 * SessionMachine::uninit() to reap the child process later. */
3774
3775 if (FAILED(rc))
3776 {
3777 /* Close the remote session, remove the remote control from the list
3778 * and reset session state to Closed (@note keep the code in sync
3779 * with the relevant part in checkForSpawnFailure()). */
3780
3781 Assert(mData->mSession.mRemoteControls.size() == 1);
3782 if (mData->mSession.mRemoteControls.size() == 1)
3783 {
3784 ErrorInfoKeeper eik;
3785 mData->mSession.mRemoteControls.front()->Uninitialize();
3786 }
3787
3788 mData->mSession.mRemoteControls.clear();
3789 mData->mSession.mState = SessionState_Unlocked;
3790 }
3791 }
3792 else
3793 {
3794 /* memorize PID of the directly opened session */
3795 if (SUCCEEDED(rc))
3796 mData->mSession.mPID = pid;
3797 }
3798
3799 if (SUCCEEDED(rc))
3800 {
3801 /* memorize the direct session control and cache IUnknown for it */
3802 mData->mSession.mDirectControl = pSessionControl;
3803 mData->mSession.mState = SessionState_Locked;
3804 /* associate the SessionMachine with this Machine */
3805 mData->mSession.mMachine = sessionMachine;
3806
3807 /* request an IUnknown pointer early from the remote party for later
3808 * identity checks (it will be internally cached within mDirectControl
3809 * at least on XPCOM) */
3810 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3811 NOREF(unk);
3812 }
3813
3814 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3815 * would break the lock order */
3816 alock.release();
3817
3818 /* uninitialize the created session machine on failure */
3819 if (FAILED(rc))
3820 sessionMachine->uninit();
3821
3822 }
3823
3824 if (SUCCEEDED(rc))
3825 {
3826 /*
3827 * tell the client watcher thread to update the set of
3828 * machines that have open sessions
3829 */
3830 mParent->updateClientWatcher();
3831
3832 if (oldState != SessionState_Locked)
3833 /* fire an event */
3834 mParent->onSessionStateChange(getId(), SessionState_Locked);
3835 }
3836
3837 return rc;
3838}
3839
3840/**
3841 * @note Locks objects!
3842 */
3843STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3844 IN_BSTR aFrontend,
3845 IN_BSTR aEnvironment,
3846 IProgress **aProgress)
3847{
3848 CheckComArgStr(aFrontend);
3849 Utf8Str strFrontend(aFrontend);
3850 Utf8Str strEnvironment(aEnvironment);
3851 /* "emergencystop" doesn't need the session, so skip the checks/interface
3852 * retrieval. This code doesn't quite fit in here, but introducing a
3853 * special API method would be even more effort, and would require explicit
3854 * support by every API client. It's better to hide the feature a bit. */
3855 if (strFrontend != "emergencystop")
3856 CheckComArgNotNull(aSession);
3857 CheckComArgOutPointerValid(aProgress);
3858
3859 AutoCaller autoCaller(this);
3860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3861
3862 HRESULT rc = S_OK;
3863 if (strFrontend.isEmpty())
3864 {
3865 Bstr bstrFrontend;
3866 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3867 if (FAILED(rc))
3868 return rc;
3869 strFrontend = bstrFrontend;
3870 if (strFrontend.isEmpty())
3871 {
3872 ComPtr<ISystemProperties> systemProperties;
3873 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3874 if (FAILED(rc))
3875 return rc;
3876 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3877 if (FAILED(rc))
3878 return rc;
3879 strFrontend = bstrFrontend;
3880 }
3881 /* paranoia - emergencystop is not a valid default */
3882 if (strFrontend == "emergencystop")
3883 strFrontend = Utf8Str::Empty;
3884 }
3885 /* default frontend: Qt GUI */
3886 if (strFrontend.isEmpty())
3887 strFrontend = "GUI/Qt";
3888
3889 if (strFrontend != "emergencystop")
3890 {
3891 /* check the session state */
3892 SessionState_T state;
3893 rc = aSession->COMGETTER(State)(&state);
3894 if (FAILED(rc))
3895 return rc;
3896
3897 if (state != SessionState_Unlocked)
3898 return setError(VBOX_E_INVALID_OBJECT_STATE,
3899 tr("The given session is busy"));
3900
3901 /* get the IInternalSessionControl interface */
3902 ComPtr<IInternalSessionControl> control(aSession);
3903 ComAssertMsgRet(!control.isNull(),
3904 ("No IInternalSessionControl interface"),
3905 E_INVALIDARG);
3906
3907 /* get the teleporter enable state for the progress object init. */
3908 BOOL fTeleporterEnabled;
3909 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3910 if (FAILED(rc))
3911 return rc;
3912
3913 /* create a progress object */
3914 ComObjPtr<ProgressProxy> progress;
3915 progress.createObject();
3916 rc = progress->init(mParent,
3917 static_cast<IMachine*>(this),
3918 Bstr(tr("Starting VM")).raw(),
3919 TRUE /* aCancelable */,
3920 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3921 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3922 2 /* uFirstOperationWeight */,
3923 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3924
3925 if (SUCCEEDED(rc))
3926 {
3927 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3928 if (SUCCEEDED(rc))
3929 {
3930 progress.queryInterfaceTo(aProgress);
3931
3932 /* signal the client watcher thread */
3933 mParent->updateClientWatcher();
3934
3935 /* fire an event */
3936 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3937 }
3938 }
3939 }
3940 else
3941 {
3942 /* no progress object - either instant success or failure */
3943 *aProgress = NULL;
3944
3945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3946
3947 if (mData->mSession.mState != SessionState_Locked)
3948 return setError(VBOX_E_INVALID_OBJECT_STATE,
3949 tr("The machine '%s' is not locked by a session"),
3950 mUserData->s.strName.c_str());
3951
3952 /* must have a VM process associated - do not kill normal API clients
3953 * with an open session */
3954 if (!Global::IsOnline(mData->mMachineState))
3955 return setError(VBOX_E_INVALID_OBJECT_STATE,
3956 tr("The machine '%s' does not have a VM process"),
3957 mUserData->s.strName.c_str());
3958
3959 /* forcibly terminate the VM process */
3960 if (mData->mSession.mPID != NIL_RTPROCESS)
3961 RTProcTerminate(mData->mSession.mPID);
3962
3963 /* signal the client watcher thread, as most likely the client has
3964 * been terminated */
3965 mParent->updateClientWatcher();
3966 }
3967
3968 return rc;
3969}
3970
3971STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3972{
3973 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3974 return setError(E_INVALIDARG,
3975 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3976 aPosition, SchemaDefs::MaxBootPosition);
3977
3978 if (aDevice == DeviceType_USB)
3979 return setError(E_NOTIMPL,
3980 tr("Booting from USB device is currently not supported"));
3981
3982 AutoCaller autoCaller(this);
3983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3984
3985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3986
3987 HRESULT rc = checkStateDependency(MutableStateDep);
3988 if (FAILED(rc)) return rc;
3989
3990 setModified(IsModified_MachineData);
3991 mHWData.backup();
3992 mHWData->mBootOrder[aPosition - 1] = aDevice;
3993
3994 return S_OK;
3995}
3996
3997STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3998{
3999 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4000 return setError(E_INVALIDARG,
4001 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4002 aPosition, SchemaDefs::MaxBootPosition);
4003
4004 AutoCaller autoCaller(this);
4005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4006
4007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4008
4009 *aDevice = mHWData->mBootOrder[aPosition - 1];
4010
4011 return S_OK;
4012}
4013
4014STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4015 LONG aControllerPort,
4016 LONG aDevice,
4017 DeviceType_T aType,
4018 IMedium *aMedium)
4019{
4020 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4021 aControllerName, aControllerPort, aDevice, aType, aMedium));
4022
4023 CheckComArgStrNotEmptyOrNull(aControllerName);
4024
4025 AutoCaller autoCaller(this);
4026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4027
4028 // request the host lock first, since might be calling Host methods for getting host drives;
4029 // next, protect the media tree all the while we're in here, as well as our member variables
4030 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4031 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4032
4033 HRESULT rc = checkStateDependency(MutableStateDep);
4034 if (FAILED(rc)) return rc;
4035
4036 /// @todo NEWMEDIA implicit machine registration
4037 if (!mData->mRegistered)
4038 return setError(VBOX_E_INVALID_OBJECT_STATE,
4039 tr("Cannot attach storage devices to an unregistered machine"));
4040
4041 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4042
4043 /* Check for an existing controller. */
4044 ComObjPtr<StorageController> ctl;
4045 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4046 if (FAILED(rc)) return rc;
4047
4048 StorageControllerType_T ctrlType;
4049 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4050 if (FAILED(rc))
4051 return setError(E_FAIL,
4052 tr("Could not get type of controller '%ls'"),
4053 aControllerName);
4054
4055 bool fSilent = false;
4056 Utf8Str strReconfig;
4057
4058 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4059 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4060 if ( mData->mMachineState == MachineState_Paused
4061 && strReconfig == "1")
4062 fSilent = true;
4063
4064 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4065 bool fHotplug = false;
4066 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4067 fHotplug = true;
4068
4069 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4070 return setError(VBOX_E_INVALID_VM_STATE,
4071 tr("Controller '%ls' does not support hotplugging"),
4072 aControllerName);
4073
4074 // check that the port and device are not out of range
4075 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
4076 if (FAILED(rc)) return rc;
4077
4078 /* check if the device slot is already busy */
4079 MediumAttachment *pAttachTemp;
4080 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4081 aControllerName,
4082 aControllerPort,
4083 aDevice)))
4084 {
4085 Medium *pMedium = pAttachTemp->i_getMedium();
4086 if (pMedium)
4087 {
4088 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4089 return setError(VBOX_E_OBJECT_IN_USE,
4090 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4091 pMedium->i_getLocationFull().c_str(),
4092 aControllerPort,
4093 aDevice,
4094 aControllerName);
4095 }
4096 else
4097 return setError(VBOX_E_OBJECT_IN_USE,
4098 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4099 aControllerPort, aDevice, aControllerName);
4100 }
4101
4102 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4103 if (aMedium && medium.isNull())
4104 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4105
4106 AutoCaller mediumCaller(medium);
4107 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4108
4109 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4110
4111 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4112 && !medium.isNull()
4113 )
4114 return setError(VBOX_E_OBJECT_IN_USE,
4115 tr("Medium '%s' is already attached to this virtual machine"),
4116 medium->i_getLocationFull().c_str());
4117
4118 if (!medium.isNull())
4119 {
4120 MediumType_T mtype = medium->i_getType();
4121 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4122 // For DVDs it's not written to the config file, so needs no global config
4123 // version bump. For floppies it's a new attribute "type", which is ignored
4124 // by older VirtualBox version, so needs no global config version bump either.
4125 // For hard disks this type is not accepted.
4126 if (mtype == MediumType_MultiAttach)
4127 {
4128 // This type is new with VirtualBox 4.0 and therefore requires settings
4129 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4130 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4131 // two reasons: The medium type is a property of the media registry tree, which
4132 // can reside in the global config file (for pre-4.0 media); we would therefore
4133 // possibly need to bump the global config version. We don't want to do that though
4134 // because that might make downgrading to pre-4.0 impossible.
4135 // As a result, we can only use these two new types if the medium is NOT in the
4136 // global registry:
4137 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4138 if ( medium->i_isInRegistry(uuidGlobalRegistry)
4139 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4140 )
4141 return setError(VBOX_E_INVALID_OBJECT_STATE,
4142 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4143 "to machines that were created with VirtualBox 4.0 or later"),
4144 medium->i_getLocationFull().c_str());
4145 }
4146 }
4147
4148 bool fIndirect = false;
4149 if (!medium.isNull())
4150 fIndirect = medium->i_isReadOnly();
4151 bool associate = true;
4152
4153 do
4154 {
4155 if ( aType == DeviceType_HardDisk
4156 && mMediaData.isBackedUp())
4157 {
4158 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4159
4160 /* check if the medium was attached to the VM before we started
4161 * changing attachments in which case the attachment just needs to
4162 * be restored */
4163 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4164 {
4165 AssertReturn(!fIndirect, E_FAIL);
4166
4167 /* see if it's the same bus/channel/device */
4168 if (pAttachTemp->i_matches(aControllerName, aControllerPort, aDevice))
4169 {
4170 /* the simplest case: restore the whole attachment
4171 * and return, nothing else to do */
4172 mMediaData->mAttachments.push_back(pAttachTemp);
4173
4174 /* Reattach the medium to the VM. */
4175 if (fHotplug || fSilent)
4176 {
4177 mediumLock.release();
4178 treeLock.release();
4179 alock.release();
4180
4181 MediumLockList *pMediumLockList(new MediumLockList());
4182
4183 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4184 true /* fMediumLockWrite */,
4185 NULL,
4186 *pMediumLockList);
4187 alock.acquire();
4188 if (FAILED(rc))
4189 delete pMediumLockList;
4190 else
4191 {
4192 mData->mSession.mLockedMedia.Unlock();
4193 alock.release();
4194 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4195 mData->mSession.mLockedMedia.Lock();
4196 alock.acquire();
4197 }
4198 alock.release();
4199
4200 if (SUCCEEDED(rc))
4201 {
4202 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4203 /* Remove lock list in case of error. */
4204 if (FAILED(rc))
4205 {
4206 mData->mSession.mLockedMedia.Unlock();
4207 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4208 mData->mSession.mLockedMedia.Lock();
4209 }
4210 }
4211 }
4212
4213 return S_OK;
4214 }
4215
4216 /* bus/channel/device differ; we need a new attachment object,
4217 * but don't try to associate it again */
4218 associate = false;
4219 break;
4220 }
4221 }
4222
4223 /* go further only if the attachment is to be indirect */
4224 if (!fIndirect)
4225 break;
4226
4227 /* perform the so called smart attachment logic for indirect
4228 * attachments. Note that smart attachment is only applicable to base
4229 * hard disks. */
4230
4231 if (medium->i_getParent().isNull())
4232 {
4233 /* first, investigate the backup copy of the current hard disk
4234 * attachments to make it possible to re-attach existing diffs to
4235 * another device slot w/o losing their contents */
4236 if (mMediaData.isBackedUp())
4237 {
4238 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4239
4240 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4241 uint32_t foundLevel = 0;
4242
4243 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4244 it != oldAtts.end();
4245 ++it)
4246 {
4247 uint32_t level = 0;
4248 MediumAttachment *pAttach = *it;
4249 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4250 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4251 if (pMedium.isNull())
4252 continue;
4253
4254 if (pMedium->i_getBase(&level) == medium)
4255 {
4256 /* skip the hard disk if its currently attached (we
4257 * cannot attach the same hard disk twice) */
4258 if (findAttachment(mMediaData->mAttachments,
4259 pMedium))
4260 continue;
4261
4262 /* matched device, channel and bus (i.e. attached to the
4263 * same place) will win and immediately stop the search;
4264 * otherwise the attachment that has the youngest
4265 * descendant of medium will be used
4266 */
4267 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
4268 {
4269 /* the simplest case: restore the whole attachment
4270 * and return, nothing else to do */
4271 mMediaData->mAttachments.push_back(*it);
4272
4273 /* Reattach the medium to the VM. */
4274 if (fHotplug || fSilent)
4275 {
4276 mediumLock.release();
4277 treeLock.release();
4278 alock.release();
4279
4280 MediumLockList *pMediumLockList(new MediumLockList());
4281
4282 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4283 true /* fMediumLockWrite */,
4284 NULL,
4285 *pMediumLockList);
4286 alock.acquire();
4287 if (FAILED(rc))
4288 delete pMediumLockList;
4289 else
4290 {
4291 mData->mSession.mLockedMedia.Unlock();
4292 alock.release();
4293 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4294 mData->mSession.mLockedMedia.Lock();
4295 alock.acquire();
4296 }
4297 alock.release();
4298
4299 if (SUCCEEDED(rc))
4300 {
4301 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4302 /* Remove lock list in case of error. */
4303 if (FAILED(rc))
4304 {
4305 mData->mSession.mLockedMedia.Unlock();
4306 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4307 mData->mSession.mLockedMedia.Lock();
4308 }
4309 }
4310 }
4311
4312 return S_OK;
4313 }
4314 else if ( foundIt == oldAtts.end()
4315 || level > foundLevel /* prefer younger */
4316 )
4317 {
4318 foundIt = it;
4319 foundLevel = level;
4320 }
4321 }
4322 }
4323
4324 if (foundIt != oldAtts.end())
4325 {
4326 /* use the previously attached hard disk */
4327 medium = (*foundIt)->i_getMedium();
4328 mediumCaller.attach(medium);
4329 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4330 mediumLock.attach(medium);
4331 /* not implicit, doesn't require association with this VM */
4332 fIndirect = false;
4333 associate = false;
4334 /* go right to the MediumAttachment creation */
4335 break;
4336 }
4337 }
4338
4339 /* must give up the medium lock and medium tree lock as below we
4340 * go over snapshots, which needs a lock with higher lock order. */
4341 mediumLock.release();
4342 treeLock.release();
4343
4344 /* then, search through snapshots for the best diff in the given
4345 * hard disk's chain to base the new diff on */
4346
4347 ComObjPtr<Medium> base;
4348 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4349 while (snap)
4350 {
4351 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4352
4353 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4354
4355 MediumAttachment *pAttachFound = NULL;
4356 uint32_t foundLevel = 0;
4357
4358 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4359 it != snapAtts.end();
4360 ++it)
4361 {
4362 MediumAttachment *pAttach = *it;
4363 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4364 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4365 if (pMedium.isNull())
4366 continue;
4367
4368 uint32_t level = 0;
4369 if (pMedium->i_getBase(&level) == medium)
4370 {
4371 /* matched device, channel and bus (i.e. attached to the
4372 * same place) will win and immediately stop the search;
4373 * otherwise the attachment that has the youngest
4374 * descendant of medium will be used
4375 */
4376 if ( pAttach->i_getDevice() == aDevice
4377 && pAttach->i_getPort() == aControllerPort
4378 && pAttach->i_getControllerName() == aControllerName
4379 )
4380 {
4381 pAttachFound = pAttach;
4382 break;
4383 }
4384 else if ( !pAttachFound
4385 || level > foundLevel /* prefer younger */
4386 )
4387 {
4388 pAttachFound = pAttach;
4389 foundLevel = level;
4390 }
4391 }
4392 }
4393
4394 if (pAttachFound)
4395 {
4396 base = pAttachFound->i_getMedium();
4397 break;
4398 }
4399
4400 snap = snap->i_getParent();
4401 }
4402
4403 /* re-lock medium tree and the medium, as we need it below */
4404 treeLock.acquire();
4405 mediumLock.acquire();
4406
4407 /* found a suitable diff, use it as a base */
4408 if (!base.isNull())
4409 {
4410 medium = base;
4411 mediumCaller.attach(medium);
4412 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4413 mediumLock.attach(medium);
4414 }
4415 }
4416
4417 Utf8Str strFullSnapshotFolder;
4418 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4419
4420 ComObjPtr<Medium> diff;
4421 diff.createObject();
4422 // store this diff in the same registry as the parent
4423 Guid uuidRegistryParent;
4424 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4425 {
4426 // parent image has no registry: this can happen if we're attaching a new immutable
4427 // image that has not yet been attached (medium then points to the base and we're
4428 // creating the diff image for the immutable, and the parent is not yet registered);
4429 // put the parent in the machine registry then
4430 mediumLock.release();
4431 treeLock.release();
4432 alock.release();
4433 addMediumToRegistry(medium);
4434 alock.acquire();
4435 treeLock.acquire();
4436 mediumLock.acquire();
4437 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4438 }
4439 rc = diff->init(mParent,
4440 medium->i_getPreferredDiffFormat(),
4441 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4442 uuidRegistryParent);
4443 if (FAILED(rc)) return rc;
4444
4445 /* Apply the normal locking logic to the entire chain. */
4446 MediumLockList *pMediumLockList(new MediumLockList());
4447 mediumLock.release();
4448 treeLock.release();
4449 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4450 true /* fMediumLockWrite */,
4451 medium,
4452 *pMediumLockList);
4453 treeLock.acquire();
4454 mediumLock.acquire();
4455 if (SUCCEEDED(rc))
4456 {
4457 mediumLock.release();
4458 treeLock.release();
4459 rc = pMediumLockList->Lock();
4460 treeLock.acquire();
4461 mediumLock.acquire();
4462 if (FAILED(rc))
4463 setError(rc,
4464 tr("Could not lock medium when creating diff '%s'"),
4465 diff->i_getLocationFull().c_str());
4466 else
4467 {
4468 /* will release the lock before the potentially lengthy
4469 * operation, so protect with the special state */
4470 MachineState_T oldState = mData->mMachineState;
4471 setMachineState(MachineState_SettingUp);
4472
4473 mediumLock.release();
4474 treeLock.release();
4475 alock.release();
4476
4477 rc = medium->i_createDiffStorage(diff,
4478 MediumVariant_Standard,
4479 pMediumLockList,
4480 NULL /* aProgress */,
4481 true /* aWait */);
4482
4483 alock.acquire();
4484 treeLock.acquire();
4485 mediumLock.acquire();
4486
4487 setMachineState(oldState);
4488 }
4489 }
4490
4491 /* Unlock the media and free the associated memory. */
4492 delete pMediumLockList;
4493
4494 if (FAILED(rc)) return rc;
4495
4496 /* use the created diff for the actual attachment */
4497 medium = diff;
4498 mediumCaller.attach(medium);
4499 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4500 mediumLock.attach(medium);
4501 }
4502 while (0);
4503
4504 ComObjPtr<MediumAttachment> attachment;
4505 attachment.createObject();
4506 rc = attachment->init(this,
4507 medium,
4508 aControllerName,
4509 aControllerPort,
4510 aDevice,
4511 aType,
4512 fIndirect,
4513 false /* fPassthrough */,
4514 false /* fTempEject */,
4515 false /* fNonRotational */,
4516 false /* fDiscard */,
4517 fHotplug /* fHotPluggable */,
4518 Utf8Str::Empty);
4519 if (FAILED(rc)) return rc;
4520
4521 if (associate && !medium.isNull())
4522 {
4523 // as the last step, associate the medium to the VM
4524 rc = medium->i_addBackReference(mData->mUuid);
4525 // here we can fail because of Deleting, or being in process of creating a Diff
4526 if (FAILED(rc)) return rc;
4527
4528 mediumLock.release();
4529 treeLock.release();
4530 alock.release();
4531 addMediumToRegistry(medium);
4532 alock.acquire();
4533 treeLock.acquire();
4534 mediumLock.acquire();
4535 }
4536
4537 /* success: finally remember the attachment */
4538 setModified(IsModified_Storage);
4539 mMediaData.backup();
4540 mMediaData->mAttachments.push_back(attachment);
4541
4542 mediumLock.release();
4543 treeLock.release();
4544 alock.release();
4545
4546 if (fHotplug || fSilent)
4547 {
4548 if (!medium.isNull())
4549 {
4550 MediumLockList *pMediumLockList(new MediumLockList());
4551
4552 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4553 true /* fMediumLockWrite */,
4554 NULL,
4555 *pMediumLockList);
4556 alock.acquire();
4557 if (FAILED(rc))
4558 delete pMediumLockList;
4559 else
4560 {
4561 mData->mSession.mLockedMedia.Unlock();
4562 alock.release();
4563 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4564 mData->mSession.mLockedMedia.Lock();
4565 alock.acquire();
4566 }
4567 alock.release();
4568 }
4569
4570 if (SUCCEEDED(rc))
4571 {
4572 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4573 /* Remove lock list in case of error. */
4574 if (FAILED(rc))
4575 {
4576 mData->mSession.mLockedMedia.Unlock();
4577 mData->mSession.mLockedMedia.Remove(attachment);
4578 mData->mSession.mLockedMedia.Lock();
4579 }
4580 }
4581 }
4582
4583 mParent->saveModifiedRegistries();
4584
4585 return rc;
4586}
4587
4588STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4589 LONG aDevice)
4590{
4591 CheckComArgStrNotEmptyOrNull(aControllerName);
4592
4593 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4594 aControllerName, aControllerPort, aDevice));
4595
4596 AutoCaller autoCaller(this);
4597 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4598
4599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4600
4601 HRESULT rc = checkStateDependency(MutableStateDep);
4602 if (FAILED(rc)) return rc;
4603
4604 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4605
4606 /* Check for an existing controller. */
4607 ComObjPtr<StorageController> ctl;
4608 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4609 if (FAILED(rc)) return rc;
4610
4611 StorageControllerType_T ctrlType;
4612 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4613 if (FAILED(rc))
4614 return setError(E_FAIL,
4615 tr("Could not get type of controller '%ls'"),
4616 aControllerName);
4617
4618 bool fSilent = false;
4619 Utf8Str strReconfig;
4620
4621 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4622 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4623 if ( mData->mMachineState == MachineState_Paused
4624 && strReconfig == "1")
4625 fSilent = true;
4626
4627 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4628 bool fHotplug = false;
4629 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4630 fHotplug = true;
4631
4632 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4633 return setError(VBOX_E_INVALID_VM_STATE,
4634 tr("Controller '%ls' does not support hotplugging"),
4635 aControllerName);
4636
4637 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4638 aControllerName,
4639 aControllerPort,
4640 aDevice);
4641 if (!pAttach)
4642 return setError(VBOX_E_OBJECT_NOT_FOUND,
4643 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4644 aDevice, aControllerPort, aControllerName);
4645
4646 if (fHotplug && !pAttach->i_getHotPluggable())
4647 return setError(VBOX_E_NOT_SUPPORTED,
4648 tr("The device slot %d on port %d of controller '%ls' does not support hotplugging"),
4649 aDevice, aControllerPort, aControllerName);
4650
4651 /*
4652 * The VM has to detach the device before we delete any implicit diffs.
4653 * If this fails we can roll back without loosing data.
4654 */
4655 if (fHotplug || fSilent)
4656 {
4657 alock.release();
4658 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4659 alock.acquire();
4660 }
4661 if (FAILED(rc)) return rc;
4662
4663 /* If we are here everything went well and we can delete the implicit now. */
4664 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4665
4666 alock.release();
4667
4668 mParent->saveModifiedRegistries();
4669
4670 return rc;
4671}
4672
4673STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4674 LONG aDevice, BOOL aPassthrough)
4675{
4676 CheckComArgStrNotEmptyOrNull(aControllerName);
4677
4678 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4679 aControllerName, aControllerPort, aDevice, aPassthrough));
4680
4681 AutoCaller autoCaller(this);
4682 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4683
4684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4685
4686 HRESULT rc = checkStateDependency(MutableStateDep);
4687 if (FAILED(rc)) return rc;
4688
4689 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4690
4691 if (Global::IsOnlineOrTransient(mData->mMachineState))
4692 return setError(VBOX_E_INVALID_VM_STATE,
4693 tr("Invalid machine state: %s"),
4694 Global::stringifyMachineState(mData->mMachineState));
4695
4696 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4697 aControllerName,
4698 aControllerPort,
4699 aDevice);
4700 if (!pAttach)
4701 return setError(VBOX_E_OBJECT_NOT_FOUND,
4702 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4703 aDevice, aControllerPort, aControllerName);
4704
4705
4706 setModified(IsModified_Storage);
4707 mMediaData.backup();
4708
4709 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4710
4711 if (pAttach->i_getType() != DeviceType_DVD)
4712 return setError(E_INVALIDARG,
4713 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4714 aDevice, aControllerPort, aControllerName);
4715 pAttach->i_updatePassthrough(!!aPassthrough);
4716
4717 return S_OK;
4718}
4719
4720STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4721 LONG aDevice, BOOL aTemporaryEject)
4722{
4723 CheckComArgStrNotEmptyOrNull(aControllerName);
4724
4725 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4726 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4727
4728 AutoCaller autoCaller(this);
4729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4730
4731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4732
4733 HRESULT rc = checkStateDependency(MutableStateDep);
4734 if (FAILED(rc)) return rc;
4735
4736 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4737 aControllerName,
4738 aControllerPort,
4739 aDevice);
4740 if (!pAttach)
4741 return setError(VBOX_E_OBJECT_NOT_FOUND,
4742 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4743 aDevice, aControllerPort, aControllerName);
4744
4745
4746 setModified(IsModified_Storage);
4747 mMediaData.backup();
4748
4749 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4750
4751 if (pAttach->i_getType() != DeviceType_DVD)
4752 return setError(E_INVALIDARG,
4753 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4754 aDevice, aControllerPort, aControllerName);
4755 pAttach->i_updateTempEject(!!aTemporaryEject);
4756
4757 return S_OK;
4758}
4759
4760STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4761 LONG aDevice, BOOL aNonRotational)
4762{
4763 CheckComArgStrNotEmptyOrNull(aControllerName);
4764
4765 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4766 aControllerName, aControllerPort, aDevice, aNonRotational));
4767
4768 AutoCaller autoCaller(this);
4769 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4770
4771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4772
4773 HRESULT rc = checkStateDependency(MutableStateDep);
4774 if (FAILED(rc)) return rc;
4775
4776 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4777
4778 if (Global::IsOnlineOrTransient(mData->mMachineState))
4779 return setError(VBOX_E_INVALID_VM_STATE,
4780 tr("Invalid machine state: %s"),
4781 Global::stringifyMachineState(mData->mMachineState));
4782
4783 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4784 aControllerName,
4785 aControllerPort,
4786 aDevice);
4787 if (!pAttach)
4788 return setError(VBOX_E_OBJECT_NOT_FOUND,
4789 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4790 aDevice, aControllerPort, aControllerName);
4791
4792
4793 setModified(IsModified_Storage);
4794 mMediaData.backup();
4795
4796 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4797
4798 if (pAttach->i_getType() != DeviceType_HardDisk)
4799 return setError(E_INVALIDARG,
4800 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"),
4801 aDevice, aControllerPort, aControllerName);
4802 pAttach->i_updateNonRotational(!!aNonRotational);
4803
4804 return S_OK;
4805}
4806
4807STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4808 LONG aDevice, BOOL aDiscard)
4809{
4810 CheckComArgStrNotEmptyOrNull(aControllerName);
4811
4812 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4813 aControllerName, aControllerPort, aDevice, aDiscard));
4814
4815 AutoCaller autoCaller(this);
4816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4817
4818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4819
4820 HRESULT rc = checkStateDependency(MutableStateDep);
4821 if (FAILED(rc)) return rc;
4822
4823 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4824
4825 if (Global::IsOnlineOrTransient(mData->mMachineState))
4826 return setError(VBOX_E_INVALID_VM_STATE,
4827 tr("Invalid machine state: %s"),
4828 Global::stringifyMachineState(mData->mMachineState));
4829
4830 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4831 aControllerName,
4832 aControllerPort,
4833 aDevice);
4834 if (!pAttach)
4835 return setError(VBOX_E_OBJECT_NOT_FOUND,
4836 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4837 aDevice, aControllerPort, aControllerName);
4838
4839
4840 setModified(IsModified_Storage);
4841 mMediaData.backup();
4842
4843 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4844
4845 if (pAttach->i_getType() != DeviceType_HardDisk)
4846 return setError(E_INVALIDARG,
4847 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"),
4848 aDevice, aControllerPort, aControllerName);
4849 pAttach->i_updateDiscard(!!aDiscard);
4850
4851 return S_OK;
4852}
4853
4854STDMETHODIMP Machine::SetHotPluggableForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4855 LONG aDevice, BOOL aHotPluggable)
4856{
4857 CheckComArgStrNotEmptyOrNull(aControllerName);
4858
4859 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4860 aControllerName, aControllerPort, aDevice, aHotPluggable));
4861
4862 AutoCaller autoCaller(this);
4863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4864
4865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4866
4867 HRESULT rc = checkStateDependency(MutableStateDep);
4868 if (FAILED(rc)) return rc;
4869
4870 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4871
4872 if (Global::IsOnlineOrTransient(mData->mMachineState))
4873 return setError(VBOX_E_INVALID_VM_STATE,
4874 tr("Invalid machine state: %s"),
4875 Global::stringifyMachineState(mData->mMachineState));
4876
4877 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4878 aControllerName,
4879 aControllerPort,
4880 aDevice);
4881 if (!pAttach)
4882 return setError(VBOX_E_OBJECT_NOT_FOUND,
4883 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4884 aDevice, aControllerPort, aControllerName);
4885
4886 /* Check for an existing controller. */
4887 ComObjPtr<StorageController> ctl;
4888 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4889 if (FAILED(rc)) return rc;
4890
4891 StorageControllerType_T ctrlType;
4892 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4893 if (FAILED(rc))
4894 return setError(E_FAIL,
4895 tr("Could not get type of controller '%ls'"),
4896 aControllerName);
4897
4898 if (!isControllerHotplugCapable(ctrlType))
4899 return setError(VBOX_E_NOT_SUPPORTED,
4900 tr("Controller '%ls' does not support changing the hot-pluggable device flag"),
4901 aControllerName);
4902
4903 setModified(IsModified_Storage);
4904 mMediaData.backup();
4905
4906 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4907
4908 if (pAttach->i_getType() == DeviceType_Floppy)
4909 return setError(E_INVALIDARG,
4910 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"),
4911 aDevice, aControllerPort, aControllerName);
4912 pAttach->i_updateHotPluggable(!!aHotPluggable);
4913
4914 return S_OK;
4915}
4916
4917STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4918 LONG aDevice)
4919{
4920 int rc = S_OK;
4921 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4922 aControllerName, aControllerPort, aDevice));
4923
4924 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4925
4926 return rc;
4927}
4928
4929STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4930 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4931{
4932 CheckComArgStrNotEmptyOrNull(aControllerName);
4933
4934 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4935 aControllerName, aControllerPort, aDevice));
4936
4937 AutoCaller autoCaller(this);
4938 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4939
4940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4941
4942 HRESULT rc = checkStateDependency(MutableStateDep);
4943 if (FAILED(rc)) return rc;
4944
4945 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4946
4947 if (Global::IsOnlineOrTransient(mData->mMachineState))
4948 return setError(VBOX_E_INVALID_VM_STATE,
4949 tr("Invalid machine state: %s"),
4950 Global::stringifyMachineState(mData->mMachineState));
4951
4952 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4953 aControllerName,
4954 aControllerPort,
4955 aDevice);
4956 if (!pAttach)
4957 return setError(VBOX_E_OBJECT_NOT_FOUND,
4958 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4959 aDevice, aControllerPort, aControllerName);
4960
4961
4962 setModified(IsModified_Storage);
4963 mMediaData.backup();
4964
4965 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4966 if (aBandwidthGroup && group.isNull())
4967 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4968
4969 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4970
4971 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4972 if (strBandwidthGroupOld.isNotEmpty())
4973 {
4974 /* Get the bandwidth group object and release it - this must not fail. */
4975 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4976 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4977 Assert(SUCCEEDED(rc));
4978
4979 pBandwidthGroupOld->i_release();
4980 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4981 }
4982
4983 if (!group.isNull())
4984 {
4985 group->i_reference();
4986 pAttach->i_updateBandwidthGroup(group->i_getName());
4987 }
4988
4989 return S_OK;
4990}
4991
4992STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4993 LONG aControllerPort,
4994 LONG aDevice,
4995 DeviceType_T aType)
4996{
4997 HRESULT rc = S_OK;
4998
4999 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
5000 aControllerName, aControllerPort, aDevice, aType));
5001
5002 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
5003
5004 return rc;
5005}
5006
5007
5008
5009STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
5010 LONG aControllerPort,
5011 LONG aDevice,
5012 BOOL aForce)
5013{
5014 int rc = S_OK;
5015 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
5016 aControllerName, aControllerPort, aForce));
5017
5018 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
5019
5020 return rc;
5021}
5022
5023STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
5024 LONG aControllerPort,
5025 LONG aDevice,
5026 IMedium *aMedium,
5027 BOOL aForce)
5028{
5029 int rc = S_OK;
5030 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
5031 aControllerName, aControllerPort, aDevice, aForce));
5032
5033 CheckComArgStrNotEmptyOrNull(aControllerName);
5034
5035 AutoCaller autoCaller(this);
5036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5037
5038 // request the host lock first, since might be calling Host methods for getting host drives;
5039 // next, protect the media tree all the while we're in here, as well as our member variables
5040 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
5041 this->lockHandle(),
5042 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5043
5044 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5045 aControllerName,
5046 aControllerPort,
5047 aDevice);
5048 if (pAttach.isNull())
5049 return setError(VBOX_E_OBJECT_NOT_FOUND,
5050 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
5051 aDevice, aControllerPort, aControllerName);
5052
5053 /* Remember previously mounted medium. The medium before taking the
5054 * backup is not necessarily the same thing. */
5055 ComObjPtr<Medium> oldmedium;
5056 oldmedium = pAttach->i_getMedium();
5057
5058 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5059 if (aMedium && pMedium.isNull())
5060 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5061
5062 AutoCaller mediumCaller(pMedium);
5063 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5064
5065 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5066 if (pMedium)
5067 {
5068 DeviceType_T mediumType = pAttach->i_getType();
5069 switch (mediumType)
5070 {
5071 case DeviceType_DVD:
5072 case DeviceType_Floppy:
5073 break;
5074
5075 default:
5076 return setError(VBOX_E_INVALID_OBJECT_STATE,
5077 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5078 aControllerPort,
5079 aDevice,
5080 aControllerName);
5081 }
5082 }
5083
5084 setModified(IsModified_Storage);
5085 mMediaData.backup();
5086
5087 {
5088 // The backup operation makes the pAttach reference point to the
5089 // old settings. Re-get the correct reference.
5090 pAttach = findAttachment(mMediaData->mAttachments,
5091 aControllerName,
5092 aControllerPort,
5093 aDevice);
5094 if (!oldmedium.isNull())
5095 oldmedium->i_removeBackReference(mData->mUuid);
5096 if (!pMedium.isNull())
5097 {
5098 pMedium->i_addBackReference(mData->mUuid);
5099
5100 mediumLock.release();
5101 multiLock.release();
5102 addMediumToRegistry(pMedium);
5103 multiLock.acquire();
5104 mediumLock.acquire();
5105 }
5106
5107 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5108 pAttach->i_updateMedium(pMedium);
5109 }
5110
5111 setModified(IsModified_Storage);
5112
5113 mediumLock.release();
5114 multiLock.release();
5115 rc = onMediumChange(pAttach, aForce);
5116 multiLock.acquire();
5117 mediumLock.acquire();
5118
5119 /* On error roll back this change only. */
5120 if (FAILED(rc))
5121 {
5122 if (!pMedium.isNull())
5123 pMedium->i_removeBackReference(mData->mUuid);
5124 pAttach = findAttachment(mMediaData->mAttachments,
5125 aControllerName,
5126 aControllerPort,
5127 aDevice);
5128 /* If the attachment is gone in the meantime, bail out. */
5129 if (pAttach.isNull())
5130 return rc;
5131 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5132 if (!oldmedium.isNull())
5133 oldmedium->i_addBackReference(mData->mUuid);
5134 pAttach->i_updateMedium(oldmedium);
5135 }
5136
5137 mediumLock.release();
5138 multiLock.release();
5139
5140 mParent->saveModifiedRegistries();
5141
5142 return rc;
5143}
5144
5145STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5146 LONG aControllerPort,
5147 LONG aDevice,
5148 IMedium **aMedium)
5149{
5150 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5151 aControllerName, aControllerPort, aDevice));
5152
5153 CheckComArgStrNotEmptyOrNull(aControllerName);
5154 CheckComArgOutPointerValid(aMedium);
5155
5156 AutoCaller autoCaller(this);
5157 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5158
5159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5160
5161 *aMedium = NULL;
5162
5163 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5164 aControllerName,
5165 aControllerPort,
5166 aDevice);
5167 if (pAttach.isNull())
5168 return setError(VBOX_E_OBJECT_NOT_FOUND,
5169 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5170 aDevice, aControllerPort, aControllerName);
5171
5172 pAttach->i_getMedium().queryInterfaceTo(aMedium);
5173
5174 return S_OK;
5175}
5176
5177STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5178{
5179 CheckComArgOutPointerValid(port);
5180 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5181
5182 AutoCaller autoCaller(this);
5183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5184
5185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5186
5187 mSerialPorts[slot].queryInterfaceTo(port);
5188
5189 return S_OK;
5190}
5191
5192STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5193{
5194 CheckComArgOutPointerValid(port);
5195 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5196
5197 AutoCaller autoCaller(this);
5198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5199
5200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5201
5202 mParallelPorts[slot].queryInterfaceTo(port);
5203
5204 return S_OK;
5205}
5206
5207STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5208{
5209 CheckComArgOutPointerValid(adapter);
5210 /* Do not assert if slot is out of range, just return the advertised
5211 status. testdriver/vbox.py triggers this in logVmInfo. */
5212 if (slot >= mNetworkAdapters.size())
5213 return setError(E_INVALIDARG,
5214 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5215 slot, mNetworkAdapters.size());
5216
5217 AutoCaller autoCaller(this);
5218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5219
5220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5221
5222 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5223
5224 return S_OK;
5225}
5226
5227STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5228{
5229 CheckComArgOutSafeArrayPointerValid(aKeys);
5230
5231 AutoCaller autoCaller(this);
5232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5233
5234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5235
5236 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5237 int i = 0;
5238 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5239 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5240 ++it, ++i)
5241 {
5242 const Utf8Str &strKey = it->first;
5243 strKey.cloneTo(&saKeys[i]);
5244 }
5245 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5246
5247 return S_OK;
5248 }
5249
5250 /**
5251 * @note Locks this object for reading.
5252 */
5253STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5254 BSTR *aValue)
5255{
5256 CheckComArgStrNotEmptyOrNull(aKey);
5257 CheckComArgOutPointerValid(aValue);
5258
5259 AutoCaller autoCaller(this);
5260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5261
5262 /* start with nothing found */
5263 Bstr bstrResult("");
5264
5265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5266
5267 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5268 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5269 // found:
5270 bstrResult = it->second; // source is a Utf8Str
5271
5272 /* return the result to caller (may be empty) */
5273 bstrResult.cloneTo(aValue);
5274
5275 return S_OK;
5276}
5277
5278 /**
5279 * @note Locks mParent for writing + this object for writing.
5280 */
5281STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5282{
5283 CheckComArgStrNotEmptyOrNull(aKey);
5284
5285 AutoCaller autoCaller(this);
5286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5287
5288 Utf8Str strKey(aKey);
5289 Utf8Str strValue(aValue);
5290 Utf8Str strOldValue; // empty
5291
5292 // locking note: we only hold the read lock briefly to look up the old value,
5293 // then release it and call the onExtraCanChange callbacks. There is a small
5294 // chance of a race insofar as the callback might be called twice if two callers
5295 // change the same key at the same time, but that's a much better solution
5296 // than the deadlock we had here before. The actual changing of the extradata
5297 // is then performed under the write lock and race-free.
5298
5299 // look up the old value first; if nothing has changed then we need not do anything
5300 {
5301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5302 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5303 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5304 strOldValue = it->second;
5305 }
5306
5307 bool fChanged;
5308 if ((fChanged = (strOldValue != strValue)))
5309 {
5310 // ask for permission from all listeners outside the locks;
5311 // onExtraDataCanChange() only briefly requests the VirtualBox
5312 // lock to copy the list of callbacks to invoke
5313 Bstr error;
5314 Bstr bstrValue(aValue);
5315
5316 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5317 {
5318 const char *sep = error.isEmpty() ? "" : ": ";
5319 CBSTR err = error.raw();
5320 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5321 sep, err));
5322 return setError(E_ACCESSDENIED,
5323 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5324 aKey,
5325 bstrValue.raw(),
5326 sep,
5327 err);
5328 }
5329
5330 // data is changing and change not vetoed: then write it out under the lock
5331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5332
5333 if (isSnapshotMachine())
5334 {
5335 HRESULT rc = checkStateDependency(MutableStateDep);
5336 if (FAILED(rc)) return rc;
5337 }
5338
5339 if (strValue.isEmpty())
5340 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5341 else
5342 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5343 // creates a new key if needed
5344
5345 bool fNeedsGlobalSaveSettings = false;
5346 saveSettings(&fNeedsGlobalSaveSettings);
5347
5348 if (fNeedsGlobalSaveSettings)
5349 {
5350 // save the global settings; for that we should hold only the VirtualBox lock
5351 alock.release();
5352 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5353 mParent->saveSettings();
5354 }
5355 }
5356
5357 // fire notification outside the lock
5358 if (fChanged)
5359 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5360
5361 return S_OK;
5362}
5363
5364STDMETHODIMP Machine::SetSettingsFilePath(IN_BSTR aFilePath, IProgress **aProgress)
5365{
5366 CheckComArgStrNotEmptyOrNull(aFilePath);
5367 CheckComArgOutPointerValid(aProgress);
5368
5369 AutoCaller autoCaller(this);
5370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5371
5372 *aProgress = NULL;
5373 ReturnComNotImplemented();
5374}
5375
5376STDMETHODIMP Machine::SaveSettings()
5377{
5378 AutoCaller autoCaller(this);
5379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5380
5381 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5382
5383 /* when there was auto-conversion, we want to save the file even if
5384 * the VM is saved */
5385 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5386 if (FAILED(rc)) return rc;
5387
5388 /* the settings file path may never be null */
5389 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5390
5391 /* save all VM data excluding snapshots */
5392 bool fNeedsGlobalSaveSettings = false;
5393 rc = saveSettings(&fNeedsGlobalSaveSettings);
5394 mlock.release();
5395
5396 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5397 {
5398 // save the global settings; for that we should hold only the VirtualBox lock
5399 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5400 rc = mParent->saveSettings();
5401 }
5402
5403 return rc;
5404}
5405
5406STDMETHODIMP Machine::DiscardSettings()
5407{
5408 AutoCaller autoCaller(this);
5409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5410
5411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5412
5413 HRESULT rc = checkStateDependency(MutableStateDep);
5414 if (FAILED(rc)) return rc;
5415
5416 /*
5417 * during this rollback, the session will be notified if data has
5418 * been actually changed
5419 */
5420 rollback(true /* aNotify */);
5421
5422 return S_OK;
5423}
5424
5425/** @note Locks objects! */
5426STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5427 ComSafeArrayOut(IMedium*, aMedia))
5428{
5429 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5430 AutoLimitedCaller autoCaller(this);
5431 AssertComRCReturnRC(autoCaller.rc());
5432
5433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5434
5435 Guid id(getId());
5436
5437 if (mData->mSession.mState != SessionState_Unlocked)
5438 return setError(VBOX_E_INVALID_OBJECT_STATE,
5439 tr("Cannot unregister the machine '%s' while it is locked"),
5440 mUserData->s.strName.c_str());
5441
5442 // wait for state dependents to drop to zero
5443 ensureNoStateDependencies();
5444
5445 if (!mData->mAccessible)
5446 {
5447 // inaccessible maschines can only be unregistered; uninitialize ourselves
5448 // here because currently there may be no unregistered that are inaccessible
5449 // (this state combination is not supported). Note releasing the caller and
5450 // leaving the lock before calling uninit()
5451 alock.release();
5452 autoCaller.release();
5453
5454 uninit();
5455
5456 mParent->unregisterMachine(this, id);
5457 // calls VirtualBox::saveSettings()
5458
5459 return S_OK;
5460 }
5461
5462 HRESULT rc = S_OK;
5463
5464 // discard saved state
5465 if (mData->mMachineState == MachineState_Saved)
5466 {
5467 // add the saved state file to the list of files the caller should delete
5468 Assert(!mSSData->strStateFilePath.isEmpty());
5469 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5470
5471 mSSData->strStateFilePath.setNull();
5472
5473 // unconditionally set the machine state to powered off, we now
5474 // know no session has locked the machine
5475 mData->mMachineState = MachineState_PoweredOff;
5476 }
5477
5478 size_t cSnapshots = 0;
5479 if (mData->mFirstSnapshot)
5480 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5481 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5482 // fail now before we start detaching media
5483 return setError(VBOX_E_INVALID_OBJECT_STATE,
5484 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5485 mUserData->s.strName.c_str(), cSnapshots);
5486
5487 // This list collects the medium objects from all medium attachments
5488 // which we will detach from the machine and its snapshots, in a specific
5489 // order which allows for closing all media without getting "media in use"
5490 // errors, simply by going through the list from the front to the back:
5491 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5492 // and must be closed before the parent media from the snapshots, or closing the parents
5493 // will fail because they still have children);
5494 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5495 // the root ("first") snapshot of the machine.
5496 MediaList llMedia;
5497
5498 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5499 && mMediaData->mAttachments.size()
5500 )
5501 {
5502 // we have media attachments: detach them all and add the Medium objects to our list
5503 if (cleanupMode != CleanupMode_UnregisterOnly)
5504 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5505 else
5506 return setError(VBOX_E_INVALID_OBJECT_STATE,
5507 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5508 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5509 }
5510
5511 if (cSnapshots)
5512 {
5513 // autoCleanup must be true here, or we would have failed above
5514
5515 // add the media from the medium attachments of the snapshots to llMedia
5516 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5517 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5518 // into the children first
5519
5520 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5521 MachineState_T oldState = mData->mMachineState;
5522 mData->mMachineState = MachineState_DeletingSnapshot;
5523
5524 // make a copy of the first snapshot so the refcount does not drop to 0
5525 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5526 // because of the AutoCaller voodoo)
5527 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5528
5529 // GO!
5530 pFirstSnapshot->i_uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5531
5532 mData->mMachineState = oldState;
5533 }
5534
5535 if (FAILED(rc))
5536 {
5537 rollbackMedia();
5538 return rc;
5539 }
5540
5541 // commit all the media changes made above
5542 commitMedia();
5543
5544 mData->mRegistered = false;
5545
5546 // machine lock no longer needed
5547 alock.release();
5548
5549 // return media to caller
5550 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5551 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5552
5553 mParent->unregisterMachine(this, id);
5554 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5555
5556 return S_OK;
5557}
5558
5559struct Machine::DeleteTask
5560{
5561 ComObjPtr<Machine> pMachine;
5562 RTCList<ComPtr<IMedium> > llMediums;
5563 StringsList llFilesToDelete;
5564 ComObjPtr<Progress> pProgress;
5565};
5566
5567STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5568{
5569 LogFlowFuncEnter();
5570
5571 AutoCaller autoCaller(this);
5572 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5573
5574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5575
5576 HRESULT rc = checkStateDependency(MutableStateDep);
5577 if (FAILED(rc)) return rc;
5578
5579 if (mData->mRegistered)
5580 return setError(VBOX_E_INVALID_VM_STATE,
5581 tr("Cannot delete settings of a registered machine"));
5582
5583 DeleteTask *pTask = new DeleteTask;
5584 pTask->pMachine = this;
5585 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5586
5587 // collect files to delete
5588 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5589
5590 for (size_t i = 0; i < sfaMedia.size(); ++i)
5591 {
5592 IMedium *pIMedium(sfaMedia[i]);
5593 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5594 if (pMedium.isNull())
5595 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5596 SafeArray<BSTR> ids;
5597 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5598 if (FAILED(rc)) return rc;
5599 /* At this point the medium should not have any back references
5600 * anymore. If it has it is attached to another VM and *must* not
5601 * deleted. */
5602 if (ids.size() < 1)
5603 pTask->llMediums.append(pMedium);
5604 }
5605 if (mData->pMachineConfigFile->fileExists())
5606 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5607
5608 pTask->pProgress.createObject();
5609 pTask->pProgress->init(getVirtualBox(),
5610 static_cast<IMachine*>(this) /* aInitiator */,
5611 Bstr(tr("Deleting files")).raw(),
5612 true /* fCancellable */,
5613 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5614 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5615
5616 int vrc = RTThreadCreate(NULL,
5617 Machine::deleteThread,
5618 (void*)pTask,
5619 0,
5620 RTTHREADTYPE_MAIN_WORKER,
5621 0,
5622 "MachineDelete");
5623
5624 pTask->pProgress.queryInterfaceTo(aProgress);
5625
5626 if (RT_FAILURE(vrc))
5627 {
5628 delete pTask;
5629 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5630 }
5631
5632 LogFlowFuncLeave();
5633
5634 return S_OK;
5635}
5636
5637/**
5638 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5639 * calls Machine::deleteTaskWorker() on the actual machine object.
5640 * @param Thread
5641 * @param pvUser
5642 * @return
5643 */
5644/*static*/
5645DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5646{
5647 LogFlowFuncEnter();
5648
5649 DeleteTask *pTask = (DeleteTask*)pvUser;
5650 Assert(pTask);
5651 Assert(pTask->pMachine);
5652 Assert(pTask->pProgress);
5653
5654 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5655 pTask->pProgress->notifyComplete(rc);
5656
5657 delete pTask;
5658
5659 LogFlowFuncLeave();
5660
5661 NOREF(Thread);
5662
5663 return VINF_SUCCESS;
5664}
5665
5666/**
5667 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5668 * @param task
5669 * @return
5670 */
5671HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5672{
5673 AutoCaller autoCaller(this);
5674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5675
5676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5677
5678 HRESULT rc = S_OK;
5679
5680 try
5681 {
5682 ULONG uLogHistoryCount = 3;
5683 ComPtr<ISystemProperties> systemProperties;
5684 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5685 if (FAILED(rc)) throw rc;
5686
5687 if (!systemProperties.isNull())
5688 {
5689 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5690 if (FAILED(rc)) throw rc;
5691 }
5692
5693 MachineState_T oldState = mData->mMachineState;
5694 setMachineState(MachineState_SettingUp);
5695 alock.release();
5696 for (size_t i = 0; i < task.llMediums.size(); ++i)
5697 {
5698 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5699 {
5700 AutoCaller mac(pMedium);
5701 if (FAILED(mac.rc())) throw mac.rc();
5702 Utf8Str strLocation = pMedium->i_getLocationFull();
5703 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5704 if (FAILED(rc)) throw rc;
5705 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5706 }
5707 ComPtr<IProgress> pProgress2;
5708 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5709 if (FAILED(rc)) throw rc;
5710 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5711 if (FAILED(rc)) throw rc;
5712 /* Check the result of the asynchronous process. */
5713 LONG iRc;
5714 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5715 if (FAILED(rc)) throw rc;
5716 /* If the thread of the progress object has an error, then
5717 * retrieve the error info from there, or it'll be lost. */
5718 if (FAILED(iRc))
5719 throw setError(ProgressErrorInfo(pProgress2));
5720
5721 /* Close the medium, deliberately without checking the return
5722 * code, and without leaving any trace in the error info, as
5723 * a failure here is a very minor issue, which shouldn't happen
5724 * as above we even managed to delete the medium. */
5725 {
5726 ErrorInfoKeeper eik;
5727 pMedium->Close();
5728 }
5729 }
5730 setMachineState(oldState);
5731 alock.acquire();
5732
5733 // delete the files pushed on the task list by Machine::Delete()
5734 // (this includes saved states of the machine and snapshots and
5735 // medium storage files from the IMedium list passed in, and the
5736 // machine XML file)
5737 StringsList::const_iterator it = task.llFilesToDelete.begin();
5738 while (it != task.llFilesToDelete.end())
5739 {
5740 const Utf8Str &strFile = *it;
5741 LogFunc(("Deleting file %s\n", strFile.c_str()));
5742 int vrc = RTFileDelete(strFile.c_str());
5743 if (RT_FAILURE(vrc))
5744 throw setError(VBOX_E_IPRT_ERROR,
5745 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5746
5747 ++it;
5748 if (it == task.llFilesToDelete.end())
5749 {
5750 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5751 if (FAILED(rc)) throw rc;
5752 break;
5753 }
5754
5755 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5756 if (FAILED(rc)) throw rc;
5757 }
5758
5759 /* delete the settings only when the file actually exists */
5760 if (mData->pMachineConfigFile->fileExists())
5761 {
5762 /* Delete any backup or uncommitted XML files. Ignore failures.
5763 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5764 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5765 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5766 RTFileDelete(otherXml.c_str());
5767 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5768 RTFileDelete(otherXml.c_str());
5769
5770 /* delete the Logs folder, nothing important should be left
5771 * there (we don't check for errors because the user might have
5772 * some private files there that we don't want to delete) */
5773 Utf8Str logFolder;
5774 getLogFolder(logFolder);
5775 Assert(logFolder.length());
5776 if (RTDirExists(logFolder.c_str()))
5777 {
5778 /* Delete all VBox.log[.N] files from the Logs folder
5779 * (this must be in sync with the rotation logic in
5780 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5781 * files that may have been created by the GUI. */
5782 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5783 logFolder.c_str(), RTPATH_DELIMITER);
5784 RTFileDelete(log.c_str());
5785 log = Utf8StrFmt("%s%cVBox.png",
5786 logFolder.c_str(), RTPATH_DELIMITER);
5787 RTFileDelete(log.c_str());
5788 for (int i = uLogHistoryCount; i > 0; i--)
5789 {
5790 log = Utf8StrFmt("%s%cVBox.log.%d",
5791 logFolder.c_str(), RTPATH_DELIMITER, i);
5792 RTFileDelete(log.c_str());
5793 log = Utf8StrFmt("%s%cVBox.png.%d",
5794 logFolder.c_str(), RTPATH_DELIMITER, i);
5795 RTFileDelete(log.c_str());
5796 }
5797
5798 RTDirRemove(logFolder.c_str());
5799 }
5800
5801 /* delete the Snapshots folder, nothing important should be left
5802 * there (we don't check for errors because the user might have
5803 * some private files there that we don't want to delete) */
5804 Utf8Str strFullSnapshotFolder;
5805 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5806 Assert(!strFullSnapshotFolder.isEmpty());
5807 if (RTDirExists(strFullSnapshotFolder.c_str()))
5808 RTDirRemove(strFullSnapshotFolder.c_str());
5809
5810 // delete the directory that contains the settings file, but only
5811 // if it matches the VM name
5812 Utf8Str settingsDir;
5813 if (isInOwnDir(&settingsDir))
5814 RTDirRemove(settingsDir.c_str());
5815 }
5816
5817 alock.release();
5818
5819 mParent->saveModifiedRegistries();
5820 }
5821 catch (HRESULT aRC) { rc = aRC; }
5822
5823 return rc;
5824}
5825
5826STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5827{
5828 CheckComArgOutPointerValid(aSnapshot);
5829
5830 AutoCaller autoCaller(this);
5831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5832
5833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5834
5835 ComObjPtr<Snapshot> pSnapshot;
5836 HRESULT rc;
5837
5838 if (!aNameOrId || !*aNameOrId)
5839 // null case (caller wants root snapshot): findSnapshotById() handles this
5840 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5841 else
5842 {
5843 Guid uuid(aNameOrId);
5844 if (uuid.isValid())
5845 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5846 else
5847 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5848 }
5849 pSnapshot.queryInterfaceTo(aSnapshot);
5850
5851 return rc;
5852}
5853
5854STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5855{
5856 CheckComArgStrNotEmptyOrNull(aName);
5857 CheckComArgStrNotEmptyOrNull(aHostPath);
5858
5859 AutoCaller autoCaller(this);
5860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5861
5862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5863
5864 HRESULT rc = checkStateDependency(MutableStateDep);
5865 if (FAILED(rc)) return rc;
5866
5867 Utf8Str strName(aName);
5868
5869 ComObjPtr<SharedFolder> sharedFolder;
5870 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5871 if (SUCCEEDED(rc))
5872 return setError(VBOX_E_OBJECT_IN_USE,
5873 tr("Shared folder named '%s' already exists"),
5874 strName.c_str());
5875
5876 sharedFolder.createObject();
5877 rc = sharedFolder->init(getMachine(),
5878 strName,
5879 aHostPath,
5880 !!aWritable,
5881 !!aAutoMount,
5882 true /* fFailOnError */);
5883 if (FAILED(rc)) return rc;
5884
5885 setModified(IsModified_SharedFolders);
5886 mHWData.backup();
5887 mHWData->mSharedFolders.push_back(sharedFolder);
5888
5889 /* inform the direct session if any */
5890 alock.release();
5891 onSharedFolderChange();
5892
5893 return S_OK;
5894}
5895
5896STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5897{
5898 CheckComArgStrNotEmptyOrNull(aName);
5899
5900 AutoCaller autoCaller(this);
5901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5902
5903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5904
5905 HRESULT rc = checkStateDependency(MutableStateDep);
5906 if (FAILED(rc)) return rc;
5907
5908 ComObjPtr<SharedFolder> sharedFolder;
5909 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5910 if (FAILED(rc)) return rc;
5911
5912 setModified(IsModified_SharedFolders);
5913 mHWData.backup();
5914 mHWData->mSharedFolders.remove(sharedFolder);
5915
5916 /* inform the direct session if any */
5917 alock.release();
5918 onSharedFolderChange();
5919
5920 return S_OK;
5921}
5922
5923STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5924{
5925 CheckComArgOutPointerValid(aCanShow);
5926
5927 /* start with No */
5928 *aCanShow = FALSE;
5929
5930 AutoCaller autoCaller(this);
5931 AssertComRCReturnRC(autoCaller.rc());
5932
5933 ComPtr<IInternalSessionControl> directControl;
5934 {
5935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5936
5937 if (mData->mSession.mState != SessionState_Locked)
5938 return setError(VBOX_E_INVALID_VM_STATE,
5939 tr("Machine is not locked for session (session state: %s)"),
5940 Global::stringifySessionState(mData->mSession.mState));
5941
5942 directControl = mData->mSession.mDirectControl;
5943 }
5944
5945 /* ignore calls made after #OnSessionEnd() is called */
5946 if (!directControl)
5947 return S_OK;
5948
5949 LONG64 dummy;
5950 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5951}
5952
5953STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5954{
5955 CheckComArgOutPointerValid(aWinId);
5956
5957 AutoCaller autoCaller(this);
5958 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5959
5960 ComPtr<IInternalSessionControl> directControl;
5961 {
5962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5963
5964 if (mData->mSession.mState != SessionState_Locked)
5965 return setError(E_FAIL,
5966 tr("Machine is not locked for session (session state: %s)"),
5967 Global::stringifySessionState(mData->mSession.mState));
5968
5969 directControl = mData->mSession.mDirectControl;
5970 }
5971
5972 /* ignore calls made after #OnSessionEnd() is called */
5973 if (!directControl)
5974 return S_OK;
5975
5976 BOOL dummy;
5977 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5978}
5979
5980#ifdef VBOX_WITH_GUEST_PROPS
5981/**
5982 * Look up a guest property in VBoxSVC's internal structures.
5983 */
5984HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5985 BSTR *aValue,
5986 LONG64 *aTimestamp,
5987 BSTR *aFlags) const
5988{
5989 using namespace guestProp;
5990
5991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5992 Utf8Str strName(aName);
5993 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5994
5995 if (it != mHWData->mGuestProperties.end())
5996 {
5997 char szFlags[MAX_FLAGS_LEN + 1];
5998 it->second.strValue.cloneTo(aValue);
5999 *aTimestamp = it->second.mTimestamp;
6000 writeFlags(it->second.mFlags, szFlags);
6001 Bstr(szFlags).cloneTo(aFlags);
6002 }
6003
6004 return S_OK;
6005}
6006
6007/**
6008 * Query the VM that a guest property belongs to for the property.
6009 * @returns E_ACCESSDENIED if the VM process is not available or not
6010 * currently handling queries and the lookup should then be done in
6011 * VBoxSVC.
6012 */
6013HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
6014 BSTR *aValue,
6015 LONG64 *aTimestamp,
6016 BSTR *aFlags) const
6017{
6018 HRESULT rc;
6019 ComPtr<IInternalSessionControl> directControl;
6020 directControl = mData->mSession.mDirectControl;
6021
6022 /* fail if we were called after #OnSessionEnd() is called. This is a
6023 * silly race condition. */
6024
6025 /** @todo This code is bothering API clients (like python script clients) with
6026 * the AccessGuestProperty call, creating unncessary IPC. Need to
6027 * have a way of figuring out which kind of direct session it is... */
6028 if (!directControl)
6029 rc = E_ACCESSDENIED;
6030 else
6031 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
6032 false /* isSetter */,
6033 aValue, aTimestamp, aFlags);
6034 return rc;
6035}
6036#endif // VBOX_WITH_GUEST_PROPS
6037
6038STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
6039 BSTR *aValue,
6040 LONG64 *aTimestamp,
6041 BSTR *aFlags)
6042{
6043#ifndef VBOX_WITH_GUEST_PROPS
6044 ReturnComNotImplemented();
6045#else // VBOX_WITH_GUEST_PROPS
6046 CheckComArgStrNotEmptyOrNull(aName);
6047 CheckComArgOutPointerValid(aValue);
6048 CheckComArgOutPointerValid(aTimestamp);
6049 CheckComArgOutPointerValid(aFlags);
6050
6051 AutoCaller autoCaller(this);
6052 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6053
6054 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
6055 if (rc == E_ACCESSDENIED)
6056 /* The VM is not running or the service is not (yet) accessible */
6057 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
6058 return rc;
6059#endif // VBOX_WITH_GUEST_PROPS
6060}
6061
6062STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
6063{
6064 LONG64 dummyTimestamp;
6065 Bstr dummyFlags;
6066 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
6067}
6068
6069STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
6070{
6071 Bstr dummyValue;
6072 Bstr dummyFlags;
6073 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
6074}
6075
6076#ifdef VBOX_WITH_GUEST_PROPS
6077/**
6078 * Set a guest property in VBoxSVC's internal structures.
6079 */
6080HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6081 IN_BSTR aFlags)
6082{
6083 using namespace guestProp;
6084
6085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6086 HRESULT rc = S_OK;
6087
6088 rc = checkStateDependency(MutableStateDep);
6089 if (FAILED(rc)) return rc;
6090
6091 try
6092 {
6093 Utf8Str utf8Name(aName);
6094 Utf8Str utf8Flags(aFlags);
6095 uint32_t fFlags = NILFLAG;
6096 if ( aFlags != NULL
6097 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6098 return setError(E_INVALIDARG,
6099 tr("Invalid guest property flag values: '%ls'"),
6100 aFlags);
6101
6102 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6103 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6104 if (it == mHWData->mGuestProperties.end())
6105 {
6106 if (!fDelete)
6107 {
6108 setModified(IsModified_MachineData);
6109 mHWData.backupEx();
6110
6111 RTTIMESPEC time;
6112 HWData::GuestProperty prop;
6113 prop.strValue = aValue;
6114 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6115 prop.mFlags = fFlags;
6116 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6117 }
6118 }
6119 else
6120 {
6121 if (it->second.mFlags & (RDONLYHOST))
6122 {
6123 rc = setError(E_ACCESSDENIED,
6124 tr("The property '%ls' cannot be changed by the host"),
6125 aName);
6126 }
6127 else
6128 {
6129 setModified(IsModified_MachineData);
6130 mHWData.backupEx();
6131
6132 /* The backupEx() operation invalidates our iterator,
6133 * so get a new one. */
6134 it = mHWData->mGuestProperties.find(utf8Name);
6135 Assert(it != mHWData->mGuestProperties.end());
6136
6137 if (!fDelete)
6138 {
6139 RTTIMESPEC time;
6140 it->second.strValue = aValue;
6141 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6142 it->second.mFlags = fFlags;
6143 }
6144 else
6145 mHWData->mGuestProperties.erase(it);
6146 }
6147 }
6148
6149 if ( SUCCEEDED(rc)
6150 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6151 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6152 RTSTR_MAX,
6153 utf8Name.c_str(),
6154 RTSTR_MAX,
6155 NULL)
6156 )
6157 )
6158 {
6159 alock.release();
6160
6161 mParent->onGuestPropertyChange(mData->mUuid, aName,
6162 aValue ? aValue : Bstr("").raw(),
6163 aFlags ? aFlags : Bstr("").raw());
6164 }
6165 }
6166 catch (std::bad_alloc &)
6167 {
6168 rc = E_OUTOFMEMORY;
6169 }
6170
6171 return rc;
6172}
6173
6174/**
6175 * Set a property on the VM that that property belongs to.
6176 * @returns E_ACCESSDENIED if the VM process is not available or not
6177 * currently handling queries and the setting should then be done in
6178 * VBoxSVC.
6179 */
6180HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6181 IN_BSTR aFlags)
6182{
6183 HRESULT rc;
6184
6185 try
6186 {
6187 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6188
6189 BSTR dummy = NULL; /* will not be changed (setter) */
6190 LONG64 dummy64;
6191 if (!directControl)
6192 rc = E_ACCESSDENIED;
6193 else
6194 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6195 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6196 true /* isSetter */,
6197 &dummy, &dummy64, &dummy);
6198 }
6199 catch (std::bad_alloc &)
6200 {
6201 rc = E_OUTOFMEMORY;
6202 }
6203
6204 return rc;
6205}
6206#endif // VBOX_WITH_GUEST_PROPS
6207
6208STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6209 IN_BSTR aFlags)
6210{
6211#ifndef VBOX_WITH_GUEST_PROPS
6212 ReturnComNotImplemented();
6213#else // VBOX_WITH_GUEST_PROPS
6214 CheckComArgStrNotEmptyOrNull(aName);
6215 CheckComArgMaybeNull(aFlags);
6216 CheckComArgMaybeNull(aValue);
6217
6218 AutoCaller autoCaller(this);
6219 if (FAILED(autoCaller.rc()))
6220 return autoCaller.rc();
6221
6222 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6223 if (rc == E_ACCESSDENIED)
6224 /* The VM is not running or the service is not (yet) accessible */
6225 rc = setGuestPropertyToService(aName, aValue, aFlags);
6226 return rc;
6227#endif // VBOX_WITH_GUEST_PROPS
6228}
6229
6230STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6231{
6232 return SetGuestProperty(aName, aValue, NULL);
6233}
6234
6235STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6236{
6237 return SetGuestProperty(aName, NULL, NULL);
6238}
6239
6240#ifdef VBOX_WITH_GUEST_PROPS
6241/**
6242 * Enumerate the guest properties in VBoxSVC's internal structures.
6243 */
6244HRESULT Machine::enumerateGuestPropertiesInService
6245 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6246 ComSafeArrayOut(BSTR, aValues),
6247 ComSafeArrayOut(LONG64, aTimestamps),
6248 ComSafeArrayOut(BSTR, aFlags))
6249{
6250 using namespace guestProp;
6251
6252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6253 Utf8Str strPatterns(aPatterns);
6254
6255 HWData::GuestPropertyMap propMap;
6256
6257 /*
6258 * Look for matching patterns and build up a list.
6259 */
6260 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6261 while (it != mHWData->mGuestProperties.end())
6262 {
6263 if ( strPatterns.isEmpty()
6264 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6265 RTSTR_MAX,
6266 it->first.c_str(),
6267 RTSTR_MAX,
6268 NULL)
6269 )
6270 {
6271 propMap.insert(*it);
6272 }
6273
6274 it++;
6275 }
6276
6277 alock.release();
6278
6279 /*
6280 * And build up the arrays for returning the property information.
6281 */
6282 size_t cEntries = propMap.size();
6283 SafeArray<BSTR> names(cEntries);
6284 SafeArray<BSTR> values(cEntries);
6285 SafeArray<LONG64> timestamps(cEntries);
6286 SafeArray<BSTR> flags(cEntries);
6287 size_t iProp = 0;
6288
6289 it = propMap.begin();
6290 while (it != propMap.end())
6291 {
6292 char szFlags[MAX_FLAGS_LEN + 1];
6293 it->first.cloneTo(&names[iProp]);
6294 it->second.strValue.cloneTo(&values[iProp]);
6295 timestamps[iProp] = it->second.mTimestamp;
6296 writeFlags(it->second.mFlags, szFlags);
6297 Bstr(szFlags).cloneTo(&flags[iProp++]);
6298 it++;
6299 }
6300 names.detachTo(ComSafeArrayOutArg(aNames));
6301 values.detachTo(ComSafeArrayOutArg(aValues));
6302 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6303 flags.detachTo(ComSafeArrayOutArg(aFlags));
6304 return S_OK;
6305}
6306
6307/**
6308 * Enumerate the properties managed by a VM.
6309 * @returns E_ACCESSDENIED if the VM process is not available or not
6310 * currently handling queries and the setting should then be done in
6311 * VBoxSVC.
6312 */
6313HRESULT Machine::enumerateGuestPropertiesOnVM
6314 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6315 ComSafeArrayOut(BSTR, aValues),
6316 ComSafeArrayOut(LONG64, aTimestamps),
6317 ComSafeArrayOut(BSTR, aFlags))
6318{
6319 HRESULT rc;
6320 ComPtr<IInternalSessionControl> directControl;
6321 directControl = mData->mSession.mDirectControl;
6322
6323 if (!directControl)
6324 rc = E_ACCESSDENIED;
6325 else
6326 rc = directControl->EnumerateGuestProperties
6327 (aPatterns, ComSafeArrayOutArg(aNames),
6328 ComSafeArrayOutArg(aValues),
6329 ComSafeArrayOutArg(aTimestamps),
6330 ComSafeArrayOutArg(aFlags));
6331 return rc;
6332}
6333#endif // VBOX_WITH_GUEST_PROPS
6334
6335STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6336 ComSafeArrayOut(BSTR, aNames),
6337 ComSafeArrayOut(BSTR, aValues),
6338 ComSafeArrayOut(LONG64, aTimestamps),
6339 ComSafeArrayOut(BSTR, aFlags))
6340{
6341#ifndef VBOX_WITH_GUEST_PROPS
6342 ReturnComNotImplemented();
6343#else // VBOX_WITH_GUEST_PROPS
6344 CheckComArgMaybeNull(aPatterns);
6345 CheckComArgOutSafeArrayPointerValid(aNames);
6346 CheckComArgOutSafeArrayPointerValid(aValues);
6347 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6348 CheckComArgOutSafeArrayPointerValid(aFlags);
6349
6350 AutoCaller autoCaller(this);
6351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6352
6353 HRESULT rc = enumerateGuestPropertiesOnVM
6354 (aPatterns, ComSafeArrayOutArg(aNames),
6355 ComSafeArrayOutArg(aValues),
6356 ComSafeArrayOutArg(aTimestamps),
6357 ComSafeArrayOutArg(aFlags));
6358 if (rc == E_ACCESSDENIED)
6359 /* The VM is not running or the service is not (yet) accessible */
6360 rc = enumerateGuestPropertiesInService
6361 (aPatterns, ComSafeArrayOutArg(aNames),
6362 ComSafeArrayOutArg(aValues),
6363 ComSafeArrayOutArg(aTimestamps),
6364 ComSafeArrayOutArg(aFlags));
6365 return rc;
6366#endif // VBOX_WITH_GUEST_PROPS
6367}
6368
6369STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6370 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6371{
6372 MediaData::AttachmentList atts;
6373
6374 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6375 if (FAILED(rc)) return rc;
6376
6377 SafeIfaceArray<IMediumAttachment> attachments(atts);
6378 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6379
6380 return S_OK;
6381}
6382
6383STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6384 LONG aControllerPort,
6385 LONG aDevice,
6386 IMediumAttachment **aAttachment)
6387{
6388 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6389 aControllerName, aControllerPort, aDevice));
6390
6391 CheckComArgStrNotEmptyOrNull(aControllerName);
6392 CheckComArgOutPointerValid(aAttachment);
6393
6394 AutoCaller autoCaller(this);
6395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6396
6397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6398
6399 *aAttachment = NULL;
6400
6401 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6402 aControllerName,
6403 aControllerPort,
6404 aDevice);
6405 if (pAttach.isNull())
6406 return setError(VBOX_E_OBJECT_NOT_FOUND,
6407 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6408 aDevice, aControllerPort, aControllerName);
6409
6410 pAttach.queryInterfaceTo(aAttachment);
6411
6412 return S_OK;
6413}
6414
6415STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6416 StorageBus_T aConnectionType,
6417 IStorageController **controller)
6418{
6419 CheckComArgStrNotEmptyOrNull(aName);
6420
6421 if ( (aConnectionType <= StorageBus_Null)
6422 || (aConnectionType > StorageBus_USB))
6423 return setError(E_INVALIDARG,
6424 tr("Invalid connection type: %d"),
6425 aConnectionType);
6426
6427 AutoCaller autoCaller(this);
6428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6429
6430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6431
6432 HRESULT rc = checkStateDependency(MutableStateDep);
6433 if (FAILED(rc)) return rc;
6434
6435 /* try to find one with the name first. */
6436 ComObjPtr<StorageController> ctrl;
6437
6438 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6439 if (SUCCEEDED(rc))
6440 return setError(VBOX_E_OBJECT_IN_USE,
6441 tr("Storage controller named '%ls' already exists"),
6442 aName);
6443
6444 ctrl.createObject();
6445
6446 /* get a new instance number for the storage controller */
6447 ULONG ulInstance = 0;
6448 bool fBootable = true;
6449 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6450 it != mStorageControllers->end();
6451 ++it)
6452 {
6453 if ((*it)->i_getStorageBus() == aConnectionType)
6454 {
6455 ULONG ulCurInst = (*it)->i_getInstance();
6456
6457 if (ulCurInst >= ulInstance)
6458 ulInstance = ulCurInst + 1;
6459
6460 /* Only one controller of each type can be marked as bootable. */
6461 if ((*it)->i_getBootable())
6462 fBootable = false;
6463 }
6464 }
6465
6466 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6467 if (FAILED(rc)) return rc;
6468
6469 setModified(IsModified_Storage);
6470 mStorageControllers.backup();
6471 mStorageControllers->push_back(ctrl);
6472
6473 ctrl.queryInterfaceTo(controller);
6474
6475 /* inform the direct session if any */
6476 alock.release();
6477 onStorageControllerChange();
6478
6479 return S_OK;
6480}
6481
6482STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6483 IStorageController **aStorageController)
6484{
6485 CheckComArgStrNotEmptyOrNull(aName);
6486
6487 AutoCaller autoCaller(this);
6488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6489
6490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6491
6492 ComObjPtr<StorageController> ctrl;
6493
6494 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6495 if (SUCCEEDED(rc))
6496 ctrl.queryInterfaceTo(aStorageController);
6497
6498 return rc;
6499}
6500
6501STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6502 IStorageController **aStorageController)
6503{
6504 AutoCaller autoCaller(this);
6505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6506
6507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6508
6509 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6510 it != mStorageControllers->end();
6511 ++it)
6512 {
6513 if ((*it)->i_getInstance() == aInstance)
6514 {
6515 (*it).queryInterfaceTo(aStorageController);
6516 return S_OK;
6517 }
6518 }
6519
6520 return setError(VBOX_E_OBJECT_NOT_FOUND,
6521 tr("Could not find a storage controller with instance number '%lu'"),
6522 aInstance);
6523}
6524
6525STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6526{
6527 AutoCaller autoCaller(this);
6528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6529
6530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 HRESULT rc = checkStateDependency(MutableStateDep);
6533 if (FAILED(rc)) return rc;
6534
6535 ComObjPtr<StorageController> ctrl;
6536
6537 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6538 if (SUCCEEDED(rc))
6539 {
6540 /* Ensure that only one controller of each type is marked as bootable. */
6541 if (fBootable == TRUE)
6542 {
6543 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6544 it != mStorageControllers->end();
6545 ++it)
6546 {
6547 ComObjPtr<StorageController> aCtrl = (*it);
6548
6549 if ( (aCtrl->i_getName() != Utf8Str(aName))
6550 && aCtrl->i_getBootable() == TRUE
6551 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6552 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6553 {
6554 aCtrl->i_setBootable(FALSE);
6555 break;
6556 }
6557 }
6558 }
6559
6560 if (SUCCEEDED(rc))
6561 {
6562 ctrl->i_setBootable(fBootable);
6563 setModified(IsModified_Storage);
6564 }
6565 }
6566
6567 if (SUCCEEDED(rc))
6568 {
6569 /* inform the direct session if any */
6570 alock.release();
6571 onStorageControllerChange();
6572 }
6573
6574 return rc;
6575}
6576
6577STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6578{
6579 CheckComArgStrNotEmptyOrNull(aName);
6580
6581 AutoCaller autoCaller(this);
6582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6583
6584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6585
6586 HRESULT rc = checkStateDependency(MutableStateDep);
6587 if (FAILED(rc)) return rc;
6588
6589 ComObjPtr<StorageController> ctrl;
6590 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6591 if (FAILED(rc)) return rc;
6592
6593 {
6594 /* find all attached devices to the appropriate storage controller and detach them all */
6595 // make a temporary list because detachDevice invalidates iterators into
6596 // mMediaData->mAttachments
6597 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6598
6599 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6600 it != llAttachments2.end();
6601 ++it)
6602 {
6603 MediumAttachment *pAttachTemp = *it;
6604
6605 AutoCaller localAutoCaller(pAttachTemp);
6606 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6607
6608 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6609
6610 if (pAttachTemp->i_getControllerName() == aName)
6611 {
6612 rc = detachDevice(pAttachTemp, alock, NULL);
6613 if (FAILED(rc)) return rc;
6614 }
6615 }
6616 }
6617
6618 /* We can remove it now. */
6619 setModified(IsModified_Storage);
6620 mStorageControllers.backup();
6621
6622 ctrl->i_unshare();
6623
6624 mStorageControllers->remove(ctrl);
6625
6626 /* inform the direct session if any */
6627 alock.release();
6628 onStorageControllerChange();
6629
6630 return S_OK;
6631}
6632
6633STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6634 IUSBController **controller)
6635{
6636 if ( (aType <= USBControllerType_Null)
6637 || (aType >= USBControllerType_Last))
6638 return setError(E_INVALIDARG,
6639 tr("Invalid USB controller type: %d"),
6640 aType);
6641
6642 AutoCaller autoCaller(this);
6643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6644
6645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6646
6647 HRESULT rc = checkStateDependency(MutableStateDep);
6648 if (FAILED(rc)) return rc;
6649
6650 /* try to find one with the same type first. */
6651 ComObjPtr<USBController> ctrl;
6652
6653 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6654 if (SUCCEEDED(rc))
6655 return setError(VBOX_E_OBJECT_IN_USE,
6656 tr("USB controller named '%ls' already exists"),
6657 aName);
6658
6659 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6660 ULONG maxInstances;
6661 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6662 if (FAILED(rc))
6663 return rc;
6664
6665 ULONG cInstances = getUSBControllerCountByType(aType);
6666 if (cInstances >= maxInstances)
6667 return setError(E_INVALIDARG,
6668 tr("Too many USB controllers of this type"));
6669
6670 ctrl.createObject();
6671
6672 rc = ctrl->init(this, aName, aType);
6673 if (FAILED(rc)) return rc;
6674
6675 setModified(IsModified_USB);
6676 mUSBControllers.backup();
6677 mUSBControllers->push_back(ctrl);
6678
6679 ctrl.queryInterfaceTo(controller);
6680
6681 /* inform the direct session if any */
6682 alock.release();
6683 onUSBControllerChange();
6684
6685 return S_OK;
6686}
6687
6688STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6689{
6690 CheckComArgStrNotEmptyOrNull(aName);
6691
6692 AutoCaller autoCaller(this);
6693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6694
6695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6696
6697 ComObjPtr<USBController> ctrl;
6698
6699 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6700 if (SUCCEEDED(rc))
6701 ctrl.queryInterfaceTo(aUSBController);
6702
6703 return rc;
6704}
6705
6706STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6707 ULONG *aControllers)
6708{
6709 CheckComArgOutPointerValid(aControllers);
6710
6711 if ( (aType <= USBControllerType_Null)
6712 || (aType >= USBControllerType_Last))
6713 return setError(E_INVALIDARG,
6714 tr("Invalid USB controller type: %d"),
6715 aType);
6716
6717 AutoCaller autoCaller(this);
6718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6719
6720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6721
6722 ComObjPtr<USBController> ctrl;
6723
6724 *aControllers = getUSBControllerCountByType(aType);
6725
6726 return S_OK;
6727}
6728
6729STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6730{
6731 CheckComArgStrNotEmptyOrNull(aName);
6732
6733 AutoCaller autoCaller(this);
6734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6735
6736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6737
6738 HRESULT rc = checkStateDependency(MutableStateDep);
6739 if (FAILED(rc)) return rc;
6740
6741 ComObjPtr<USBController> ctrl;
6742 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6743 if (FAILED(rc)) return rc;
6744
6745 setModified(IsModified_USB);
6746 mUSBControllers.backup();
6747
6748 ctrl->i_unshare();
6749
6750 mUSBControllers->remove(ctrl);
6751
6752 /* inform the direct session if any */
6753 alock.release();
6754 onUSBControllerChange();
6755
6756 return S_OK;
6757}
6758
6759STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6760 ULONG *puOriginX,
6761 ULONG *puOriginY,
6762 ULONG *puWidth,
6763 ULONG *puHeight,
6764 BOOL *pfEnabled)
6765{
6766 LogFlowThisFunc(("\n"));
6767
6768 CheckComArgNotNull(puOriginX);
6769 CheckComArgNotNull(puOriginY);
6770 CheckComArgNotNull(puWidth);
6771 CheckComArgNotNull(puHeight);
6772 CheckComArgNotNull(pfEnabled);
6773
6774 uint32_t u32OriginX= 0;
6775 uint32_t u32OriginY= 0;
6776 uint32_t u32Width = 0;
6777 uint32_t u32Height = 0;
6778 uint16_t u16Flags = 0;
6779
6780 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6781 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6782 if (RT_FAILURE(vrc))
6783 {
6784#ifdef RT_OS_WINDOWS
6785 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6786 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6787 * So just assign fEnable to TRUE again.
6788 * The right fix would be to change GUI API wrappers to make sure that parameters
6789 * are changed only if API succeeds.
6790 */
6791 *pfEnabled = TRUE;
6792#endif
6793 return setError(VBOX_E_IPRT_ERROR,
6794 tr("Saved guest size is not available (%Rrc)"),
6795 vrc);
6796 }
6797
6798 *puOriginX = u32OriginX;
6799 *puOriginY = u32OriginY;
6800 *puWidth = u32Width;
6801 *puHeight = u32Height;
6802 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6803
6804 return S_OK;
6805}
6806
6807STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6808{
6809 LogFlowThisFunc(("\n"));
6810
6811 CheckComArgNotNull(aSize);
6812 CheckComArgNotNull(aWidth);
6813 CheckComArgNotNull(aHeight);
6814
6815 if (aScreenId != 0)
6816 return E_NOTIMPL;
6817
6818 AutoCaller autoCaller(this);
6819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6820
6821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6822
6823 uint8_t *pu8Data = NULL;
6824 uint32_t cbData = 0;
6825 uint32_t u32Width = 0;
6826 uint32_t u32Height = 0;
6827
6828 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6829
6830 if (RT_FAILURE(vrc))
6831 return setError(VBOX_E_IPRT_ERROR,
6832 tr("Saved screenshot data is not available (%Rrc)"),
6833 vrc);
6834
6835 *aSize = cbData;
6836 *aWidth = u32Width;
6837 *aHeight = u32Height;
6838
6839 freeSavedDisplayScreenshot(pu8Data);
6840
6841 return S_OK;
6842}
6843
6844STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6845{
6846 LogFlowThisFunc(("\n"));
6847
6848 CheckComArgNotNull(aWidth);
6849 CheckComArgNotNull(aHeight);
6850 CheckComArgOutSafeArrayPointerValid(aData);
6851
6852 if (aScreenId != 0)
6853 return E_NOTIMPL;
6854
6855 AutoCaller autoCaller(this);
6856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6857
6858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6859
6860 uint8_t *pu8Data = NULL;
6861 uint32_t cbData = 0;
6862 uint32_t u32Width = 0;
6863 uint32_t u32Height = 0;
6864
6865 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6866
6867 if (RT_FAILURE(vrc))
6868 return setError(VBOX_E_IPRT_ERROR,
6869 tr("Saved screenshot data is not available (%Rrc)"),
6870 vrc);
6871
6872 *aWidth = u32Width;
6873 *aHeight = u32Height;
6874
6875 com::SafeArray<BYTE> bitmap(cbData);
6876 /* Convert pixels to format expected by the API caller. */
6877 if (aBGR)
6878 {
6879 /* [0] B, [1] G, [2] R, [3] A. */
6880 for (unsigned i = 0; i < cbData; i += 4)
6881 {
6882 bitmap[i] = pu8Data[i];
6883 bitmap[i + 1] = pu8Data[i + 1];
6884 bitmap[i + 2] = pu8Data[i + 2];
6885 bitmap[i + 3] = 0xff;
6886 }
6887 }
6888 else
6889 {
6890 /* [0] R, [1] G, [2] B, [3] A. */
6891 for (unsigned i = 0; i < cbData; i += 4)
6892 {
6893 bitmap[i] = pu8Data[i + 2];
6894 bitmap[i + 1] = pu8Data[i + 1];
6895 bitmap[i + 2] = pu8Data[i];
6896 bitmap[i + 3] = 0xff;
6897 }
6898 }
6899 bitmap.detachTo(ComSafeArrayOutArg(aData));
6900
6901 freeSavedDisplayScreenshot(pu8Data);
6902
6903 return S_OK;
6904}
6905
6906
6907STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6908{
6909 LogFlowThisFunc(("\n"));
6910
6911 CheckComArgNotNull(aWidth);
6912 CheckComArgNotNull(aHeight);
6913 CheckComArgOutSafeArrayPointerValid(aData);
6914
6915 if (aScreenId != 0)
6916 return E_NOTIMPL;
6917
6918 AutoCaller autoCaller(this);
6919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6920
6921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6922
6923 uint8_t *pu8Data = NULL;
6924 uint32_t cbData = 0;
6925 uint32_t u32Width = 0;
6926 uint32_t u32Height = 0;
6927
6928 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6929
6930 if (RT_FAILURE(vrc))
6931 return setError(VBOX_E_IPRT_ERROR,
6932 tr("Saved screenshot data is not available (%Rrc)"),
6933 vrc);
6934
6935 *aWidth = u32Width;
6936 *aHeight = u32Height;
6937
6938 HRESULT rc = S_OK;
6939 uint8_t *pu8PNG = NULL;
6940 uint32_t cbPNG = 0;
6941 uint32_t cxPNG = 0;
6942 uint32_t cyPNG = 0;
6943
6944 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6945
6946 if (RT_SUCCESS(vrc))
6947 {
6948 com::SafeArray<BYTE> screenData(cbPNG);
6949 screenData.initFrom(pu8PNG, cbPNG);
6950 if (pu8PNG)
6951 RTMemFree(pu8PNG);
6952 screenData.detachTo(ComSafeArrayOutArg(aData));
6953 }
6954 else
6955 {
6956 if (pu8PNG)
6957 RTMemFree(pu8PNG);
6958 return setError(VBOX_E_IPRT_ERROR,
6959 tr("Could not convert screenshot to PNG (%Rrc)"),
6960 vrc);
6961 }
6962
6963 freeSavedDisplayScreenshot(pu8Data);
6964
6965 return rc;
6966}
6967
6968STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6969{
6970 LogFlowThisFunc(("\n"));
6971
6972 CheckComArgNotNull(aSize);
6973 CheckComArgNotNull(aWidth);
6974 CheckComArgNotNull(aHeight);
6975
6976 if (aScreenId != 0)
6977 return E_NOTIMPL;
6978
6979 AutoCaller autoCaller(this);
6980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6981
6982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6983
6984 uint8_t *pu8Data = NULL;
6985 uint32_t cbData = 0;
6986 uint32_t u32Width = 0;
6987 uint32_t u32Height = 0;
6988
6989 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6990
6991 if (RT_FAILURE(vrc))
6992 return setError(VBOX_E_IPRT_ERROR,
6993 tr("Saved screenshot data is not available (%Rrc)"),
6994 vrc);
6995
6996 *aSize = cbData;
6997 *aWidth = u32Width;
6998 *aHeight = u32Height;
6999
7000 freeSavedDisplayScreenshot(pu8Data);
7001
7002 return S_OK;
7003}
7004
7005STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
7006{
7007 LogFlowThisFunc(("\n"));
7008
7009 CheckComArgNotNull(aWidth);
7010 CheckComArgNotNull(aHeight);
7011 CheckComArgOutSafeArrayPointerValid(aData);
7012
7013 if (aScreenId != 0)
7014 return E_NOTIMPL;
7015
7016 AutoCaller autoCaller(this);
7017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7018
7019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7020
7021 uint8_t *pu8Data = NULL;
7022 uint32_t cbData = 0;
7023 uint32_t u32Width = 0;
7024 uint32_t u32Height = 0;
7025
7026 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
7027
7028 if (RT_FAILURE(vrc))
7029 return setError(VBOX_E_IPRT_ERROR,
7030 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
7031 vrc);
7032
7033 *aWidth = u32Width;
7034 *aHeight = u32Height;
7035
7036 com::SafeArray<BYTE> png(cbData);
7037 png.initFrom(pu8Data, cbData);
7038 png.detachTo(ComSafeArrayOutArg(aData));
7039
7040 freeSavedDisplayScreenshot(pu8Data);
7041
7042 return S_OK;
7043}
7044
7045STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
7046{
7047 HRESULT rc = S_OK;
7048 LogFlowThisFunc(("\n"));
7049
7050 AutoCaller autoCaller(this);
7051 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7052
7053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7054
7055 if (!mHWData->mCPUHotPlugEnabled)
7056 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7057
7058 if (aCpu >= mHWData->mCPUCount)
7059 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
7060
7061 if (mHWData->mCPUAttached[aCpu])
7062 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
7063
7064 alock.release();
7065 rc = onCPUChange(aCpu, false);
7066 alock.acquire();
7067 if (FAILED(rc)) return rc;
7068
7069 setModified(IsModified_MachineData);
7070 mHWData.backup();
7071 mHWData->mCPUAttached[aCpu] = true;
7072
7073 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7074 if (Global::IsOnline(mData->mMachineState))
7075 saveSettings(NULL);
7076
7077 return S_OK;
7078}
7079
7080STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7081{
7082 HRESULT rc = S_OK;
7083 LogFlowThisFunc(("\n"));
7084
7085 AutoCaller autoCaller(this);
7086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7087
7088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7089
7090 if (!mHWData->mCPUHotPlugEnabled)
7091 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7092
7093 if (aCpu >= SchemaDefs::MaxCPUCount)
7094 return setError(E_INVALIDARG,
7095 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7096 SchemaDefs::MaxCPUCount);
7097
7098 if (!mHWData->mCPUAttached[aCpu])
7099 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7100
7101 /* CPU 0 can't be detached */
7102 if (aCpu == 0)
7103 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7104
7105 alock.release();
7106 rc = onCPUChange(aCpu, true);
7107 alock.acquire();
7108 if (FAILED(rc)) return rc;
7109
7110 setModified(IsModified_MachineData);
7111 mHWData.backup();
7112 mHWData->mCPUAttached[aCpu] = false;
7113
7114 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7115 if (Global::IsOnline(mData->mMachineState))
7116 saveSettings(NULL);
7117
7118 return S_OK;
7119}
7120
7121STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7122{
7123 LogFlowThisFunc(("\n"));
7124
7125 CheckComArgNotNull(aCpuAttached);
7126
7127 *aCpuAttached = false;
7128
7129 AutoCaller autoCaller(this);
7130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7131
7132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7133
7134 /* If hotplug is enabled the CPU is always enabled. */
7135 if (!mHWData->mCPUHotPlugEnabled)
7136 {
7137 if (aCpu < mHWData->mCPUCount)
7138 *aCpuAttached = true;
7139 }
7140 else
7141 {
7142 if (aCpu < SchemaDefs::MaxCPUCount)
7143 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7144 }
7145
7146 return S_OK;
7147}
7148
7149STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7150{
7151 CheckComArgOutPointerValid(aName);
7152
7153 AutoCaller autoCaller(this);
7154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7155
7156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7157
7158 Utf8Str log = queryLogFilename(aIdx);
7159 if (!RTFileExists(log.c_str()))
7160 log.setNull();
7161 log.cloneTo(aName);
7162
7163 return S_OK;
7164}
7165
7166STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7167{
7168 LogFlowThisFunc(("\n"));
7169 CheckComArgOutSafeArrayPointerValid(aData);
7170 if (aSize < 0)
7171 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7172
7173 AutoCaller autoCaller(this);
7174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7175
7176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7177
7178 HRESULT rc = S_OK;
7179 Utf8Str log = queryLogFilename(aIdx);
7180
7181 /* do not unnecessarily hold the lock while doing something which does
7182 * not need the lock and potentially takes a long time. */
7183 alock.release();
7184
7185 /* Limit the chunk size to 32K for now, as that gives better performance
7186 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7187 * One byte expands to approx. 25 bytes of breathtaking XML. */
7188 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7189 com::SafeArray<BYTE> logData(cbData);
7190
7191 RTFILE LogFile;
7192 int vrc = RTFileOpen(&LogFile, log.c_str(),
7193 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7194 if (RT_SUCCESS(vrc))
7195 {
7196 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7197 if (RT_SUCCESS(vrc))
7198 logData.resize(cbData);
7199 else
7200 rc = setError(VBOX_E_IPRT_ERROR,
7201 tr("Could not read log file '%s' (%Rrc)"),
7202 log.c_str(), vrc);
7203 RTFileClose(LogFile);
7204 }
7205 else
7206 rc = setError(VBOX_E_IPRT_ERROR,
7207 tr("Could not open log file '%s' (%Rrc)"),
7208 log.c_str(), vrc);
7209
7210 if (FAILED(rc))
7211 logData.resize(0);
7212 logData.detachTo(ComSafeArrayOutArg(aData));
7213
7214 return rc;
7215}
7216
7217
7218/**
7219 * Currently this method doesn't attach device to the running VM,
7220 * just makes sure it's plugged on next VM start.
7221 */
7222STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7223{
7224 AutoCaller autoCaller(this);
7225 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7226
7227 // lock scope
7228 {
7229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7230
7231 HRESULT rc = checkStateDependency(MutableStateDep);
7232 if (FAILED(rc)) return rc;
7233
7234 ChipsetType_T aChipset = ChipsetType_PIIX3;
7235 COMGETTER(ChipsetType)(&aChipset);
7236
7237 if (aChipset != ChipsetType_ICH9)
7238 {
7239 return setError(E_INVALIDARG,
7240 tr("Host PCI attachment only supported with ICH9 chipset"));
7241 }
7242
7243 // check if device with this host PCI address already attached
7244 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7245 it != mHWData->mPCIDeviceAssignments.end();
7246 ++it)
7247 {
7248 LONG iHostAddress = -1;
7249 ComPtr<PCIDeviceAttachment> pAttach;
7250 pAttach = *it;
7251 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7252 if (iHostAddress == hostAddress)
7253 return setError(E_INVALIDARG,
7254 tr("Device with host PCI address already attached to this VM"));
7255 }
7256
7257 ComObjPtr<PCIDeviceAttachment> pda;
7258 char name[32];
7259
7260 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7261 Bstr bname(name);
7262 pda.createObject();
7263 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7264 setModified(IsModified_MachineData);
7265 mHWData.backup();
7266 mHWData->mPCIDeviceAssignments.push_back(pda);
7267 }
7268
7269 return S_OK;
7270}
7271
7272/**
7273 * Currently this method doesn't detach device from the running VM,
7274 * just makes sure it's not plugged on next VM start.
7275 */
7276STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7277{
7278 AutoCaller autoCaller(this);
7279 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7280
7281 ComObjPtr<PCIDeviceAttachment> pAttach;
7282 bool fRemoved = false;
7283 HRESULT rc;
7284
7285 // lock scope
7286 {
7287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7288
7289 rc = checkStateDependency(MutableStateDep);
7290 if (FAILED(rc)) return rc;
7291
7292 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7293 it != mHWData->mPCIDeviceAssignments.end();
7294 ++it)
7295 {
7296 LONG iHostAddress = -1;
7297 pAttach = *it;
7298 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7299 if (iHostAddress != -1 && iHostAddress == hostAddress)
7300 {
7301 setModified(IsModified_MachineData);
7302 mHWData.backup();
7303 mHWData->mPCIDeviceAssignments.remove(pAttach);
7304 fRemoved = true;
7305 break;
7306 }
7307 }
7308 }
7309
7310
7311 /* Fire event outside of the lock */
7312 if (fRemoved)
7313 {
7314 Assert(!pAttach.isNull());
7315 ComPtr<IEventSource> es;
7316 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7317 Assert(SUCCEEDED(rc));
7318 Bstr mid;
7319 rc = this->COMGETTER(Id)(mid.asOutParam());
7320 Assert(SUCCEEDED(rc));
7321 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7322 }
7323
7324 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7325 tr("No host PCI device %08x attached"),
7326 hostAddress
7327 );
7328}
7329
7330STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7331{
7332 CheckComArgOutSafeArrayPointerValid(aAssignments);
7333
7334 AutoCaller autoCaller(this);
7335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7336
7337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7338
7339 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7340 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7341
7342 return S_OK;
7343}
7344
7345STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7346{
7347 CheckComArgOutPointerValid(aBandwidthControl);
7348
7349 AutoCaller autoCaller(this);
7350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7351
7352 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7353
7354 return S_OK;
7355}
7356
7357STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7358{
7359 CheckComArgOutPointerValid(pfEnabled);
7360 AutoCaller autoCaller(this);
7361 HRESULT hrc = autoCaller.rc();
7362 if (SUCCEEDED(hrc))
7363 {
7364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7365 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7366 }
7367 return hrc;
7368}
7369
7370STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7371{
7372 AutoCaller autoCaller(this);
7373 HRESULT hrc = autoCaller.rc();
7374 if (SUCCEEDED(hrc))
7375 {
7376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7377 hrc = checkStateDependency(MutableStateDep);
7378 if (SUCCEEDED(hrc))
7379 {
7380 hrc = mHWData.backupEx();
7381 if (SUCCEEDED(hrc))
7382 {
7383 setModified(IsModified_MachineData);
7384 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7385 }
7386 }
7387 }
7388 return hrc;
7389}
7390
7391STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7392{
7393 CheckComArgOutPointerValid(pbstrConfig);
7394 AutoCaller autoCaller(this);
7395 HRESULT hrc = autoCaller.rc();
7396 if (SUCCEEDED(hrc))
7397 {
7398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7399 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7400 }
7401 return hrc;
7402}
7403
7404STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7405{
7406 CheckComArgStr(bstrConfig);
7407 AutoCaller autoCaller(this);
7408 HRESULT hrc = autoCaller.rc();
7409 if (SUCCEEDED(hrc))
7410 {
7411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7412 hrc = checkStateDependency(MutableStateDep);
7413 if (SUCCEEDED(hrc))
7414 {
7415 hrc = mHWData.backupEx();
7416 if (SUCCEEDED(hrc))
7417 {
7418 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7419 if (SUCCEEDED(hrc))
7420 setModified(IsModified_MachineData);
7421 }
7422 }
7423 }
7424 return hrc;
7425
7426}
7427
7428STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7429{
7430 CheckComArgOutPointerValid(pfAllow);
7431 AutoCaller autoCaller(this);
7432 HRESULT hrc = autoCaller.rc();
7433 if (SUCCEEDED(hrc))
7434 {
7435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7436 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7437 }
7438 return hrc;
7439}
7440
7441STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7442{
7443 AutoCaller autoCaller(this);
7444 HRESULT hrc = autoCaller.rc();
7445 if (SUCCEEDED(hrc))
7446 {
7447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7448 hrc = checkStateDependency(MutableStateDep);
7449 if (SUCCEEDED(hrc))
7450 {
7451 hrc = mHWData.backupEx();
7452 if (SUCCEEDED(hrc))
7453 {
7454 setModified(IsModified_MachineData);
7455 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7456 }
7457 }
7458 }
7459 return hrc;
7460}
7461
7462STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7463{
7464 CheckComArgOutPointerValid(pfEnabled);
7465 AutoCaller autoCaller(this);
7466 HRESULT hrc = autoCaller.rc();
7467 if (SUCCEEDED(hrc))
7468 {
7469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7470 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7471 }
7472 return hrc;
7473}
7474
7475STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7476{
7477 AutoCaller autoCaller(this);
7478 HRESULT hrc = autoCaller.rc();
7479 if (SUCCEEDED(hrc))
7480 {
7481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7482 hrc = checkStateDependency(MutableStateDep);
7483 if ( SUCCEEDED(hrc)
7484 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7485 {
7486 AutostartDb *autostartDb = mParent->getAutostartDb();
7487 int vrc;
7488
7489 if (fEnabled)
7490 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7491 else
7492 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7493
7494 if (RT_SUCCESS(vrc))
7495 {
7496 hrc = mHWData.backupEx();
7497 if (SUCCEEDED(hrc))
7498 {
7499 setModified(IsModified_MachineData);
7500 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7501 }
7502 }
7503 else if (vrc == VERR_NOT_SUPPORTED)
7504 hrc = setError(VBOX_E_NOT_SUPPORTED,
7505 tr("The VM autostart feature is not supported on this platform"));
7506 else if (vrc == VERR_PATH_NOT_FOUND)
7507 hrc = setError(E_FAIL,
7508 tr("The path to the autostart database is not set"));
7509 else
7510 hrc = setError(E_UNEXPECTED,
7511 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7512 fEnabled ? "Adding" : "Removing",
7513 mUserData->s.strName.c_str(), vrc);
7514 }
7515 }
7516 return hrc;
7517}
7518
7519STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7520{
7521 CheckComArgOutPointerValid(puDelay);
7522 AutoCaller autoCaller(this);
7523 HRESULT hrc = autoCaller.rc();
7524 if (SUCCEEDED(hrc))
7525 {
7526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7527 *puDelay = mHWData->mAutostart.uAutostartDelay;
7528 }
7529 return hrc;
7530}
7531
7532STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7533{
7534 AutoCaller autoCaller(this);
7535 HRESULT hrc = autoCaller.rc();
7536 if (SUCCEEDED(hrc))
7537 {
7538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7539 hrc = checkStateDependency(MutableStateDep);
7540 if (SUCCEEDED(hrc))
7541 {
7542 hrc = mHWData.backupEx();
7543 if (SUCCEEDED(hrc))
7544 {
7545 setModified(IsModified_MachineData);
7546 mHWData->mAutostart.uAutostartDelay = uDelay;
7547 }
7548 }
7549 }
7550 return hrc;
7551}
7552
7553STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7554{
7555 CheckComArgOutPointerValid(penmAutostopType);
7556 AutoCaller autoCaller(this);
7557 HRESULT hrc = autoCaller.rc();
7558 if (SUCCEEDED(hrc))
7559 {
7560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7561 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7562 }
7563 return hrc;
7564}
7565
7566STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7567{
7568 AutoCaller autoCaller(this);
7569 HRESULT hrc = autoCaller.rc();
7570 if (SUCCEEDED(hrc))
7571 {
7572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7573 hrc = checkStateDependency(MutableStateDep);
7574 if ( SUCCEEDED(hrc)
7575 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7576 {
7577 AutostartDb *autostartDb = mParent->getAutostartDb();
7578 int vrc;
7579
7580 if (enmAutostopType != AutostopType_Disabled)
7581 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7582 else
7583 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7584
7585 if (RT_SUCCESS(vrc))
7586 {
7587 hrc = mHWData.backupEx();
7588 if (SUCCEEDED(hrc))
7589 {
7590 setModified(IsModified_MachineData);
7591 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7592 }
7593 }
7594 else if (vrc == VERR_NOT_SUPPORTED)
7595 hrc = setError(VBOX_E_NOT_SUPPORTED,
7596 tr("The VM autostop feature is not supported on this platform"));
7597 else if (vrc == VERR_PATH_NOT_FOUND)
7598 hrc = setError(E_FAIL,
7599 tr("The path to the autostart database is not set"));
7600 else
7601 hrc = setError(E_UNEXPECTED,
7602 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7603 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7604 mUserData->s.strName.c_str(), vrc);
7605 }
7606 }
7607 return hrc;
7608}
7609
7610STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7611{
7612 CheckComArgOutPointerValid(aDefaultFrontend);
7613 AutoCaller autoCaller(this);
7614 HRESULT hrc = autoCaller.rc();
7615 if (SUCCEEDED(hrc))
7616 {
7617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7618 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7619 }
7620 return hrc;
7621}
7622
7623STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7624{
7625 CheckComArgStr(aDefaultFrontend);
7626 AutoCaller autoCaller(this);
7627 HRESULT hrc = autoCaller.rc();
7628 if (SUCCEEDED(hrc))
7629 {
7630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7631 hrc = checkStateDependency(MutableOrSavedStateDep);
7632 if (SUCCEEDED(hrc))
7633 {
7634 hrc = mHWData.backupEx();
7635 if (SUCCEEDED(hrc))
7636 {
7637 setModified(IsModified_MachineData);
7638 mHWData->mDefaultFrontend = aDefaultFrontend;
7639 }
7640 }
7641 }
7642 return hrc;
7643}
7644
7645STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7646{
7647 CheckComArgSafeArrayNotNull(aIcon);
7648 CheckComArgOutSafeArrayPointerValid(aIcon);
7649 AutoCaller autoCaller(this);
7650 HRESULT hrc = autoCaller.rc();
7651 if (SUCCEEDED(hrc))
7652 {
7653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7654 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7655 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7656 icon.detachTo(ComSafeArrayOutArg(aIcon));
7657 }
7658 return hrc;
7659}
7660
7661STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7662{
7663 CheckComArgSafeArrayNotNull(aIcon);
7664 AutoCaller autoCaller(this);
7665 HRESULT hrc = autoCaller.rc();
7666 if (SUCCEEDED(hrc))
7667 {
7668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7669 hrc = checkStateDependency(MutableOrSavedStateDep);
7670 if (SUCCEEDED(hrc))
7671 {
7672 setModified(IsModified_MachineData);
7673 mUserData.backup();
7674 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7675 mUserData->mIcon.resize(icon.size());
7676 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7677 }
7678 }
7679 return hrc;
7680}
7681
7682STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7683{
7684 CheckComArgOutPointerValid(aAvailable);
7685
7686 AutoCaller autoCaller(this);
7687 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7688
7689#ifdef VBOX_WITH_USB
7690 *aAvailable = true;
7691#else
7692 *aAvailable = false;
7693#endif
7694 return S_OK;
7695}
7696
7697STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7698{
7699 LogFlowFuncEnter();
7700
7701 CheckComArgNotNull(pTarget);
7702 CheckComArgOutPointerValid(pProgress);
7703
7704 /* Convert the options. */
7705 RTCList<CloneOptions_T> optList;
7706 if (options != NULL)
7707 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7708
7709 if (optList.contains(CloneOptions_Link))
7710 {
7711 if (!isSnapshotMachine())
7712 return setError(E_INVALIDARG,
7713 tr("Linked clone can only be created from a snapshot"));
7714 if (mode != CloneMode_MachineState)
7715 return setError(E_INVALIDARG,
7716 tr("Linked clone can only be created for a single machine state"));
7717 }
7718 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7719
7720 AutoCaller autoCaller(this);
7721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7722
7723
7724 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7725
7726 HRESULT rc = pWorker->start(pProgress);
7727
7728 LogFlowFuncLeave();
7729
7730 return rc;
7731}
7732
7733// public methods for internal purposes
7734/////////////////////////////////////////////////////////////////////////////
7735
7736/**
7737 * Adds the given IsModified_* flag to the dirty flags of the machine.
7738 * This must be called either during loadSettings or under the machine write lock.
7739 * @param fl
7740 */
7741void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7742{
7743 mData->flModifications |= fl;
7744 if (fAllowStateModification && isStateModificationAllowed())
7745 mData->mCurrentStateModified = true;
7746}
7747
7748/**
7749 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7750 * care of the write locking.
7751 *
7752 * @param fModifications The flag to add.
7753 */
7754void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7755{
7756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7757 setModified(fModification, fAllowStateModification);
7758}
7759
7760/**
7761 * Saves the registry entry of this machine to the given configuration node.
7762 *
7763 * @param aEntryNode Node to save the registry entry to.
7764 *
7765 * @note locks this object for reading.
7766 */
7767HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7768{
7769 AutoLimitedCaller autoCaller(this);
7770 AssertComRCReturnRC(autoCaller.rc());
7771
7772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7773
7774 data.uuid = mData->mUuid;
7775 data.strSettingsFile = mData->m_strConfigFile;
7776
7777 return S_OK;
7778}
7779
7780/**
7781 * Calculates the absolute path of the given path taking the directory of the
7782 * machine settings file as the current directory.
7783 *
7784 * @param aPath Path to calculate the absolute path for.
7785 * @param aResult Where to put the result (used only on success, can be the
7786 * same Utf8Str instance as passed in @a aPath).
7787 * @return IPRT result.
7788 *
7789 * @note Locks this object for reading.
7790 */
7791int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7792{
7793 AutoCaller autoCaller(this);
7794 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7795
7796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7797
7798 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7799
7800 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7801
7802 strSettingsDir.stripFilename();
7803 char folder[RTPATH_MAX];
7804 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7805 if (RT_SUCCESS(vrc))
7806 aResult = folder;
7807
7808 return vrc;
7809}
7810
7811/**
7812 * Copies strSource to strTarget, making it relative to the machine folder
7813 * if it is a subdirectory thereof, or simply copying it otherwise.
7814 *
7815 * @param strSource Path to evaluate and copy.
7816 * @param strTarget Buffer to receive target path.
7817 *
7818 * @note Locks this object for reading.
7819 */
7820void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7821 Utf8Str &strTarget)
7822{
7823 AutoCaller autoCaller(this);
7824 AssertComRCReturn(autoCaller.rc(), (void)0);
7825
7826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7827
7828 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7829 // use strTarget as a temporary buffer to hold the machine settings dir
7830 strTarget = mData->m_strConfigFileFull;
7831 strTarget.stripFilename();
7832 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7833 {
7834 // is relative: then append what's left
7835 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7836 // for empty paths (only possible for subdirs) use "." to avoid
7837 // triggering default settings for not present config attributes.
7838 if (strTarget.isEmpty())
7839 strTarget = ".";
7840 }
7841 else
7842 // is not relative: then overwrite
7843 strTarget = strSource;
7844}
7845
7846/**
7847 * Returns the full path to the machine's log folder in the
7848 * \a aLogFolder argument.
7849 */
7850void Machine::getLogFolder(Utf8Str &aLogFolder)
7851{
7852 AutoCaller autoCaller(this);
7853 AssertComRCReturnVoid(autoCaller.rc());
7854
7855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7856
7857 char szTmp[RTPATH_MAX];
7858 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7859 if (RT_SUCCESS(vrc))
7860 {
7861 if (szTmp[0] && !mUserData.isNull())
7862 {
7863 char szTmp2[RTPATH_MAX];
7864 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7865 if (RT_SUCCESS(vrc))
7866 aLogFolder = BstrFmt("%s%c%s",
7867 szTmp2,
7868 RTPATH_DELIMITER,
7869 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7870 }
7871 else
7872 vrc = VERR_PATH_IS_RELATIVE;
7873 }
7874
7875 if (RT_FAILURE(vrc))
7876 {
7877 // fallback if VBOX_USER_LOGHOME is not set or invalid
7878 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7879 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7880 aLogFolder.append(RTPATH_DELIMITER);
7881 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7882 }
7883}
7884
7885/**
7886 * Returns the full path to the machine's log file for an given index.
7887 */
7888Utf8Str Machine::queryLogFilename(ULONG idx)
7889{
7890 Utf8Str logFolder;
7891 getLogFolder(logFolder);
7892 Assert(logFolder.length());
7893 Utf8Str log;
7894 if (idx == 0)
7895 log = Utf8StrFmt("%s%cVBox.log",
7896 logFolder.c_str(), RTPATH_DELIMITER);
7897 else
7898 log = Utf8StrFmt("%s%cVBox.log.%d",
7899 logFolder.c_str(), RTPATH_DELIMITER, idx);
7900 return log;
7901}
7902
7903/**
7904 * Composes a unique saved state filename based on the current system time. The filename is
7905 * granular to the second so this will work so long as no more than one snapshot is taken on
7906 * a machine per second.
7907 *
7908 * Before version 4.1, we used this formula for saved state files:
7909 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7910 * which no longer works because saved state files can now be shared between the saved state of the
7911 * "saved" machine and an online snapshot, and the following would cause problems:
7912 * 1) save machine
7913 * 2) create online snapshot from that machine state --> reusing saved state file
7914 * 3) save machine again --> filename would be reused, breaking the online snapshot
7915 *
7916 * So instead we now use a timestamp.
7917 *
7918 * @param str
7919 */
7920void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7921{
7922 AutoCaller autoCaller(this);
7923 AssertComRCReturnVoid(autoCaller.rc());
7924
7925 {
7926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7927 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7928 }
7929
7930 RTTIMESPEC ts;
7931 RTTimeNow(&ts);
7932 RTTIME time;
7933 RTTimeExplode(&time, &ts);
7934
7935 strStateFilePath += RTPATH_DELIMITER;
7936 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7937 time.i32Year, time.u8Month, time.u8MonthDay,
7938 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7939}
7940
7941/**
7942 * Returns the full path to the default video capture file.
7943 */
7944void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7945{
7946 AutoCaller autoCaller(this);
7947 AssertComRCReturnVoid(autoCaller.rc());
7948
7949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7950
7951 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7952 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7953 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7954}
7955
7956/**
7957 * Returns whether at least one USB controller is present for the VM.
7958 */
7959bool Machine::isUSBControllerPresent()
7960{
7961 AutoCaller autoCaller(this);
7962 AssertComRCReturn(autoCaller.rc(), false);
7963
7964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7965
7966 return (mUSBControllers->size() > 0);
7967}
7968
7969/**
7970 * @note Locks this object for writing, calls the client process
7971 * (inside the lock).
7972 */
7973HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7974 const Utf8Str &strFrontend,
7975 const Utf8Str &strEnvironment,
7976 ProgressProxy *aProgress)
7977{
7978 LogFlowThisFuncEnter();
7979
7980 AssertReturn(aControl, E_FAIL);
7981 AssertReturn(aProgress, E_FAIL);
7982 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7983
7984 AutoCaller autoCaller(this);
7985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7986
7987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7988
7989 if (!mData->mRegistered)
7990 return setError(E_UNEXPECTED,
7991 tr("The machine '%s' is not registered"),
7992 mUserData->s.strName.c_str());
7993
7994 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7995
7996 if ( mData->mSession.mState == SessionState_Locked
7997 || mData->mSession.mState == SessionState_Spawning
7998 || mData->mSession.mState == SessionState_Unlocking)
7999 return setError(VBOX_E_INVALID_OBJECT_STATE,
8000 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
8001 mUserData->s.strName.c_str());
8002
8003 /* may not be busy */
8004 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
8005
8006 /* get the path to the executable */
8007 char szPath[RTPATH_MAX];
8008 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
8009 size_t sz = strlen(szPath);
8010 szPath[sz++] = RTPATH_DELIMITER;
8011 szPath[sz] = 0;
8012 char *cmd = szPath + sz;
8013 sz = sizeof(szPath) - sz;
8014
8015 int vrc = VINF_SUCCESS;
8016 RTPROCESS pid = NIL_RTPROCESS;
8017
8018 RTENV env = RTENV_DEFAULT;
8019
8020 if (!strEnvironment.isEmpty())
8021 {
8022 char *newEnvStr = NULL;
8023
8024 do
8025 {
8026 /* clone the current environment */
8027 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
8028 AssertRCBreakStmt(vrc2, vrc = vrc2);
8029
8030 newEnvStr = RTStrDup(strEnvironment.c_str());
8031 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
8032
8033 /* put new variables to the environment
8034 * (ignore empty variable names here since RTEnv API
8035 * intentionally doesn't do that) */
8036 char *var = newEnvStr;
8037 for (char *p = newEnvStr; *p; ++p)
8038 {
8039 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
8040 {
8041 *p = '\0';
8042 if (*var)
8043 {
8044 char *val = strchr(var, '=');
8045 if (val)
8046 {
8047 *val++ = '\0';
8048 vrc2 = RTEnvSetEx(env, var, val);
8049 }
8050 else
8051 vrc2 = RTEnvUnsetEx(env, var);
8052 if (RT_FAILURE(vrc2))
8053 break;
8054 }
8055 var = p + 1;
8056 }
8057 }
8058 if (RT_SUCCESS(vrc2) && *var)
8059 vrc2 = RTEnvPutEx(env, var);
8060
8061 AssertRCBreakStmt(vrc2, vrc = vrc2);
8062 }
8063 while (0);
8064
8065 if (newEnvStr != NULL)
8066 RTStrFree(newEnvStr);
8067 }
8068
8069#ifdef VBOX_WITH_QTGUI
8070 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
8071 {
8072# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
8073 /* Modify the base path so that we don't need to use ".." below. */
8074 RTPathStripTrailingSlash(szPath);
8075 RTPathStripFilename(szPath);
8076 sz = strlen(szPath);
8077 cmd = szPath + sz;
8078 sz = sizeof(szPath) - sz;
8079
8080#define OSX_APP_NAME "VirtualBoxVM"
8081#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
8082
8083 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
8084 if ( strAppOverride.contains(".")
8085 || strAppOverride.contains("/")
8086 || strAppOverride.contains("\\")
8087 || strAppOverride.contains(":"))
8088 strAppOverride.setNull();
8089 Utf8Str strAppPath;
8090 if (!strAppOverride.isEmpty())
8091 {
8092 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8093 Utf8Str strFullPath(szPath);
8094 strFullPath.append(strAppPath);
8095 /* there is a race, but people using this deserve the failure */
8096 if (!RTFileExists(strFullPath.c_str()))
8097 strAppOverride.setNull();
8098 }
8099 if (strAppOverride.isEmpty())
8100 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8101 const char *VirtualBox_exe = strAppPath.c_str();
8102 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8103# else
8104 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8105 Assert(sz >= sizeof(VirtualBox_exe));
8106# endif
8107 strcpy(cmd, VirtualBox_exe);
8108
8109 Utf8Str idStr = mData->mUuid.toString();
8110 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8111 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8112 }
8113#else /* !VBOX_WITH_QTGUI */
8114 if (0)
8115 ;
8116#endif /* VBOX_WITH_QTGUI */
8117
8118 else
8119
8120#ifdef VBOX_WITH_VBOXSDL
8121 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8122 {
8123 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8124 Assert(sz >= sizeof(VBoxSDL_exe));
8125 strcpy(cmd, VBoxSDL_exe);
8126
8127 Utf8Str idStr = mData->mUuid.toString();
8128 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8129 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8130 }
8131#else /* !VBOX_WITH_VBOXSDL */
8132 if (0)
8133 ;
8134#endif /* !VBOX_WITH_VBOXSDL */
8135
8136 else
8137
8138#ifdef VBOX_WITH_HEADLESS
8139 if ( strFrontend == "headless"
8140 || strFrontend == "capture"
8141 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8142 )
8143 {
8144 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8145 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8146 * and a VM works even if the server has not been installed.
8147 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8148 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8149 * differently in 4.0 and 3.x.
8150 */
8151 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8152 Assert(sz >= sizeof(VBoxHeadless_exe));
8153 strcpy(cmd, VBoxHeadless_exe);
8154
8155 Utf8Str idStr = mData->mUuid.toString();
8156 /* Leave space for "--capture" arg. */
8157 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8158 "--startvm", idStr.c_str(),
8159 "--vrde", "config",
8160 0, /* For "--capture". */
8161 0 };
8162 if (strFrontend == "capture")
8163 {
8164 unsigned pos = RT_ELEMENTS(args) - 2;
8165 args[pos] = "--capture";
8166 }
8167 vrc = RTProcCreate(szPath, args, env,
8168#ifdef RT_OS_WINDOWS
8169 RTPROC_FLAGS_NO_WINDOW
8170#else
8171 0
8172#endif
8173 , &pid);
8174 }
8175#else /* !VBOX_WITH_HEADLESS */
8176 if (0)
8177 ;
8178#endif /* !VBOX_WITH_HEADLESS */
8179 else
8180 {
8181 RTEnvDestroy(env);
8182 return setError(E_INVALIDARG,
8183 tr("Invalid frontend name: '%s'"),
8184 strFrontend.c_str());
8185 }
8186
8187 RTEnvDestroy(env);
8188
8189 if (RT_FAILURE(vrc))
8190 return setError(VBOX_E_IPRT_ERROR,
8191 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8192 mUserData->s.strName.c_str(), vrc);
8193
8194 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8195
8196 /*
8197 * Note that we don't release the lock here before calling the client,
8198 * because it doesn't need to call us back if called with a NULL argument.
8199 * Releasing the lock here is dangerous because we didn't prepare the
8200 * launch data yet, but the client we've just started may happen to be
8201 * too fast and call LockMachine() that will fail (because of PID, etc.),
8202 * so that the Machine will never get out of the Spawning session state.
8203 */
8204
8205 /* inform the session that it will be a remote one */
8206 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8207#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8208 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8209#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8210 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8211#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8212 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8213
8214 if (FAILED(rc))
8215 {
8216 /* restore the session state */
8217 mData->mSession.mState = SessionState_Unlocked;
8218 alock.release();
8219 mParent->addProcessToReap(pid);
8220 /* The failure may occur w/o any error info (from RPC), so provide one */
8221 return setError(VBOX_E_VM_ERROR,
8222 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8223 }
8224
8225 /* attach launch data to the machine */
8226 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8227 mData->mSession.mRemoteControls.push_back(aControl);
8228 mData->mSession.mProgress = aProgress;
8229 mData->mSession.mPID = pid;
8230 mData->mSession.mState = SessionState_Spawning;
8231 mData->mSession.mType = strFrontend;
8232
8233 alock.release();
8234 mParent->addProcessToReap(pid);
8235
8236 LogFlowThisFuncLeave();
8237 return S_OK;
8238}
8239
8240/**
8241 * Returns @c true if the given session machine instance has an open direct
8242 * session (and optionally also for direct sessions which are closing) and
8243 * returns the session control machine instance if so.
8244 *
8245 * Note that when the method returns @c false, the arguments remain unchanged.
8246 *
8247 * @param aMachine Session machine object.
8248 * @param aControl Direct session control object (optional).
8249 * @param aAllowClosing If true then additionally a session which is currently
8250 * being closed will also be allowed.
8251 *
8252 * @note locks this object for reading.
8253 */
8254bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8255 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8256 bool aAllowClosing /*= false*/)
8257{
8258 AutoLimitedCaller autoCaller(this);
8259 AssertComRCReturn(autoCaller.rc(), false);
8260
8261 /* just return false for inaccessible machines */
8262 if (autoCaller.state() != Ready)
8263 return false;
8264
8265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8266
8267 if ( mData->mSession.mState == SessionState_Locked
8268 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8269 )
8270 {
8271 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8272
8273 aMachine = mData->mSession.mMachine;
8274
8275 if (aControl != NULL)
8276 *aControl = mData->mSession.mDirectControl;
8277
8278 return true;
8279 }
8280
8281 return false;
8282}
8283
8284/**
8285 * Returns @c true if the given machine has an spawning direct session.
8286 *
8287 * @note locks this object for reading.
8288 */
8289bool Machine::isSessionSpawning()
8290{
8291 AutoLimitedCaller autoCaller(this);
8292 AssertComRCReturn(autoCaller.rc(), false);
8293
8294 /* just return false for inaccessible machines */
8295 if (autoCaller.state() != Ready)
8296 return false;
8297
8298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8299
8300 if (mData->mSession.mState == SessionState_Spawning)
8301 return true;
8302
8303 return false;
8304}
8305
8306/**
8307 * Called from the client watcher thread to check for unexpected client process
8308 * death during Session_Spawning state (e.g. before it successfully opened a
8309 * direct session).
8310 *
8311 * On Win32 and on OS/2, this method is called only when we've got the
8312 * direct client's process termination notification, so it always returns @c
8313 * true.
8314 *
8315 * On other platforms, this method returns @c true if the client process is
8316 * terminated and @c false if it's still alive.
8317 *
8318 * @note Locks this object for writing.
8319 */
8320bool Machine::checkForSpawnFailure()
8321{
8322 AutoCaller autoCaller(this);
8323 if (!autoCaller.isOk())
8324 {
8325 /* nothing to do */
8326 LogFlowThisFunc(("Already uninitialized!\n"));
8327 return true;
8328 }
8329
8330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8331
8332 if (mData->mSession.mState != SessionState_Spawning)
8333 {
8334 /* nothing to do */
8335 LogFlowThisFunc(("Not spawning any more!\n"));
8336 return true;
8337 }
8338
8339 HRESULT rc = S_OK;
8340
8341 /* PID not yet initialized, skip check. */
8342 if (mData->mSession.mPID == NIL_RTPROCESS)
8343 return false;
8344
8345 RTPROCSTATUS status;
8346 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8347
8348 if (vrc != VERR_PROCESS_RUNNING)
8349 {
8350 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8351 rc = setError(E_FAIL,
8352 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8353 getName().c_str(), status.iStatus);
8354 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8355 rc = setError(E_FAIL,
8356 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8357 getName().c_str(), status.iStatus);
8358 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8359 rc = setError(E_FAIL,
8360 tr("The virtual machine '%s' has terminated abnormally"),
8361 getName().c_str(), status.iStatus);
8362 else
8363 rc = setError(E_FAIL,
8364 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8365 getName().c_str(), vrc);
8366 }
8367
8368 if (FAILED(rc))
8369 {
8370 /* Close the remote session, remove the remote control from the list
8371 * and reset session state to Closed (@note keep the code in sync with
8372 * the relevant part in LockMachine()). */
8373
8374 Assert(mData->mSession.mRemoteControls.size() == 1);
8375 if (mData->mSession.mRemoteControls.size() == 1)
8376 {
8377 ErrorInfoKeeper eik;
8378 mData->mSession.mRemoteControls.front()->Uninitialize();
8379 }
8380
8381 mData->mSession.mRemoteControls.clear();
8382 mData->mSession.mState = SessionState_Unlocked;
8383
8384 /* finalize the progress after setting the state */
8385 if (!mData->mSession.mProgress.isNull())
8386 {
8387 mData->mSession.mProgress->notifyComplete(rc);
8388 mData->mSession.mProgress.setNull();
8389 }
8390
8391 mData->mSession.mPID = NIL_RTPROCESS;
8392
8393 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8394 return true;
8395 }
8396
8397 return false;
8398}
8399
8400/**
8401 * Checks whether the machine can be registered. If so, commits and saves
8402 * all settings.
8403 *
8404 * @note Must be called from mParent's write lock. Locks this object and
8405 * children for writing.
8406 */
8407HRESULT Machine::prepareRegister()
8408{
8409 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8410
8411 AutoLimitedCaller autoCaller(this);
8412 AssertComRCReturnRC(autoCaller.rc());
8413
8414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8415
8416 /* wait for state dependents to drop to zero */
8417 ensureNoStateDependencies();
8418
8419 if (!mData->mAccessible)
8420 return setError(VBOX_E_INVALID_OBJECT_STATE,
8421 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8422 mUserData->s.strName.c_str(),
8423 mData->mUuid.toString().c_str());
8424
8425 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8426
8427 if (mData->mRegistered)
8428 return setError(VBOX_E_INVALID_OBJECT_STATE,
8429 tr("The machine '%s' with UUID {%s} is already registered"),
8430 mUserData->s.strName.c_str(),
8431 mData->mUuid.toString().c_str());
8432
8433 HRESULT rc = S_OK;
8434
8435 // Ensure the settings are saved. If we are going to be registered and
8436 // no config file exists yet, create it by calling saveSettings() too.
8437 if ( (mData->flModifications)
8438 || (!mData->pMachineConfigFile->fileExists())
8439 )
8440 {
8441 rc = saveSettings(NULL);
8442 // no need to check whether VirtualBox.xml needs saving too since
8443 // we can't have a machine XML file rename pending
8444 if (FAILED(rc)) return rc;
8445 }
8446
8447 /* more config checking goes here */
8448
8449 if (SUCCEEDED(rc))
8450 {
8451 /* we may have had implicit modifications we want to fix on success */
8452 commit();
8453
8454 mData->mRegistered = true;
8455 }
8456 else
8457 {
8458 /* we may have had implicit modifications we want to cancel on failure*/
8459 rollback(false /* aNotify */);
8460 }
8461
8462 return rc;
8463}
8464
8465/**
8466 * Increases the number of objects dependent on the machine state or on the
8467 * registered state. Guarantees that these two states will not change at least
8468 * until #releaseStateDependency() is called.
8469 *
8470 * Depending on the @a aDepType value, additional state checks may be made.
8471 * These checks will set extended error info on failure. See
8472 * #checkStateDependency() for more info.
8473 *
8474 * If this method returns a failure, the dependency is not added and the caller
8475 * is not allowed to rely on any particular machine state or registration state
8476 * value and may return the failed result code to the upper level.
8477 *
8478 * @param aDepType Dependency type to add.
8479 * @param aState Current machine state (NULL if not interested).
8480 * @param aRegistered Current registered state (NULL if not interested).
8481 *
8482 * @note Locks this object for writing.
8483 */
8484HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8485 MachineState_T *aState /* = NULL */,
8486 BOOL *aRegistered /* = NULL */)
8487{
8488 AutoCaller autoCaller(this);
8489 AssertComRCReturnRC(autoCaller.rc());
8490
8491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8492
8493 HRESULT rc = checkStateDependency(aDepType);
8494 if (FAILED(rc)) return rc;
8495
8496 {
8497 if (mData->mMachineStateChangePending != 0)
8498 {
8499 /* ensureNoStateDependencies() is waiting for state dependencies to
8500 * drop to zero so don't add more. It may make sense to wait a bit
8501 * and retry before reporting an error (since the pending state
8502 * transition should be really quick) but let's just assert for
8503 * now to see if it ever happens on practice. */
8504
8505 AssertFailed();
8506
8507 return setError(E_ACCESSDENIED,
8508 tr("Machine state change is in progress. Please retry the operation later."));
8509 }
8510
8511 ++mData->mMachineStateDeps;
8512 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8513 }
8514
8515 if (aState)
8516 *aState = mData->mMachineState;
8517 if (aRegistered)
8518 *aRegistered = mData->mRegistered;
8519
8520 return S_OK;
8521}
8522
8523/**
8524 * Decreases the number of objects dependent on the machine state.
8525 * Must always complete the #addStateDependency() call after the state
8526 * dependency is no more necessary.
8527 */
8528void Machine::releaseStateDependency()
8529{
8530 AutoCaller autoCaller(this);
8531 AssertComRCReturnVoid(autoCaller.rc());
8532
8533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8534
8535 /* releaseStateDependency() w/o addStateDependency()? */
8536 AssertReturnVoid(mData->mMachineStateDeps != 0);
8537 -- mData->mMachineStateDeps;
8538
8539 if (mData->mMachineStateDeps == 0)
8540 {
8541 /* inform ensureNoStateDependencies() that there are no more deps */
8542 if (mData->mMachineStateChangePending != 0)
8543 {
8544 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8545 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8546 }
8547 }
8548}
8549
8550Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8551{
8552 /* start with nothing found */
8553 Utf8Str strResult("");
8554
8555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8556
8557 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8558 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8559 // found:
8560 strResult = it->second; // source is a Utf8Str
8561
8562 return strResult;
8563}
8564
8565// protected methods
8566/////////////////////////////////////////////////////////////////////////////
8567
8568/**
8569 * Performs machine state checks based on the @a aDepType value. If a check
8570 * fails, this method will set extended error info, otherwise it will return
8571 * S_OK. It is supposed, that on failure, the caller will immediately return
8572 * the return value of this method to the upper level.
8573 *
8574 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8575 *
8576 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8577 * current state of this machine object allows to change settings of the
8578 * machine (i.e. the machine is not registered, or registered but not running
8579 * and not saved). It is useful to call this method from Machine setters
8580 * before performing any change.
8581 *
8582 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8583 * as for MutableStateDep except that if the machine is saved, S_OK is also
8584 * returned. This is useful in setters which allow changing machine
8585 * properties when it is in the saved state.
8586 *
8587 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8588 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8589 * Aborted).
8590 *
8591 * @param aDepType Dependency type to check.
8592 *
8593 * @note Non Machine based classes should use #addStateDependency() and
8594 * #releaseStateDependency() methods or the smart AutoStateDependency
8595 * template.
8596 *
8597 * @note This method must be called from under this object's read or write
8598 * lock.
8599 */
8600HRESULT Machine::checkStateDependency(StateDependency aDepType)
8601{
8602 switch (aDepType)
8603 {
8604 case AnyStateDep:
8605 {
8606 break;
8607 }
8608 case MutableStateDep:
8609 {
8610 if ( mData->mRegistered
8611 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8612 || ( mData->mMachineState != MachineState_Paused
8613 && mData->mMachineState != MachineState_Running
8614 && mData->mMachineState != MachineState_Aborted
8615 && mData->mMachineState != MachineState_Teleported
8616 && mData->mMachineState != MachineState_PoweredOff
8617 )
8618 )
8619 )
8620 return setError(VBOX_E_INVALID_VM_STATE,
8621 tr("The machine is not mutable (state is %s)"),
8622 Global::stringifyMachineState(mData->mMachineState));
8623 break;
8624 }
8625 case MutableOrSavedStateDep:
8626 {
8627 if ( mData->mRegistered
8628 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8629 || ( mData->mMachineState != MachineState_Paused
8630 && mData->mMachineState != MachineState_Running
8631 && mData->mMachineState != MachineState_Aborted
8632 && mData->mMachineState != MachineState_Teleported
8633 && mData->mMachineState != MachineState_Saved
8634 && mData->mMachineState != MachineState_PoweredOff
8635 )
8636 )
8637 )
8638 return setError(VBOX_E_INVALID_VM_STATE,
8639 tr("The machine is not mutable (state is %s)"),
8640 Global::stringifyMachineState(mData->mMachineState));
8641 break;
8642 }
8643 case OfflineStateDep:
8644 {
8645 if ( mData->mRegistered
8646 && ( !isSessionMachine()
8647 || ( mData->mMachineState != MachineState_PoweredOff
8648 && mData->mMachineState != MachineState_Saved
8649 && mData->mMachineState != MachineState_Aborted
8650 && mData->mMachineState != MachineState_Teleported
8651 )
8652 )
8653 )
8654 return setError(VBOX_E_INVALID_VM_STATE,
8655 tr("The machine is not offline (state is %s)"),
8656 Global::stringifyMachineState(mData->mMachineState));
8657 break;
8658 }
8659 }
8660
8661 return S_OK;
8662}
8663
8664/**
8665 * Helper to initialize all associated child objects and allocate data
8666 * structures.
8667 *
8668 * This method must be called as a part of the object's initialization procedure
8669 * (usually done in the #init() method).
8670 *
8671 * @note Must be called only from #init() or from #registeredInit().
8672 */
8673HRESULT Machine::initDataAndChildObjects()
8674{
8675 AutoCaller autoCaller(this);
8676 AssertComRCReturnRC(autoCaller.rc());
8677 AssertComRCReturn(autoCaller.state() == InInit ||
8678 autoCaller.state() == Limited, E_FAIL);
8679
8680 AssertReturn(!mData->mAccessible, E_FAIL);
8681
8682 /* allocate data structures */
8683 mSSData.allocate();
8684 mUserData.allocate();
8685 mHWData.allocate();
8686 mMediaData.allocate();
8687 mStorageControllers.allocate();
8688 mUSBControllers.allocate();
8689
8690 /* initialize mOSTypeId */
8691 mUserData->s.strOsType = mParent->getUnknownOSType()->i_id();
8692
8693 /* create associated BIOS settings object */
8694 unconst(mBIOSSettings).createObject();
8695 mBIOSSettings->init(this);
8696
8697 /* create an associated VRDE object (default is disabled) */
8698 unconst(mVRDEServer).createObject();
8699 mVRDEServer->init(this);
8700
8701 /* create associated serial port objects */
8702 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8703 {
8704 unconst(mSerialPorts[slot]).createObject();
8705 mSerialPorts[slot]->init(this, slot);
8706 }
8707
8708 /* create associated parallel port objects */
8709 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8710 {
8711 unconst(mParallelPorts[slot]).createObject();
8712 mParallelPorts[slot]->init(this, slot);
8713 }
8714
8715 /* create the audio adapter object (always present, default is disabled) */
8716 unconst(mAudioAdapter).createObject();
8717 mAudioAdapter->init(this);
8718
8719 /* create the USB device filters object (always present) */
8720 unconst(mUSBDeviceFilters).createObject();
8721 mUSBDeviceFilters->init(this);
8722
8723 /* create associated network adapter objects */
8724 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8725 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8726 {
8727 unconst(mNetworkAdapters[slot]).createObject();
8728 mNetworkAdapters[slot]->init(this, slot);
8729 }
8730
8731 /* create the bandwidth control */
8732 unconst(mBandwidthControl).createObject();
8733 mBandwidthControl->init(this);
8734
8735 return S_OK;
8736}
8737
8738/**
8739 * Helper to uninitialize all associated child objects and to free all data
8740 * structures.
8741 *
8742 * This method must be called as a part of the object's uninitialization
8743 * procedure (usually done in the #uninit() method).
8744 *
8745 * @note Must be called only from #uninit() or from #registeredInit().
8746 */
8747void Machine::uninitDataAndChildObjects()
8748{
8749 AutoCaller autoCaller(this);
8750 AssertComRCReturnVoid(autoCaller.rc());
8751 AssertComRCReturnVoid( autoCaller.state() == InUninit
8752 || autoCaller.state() == Limited);
8753
8754 /* tell all our other child objects we've been uninitialized */
8755 if (mBandwidthControl)
8756 {
8757 mBandwidthControl->uninit();
8758 unconst(mBandwidthControl).setNull();
8759 }
8760
8761 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8762 {
8763 if (mNetworkAdapters[slot])
8764 {
8765 mNetworkAdapters[slot]->uninit();
8766 unconst(mNetworkAdapters[slot]).setNull();
8767 }
8768 }
8769
8770 if (mUSBDeviceFilters)
8771 {
8772 mUSBDeviceFilters->uninit();
8773 unconst(mUSBDeviceFilters).setNull();
8774 }
8775
8776 if (mAudioAdapter)
8777 {
8778 mAudioAdapter->uninit();
8779 unconst(mAudioAdapter).setNull();
8780 }
8781
8782 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8783 {
8784 if (mParallelPorts[slot])
8785 {
8786 mParallelPorts[slot]->uninit();
8787 unconst(mParallelPorts[slot]).setNull();
8788 }
8789 }
8790
8791 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8792 {
8793 if (mSerialPorts[slot])
8794 {
8795 mSerialPorts[slot]->uninit();
8796 unconst(mSerialPorts[slot]).setNull();
8797 }
8798 }
8799
8800 if (mVRDEServer)
8801 {
8802 mVRDEServer->uninit();
8803 unconst(mVRDEServer).setNull();
8804 }
8805
8806 if (mBIOSSettings)
8807 {
8808 mBIOSSettings->uninit();
8809 unconst(mBIOSSettings).setNull();
8810 }
8811
8812 /* Deassociate media (only when a real Machine or a SnapshotMachine
8813 * instance is uninitialized; SessionMachine instances refer to real
8814 * Machine media). This is necessary for a clean re-initialization of
8815 * the VM after successfully re-checking the accessibility state. Note
8816 * that in case of normal Machine or SnapshotMachine uninitialization (as
8817 * a result of unregistering or deleting the snapshot), outdated media
8818 * attachments will already be uninitialized and deleted, so this
8819 * code will not affect them. */
8820 if ( !!mMediaData
8821 && (!isSessionMachine())
8822 )
8823 {
8824 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8825 it != mMediaData->mAttachments.end();
8826 ++it)
8827 {
8828 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8829 if (pMedium.isNull())
8830 continue;
8831 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, getSnapshotId());
8832 AssertComRC(rc);
8833 }
8834 }
8835
8836 if (!isSessionMachine() && !isSnapshotMachine())
8837 {
8838 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8839 if (mData->mFirstSnapshot)
8840 {
8841 // snapshots tree is protected by machine write lock; strictly
8842 // this isn't necessary here since we're deleting the entire
8843 // machine, but otherwise we assert in Snapshot::uninit()
8844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8845 mData->mFirstSnapshot->uninit();
8846 mData->mFirstSnapshot.setNull();
8847 }
8848
8849 mData->mCurrentSnapshot.setNull();
8850 }
8851
8852 /* free data structures (the essential mData structure is not freed here
8853 * since it may be still in use) */
8854 mMediaData.free();
8855 mStorageControllers.free();
8856 mUSBControllers.free();
8857 mHWData.free();
8858 mUserData.free();
8859 mSSData.free();
8860}
8861
8862/**
8863 * Returns a pointer to the Machine object for this machine that acts like a
8864 * parent for complex machine data objects such as shared folders, etc.
8865 *
8866 * For primary Machine objects and for SnapshotMachine objects, returns this
8867 * object's pointer itself. For SessionMachine objects, returns the peer
8868 * (primary) machine pointer.
8869 */
8870Machine* Machine::getMachine()
8871{
8872 if (isSessionMachine())
8873 return (Machine*)mPeer;
8874 return this;
8875}
8876
8877/**
8878 * Makes sure that there are no machine state dependents. If necessary, waits
8879 * for the number of dependents to drop to zero.
8880 *
8881 * Make sure this method is called from under this object's write lock to
8882 * guarantee that no new dependents may be added when this method returns
8883 * control to the caller.
8884 *
8885 * @note Locks this object for writing. The lock will be released while waiting
8886 * (if necessary).
8887 *
8888 * @warning To be used only in methods that change the machine state!
8889 */
8890void Machine::ensureNoStateDependencies()
8891{
8892 AssertReturnVoid(isWriteLockOnCurrentThread());
8893
8894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8895
8896 /* Wait for all state dependents if necessary */
8897 if (mData->mMachineStateDeps != 0)
8898 {
8899 /* lazy semaphore creation */
8900 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8901 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8902
8903 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8904 mData->mMachineStateDeps));
8905
8906 ++mData->mMachineStateChangePending;
8907
8908 /* reset the semaphore before waiting, the last dependent will signal
8909 * it */
8910 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8911
8912 alock.release();
8913
8914 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8915
8916 alock.acquire();
8917
8918 -- mData->mMachineStateChangePending;
8919 }
8920}
8921
8922/**
8923 * Changes the machine state and informs callbacks.
8924 *
8925 * This method is not intended to fail so it either returns S_OK or asserts (and
8926 * returns a failure).
8927 *
8928 * @note Locks this object for writing.
8929 */
8930HRESULT Machine::setMachineState(MachineState_T aMachineState)
8931{
8932 LogFlowThisFuncEnter();
8933 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8934
8935 AutoCaller autoCaller(this);
8936 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8937
8938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8939
8940 /* wait for state dependents to drop to zero */
8941 ensureNoStateDependencies();
8942
8943 if (mData->mMachineState != aMachineState)
8944 {
8945 mData->mMachineState = aMachineState;
8946
8947 RTTimeNow(&mData->mLastStateChange);
8948
8949 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8950 }
8951
8952 LogFlowThisFuncLeave();
8953 return S_OK;
8954}
8955
8956/**
8957 * Searches for a shared folder with the given logical name
8958 * in the collection of shared folders.
8959 *
8960 * @param aName logical name of the shared folder
8961 * @param aSharedFolder where to return the found object
8962 * @param aSetError whether to set the error info if the folder is
8963 * not found
8964 * @return
8965 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8966 *
8967 * @note
8968 * must be called from under the object's lock!
8969 */
8970HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8971 ComObjPtr<SharedFolder> &aSharedFolder,
8972 bool aSetError /* = false */)
8973{
8974 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8975 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8976 it != mHWData->mSharedFolders.end();
8977 ++it)
8978 {
8979 SharedFolder *pSF = *it;
8980 AutoCaller autoCaller(pSF);
8981 if (pSF->getName() == aName)
8982 {
8983 aSharedFolder = pSF;
8984 rc = S_OK;
8985 break;
8986 }
8987 }
8988
8989 if (aSetError && FAILED(rc))
8990 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8991
8992 return rc;
8993}
8994
8995/**
8996 * Initializes all machine instance data from the given settings structures
8997 * from XML. The exception is the machine UUID which needs special handling
8998 * depending on the caller's use case, so the caller needs to set that herself.
8999 *
9000 * This gets called in several contexts during machine initialization:
9001 *
9002 * -- When machine XML exists on disk already and needs to be loaded into memory,
9003 * for example, from registeredInit() to load all registered machines on
9004 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
9005 * attached to the machine should be part of some media registry already.
9006 *
9007 * -- During OVF import, when a machine config has been constructed from an
9008 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9009 * ensure that the media listed as attachments in the config (which have
9010 * been imported from the OVF) receive the correct registry ID.
9011 *
9012 * -- During VM cloning.
9013 *
9014 * @param config Machine settings from XML.
9015 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
9016 * @return
9017 */
9018HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9019 const Guid *puuidRegistry)
9020{
9021 // copy name, description, OS type, teleporter, UTC etc.
9022 mUserData->s = config.machineUserData;
9023
9024 // Decode the Icon overide data from config userdata and set onto Machine.
9025 #define DECODE_STR_MAX _1M
9026 const char* pszStr = config.machineUserData.ovIcon.c_str();
9027 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
9028 if (cbOut > DECODE_STR_MAX)
9029 return setError(E_FAIL,
9030 tr("Icon Data too long.'%d' > '%d'"),
9031 cbOut,
9032 DECODE_STR_MAX);
9033 com::SafeArray<BYTE> iconByte(cbOut);
9034 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
9035 if (FAILED(rc))
9036 return setError(E_FAIL,
9037 tr("Failure to Decode Icon Data. '%s' (%d)"),
9038 pszStr,
9039 rc);
9040 mUserData->mIcon.resize(iconByte.size());
9041 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
9042
9043 // look up the object by Id to check it is valid
9044 ComPtr<IGuestOSType> guestOSType;
9045 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9046 guestOSType.asOutParam());
9047 if (FAILED(rc)) return rc;
9048
9049 // stateFile (optional)
9050 if (config.strStateFile.isEmpty())
9051 mSSData->strStateFilePath.setNull();
9052 else
9053 {
9054 Utf8Str stateFilePathFull(config.strStateFile);
9055 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9056 if (RT_FAILURE(vrc))
9057 return setError(E_FAIL,
9058 tr("Invalid saved state file path '%s' (%Rrc)"),
9059 config.strStateFile.c_str(),
9060 vrc);
9061 mSSData->strStateFilePath = stateFilePathFull;
9062 }
9063
9064 // snapshot folder needs special processing so set it again
9065 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9066 if (FAILED(rc)) return rc;
9067
9068 /* Copy the extra data items (Not in any case config is already the same as
9069 * mData->pMachineConfigFile, like when the xml files are read from disk. So
9070 * make sure the extra data map is copied). */
9071 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9072
9073 /* currentStateModified (optional, default is true) */
9074 mData->mCurrentStateModified = config.fCurrentStateModified;
9075
9076 mData->mLastStateChange = config.timeLastStateChange;
9077
9078 /*
9079 * note: all mUserData members must be assigned prior this point because
9080 * we need to commit changes in order to let mUserData be shared by all
9081 * snapshot machine instances.
9082 */
9083 mUserData.commitCopy();
9084
9085 // machine registry, if present (must be loaded before snapshots)
9086 if (config.canHaveOwnMediaRegistry())
9087 {
9088 // determine machine folder
9089 Utf8Str strMachineFolder = getSettingsFileFull();
9090 strMachineFolder.stripFilename();
9091 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9092 config.mediaRegistry,
9093 strMachineFolder);
9094 if (FAILED(rc)) return rc;
9095 }
9096
9097 /* Snapshot node (optional) */
9098 size_t cRootSnapshots;
9099 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9100 {
9101 // there must be only one root snapshot
9102 Assert(cRootSnapshots == 1);
9103
9104 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9105
9106 rc = loadSnapshot(snap,
9107 config.uuidCurrentSnapshot,
9108 NULL); // no parent == first snapshot
9109 if (FAILED(rc)) return rc;
9110 }
9111
9112 // hardware data
9113 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9114 if (FAILED(rc)) return rc;
9115
9116 // load storage controllers
9117 rc = loadStorageControllers(config.storageMachine,
9118 puuidRegistry,
9119 NULL /* puuidSnapshot */);
9120 if (FAILED(rc)) return rc;
9121
9122 /*
9123 * NOTE: the assignment below must be the last thing to do,
9124 * otherwise it will be not possible to change the settings
9125 * somewhere in the code above because all setters will be
9126 * blocked by checkStateDependency(MutableStateDep).
9127 */
9128
9129 /* set the machine state to Aborted or Saved when appropriate */
9130 if (config.fAborted)
9131 {
9132 mSSData->strStateFilePath.setNull();
9133
9134 /* no need to use setMachineState() during init() */
9135 mData->mMachineState = MachineState_Aborted;
9136 }
9137 else if (!mSSData->strStateFilePath.isEmpty())
9138 {
9139 /* no need to use setMachineState() during init() */
9140 mData->mMachineState = MachineState_Saved;
9141 }
9142
9143 // after loading settings, we are no longer different from the XML on disk
9144 mData->flModifications = 0;
9145
9146 return S_OK;
9147}
9148
9149/**
9150 * Recursively loads all snapshots starting from the given.
9151 *
9152 * @param aNode <Snapshot> node.
9153 * @param aCurSnapshotId Current snapshot ID from the settings file.
9154 * @param aParentSnapshot Parent snapshot.
9155 */
9156HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9157 const Guid &aCurSnapshotId,
9158 Snapshot *aParentSnapshot)
9159{
9160 AssertReturn(!isSnapshotMachine(), E_FAIL);
9161 AssertReturn(!isSessionMachine(), E_FAIL);
9162
9163 HRESULT rc = S_OK;
9164
9165 Utf8Str strStateFile;
9166 if (!data.strStateFile.isEmpty())
9167 {
9168 /* optional */
9169 strStateFile = data.strStateFile;
9170 int vrc = calculateFullPath(strStateFile, strStateFile);
9171 if (RT_FAILURE(vrc))
9172 return setError(E_FAIL,
9173 tr("Invalid saved state file path '%s' (%Rrc)"),
9174 strStateFile.c_str(),
9175 vrc);
9176 }
9177
9178 /* create a snapshot machine object */
9179 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9180 pSnapshotMachine.createObject();
9181 rc = pSnapshotMachine->initFromSettings(this,
9182 data.hardware,
9183 &data.debugging,
9184 &data.autostart,
9185 data.storage,
9186 data.uuid.ref(),
9187 strStateFile);
9188 if (FAILED(rc)) return rc;
9189
9190 /* create a snapshot object */
9191 ComObjPtr<Snapshot> pSnapshot;
9192 pSnapshot.createObject();
9193 /* initialize the snapshot */
9194 rc = pSnapshot->init(mParent, // VirtualBox object
9195 data.uuid,
9196 data.strName,
9197 data.strDescription,
9198 data.timestamp,
9199 pSnapshotMachine,
9200 aParentSnapshot);
9201 if (FAILED(rc)) return rc;
9202
9203 /* memorize the first snapshot if necessary */
9204 if (!mData->mFirstSnapshot)
9205 mData->mFirstSnapshot = pSnapshot;
9206
9207 /* memorize the current snapshot when appropriate */
9208 if ( !mData->mCurrentSnapshot
9209 && pSnapshot->i_getId() == aCurSnapshotId
9210 )
9211 mData->mCurrentSnapshot = pSnapshot;
9212
9213 // now create the children
9214 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9215 it != data.llChildSnapshots.end();
9216 ++it)
9217 {
9218 const settings::Snapshot &childData = *it;
9219 // recurse
9220 rc = loadSnapshot(childData,
9221 aCurSnapshotId,
9222 pSnapshot); // parent = the one we created above
9223 if (FAILED(rc)) return rc;
9224 }
9225
9226 return rc;
9227}
9228
9229/**
9230 * Loads settings into mHWData.
9231 *
9232 * @param data Reference to the hardware settings.
9233 * @param pDbg Pointer to the debugging settings.
9234 * @param pAutostart Pointer to the autostart settings.
9235 */
9236HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9237 const settings::Autostart *pAutostart)
9238{
9239 AssertReturn(!isSessionMachine(), E_FAIL);
9240
9241 HRESULT rc = S_OK;
9242
9243 try
9244 {
9245 /* The hardware version attribute (optional). */
9246 mHWData->mHWVersion = data.strVersion;
9247 mHWData->mHardwareUUID = data.uuid;
9248
9249 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9250 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9251 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9252 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9253 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9254 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9255 mHWData->mPAEEnabled = data.fPAE;
9256 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9257 mHWData->mLongMode = data.enmLongMode;
9258 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9259 mHWData->mCPUCount = data.cCPUs;
9260 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9261 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9262
9263 // cpu
9264 if (mHWData->mCPUHotPlugEnabled)
9265 {
9266 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9267 it != data.llCpus.end();
9268 ++it)
9269 {
9270 const settings::Cpu &cpu = *it;
9271
9272 mHWData->mCPUAttached[cpu.ulId] = true;
9273 }
9274 }
9275
9276 // cpuid leafs
9277 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9278 it != data.llCpuIdLeafs.end();
9279 ++it)
9280 {
9281 const settings::CpuIdLeaf &leaf = *it;
9282
9283 switch (leaf.ulId)
9284 {
9285 case 0x0:
9286 case 0x1:
9287 case 0x2:
9288 case 0x3:
9289 case 0x4:
9290 case 0x5:
9291 case 0x6:
9292 case 0x7:
9293 case 0x8:
9294 case 0x9:
9295 case 0xA:
9296 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9297 break;
9298
9299 case 0x80000000:
9300 case 0x80000001:
9301 case 0x80000002:
9302 case 0x80000003:
9303 case 0x80000004:
9304 case 0x80000005:
9305 case 0x80000006:
9306 case 0x80000007:
9307 case 0x80000008:
9308 case 0x80000009:
9309 case 0x8000000A:
9310 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9311 break;
9312
9313 default:
9314 /* just ignore */
9315 break;
9316 }
9317 }
9318
9319 mHWData->mMemorySize = data.ulMemorySizeMB;
9320 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9321
9322 // boot order
9323 for (size_t i = 0;
9324 i < RT_ELEMENTS(mHWData->mBootOrder);
9325 i++)
9326 {
9327 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9328 if (it == data.mapBootOrder.end())
9329 mHWData->mBootOrder[i] = DeviceType_Null;
9330 else
9331 mHWData->mBootOrder[i] = it->second;
9332 }
9333
9334 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9335 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9336 mHWData->mMonitorCount = data.cMonitors;
9337 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9338 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9339 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9340 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9341 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9342 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9343 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9344 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9345 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9346 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9347 if (!data.strVideoCaptureFile.isEmpty())
9348 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9349 else
9350 mHWData->mVideoCaptureFile.setNull();
9351 mHWData->mFirmwareType = data.firmwareType;
9352 mHWData->mPointingHIDType = data.pointingHIDType;
9353 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9354 mHWData->mChipsetType = data.chipsetType;
9355 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9356 mHWData->mHPETEnabled = data.fHPETEnabled;
9357
9358 /* VRDEServer */
9359 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9360 if (FAILED(rc)) return rc;
9361
9362 /* BIOS */
9363 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9364 if (FAILED(rc)) return rc;
9365
9366 // Bandwidth control (must come before network adapters)
9367 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9368 if (FAILED(rc)) return rc;
9369
9370 /* Shared folders */
9371 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9372 it != data.usbSettings.llUSBControllers.end();
9373 ++it)
9374 {
9375 const settings::USBController &settingsCtrl = *it;
9376 ComObjPtr<USBController> newCtrl;
9377
9378 newCtrl.createObject();
9379 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9380 mUSBControllers->push_back(newCtrl);
9381 }
9382
9383 /* USB device filters */
9384 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9385 if (FAILED(rc)) return rc;
9386
9387 // network adapters
9388 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9389 uint32_t oldCount = mNetworkAdapters.size();
9390 if (newCount > oldCount)
9391 {
9392 mNetworkAdapters.resize(newCount);
9393 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9394 {
9395 unconst(mNetworkAdapters[slot]).createObject();
9396 mNetworkAdapters[slot]->init(this, slot);
9397 }
9398 }
9399 else if (newCount < oldCount)
9400 mNetworkAdapters.resize(newCount);
9401 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9402 it != data.llNetworkAdapters.end();
9403 ++it)
9404 {
9405 const settings::NetworkAdapter &nic = *it;
9406
9407 /* slot unicity is guaranteed by XML Schema */
9408 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9409 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9410 if (FAILED(rc)) return rc;
9411 }
9412
9413 // serial ports
9414 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9415 it != data.llSerialPorts.end();
9416 ++it)
9417 {
9418 const settings::SerialPort &s = *it;
9419
9420 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9421 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9422 if (FAILED(rc)) return rc;
9423 }
9424
9425 // parallel ports (optional)
9426 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9427 it != data.llParallelPorts.end();
9428 ++it)
9429 {
9430 const settings::ParallelPort &p = *it;
9431
9432 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9433 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9434 if (FAILED(rc)) return rc;
9435 }
9436
9437 /* AudioAdapter */
9438 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9439 if (FAILED(rc)) return rc;
9440
9441 /* Shared folders */
9442 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9443 it != data.llSharedFolders.end();
9444 ++it)
9445 {
9446 const settings::SharedFolder &sf = *it;
9447
9448 ComObjPtr<SharedFolder> sharedFolder;
9449 /* Check for double entries. Not allowed! */
9450 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9451 if (SUCCEEDED(rc))
9452 return setError(VBOX_E_OBJECT_IN_USE,
9453 tr("Shared folder named '%s' already exists"),
9454 sf.strName.c_str());
9455
9456 /* Create the new shared folder. Don't break on error. This will be
9457 * reported when the machine starts. */
9458 sharedFolder.createObject();
9459 rc = sharedFolder->init(getMachine(),
9460 sf.strName,
9461 sf.strHostPath,
9462 RT_BOOL(sf.fWritable),
9463 RT_BOOL(sf.fAutoMount),
9464 false /* fFailOnError */);
9465 if (FAILED(rc)) return rc;
9466 mHWData->mSharedFolders.push_back(sharedFolder);
9467 }
9468
9469 // Clipboard
9470 mHWData->mClipboardMode = data.clipboardMode;
9471
9472 // drag'n'drop
9473 mHWData->mDragAndDropMode = data.dragAndDropMode;
9474
9475 // guest settings
9476 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9477
9478 // IO settings
9479 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9480 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9481
9482 // Host PCI devices
9483 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9484 it != data.pciAttachments.end();
9485 ++it)
9486 {
9487 const settings::HostPCIDeviceAttachment &hpda = *it;
9488 ComObjPtr<PCIDeviceAttachment> pda;
9489
9490 pda.createObject();
9491 pda->loadSettings(this, hpda);
9492 mHWData->mPCIDeviceAssignments.push_back(pda);
9493 }
9494
9495 /*
9496 * (The following isn't really real hardware, but it lives in HWData
9497 * for reasons of convenience.)
9498 */
9499
9500#ifdef VBOX_WITH_GUEST_PROPS
9501 /* Guest properties (optional) */
9502 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9503 it != data.llGuestProperties.end();
9504 ++it)
9505 {
9506 const settings::GuestProperty &prop = *it;
9507 uint32_t fFlags = guestProp::NILFLAG;
9508 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9509 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9510 mHWData->mGuestProperties[prop.strName] = property;
9511 }
9512
9513 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9514#endif /* VBOX_WITH_GUEST_PROPS defined */
9515
9516 rc = loadDebugging(pDbg);
9517 if (FAILED(rc))
9518 return rc;
9519
9520 mHWData->mAutostart = *pAutostart;
9521
9522 /* default frontend */
9523 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9524 }
9525 catch(std::bad_alloc &)
9526 {
9527 return E_OUTOFMEMORY;
9528 }
9529
9530 AssertComRC(rc);
9531 return rc;
9532}
9533
9534/**
9535 * Called from Machine::loadHardware() to load the debugging settings of the
9536 * machine.
9537 *
9538 * @param pDbg Pointer to the settings.
9539 */
9540HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9541{
9542 mHWData->mDebugging = *pDbg;
9543 /* no more processing currently required, this will probably change. */
9544 return S_OK;
9545}
9546
9547/**
9548 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9549 *
9550 * @param data
9551 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9552 * @param puuidSnapshot
9553 * @return
9554 */
9555HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9556 const Guid *puuidRegistry,
9557 const Guid *puuidSnapshot)
9558{
9559 AssertReturn(!isSessionMachine(), E_FAIL);
9560
9561 HRESULT rc = S_OK;
9562
9563 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9564 it != data.llStorageControllers.end();
9565 ++it)
9566 {
9567 const settings::StorageController &ctlData = *it;
9568
9569 ComObjPtr<StorageController> pCtl;
9570 /* Try to find one with the name first. */
9571 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9572 if (SUCCEEDED(rc))
9573 return setError(VBOX_E_OBJECT_IN_USE,
9574 tr("Storage controller named '%s' already exists"),
9575 ctlData.strName.c_str());
9576
9577 pCtl.createObject();
9578 rc = pCtl->init(this,
9579 ctlData.strName,
9580 ctlData.storageBus,
9581 ctlData.ulInstance,
9582 ctlData.fBootable);
9583 if (FAILED(rc)) return rc;
9584
9585 mStorageControllers->push_back(pCtl);
9586
9587 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9588 if (FAILED(rc)) return rc;
9589
9590 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9591 if (FAILED(rc)) return rc;
9592
9593 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9594 if (FAILED(rc)) return rc;
9595
9596 /* Set IDE emulation settings (only for AHCI controller). */
9597 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9598 {
9599 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9600 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9601 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9602 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9603 )
9604 return rc;
9605 }
9606
9607 /* Load the attached devices now. */
9608 rc = loadStorageDevices(pCtl,
9609 ctlData,
9610 puuidRegistry,
9611 puuidSnapshot);
9612 if (FAILED(rc)) return rc;
9613 }
9614
9615 return S_OK;
9616}
9617
9618/**
9619 * Called from loadStorageControllers for a controller's devices.
9620 *
9621 * @param aStorageController
9622 * @param data
9623 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9624 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9625 * @return
9626 */
9627HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9628 const settings::StorageController &data,
9629 const Guid *puuidRegistry,
9630 const Guid *puuidSnapshot)
9631{
9632 HRESULT rc = S_OK;
9633
9634 /* paranoia: detect duplicate attachments */
9635 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9636 it != data.llAttachedDevices.end();
9637 ++it)
9638 {
9639 const settings::AttachedDevice &ad = *it;
9640
9641 for (settings::AttachedDevicesList::const_iterator it2 = it;
9642 it2 != data.llAttachedDevices.end();
9643 ++it2)
9644 {
9645 if (it == it2)
9646 continue;
9647
9648 const settings::AttachedDevice &ad2 = *it2;
9649
9650 if ( ad.lPort == ad2.lPort
9651 && ad.lDevice == ad2.lDevice)
9652 {
9653 return setError(E_FAIL,
9654 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9655 aStorageController->i_getName().c_str(),
9656 ad.lPort,
9657 ad.lDevice,
9658 mUserData->s.strName.c_str());
9659 }
9660 }
9661 }
9662
9663 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9664 it != data.llAttachedDevices.end();
9665 ++it)
9666 {
9667 const settings::AttachedDevice &dev = *it;
9668 ComObjPtr<Medium> medium;
9669
9670 switch (dev.deviceType)
9671 {
9672 case DeviceType_Floppy:
9673 case DeviceType_DVD:
9674 if (dev.strHostDriveSrc.isNotEmpty())
9675 rc = mParent->host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9676 else
9677 rc = mParent->findRemoveableMedium(dev.deviceType,
9678 dev.uuid,
9679 false /* fRefresh */,
9680 false /* aSetError */,
9681 medium);
9682 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9683 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9684 rc = S_OK;
9685 break;
9686
9687 case DeviceType_HardDisk:
9688 {
9689 /* find a hard disk by UUID */
9690 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9691 if (FAILED(rc))
9692 {
9693 if (isSnapshotMachine())
9694 {
9695 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9696 // so the user knows that the bad disk is in a snapshot somewhere
9697 com::ErrorInfo info;
9698 return setError(E_FAIL,
9699 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9700 puuidSnapshot->raw(),
9701 info.getText().raw());
9702 }
9703 else
9704 return rc;
9705 }
9706
9707 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9708
9709 if (medium->i_getType() == MediumType_Immutable)
9710 {
9711 if (isSnapshotMachine())
9712 return setError(E_FAIL,
9713 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9714 "of the virtual machine '%s' ('%s')"),
9715 medium->i_getLocationFull().c_str(),
9716 dev.uuid.raw(),
9717 puuidSnapshot->raw(),
9718 mUserData->s.strName.c_str(),
9719 mData->m_strConfigFileFull.c_str());
9720
9721 return setError(E_FAIL,
9722 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9723 medium->i_getLocationFull().c_str(),
9724 dev.uuid.raw(),
9725 mUserData->s.strName.c_str(),
9726 mData->m_strConfigFileFull.c_str());
9727 }
9728
9729 if (medium->i_getType() == MediumType_MultiAttach)
9730 {
9731 if (isSnapshotMachine())
9732 return setError(E_FAIL,
9733 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9734 "of the virtual machine '%s' ('%s')"),
9735 medium->i_getLocationFull().c_str(),
9736 dev.uuid.raw(),
9737 puuidSnapshot->raw(),
9738 mUserData->s.strName.c_str(),
9739 mData->m_strConfigFileFull.c_str());
9740
9741 return setError(E_FAIL,
9742 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9743 medium->i_getLocationFull().c_str(),
9744 dev.uuid.raw(),
9745 mUserData->s.strName.c_str(),
9746 mData->m_strConfigFileFull.c_str());
9747 }
9748
9749 if ( !isSnapshotMachine()
9750 && medium->i_getChildren().size() != 0
9751 )
9752 return setError(E_FAIL,
9753 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9754 "because it has %d differencing child hard disks"),
9755 medium->i_getLocationFull().c_str(),
9756 dev.uuid.raw(),
9757 mUserData->s.strName.c_str(),
9758 mData->m_strConfigFileFull.c_str(),
9759 medium->i_getChildren().size());
9760
9761 if (findAttachment(mMediaData->mAttachments,
9762 medium))
9763 return setError(E_FAIL,
9764 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9765 medium->i_getLocationFull().c_str(),
9766 dev.uuid.raw(),
9767 mUserData->s.strName.c_str(),
9768 mData->m_strConfigFileFull.c_str());
9769
9770 break;
9771 }
9772
9773 default:
9774 return setError(E_FAIL,
9775 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9776 medium->i_getLocationFull().c_str(),
9777 mUserData->s.strName.c_str(),
9778 mData->m_strConfigFileFull.c_str());
9779 }
9780
9781 if (FAILED(rc))
9782 break;
9783
9784 /* Bandwidth groups are loaded at this point. */
9785 ComObjPtr<BandwidthGroup> pBwGroup;
9786
9787 if (!dev.strBwGroup.isEmpty())
9788 {
9789 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9790 if (FAILED(rc))
9791 return setError(E_FAIL,
9792 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9793 medium->i_getLocationFull().c_str(),
9794 dev.strBwGroup.c_str(),
9795 mUserData->s.strName.c_str(),
9796 mData->m_strConfigFileFull.c_str());
9797 pBwGroup->i_reference();
9798 }
9799
9800 const Bstr controllerName = aStorageController->i_getName();
9801 ComObjPtr<MediumAttachment> pAttachment;
9802 pAttachment.createObject();
9803 rc = pAttachment->init(this,
9804 medium,
9805 controllerName,
9806 dev.lPort,
9807 dev.lDevice,
9808 dev.deviceType,
9809 false,
9810 dev.fPassThrough,
9811 dev.fTempEject,
9812 dev.fNonRotational,
9813 dev.fDiscard,
9814 dev.fHotPluggable,
9815 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9816 if (FAILED(rc)) break;
9817
9818 /* associate the medium with this machine and snapshot */
9819 if (!medium.isNull())
9820 {
9821 AutoCaller medCaller(medium);
9822 if (FAILED(medCaller.rc())) return medCaller.rc();
9823 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9824
9825 if (isSnapshotMachine())
9826 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9827 else
9828 rc = medium->i_addBackReference(mData->mUuid);
9829 /* If the medium->addBackReference fails it sets an appropriate
9830 * error message, so no need to do any guesswork here. */
9831
9832 if (puuidRegistry)
9833 // caller wants registry ID to be set on all attached media (OVF import case)
9834 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9835 }
9836
9837 if (FAILED(rc))
9838 break;
9839
9840 /* back up mMediaData to let registeredInit() properly rollback on failure
9841 * (= limited accessibility) */
9842 setModified(IsModified_Storage);
9843 mMediaData.backup();
9844 mMediaData->mAttachments.push_back(pAttachment);
9845 }
9846
9847 return rc;
9848}
9849
9850/**
9851 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9852 *
9853 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9854 * @param aSnapshot where to return the found snapshot
9855 * @param aSetError true to set extended error info on failure
9856 */
9857HRESULT Machine::findSnapshotById(const Guid &aId,
9858 ComObjPtr<Snapshot> &aSnapshot,
9859 bool aSetError /* = false */)
9860{
9861 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9862
9863 if (!mData->mFirstSnapshot)
9864 {
9865 if (aSetError)
9866 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9867 return E_FAIL;
9868 }
9869
9870 if (aId.isZero())
9871 aSnapshot = mData->mFirstSnapshot;
9872 else
9873 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9874
9875 if (!aSnapshot)
9876 {
9877 if (aSetError)
9878 return setError(E_FAIL,
9879 tr("Could not find a snapshot with UUID {%s}"),
9880 aId.toString().c_str());
9881 return E_FAIL;
9882 }
9883
9884 return S_OK;
9885}
9886
9887/**
9888 * Returns the snapshot with the given name or fails of no such snapshot.
9889 *
9890 * @param aName snapshot name to find
9891 * @param aSnapshot where to return the found snapshot
9892 * @param aSetError true to set extended error info on failure
9893 */
9894HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9895 ComObjPtr<Snapshot> &aSnapshot,
9896 bool aSetError /* = false */)
9897{
9898 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9899
9900 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9901
9902 if (!mData->mFirstSnapshot)
9903 {
9904 if (aSetError)
9905 return setError(VBOX_E_OBJECT_NOT_FOUND,
9906 tr("This machine does not have any snapshots"));
9907 return VBOX_E_OBJECT_NOT_FOUND;
9908 }
9909
9910 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9911
9912 if (!aSnapshot)
9913 {
9914 if (aSetError)
9915 return setError(VBOX_E_OBJECT_NOT_FOUND,
9916 tr("Could not find a snapshot named '%s'"), strName.c_str());
9917 return VBOX_E_OBJECT_NOT_FOUND;
9918 }
9919
9920 return S_OK;
9921}
9922
9923/**
9924 * Returns a storage controller object with the given name.
9925 *
9926 * @param aName storage controller name to find
9927 * @param aStorageController where to return the found storage controller
9928 * @param aSetError true to set extended error info on failure
9929 */
9930HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9931 ComObjPtr<StorageController> &aStorageController,
9932 bool aSetError /* = false */)
9933{
9934 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9935
9936 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9937 it != mStorageControllers->end();
9938 ++it)
9939 {
9940 if ((*it)->i_getName() == aName)
9941 {
9942 aStorageController = (*it);
9943 return S_OK;
9944 }
9945 }
9946
9947 if (aSetError)
9948 return setError(VBOX_E_OBJECT_NOT_FOUND,
9949 tr("Could not find a storage controller named '%s'"),
9950 aName.c_str());
9951 return VBOX_E_OBJECT_NOT_FOUND;
9952}
9953
9954/**
9955 * Returns a USB controller object with the given name.
9956 *
9957 * @param aName USB controller name to find
9958 * @param aUSBController where to return the found USB controller
9959 * @param aSetError true to set extended error info on failure
9960 */
9961HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9962 ComObjPtr<USBController> &aUSBController,
9963 bool aSetError /* = false */)
9964{
9965 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9966
9967 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9968 it != mUSBControllers->end();
9969 ++it)
9970 {
9971 if ((*it)->i_getName() == aName)
9972 {
9973 aUSBController = (*it);
9974 return S_OK;
9975 }
9976 }
9977
9978 if (aSetError)
9979 return setError(VBOX_E_OBJECT_NOT_FOUND,
9980 tr("Could not find a storage controller named '%s'"),
9981 aName.c_str());
9982 return VBOX_E_OBJECT_NOT_FOUND;
9983}
9984
9985/**
9986 * Returns the number of USB controller instance of the given type.
9987 *
9988 * @param enmType USB controller type.
9989 */
9990ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9991{
9992 ULONG cCtrls = 0;
9993
9994 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9995 it != mUSBControllers->end();
9996 ++it)
9997 {
9998 if ((*it)->i_getControllerType() == enmType)
9999 cCtrls++;
10000 }
10001
10002 return cCtrls;
10003}
10004
10005HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
10006 MediaData::AttachmentList &atts)
10007{
10008 AutoCaller autoCaller(this);
10009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10010
10011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10012
10013 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
10014 it != mMediaData->mAttachments.end();
10015 ++it)
10016 {
10017 const ComObjPtr<MediumAttachment> &pAtt = *it;
10018
10019 // should never happen, but deal with NULL pointers in the list.
10020 AssertStmt(!pAtt.isNull(), continue);
10021
10022 // getControllerName() needs caller+read lock
10023 AutoCaller autoAttCaller(pAtt);
10024 if (FAILED(autoAttCaller.rc()))
10025 {
10026 atts.clear();
10027 return autoAttCaller.rc();
10028 }
10029 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10030
10031 if (pAtt->i_getControllerName() == aName)
10032 atts.push_back(pAtt);
10033 }
10034
10035 return S_OK;
10036}
10037
10038/**
10039 * Helper for #saveSettings. Cares about renaming the settings directory and
10040 * file if the machine name was changed and about creating a new settings file
10041 * if this is a new machine.
10042 *
10043 * @note Must be never called directly but only from #saveSettings().
10044 */
10045HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
10046{
10047 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10048
10049 HRESULT rc = S_OK;
10050
10051 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10052
10053 /// @todo need to handle primary group change, too
10054
10055 /* attempt to rename the settings file if machine name is changed */
10056 if ( mUserData->s.fNameSync
10057 && mUserData.isBackedUp()
10058 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10059 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10060 )
10061 {
10062 bool dirRenamed = false;
10063 bool fileRenamed = false;
10064
10065 Utf8Str configFile, newConfigFile;
10066 Utf8Str configFilePrev, newConfigFilePrev;
10067 Utf8Str configDir, newConfigDir;
10068
10069 do
10070 {
10071 int vrc = VINF_SUCCESS;
10072
10073 Utf8Str name = mUserData.backedUpData()->s.strName;
10074 Utf8Str newName = mUserData->s.strName;
10075 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10076 if (group == "/")
10077 group.setNull();
10078 Utf8Str newGroup = mUserData->s.llGroups.front();
10079 if (newGroup == "/")
10080 newGroup.setNull();
10081
10082 configFile = mData->m_strConfigFileFull;
10083
10084 /* first, rename the directory if it matches the group and machine name */
10085 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
10086 group.c_str(), RTPATH_DELIMITER, name.c_str());
10087 /** @todo hack, make somehow use of ComposeMachineFilename */
10088 if (mUserData->s.fDirectoryIncludesUUID)
10089 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10090 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10091 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10092 /** @todo hack, make somehow use of ComposeMachineFilename */
10093 if (mUserData->s.fDirectoryIncludesUUID)
10094 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10095 configDir = configFile;
10096 configDir.stripFilename();
10097 newConfigDir = configDir;
10098 if ( configDir.length() >= groupPlusName.length()
10099 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10100 {
10101 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10102 Utf8Str newConfigBaseDir(newConfigDir);
10103 newConfigDir.append(newGroupPlusName);
10104 /* consistency: use \ if appropriate on the platform */
10105 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10106 /* new dir and old dir cannot be equal here because of 'if'
10107 * above and because name != newName */
10108 Assert(configDir != newConfigDir);
10109 if (!fSettingsFileIsNew)
10110 {
10111 /* perform real rename only if the machine is not new */
10112 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10113 if ( vrc == VERR_FILE_NOT_FOUND
10114 || vrc == VERR_PATH_NOT_FOUND)
10115 {
10116 /* create the parent directory, then retry renaming */
10117 Utf8Str parent(newConfigDir);
10118 parent.stripFilename();
10119 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10120 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10121 }
10122 if (RT_FAILURE(vrc))
10123 {
10124 rc = setError(E_FAIL,
10125 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10126 configDir.c_str(),
10127 newConfigDir.c_str(),
10128 vrc);
10129 break;
10130 }
10131 /* delete subdirectories which are no longer needed */
10132 Utf8Str dir(configDir);
10133 dir.stripFilename();
10134 while (dir != newConfigBaseDir && dir != ".")
10135 {
10136 vrc = RTDirRemove(dir.c_str());
10137 if (RT_FAILURE(vrc))
10138 break;
10139 dir.stripFilename();
10140 }
10141 dirRenamed = true;
10142 }
10143 }
10144
10145 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10146 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10147
10148 /* then try to rename the settings file itself */
10149 if (newConfigFile != configFile)
10150 {
10151 /* get the path to old settings file in renamed directory */
10152 configFile = Utf8StrFmt("%s%c%s",
10153 newConfigDir.c_str(),
10154 RTPATH_DELIMITER,
10155 RTPathFilename(configFile.c_str()));
10156 if (!fSettingsFileIsNew)
10157 {
10158 /* perform real rename only if the machine is not new */
10159 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10160 if (RT_FAILURE(vrc))
10161 {
10162 rc = setError(E_FAIL,
10163 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10164 configFile.c_str(),
10165 newConfigFile.c_str(),
10166 vrc);
10167 break;
10168 }
10169 fileRenamed = true;
10170 configFilePrev = configFile;
10171 configFilePrev += "-prev";
10172 newConfigFilePrev = newConfigFile;
10173 newConfigFilePrev += "-prev";
10174 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10175 }
10176 }
10177
10178 // update m_strConfigFileFull amd mConfigFile
10179 mData->m_strConfigFileFull = newConfigFile;
10180 // compute the relative path too
10181 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10182
10183 // store the old and new so that VirtualBox::saveSettings() can update
10184 // the media registry
10185 if ( mData->mRegistered
10186 && configDir != newConfigDir)
10187 {
10188 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10189
10190 if (pfNeedsGlobalSaveSettings)
10191 *pfNeedsGlobalSaveSettings = true;
10192 }
10193
10194 // in the saved state file path, replace the old directory with the new directory
10195 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10196 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10197
10198 // and do the same thing for the saved state file paths of all the online snapshots
10199 if (mData->mFirstSnapshot)
10200 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10201 newConfigDir.c_str());
10202 }
10203 while (0);
10204
10205 if (FAILED(rc))
10206 {
10207 /* silently try to rename everything back */
10208 if (fileRenamed)
10209 {
10210 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10211 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10212 }
10213 if (dirRenamed)
10214 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10215 }
10216
10217 if (FAILED(rc)) return rc;
10218 }
10219
10220 if (fSettingsFileIsNew)
10221 {
10222 /* create a virgin config file */
10223 int vrc = VINF_SUCCESS;
10224
10225 /* ensure the settings directory exists */
10226 Utf8Str path(mData->m_strConfigFileFull);
10227 path.stripFilename();
10228 if (!RTDirExists(path.c_str()))
10229 {
10230 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10231 if (RT_FAILURE(vrc))
10232 {
10233 return setError(E_FAIL,
10234 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10235 path.c_str(),
10236 vrc);
10237 }
10238 }
10239
10240 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10241 path = Utf8Str(mData->m_strConfigFileFull);
10242 RTFILE f = NIL_RTFILE;
10243 vrc = RTFileOpen(&f, path.c_str(),
10244 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10245 if (RT_FAILURE(vrc))
10246 return setError(E_FAIL,
10247 tr("Could not create the settings file '%s' (%Rrc)"),
10248 path.c_str(),
10249 vrc);
10250 RTFileClose(f);
10251 }
10252
10253 return rc;
10254}
10255
10256/**
10257 * Saves and commits machine data, user data and hardware data.
10258 *
10259 * Note that on failure, the data remains uncommitted.
10260 *
10261 * @a aFlags may combine the following flags:
10262 *
10263 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10264 * Used when saving settings after an operation that makes them 100%
10265 * correspond to the settings from the current snapshot.
10266 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10267 * #isReallyModified() returns false. This is necessary for cases when we
10268 * change machine data directly, not through the backup()/commit() mechanism.
10269 * - SaveS_Force: settings will be saved without doing a deep compare of the
10270 * settings structures. This is used when this is called because snapshots
10271 * have changed to avoid the overhead of the deep compare.
10272 *
10273 * @note Must be called from under this object's write lock. Locks children for
10274 * writing.
10275 *
10276 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10277 * initialized to false and that will be set to true by this function if
10278 * the caller must invoke VirtualBox::saveSettings() because the global
10279 * settings have changed. This will happen if a machine rename has been
10280 * saved and the global machine and media registries will therefore need
10281 * updating.
10282 */
10283HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10284 int aFlags /*= 0*/)
10285{
10286 LogFlowThisFuncEnter();
10287
10288 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10289
10290 /* make sure child objects are unable to modify the settings while we are
10291 * saving them */
10292 ensureNoStateDependencies();
10293
10294 AssertReturn(!isSnapshotMachine(),
10295 E_FAIL);
10296
10297 HRESULT rc = S_OK;
10298 bool fNeedsWrite = false;
10299
10300 /* First, prepare to save settings. It will care about renaming the
10301 * settings directory and file if the machine name was changed and about
10302 * creating a new settings file if this is a new machine. */
10303 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10304 if (FAILED(rc)) return rc;
10305
10306 // keep a pointer to the current settings structures
10307 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10308 settings::MachineConfigFile *pNewConfig = NULL;
10309
10310 try
10311 {
10312 // make a fresh one to have everyone write stuff into
10313 pNewConfig = new settings::MachineConfigFile(NULL);
10314 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10315
10316 // now go and copy all the settings data from COM to the settings structures
10317 // (this calles saveSettings() on all the COM objects in the machine)
10318 copyMachineDataToSettings(*pNewConfig);
10319
10320 if (aFlags & SaveS_ResetCurStateModified)
10321 {
10322 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10323 mData->mCurrentStateModified = FALSE;
10324 fNeedsWrite = true; // always, no need to compare
10325 }
10326 else if (aFlags & SaveS_Force)
10327 {
10328 fNeedsWrite = true; // always, no need to compare
10329 }
10330 else
10331 {
10332 if (!mData->mCurrentStateModified)
10333 {
10334 // do a deep compare of the settings that we just saved with the settings
10335 // previously stored in the config file; this invokes MachineConfigFile::operator==
10336 // which does a deep compare of all the settings, which is expensive but less expensive
10337 // than writing out XML in vain
10338 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10339
10340 // could still be modified if any settings changed
10341 mData->mCurrentStateModified = fAnySettingsChanged;
10342
10343 fNeedsWrite = fAnySettingsChanged;
10344 }
10345 else
10346 fNeedsWrite = true;
10347 }
10348
10349 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10350
10351 if (fNeedsWrite)
10352 // now spit it all out!
10353 pNewConfig->write(mData->m_strConfigFileFull);
10354
10355 mData->pMachineConfigFile = pNewConfig;
10356 delete pOldConfig;
10357 commit();
10358
10359 // after saving settings, we are no longer different from the XML on disk
10360 mData->flModifications = 0;
10361 }
10362 catch (HRESULT err)
10363 {
10364 // we assume that error info is set by the thrower
10365 rc = err;
10366
10367 // restore old config
10368 delete pNewConfig;
10369 mData->pMachineConfigFile = pOldConfig;
10370 }
10371 catch (...)
10372 {
10373 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10374 }
10375
10376 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10377 {
10378 /* Fire the data change event, even on failure (since we've already
10379 * committed all data). This is done only for SessionMachines because
10380 * mutable Machine instances are always not registered (i.e. private
10381 * to the client process that creates them) and thus don't need to
10382 * inform callbacks. */
10383 if (isSessionMachine())
10384 mParent->onMachineDataChange(mData->mUuid);
10385 }
10386
10387 LogFlowThisFunc(("rc=%08X\n", rc));
10388 LogFlowThisFuncLeave();
10389 return rc;
10390}
10391
10392/**
10393 * Implementation for saving the machine settings into the given
10394 * settings::MachineConfigFile instance. This copies machine extradata
10395 * from the previous machine config file in the instance data, if any.
10396 *
10397 * This gets called from two locations:
10398 *
10399 * -- Machine::saveSettings(), during the regular XML writing;
10400 *
10401 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10402 * exported to OVF and we write the VirtualBox proprietary XML
10403 * into a <vbox:Machine> tag.
10404 *
10405 * This routine fills all the fields in there, including snapshots, *except*
10406 * for the following:
10407 *
10408 * -- fCurrentStateModified. There is some special logic associated with that.
10409 *
10410 * The caller can then call MachineConfigFile::write() or do something else
10411 * with it.
10412 *
10413 * Caller must hold the machine lock!
10414 *
10415 * This throws XML errors and HRESULT, so the caller must have a catch block!
10416 */
10417void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10418{
10419 // deep copy extradata
10420 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10421
10422 config.uuid = mData->mUuid;
10423
10424 // copy name, description, OS type, teleport, UTC etc.
10425 config.machineUserData = mUserData->s;
10426
10427 // Encode the Icon Override data from Machine and store on config userdata.
10428 com::SafeArray<BYTE> iconByte;
10429 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10430 ssize_t cbData = iconByte.size();
10431 if (cbData > 0)
10432 {
10433 ssize_t cchOut = RTBase64EncodedLength(cbData);
10434 Utf8Str strIconData;
10435 strIconData.reserve(cchOut+1);
10436 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10437 strIconData.mutableRaw(), strIconData.capacity(),
10438 NULL);
10439 if (RT_FAILURE(vrc))
10440 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10441 strIconData.jolt();
10442 config.machineUserData.ovIcon = strIconData;
10443 }
10444 else
10445 config.machineUserData.ovIcon.setNull();
10446
10447 if ( mData->mMachineState == MachineState_Saved
10448 || mData->mMachineState == MachineState_Restoring
10449 // when deleting a snapshot we may or may not have a saved state in the current state,
10450 // so let's not assert here please
10451 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10452 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10453 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10454 && (!mSSData->strStateFilePath.isEmpty())
10455 )
10456 )
10457 {
10458 Assert(!mSSData->strStateFilePath.isEmpty());
10459 /* try to make the file name relative to the settings file dir */
10460 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10461 }
10462 else
10463 {
10464 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10465 config.strStateFile.setNull();
10466 }
10467
10468 if (mData->mCurrentSnapshot)
10469 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10470 else
10471 config.uuidCurrentSnapshot.clear();
10472
10473 config.timeLastStateChange = mData->mLastStateChange;
10474 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10475 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10476
10477 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10478 if (FAILED(rc)) throw rc;
10479
10480 rc = saveStorageControllers(config.storageMachine);
10481 if (FAILED(rc)) throw rc;
10482
10483 // save machine's media registry if this is VirtualBox 4.0 or later
10484 if (config.canHaveOwnMediaRegistry())
10485 {
10486 // determine machine folder
10487 Utf8Str strMachineFolder = getSettingsFileFull();
10488 strMachineFolder.stripFilename();
10489 mParent->saveMediaRegistry(config.mediaRegistry,
10490 getId(), // only media with registry ID == machine UUID
10491 strMachineFolder);
10492 // this throws HRESULT
10493 }
10494
10495 // save snapshots
10496 rc = saveAllSnapshots(config);
10497 if (FAILED(rc)) throw rc;
10498}
10499
10500/**
10501 * Saves all snapshots of the machine into the given machine config file. Called
10502 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10503 * @param config
10504 * @return
10505 */
10506HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10507{
10508 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10509
10510 HRESULT rc = S_OK;
10511
10512 try
10513 {
10514 config.llFirstSnapshot.clear();
10515
10516 if (mData->mFirstSnapshot)
10517 {
10518 settings::Snapshot snapNew;
10519 config.llFirstSnapshot.push_back(snapNew);
10520
10521 // get reference to the fresh copy of the snapshot on the list and
10522 // work on that copy directly to avoid excessive copying later
10523 settings::Snapshot &snap = config.llFirstSnapshot.front();
10524
10525 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
10526 if (FAILED(rc)) throw rc;
10527 }
10528
10529// if (mType == IsSessionMachine)
10530// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10531
10532 }
10533 catch (HRESULT err)
10534 {
10535 /* we assume that error info is set by the thrower */
10536 rc = err;
10537 }
10538 catch (...)
10539 {
10540 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10541 }
10542
10543 return rc;
10544}
10545
10546/**
10547 * Saves the VM hardware configuration. It is assumed that the
10548 * given node is empty.
10549 *
10550 * @param data Reference to the settings object for the hardware config.
10551 * @param pDbg Pointer to the settings object for the debugging config
10552 * which happens to live in mHWData.
10553 * @param pAutostart Pointer to the settings object for the autostart config
10554 * which happens to live in mHWData.
10555 */
10556HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10557 settings::Autostart *pAutostart)
10558{
10559 HRESULT rc = S_OK;
10560
10561 try
10562 {
10563 /* The hardware version attribute (optional).
10564 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10565 if ( mHWData->mHWVersion == "1"
10566 && mSSData->strStateFilePath.isEmpty()
10567 )
10568 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. */
10569
10570 data.strVersion = mHWData->mHWVersion;
10571 data.uuid = mHWData->mHardwareUUID;
10572
10573 // CPU
10574 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10575 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10576 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10577 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10578 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10579 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10580 data.fPAE = !!mHWData->mPAEEnabled;
10581 data.enmLongMode = mHWData->mLongMode;
10582 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10583 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10584
10585 /* Standard and Extended CPUID leafs. */
10586 data.llCpuIdLeafs.clear();
10587 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10588 {
10589 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10590 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10591 }
10592 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10593 {
10594 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10595 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10596 }
10597
10598 data.cCPUs = mHWData->mCPUCount;
10599 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10600 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10601
10602 data.llCpus.clear();
10603 if (data.fCpuHotPlug)
10604 {
10605 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10606 {
10607 if (mHWData->mCPUAttached[idx])
10608 {
10609 settings::Cpu cpu;
10610 cpu.ulId = idx;
10611 data.llCpus.push_back(cpu);
10612 }
10613 }
10614 }
10615
10616 // memory
10617 data.ulMemorySizeMB = mHWData->mMemorySize;
10618 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10619
10620 // firmware
10621 data.firmwareType = mHWData->mFirmwareType;
10622
10623 // HID
10624 data.pointingHIDType = mHWData->mPointingHIDType;
10625 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10626
10627 // chipset
10628 data.chipsetType = mHWData->mChipsetType;
10629
10630 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10631
10632 // HPET
10633 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10634
10635 // boot order
10636 data.mapBootOrder.clear();
10637 for (size_t i = 0;
10638 i < RT_ELEMENTS(mHWData->mBootOrder);
10639 ++i)
10640 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10641
10642 // display
10643 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10644 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10645 data.cMonitors = mHWData->mMonitorCount;
10646 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10647 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10648 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10649 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10650 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10651 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10652 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10653 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10654 {
10655 if (mHWData->maVideoCaptureScreens[i])
10656 ASMBitSet(&data.u64VideoCaptureScreens, i);
10657 else
10658 ASMBitClear(&data.u64VideoCaptureScreens, i);
10659 }
10660 /* store relative video capture file if possible */
10661 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10662
10663 /* VRDEServer settings (optional) */
10664 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10665 if (FAILED(rc)) throw rc;
10666
10667 /* BIOS (required) */
10668 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10669 if (FAILED(rc)) throw rc;
10670
10671 /* USB Controller (required) */
10672 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10673 it != mUSBControllers->end();
10674 ++it)
10675 {
10676 ComObjPtr<USBController> ctrl = *it;
10677 settings::USBController settingsCtrl;
10678
10679 settingsCtrl.strName = ctrl->i_getName();
10680 settingsCtrl.enmType = ctrl->i_getControllerType();
10681
10682 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10683 }
10684
10685 /* USB device filters (required) */
10686 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10687 if (FAILED(rc)) throw rc;
10688
10689 /* Network adapters (required) */
10690 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10691 data.llNetworkAdapters.clear();
10692 /* Write out only the nominal number of network adapters for this
10693 * chipset type. Since Machine::commit() hasn't been called there
10694 * may be extra NIC settings in the vector. */
10695 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10696 {
10697 settings::NetworkAdapter nic;
10698 nic.ulSlot = slot;
10699 /* paranoia check... must not be NULL, but must not crash either. */
10700 if (mNetworkAdapters[slot])
10701 {
10702 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10703 if (FAILED(rc)) throw rc;
10704
10705 data.llNetworkAdapters.push_back(nic);
10706 }
10707 }
10708
10709 /* Serial ports */
10710 data.llSerialPorts.clear();
10711 for (ULONG slot = 0;
10712 slot < RT_ELEMENTS(mSerialPorts);
10713 ++slot)
10714 {
10715 settings::SerialPort s;
10716 s.ulSlot = slot;
10717 rc = mSerialPorts[slot]->i_saveSettings(s);
10718 if (FAILED(rc)) return rc;
10719
10720 data.llSerialPorts.push_back(s);
10721 }
10722
10723 /* Parallel ports */
10724 data.llParallelPorts.clear();
10725 for (ULONG slot = 0;
10726 slot < RT_ELEMENTS(mParallelPorts);
10727 ++slot)
10728 {
10729 settings::ParallelPort p;
10730 p.ulSlot = slot;
10731 rc = mParallelPorts[slot]->i_saveSettings(p);
10732 if (FAILED(rc)) return rc;
10733
10734 data.llParallelPorts.push_back(p);
10735 }
10736
10737 /* Audio adapter */
10738 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10739 if (FAILED(rc)) return rc;
10740
10741 /* Shared folders */
10742 data.llSharedFolders.clear();
10743 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10744 it != mHWData->mSharedFolders.end();
10745 ++it)
10746 {
10747 SharedFolder *pSF = *it;
10748 AutoCaller sfCaller(pSF);
10749 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10750 settings::SharedFolder sf;
10751 sf.strName = pSF->getName();
10752 sf.strHostPath = pSF->getHostPath();
10753 sf.fWritable = !!pSF->isWritable();
10754 sf.fAutoMount = !!pSF->isAutoMounted();
10755
10756 data.llSharedFolders.push_back(sf);
10757 }
10758
10759 // clipboard
10760 data.clipboardMode = mHWData->mClipboardMode;
10761
10762 // drag'n'drop
10763 data.dragAndDropMode = mHWData->mDragAndDropMode;
10764
10765 /* Guest */
10766 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10767
10768 // IO settings
10769 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10770 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10771
10772 /* BandwidthControl (required) */
10773 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10774 if (FAILED(rc)) throw rc;
10775
10776 /* Host PCI devices */
10777 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10778 it != mHWData->mPCIDeviceAssignments.end();
10779 ++it)
10780 {
10781 ComObjPtr<PCIDeviceAttachment> pda = *it;
10782 settings::HostPCIDeviceAttachment hpda;
10783
10784 rc = pda->saveSettings(hpda);
10785 if (FAILED(rc)) throw rc;
10786
10787 data.pciAttachments.push_back(hpda);
10788 }
10789
10790
10791 // guest properties
10792 data.llGuestProperties.clear();
10793#ifdef VBOX_WITH_GUEST_PROPS
10794 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10795 it != mHWData->mGuestProperties.end();
10796 ++it)
10797 {
10798 HWData::GuestProperty property = it->second;
10799
10800 /* Remove transient guest properties at shutdown unless we
10801 * are saving state */
10802 if ( ( mData->mMachineState == MachineState_PoweredOff
10803 || mData->mMachineState == MachineState_Aborted
10804 || mData->mMachineState == MachineState_Teleported)
10805 && ( property.mFlags & guestProp::TRANSIENT
10806 || property.mFlags & guestProp::TRANSRESET))
10807 continue;
10808 settings::GuestProperty prop;
10809 prop.strName = it->first;
10810 prop.strValue = property.strValue;
10811 prop.timestamp = property.mTimestamp;
10812 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10813 guestProp::writeFlags(property.mFlags, szFlags);
10814 prop.strFlags = szFlags;
10815
10816 data.llGuestProperties.push_back(prop);
10817 }
10818
10819 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10820 /* I presume this doesn't require a backup(). */
10821 mData->mGuestPropertiesModified = FALSE;
10822#endif /* VBOX_WITH_GUEST_PROPS defined */
10823
10824 *pDbg = mHWData->mDebugging;
10825 *pAutostart = mHWData->mAutostart;
10826
10827 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10828 }
10829 catch(std::bad_alloc &)
10830 {
10831 return E_OUTOFMEMORY;
10832 }
10833
10834 AssertComRC(rc);
10835 return rc;
10836}
10837
10838/**
10839 * Saves the storage controller configuration.
10840 *
10841 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10842 */
10843HRESULT Machine::saveStorageControllers(settings::Storage &data)
10844{
10845 data.llStorageControllers.clear();
10846
10847 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10848 it != mStorageControllers->end();
10849 ++it)
10850 {
10851 HRESULT rc;
10852 ComObjPtr<StorageController> pCtl = *it;
10853
10854 settings::StorageController ctl;
10855 ctl.strName = pCtl->i_getName();
10856 ctl.controllerType = pCtl->i_getControllerType();
10857 ctl.storageBus = pCtl->i_getStorageBus();
10858 ctl.ulInstance = pCtl->i_getInstance();
10859 ctl.fBootable = pCtl->i_getBootable();
10860
10861 /* Save the port count. */
10862 ULONG portCount;
10863 rc = pCtl->COMGETTER(PortCount)(&portCount);
10864 ComAssertComRCRet(rc, rc);
10865 ctl.ulPortCount = portCount;
10866
10867 /* Save fUseHostIOCache */
10868 BOOL fUseHostIOCache;
10869 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10870 ComAssertComRCRet(rc, rc);
10871 ctl.fUseHostIOCache = !!fUseHostIOCache;
10872
10873 /* Save IDE emulation settings. */
10874 if (ctl.controllerType == StorageControllerType_IntelAhci)
10875 {
10876 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10877 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10878 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10879 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10880 )
10881 ComAssertComRCRet(rc, rc);
10882 }
10883
10884 /* save the devices now. */
10885 rc = saveStorageDevices(pCtl, ctl);
10886 ComAssertComRCRet(rc, rc);
10887
10888 data.llStorageControllers.push_back(ctl);
10889 }
10890
10891 return S_OK;
10892}
10893
10894/**
10895 * Saves the hard disk configuration.
10896 */
10897HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10898 settings::StorageController &data)
10899{
10900 MediaData::AttachmentList atts;
10901
10902 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->i_getName()).raw(), atts);
10903 if (FAILED(rc)) return rc;
10904
10905 data.llAttachedDevices.clear();
10906 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10907 it != atts.end();
10908 ++it)
10909 {
10910 settings::AttachedDevice dev;
10911
10912 MediumAttachment *pAttach = *it;
10913 Medium *pMedium = pAttach->i_getMedium();
10914
10915 dev.deviceType = pAttach->i_getType();
10916 dev.lPort = pAttach->i_getPort();
10917 dev.lDevice = pAttach->i_getDevice();
10918 dev.fPassThrough = pAttach->i_getPassthrough();
10919 dev.fHotPluggable = pAttach->i_getHotPluggable();
10920 if (pMedium)
10921 {
10922 if (pMedium->i_isHostDrive())
10923 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10924 else
10925 dev.uuid = pMedium->i_getId();
10926 dev.fTempEject = pAttach->i_getTempEject();
10927 dev.fNonRotational = pAttach->i_getNonRotational();
10928 dev.fDiscard = pAttach->i_getDiscard();
10929 }
10930
10931 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10932
10933 data.llAttachedDevices.push_back(dev);
10934 }
10935
10936 return S_OK;
10937}
10938
10939/**
10940 * Saves machine state settings as defined by aFlags
10941 * (SaveSTS_* values).
10942 *
10943 * @param aFlags Combination of SaveSTS_* flags.
10944 *
10945 * @note Locks objects for writing.
10946 */
10947HRESULT Machine::saveStateSettings(int aFlags)
10948{
10949 if (aFlags == 0)
10950 return S_OK;
10951
10952 AutoCaller autoCaller(this);
10953 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10954
10955 /* This object's write lock is also necessary to serialize file access
10956 * (prevent concurrent reads and writes) */
10957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10958
10959 HRESULT rc = S_OK;
10960
10961 Assert(mData->pMachineConfigFile);
10962
10963 try
10964 {
10965 if (aFlags & SaveSTS_CurStateModified)
10966 mData->pMachineConfigFile->fCurrentStateModified = true;
10967
10968 if (aFlags & SaveSTS_StateFilePath)
10969 {
10970 if (!mSSData->strStateFilePath.isEmpty())
10971 /* try to make the file name relative to the settings file dir */
10972 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10973 else
10974 mData->pMachineConfigFile->strStateFile.setNull();
10975 }
10976
10977 if (aFlags & SaveSTS_StateTimeStamp)
10978 {
10979 Assert( mData->mMachineState != MachineState_Aborted
10980 || mSSData->strStateFilePath.isEmpty());
10981
10982 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10983
10984 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10985//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10986 }
10987
10988 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10989 }
10990 catch (...)
10991 {
10992 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10993 }
10994
10995 return rc;
10996}
10997
10998/**
10999 * Ensures that the given medium is added to a media registry. If this machine
11000 * was created with 4.0 or later, then the machine registry is used. Otherwise
11001 * the global VirtualBox media registry is used.
11002 *
11003 * Caller must NOT hold machine lock, media tree or any medium locks!
11004 *
11005 * @param pMedium
11006 */
11007void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11008{
11009 /* Paranoia checks: do not hold machine or media tree locks. */
11010 AssertReturnVoid(!isWriteLockOnCurrentThread());
11011 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11012
11013 ComObjPtr<Medium> pBase;
11014 {
11015 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11016 pBase = pMedium->i_getBase();
11017 }
11018
11019 /* Paranoia checks: do not hold medium locks. */
11020 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11021 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11022
11023 // decide which medium registry to use now that the medium is attached:
11024 Guid uuid;
11025 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
11026 // machine XML is VirtualBox 4.0 or higher:
11027 uuid = getId(); // machine UUID
11028 else
11029 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
11030
11031 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
11032 mParent->markRegistryModified(uuid);
11033
11034 /* For more complex hard disk structures it can happen that the base
11035 * medium isn't yet associated with any medium registry. Do that now. */
11036 if (pMedium != pBase)
11037 {
11038 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
11039 mParent->markRegistryModified(uuid);
11040 }
11041}
11042
11043/**
11044 * Creates differencing hard disks for all normal hard disks attached to this
11045 * machine and a new set of attachments to refer to created disks.
11046 *
11047 * Used when taking a snapshot or when deleting the current state. Gets called
11048 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11049 *
11050 * This method assumes that mMediaData contains the original hard disk attachments
11051 * it needs to create diffs for. On success, these attachments will be replaced
11052 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
11053 * called to delete created diffs which will also rollback mMediaData and restore
11054 * whatever was backed up before calling this method.
11055 *
11056 * Attachments with non-normal hard disks are left as is.
11057 *
11058 * If @a aOnline is @c false then the original hard disks that require implicit
11059 * diffs will be locked for reading. Otherwise it is assumed that they are
11060 * already locked for writing (when the VM was started). Note that in the latter
11061 * case it is responsibility of the caller to lock the newly created diffs for
11062 * writing if this method succeeds.
11063 *
11064 * @param aProgress Progress object to run (must contain at least as
11065 * many operations left as the number of hard disks
11066 * attached).
11067 * @param aOnline Whether the VM was online prior to this operation.
11068 *
11069 * @note The progress object is not marked as completed, neither on success nor
11070 * on failure. This is a responsibility of the caller.
11071 *
11072 * @note Locks this object and the media tree for writing.
11073 */
11074HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
11075 ULONG aWeight,
11076 bool aOnline)
11077{
11078 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11079
11080 AutoCaller autoCaller(this);
11081 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11082
11083 AutoMultiWriteLock2 alock(this->lockHandle(),
11084 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11085
11086 /* must be in a protective state because we release the lock below */
11087 AssertReturn( mData->mMachineState == MachineState_Saving
11088 || mData->mMachineState == MachineState_LiveSnapshotting
11089 || mData->mMachineState == MachineState_RestoringSnapshot
11090 || mData->mMachineState == MachineState_DeletingSnapshot
11091 , E_FAIL);
11092
11093 HRESULT rc = S_OK;
11094
11095 // use appropriate locked media map (online or offline)
11096 MediumLockListMap lockedMediaOffline;
11097 MediumLockListMap *lockedMediaMap;
11098 if (aOnline)
11099 lockedMediaMap = &mData->mSession.mLockedMedia;
11100 else
11101 lockedMediaMap = &lockedMediaOffline;
11102
11103 try
11104 {
11105 if (!aOnline)
11106 {
11107 /* lock all attached hard disks early to detect "in use"
11108 * situations before creating actual diffs */
11109 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11110 it != mMediaData->mAttachments.end();
11111 ++it)
11112 {
11113 MediumAttachment* pAtt = *it;
11114 if (pAtt->i_getType() == DeviceType_HardDisk)
11115 {
11116 Medium* pMedium = pAtt->i_getMedium();
11117 Assert(pMedium);
11118
11119 MediumLockList *pMediumLockList(new MediumLockList());
11120 alock.release();
11121 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11122 false /* fMediumLockWrite */,
11123 NULL,
11124 *pMediumLockList);
11125 alock.acquire();
11126 if (FAILED(rc))
11127 {
11128 delete pMediumLockList;
11129 throw rc;
11130 }
11131 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11132 if (FAILED(rc))
11133 {
11134 throw setError(rc,
11135 tr("Collecting locking information for all attached media failed"));
11136 }
11137 }
11138 }
11139
11140 /* Now lock all media. If this fails, nothing is locked. */
11141 alock.release();
11142 rc = lockedMediaMap->Lock();
11143 alock.acquire();
11144 if (FAILED(rc))
11145 {
11146 throw setError(rc,
11147 tr("Locking of attached media failed"));
11148 }
11149 }
11150
11151 /* remember the current list (note that we don't use backup() since
11152 * mMediaData may be already backed up) */
11153 MediaData::AttachmentList atts = mMediaData->mAttachments;
11154
11155 /* start from scratch */
11156 mMediaData->mAttachments.clear();
11157
11158 /* go through remembered attachments and create diffs for normal hard
11159 * disks and attach them */
11160 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11161 it != atts.end();
11162 ++it)
11163 {
11164 MediumAttachment* pAtt = *it;
11165
11166 DeviceType_T devType = pAtt->i_getType();
11167 Medium* pMedium = pAtt->i_getMedium();
11168
11169 if ( devType != DeviceType_HardDisk
11170 || pMedium == NULL
11171 || pMedium->i_getType() != MediumType_Normal)
11172 {
11173 /* copy the attachment as is */
11174
11175 /** @todo the progress object created in Console::TakeSnaphot
11176 * only expects operations for hard disks. Later other
11177 * device types need to show up in the progress as well. */
11178 if (devType == DeviceType_HardDisk)
11179 {
11180 if (pMedium == NULL)
11181 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11182 aWeight); // weight
11183 else
11184 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11185 pMedium->i_getBase()->i_getName().c_str()).raw(),
11186 aWeight); // weight
11187 }
11188
11189 mMediaData->mAttachments.push_back(pAtt);
11190 continue;
11191 }
11192
11193 /* need a diff */
11194 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11195 pMedium->i_getBase()->i_getName().c_str()).raw(),
11196 aWeight); // weight
11197
11198 Utf8Str strFullSnapshotFolder;
11199 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11200
11201 ComObjPtr<Medium> diff;
11202 diff.createObject();
11203 // store the diff in the same registry as the parent
11204 // (this cannot fail here because we can't create implicit diffs for
11205 // unregistered images)
11206 Guid uuidRegistryParent;
11207 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11208 Assert(fInRegistry); NOREF(fInRegistry);
11209 rc = diff->init(mParent,
11210 pMedium->i_getPreferredDiffFormat(),
11211 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11212 uuidRegistryParent);
11213 if (FAILED(rc)) throw rc;
11214
11215 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11216 * the push_back? Looks like we're going to release medium with the
11217 * wrong kind of lock (general issue with if we fail anywhere at all)
11218 * and an orphaned VDI in the snapshots folder. */
11219
11220 /* update the appropriate lock list */
11221 MediumLockList *pMediumLockList;
11222 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11223 AssertComRCThrowRC(rc);
11224 if (aOnline)
11225 {
11226 alock.release();
11227 /* The currently attached medium will be read-only, change
11228 * the lock type to read. */
11229 rc = pMediumLockList->Update(pMedium, false);
11230 alock.acquire();
11231 AssertComRCThrowRC(rc);
11232 }
11233
11234 /* release the locks before the potentially lengthy operation */
11235 alock.release();
11236 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
11237 pMediumLockList,
11238 NULL /* aProgress */,
11239 true /* aWait */);
11240 alock.acquire();
11241 if (FAILED(rc)) throw rc;
11242
11243 /* actual lock list update is done in Medium::commitMedia */
11244
11245 rc = diff->i_addBackReference(mData->mUuid);
11246 AssertComRCThrowRC(rc);
11247
11248 /* add a new attachment */
11249 ComObjPtr<MediumAttachment> attachment;
11250 attachment.createObject();
11251 rc = attachment->init(this,
11252 diff,
11253 pAtt->i_getControllerName(),
11254 pAtt->i_getPort(),
11255 pAtt->i_getDevice(),
11256 DeviceType_HardDisk,
11257 true /* aImplicit */,
11258 false /* aPassthrough */,
11259 false /* aTempEject */,
11260 pAtt->i_getNonRotational(),
11261 pAtt->i_getDiscard(),
11262 pAtt->i_getHotPluggable(),
11263 pAtt->i_getBandwidthGroup());
11264 if (FAILED(rc)) throw rc;
11265
11266 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11267 AssertComRCThrowRC(rc);
11268 mMediaData->mAttachments.push_back(attachment);
11269 }
11270 }
11271 catch (HRESULT aRC) { rc = aRC; }
11272
11273 /* unlock all hard disks we locked when there is no VM */
11274 if (!aOnline)
11275 {
11276 ErrorInfoKeeper eik;
11277
11278 HRESULT rc1 = lockedMediaMap->Clear();
11279 AssertComRC(rc1);
11280 }
11281
11282 return rc;
11283}
11284
11285/**
11286 * Deletes implicit differencing hard disks created either by
11287 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11288 *
11289 * Note that to delete hard disks created by #AttachDevice() this method is
11290 * called from #fixupMedia() when the changes are rolled back.
11291 *
11292 * @note Locks this object and the media tree for writing.
11293 */
11294HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11295{
11296 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11297
11298 AutoCaller autoCaller(this);
11299 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11300
11301 AutoMultiWriteLock2 alock(this->lockHandle(),
11302 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11303
11304 /* We absolutely must have backed up state. */
11305 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11306
11307 /* Check if there are any implicitly created diff images. */
11308 bool fImplicitDiffs = false;
11309 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11310 it != mMediaData->mAttachments.end();
11311 ++it)
11312 {
11313 const ComObjPtr<MediumAttachment> &pAtt = *it;
11314 if (pAtt->i_isImplicit())
11315 {
11316 fImplicitDiffs = true;
11317 break;
11318 }
11319 }
11320 /* If there is nothing to do, leave early. This saves lots of image locking
11321 * effort. It also avoids a MachineStateChanged event without real reason.
11322 * This is important e.g. when loading a VM config, because there should be
11323 * no events. Otherwise API clients can become thoroughly confused for
11324 * inaccessible VMs (the code for loading VM configs uses this method for
11325 * cleanup if the config makes no sense), as they take such events as an
11326 * indication that the VM is alive, and they would force the VM config to
11327 * be reread, leading to an endless loop. */
11328 if (!fImplicitDiffs)
11329 return S_OK;
11330
11331 HRESULT rc = S_OK;
11332 MachineState_T oldState = mData->mMachineState;
11333
11334 /* will release the lock before the potentially lengthy operation,
11335 * so protect with the special state (unless already protected) */
11336 if ( oldState != MachineState_Saving
11337 && oldState != MachineState_LiveSnapshotting
11338 && oldState != MachineState_RestoringSnapshot
11339 && oldState != MachineState_DeletingSnapshot
11340 && oldState != MachineState_DeletingSnapshotOnline
11341 && oldState != MachineState_DeletingSnapshotPaused
11342 )
11343 setMachineState(MachineState_SettingUp);
11344
11345 // use appropriate locked media map (online or offline)
11346 MediumLockListMap lockedMediaOffline;
11347 MediumLockListMap *lockedMediaMap;
11348 if (aOnline)
11349 lockedMediaMap = &mData->mSession.mLockedMedia;
11350 else
11351 lockedMediaMap = &lockedMediaOffline;
11352
11353 try
11354 {
11355 if (!aOnline)
11356 {
11357 /* lock all attached hard disks early to detect "in use"
11358 * situations before deleting actual diffs */
11359 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11360 it != mMediaData->mAttachments.end();
11361 ++it)
11362 {
11363 MediumAttachment* pAtt = *it;
11364 if (pAtt->i_getType() == DeviceType_HardDisk)
11365 {
11366 Medium* pMedium = pAtt->i_getMedium();
11367 Assert(pMedium);
11368
11369 MediumLockList *pMediumLockList(new MediumLockList());
11370 alock.release();
11371 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11372 false /* fMediumLockWrite */,
11373 NULL,
11374 *pMediumLockList);
11375 alock.acquire();
11376
11377 if (FAILED(rc))
11378 {
11379 delete pMediumLockList;
11380 throw rc;
11381 }
11382
11383 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11384 if (FAILED(rc))
11385 throw rc;
11386 }
11387 }
11388
11389 if (FAILED(rc))
11390 throw rc;
11391 } // end of offline
11392
11393 /* Lock lists are now up to date and include implicitly created media */
11394
11395 /* Go through remembered attachments and delete all implicitly created
11396 * diffs and fix up the attachment information */
11397 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11398 MediaData::AttachmentList implicitAtts;
11399 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11400 it != mMediaData->mAttachments.end();
11401 ++it)
11402 {
11403 ComObjPtr<MediumAttachment> pAtt = *it;
11404 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11405 if (pMedium.isNull())
11406 continue;
11407
11408 // Implicit attachments go on the list for deletion and back references are removed.
11409 if (pAtt->i_isImplicit())
11410 {
11411 /* Deassociate and mark for deletion */
11412 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11413 rc = pMedium->i_removeBackReference(mData->mUuid);
11414 if (FAILED(rc))
11415 throw rc;
11416 implicitAtts.push_back(pAtt);
11417 continue;
11418 }
11419
11420 /* Was this medium attached before? */
11421 if (!findAttachment(oldAtts, pMedium))
11422 {
11423 /* no: de-associate */
11424 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11425 rc = pMedium->i_removeBackReference(mData->mUuid);
11426 if (FAILED(rc))
11427 throw rc;
11428 continue;
11429 }
11430 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11431 }
11432
11433 /* If there are implicit attachments to delete, throw away the lock
11434 * map contents (which will unlock all media) since the medium
11435 * attachments will be rolled back. Below we need to completely
11436 * recreate the lock map anyway since it is infinitely complex to
11437 * do this incrementally (would need reconstructing each attachment
11438 * change, which would be extremely hairy). */
11439 if (implicitAtts.size() != 0)
11440 {
11441 ErrorInfoKeeper eik;
11442
11443 HRESULT rc1 = lockedMediaMap->Clear();
11444 AssertComRC(rc1);
11445 }
11446
11447 /* rollback hard disk changes */
11448 mMediaData.rollback();
11449
11450 MultiResult mrc(S_OK);
11451
11452 // Delete unused implicit diffs.
11453 if (implicitAtts.size() != 0)
11454 {
11455 alock.release();
11456
11457 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11458 it != implicitAtts.end();
11459 ++it)
11460 {
11461 // Remove medium associated with this attachment.
11462 ComObjPtr<MediumAttachment> pAtt = *it;
11463 Assert(pAtt);
11464 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11465 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11466 Assert(pMedium);
11467
11468 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11469 // continue on delete failure, just collect error messages
11470 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(), pMedium->i_getLocationFull().c_str() ));
11471 mrc = rc;
11472 }
11473
11474 alock.acquire();
11475
11476 /* if there is a VM recreate media lock map as mentioned above,
11477 * otherwise it is a waste of time and we leave things unlocked */
11478 if (aOnline)
11479 {
11480 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11481 /* must never be NULL, but better safe than sorry */
11482 if (!pMachine.isNull())
11483 {
11484 alock.release();
11485 rc = mData->mSession.mMachine->lockMedia();
11486 alock.acquire();
11487 if (FAILED(rc))
11488 throw rc;
11489 }
11490 }
11491 }
11492 }
11493 catch (HRESULT aRC) {rc = aRC;}
11494
11495 if (mData->mMachineState == MachineState_SettingUp)
11496 setMachineState(oldState);
11497
11498 /* unlock all hard disks we locked when there is no VM */
11499 if (!aOnline)
11500 {
11501 ErrorInfoKeeper eik;
11502
11503 HRESULT rc1 = lockedMediaMap->Clear();
11504 AssertComRC(rc1);
11505 }
11506
11507 return rc;
11508}
11509
11510
11511/**
11512 * Looks through the given list of media attachments for one with the given parameters
11513 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11514 * can be searched as well if needed.
11515 *
11516 * @param list
11517 * @param aControllerName
11518 * @param aControllerPort
11519 * @param aDevice
11520 * @return
11521 */
11522MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11523 IN_BSTR aControllerName,
11524 LONG aControllerPort,
11525 LONG aDevice)
11526{
11527 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11528 it != ll.end();
11529 ++it)
11530 {
11531 MediumAttachment *pAttach = *it;
11532 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11533 return pAttach;
11534 }
11535
11536 return NULL;
11537}
11538
11539/**
11540 * Looks through the given list of media attachments for one with the given parameters
11541 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11542 * can be searched as well if needed.
11543 *
11544 * @param list
11545 * @param aControllerName
11546 * @param aControllerPort
11547 * @param aDevice
11548 * @return
11549 */
11550MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11551 ComObjPtr<Medium> pMedium)
11552{
11553 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11554 it != ll.end();
11555 ++it)
11556 {
11557 MediumAttachment *pAttach = *it;
11558 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11559 if (pMediumThis == pMedium)
11560 return pAttach;
11561 }
11562
11563 return NULL;
11564}
11565
11566/**
11567 * Looks through the given list of media attachments for one with the given parameters
11568 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11569 * can be searched as well if needed.
11570 *
11571 * @param list
11572 * @param aControllerName
11573 * @param aControllerPort
11574 * @param aDevice
11575 * @return
11576 */
11577MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11578 Guid &id)
11579{
11580 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11581 it != ll.end();
11582 ++it)
11583 {
11584 MediumAttachment *pAttach = *it;
11585 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11586 if (pMediumThis->i_getId() == id)
11587 return pAttach;
11588 }
11589
11590 return NULL;
11591}
11592
11593/**
11594 * Main implementation for Machine::DetachDevice. This also gets called
11595 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11596 *
11597 * @param pAttach Medium attachment to detach.
11598 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11599 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11600 * @return
11601 */
11602HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11603 AutoWriteLock &writeLock,
11604 Snapshot *pSnapshot)
11605{
11606 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11607 DeviceType_T mediumType = pAttach->i_getType();
11608
11609 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11610
11611 if (pAttach->i_isImplicit())
11612 {
11613 /* attempt to implicitly delete the implicitly created diff */
11614
11615 /// @todo move the implicit flag from MediumAttachment to Medium
11616 /// and forbid any hard disk operation when it is implicit. Or maybe
11617 /// a special media state for it to make it even more simple.
11618
11619 Assert(mMediaData.isBackedUp());
11620
11621 /* will release the lock before the potentially lengthy operation, so
11622 * protect with the special state */
11623 MachineState_T oldState = mData->mMachineState;
11624 setMachineState(MachineState_SettingUp);
11625
11626 writeLock.release();
11627
11628 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11629 true /*aWait*/);
11630
11631 writeLock.acquire();
11632
11633 setMachineState(oldState);
11634
11635 if (FAILED(rc)) return rc;
11636 }
11637
11638 setModified(IsModified_Storage);
11639 mMediaData.backup();
11640 mMediaData->mAttachments.remove(pAttach);
11641
11642 if (!oldmedium.isNull())
11643 {
11644 // if this is from a snapshot, do not defer detachment to commitMedia()
11645 if (pSnapshot)
11646 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11647 // else if non-hard disk media, do not defer detachment to commitMedia() either
11648 else if (mediumType != DeviceType_HardDisk)
11649 oldmedium->i_removeBackReference(mData->mUuid);
11650 }
11651
11652 return S_OK;
11653}
11654
11655/**
11656 * Goes thru all media of the given list and
11657 *
11658 * 1) calls detachDevice() on each of them for this machine and
11659 * 2) adds all Medium objects found in the process to the given list,
11660 * depending on cleanupMode.
11661 *
11662 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11663 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11664 * media to the list.
11665 *
11666 * This gets called from Machine::Unregister, both for the actual Machine and
11667 * the SnapshotMachine objects that might be found in the snapshots.
11668 *
11669 * Requires caller and locking. The machine lock must be passed in because it
11670 * will be passed on to detachDevice which needs it for temporary unlocking.
11671 *
11672 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11673 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11674 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11675 * otherwise no media get added.
11676 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11677 * @return
11678 */
11679HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11680 Snapshot *pSnapshot,
11681 CleanupMode_T cleanupMode,
11682 MediaList &llMedia)
11683{
11684 Assert(isWriteLockOnCurrentThread());
11685
11686 HRESULT rc;
11687
11688 // make a temporary list because detachDevice invalidates iterators into
11689 // mMediaData->mAttachments
11690 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11691
11692 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11693 it != llAttachments2.end();
11694 ++it)
11695 {
11696 ComObjPtr<MediumAttachment> &pAttach = *it;
11697 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11698
11699 if (!pMedium.isNull())
11700 {
11701 AutoCaller mac(pMedium);
11702 if (FAILED(mac.rc())) return mac.rc();
11703 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11704 DeviceType_T devType = pMedium->i_getDeviceType();
11705 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11706 && devType == DeviceType_HardDisk)
11707 || (cleanupMode == CleanupMode_Full)
11708 )
11709 {
11710 llMedia.push_back(pMedium);
11711 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11712 /*
11713 * Search for medias which are not attached to any machine, but
11714 * in the chain to an attached disk. Mediums are only consided
11715 * if they are:
11716 * - have only one child
11717 * - no references to any machines
11718 * - are of normal medium type
11719 */
11720 while (!pParent.isNull())
11721 {
11722 AutoCaller mac1(pParent);
11723 if (FAILED(mac1.rc())) return mac1.rc();
11724 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11725 if (pParent->i_getChildren().size() == 1)
11726 {
11727 if ( pParent->i_getMachineBackRefCount() == 0
11728 && pParent->i_getType() == MediumType_Normal
11729 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11730 llMedia.push_back(pParent);
11731 }
11732 else
11733 break;
11734 pParent = pParent->i_getParent();
11735 }
11736 }
11737 }
11738
11739 // real machine: then we need to use the proper method
11740 rc = detachDevice(pAttach, writeLock, pSnapshot);
11741
11742 if (FAILED(rc))
11743 return rc;
11744 }
11745
11746 return S_OK;
11747}
11748
11749/**
11750 * Perform deferred hard disk detachments.
11751 *
11752 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11753 * backed up).
11754 *
11755 * If @a aOnline is @c true then this method will also unlock the old hard disks
11756 * for which the new implicit diffs were created and will lock these new diffs for
11757 * writing.
11758 *
11759 * @param aOnline Whether the VM was online prior to this operation.
11760 *
11761 * @note Locks this object for writing!
11762 */
11763void Machine::commitMedia(bool aOnline /*= false*/)
11764{
11765 AutoCaller autoCaller(this);
11766 AssertComRCReturnVoid(autoCaller.rc());
11767
11768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11769
11770 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11771
11772 HRESULT rc = S_OK;
11773
11774 /* no attach/detach operations -- nothing to do */
11775 if (!mMediaData.isBackedUp())
11776 return;
11777
11778 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11779 bool fMediaNeedsLocking = false;
11780
11781 /* enumerate new attachments */
11782 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11783 it != mMediaData->mAttachments.end();
11784 ++it)
11785 {
11786 MediumAttachment *pAttach = *it;
11787
11788 pAttach->i_commit();
11789
11790 Medium* pMedium = pAttach->i_getMedium();
11791 bool fImplicit = pAttach->i_isImplicit();
11792
11793 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11794 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11795 fImplicit));
11796
11797 /** @todo convert all this Machine-based voodoo to MediumAttachment
11798 * based commit logic. */
11799 if (fImplicit)
11800 {
11801 /* convert implicit attachment to normal */
11802 pAttach->i_setImplicit(false);
11803
11804 if ( aOnline
11805 && pMedium
11806 && pAttach->i_getType() == DeviceType_HardDisk
11807 )
11808 {
11809 ComObjPtr<Medium> parent = pMedium->i_getParent();
11810 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11811
11812 /* update the appropriate lock list */
11813 MediumLockList *pMediumLockList;
11814 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11815 AssertComRC(rc);
11816 if (pMediumLockList)
11817 {
11818 /* unlock if there's a need to change the locking */
11819 if (!fMediaNeedsLocking)
11820 {
11821 rc = mData->mSession.mLockedMedia.Unlock();
11822 AssertComRC(rc);
11823 fMediaNeedsLocking = true;
11824 }
11825 rc = pMediumLockList->Update(parent, false);
11826 AssertComRC(rc);
11827 rc = pMediumLockList->Append(pMedium, true);
11828 AssertComRC(rc);
11829 }
11830 }
11831
11832 continue;
11833 }
11834
11835 if (pMedium)
11836 {
11837 /* was this medium attached before? */
11838 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11839 oldIt != oldAtts.end();
11840 ++oldIt)
11841 {
11842 MediumAttachment *pOldAttach = *oldIt;
11843 if (pOldAttach->i_getMedium() == pMedium)
11844 {
11845 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11846
11847 /* yes: remove from old to avoid de-association */
11848 oldAtts.erase(oldIt);
11849 break;
11850 }
11851 }
11852 }
11853 }
11854
11855 /* enumerate remaining old attachments and de-associate from the
11856 * current machine state */
11857 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11858 it != oldAtts.end();
11859 ++it)
11860 {
11861 MediumAttachment *pAttach = *it;
11862 Medium* pMedium = pAttach->i_getMedium();
11863
11864 /* Detach only hard disks, since DVD/floppy media is detached
11865 * instantly in MountMedium. */
11866 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11867 {
11868 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11869
11870 /* now de-associate from the current machine state */
11871 rc = pMedium->i_removeBackReference(mData->mUuid);
11872 AssertComRC(rc);
11873
11874 if (aOnline)
11875 {
11876 /* unlock since medium is not used anymore */
11877 MediumLockList *pMediumLockList;
11878 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11879 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11880 {
11881 /* this happens for online snapshots, there the attachment
11882 * is changing, but only to a diff image created under
11883 * the old one, so there is no separate lock list */
11884 Assert(!pMediumLockList);
11885 }
11886 else
11887 {
11888 AssertComRC(rc);
11889 if (pMediumLockList)
11890 {
11891 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11892 AssertComRC(rc);
11893 }
11894 }
11895 }
11896 }
11897 }
11898
11899 /* take media locks again so that the locking state is consistent */
11900 if (fMediaNeedsLocking)
11901 {
11902 Assert(aOnline);
11903 rc = mData->mSession.mLockedMedia.Lock();
11904 AssertComRC(rc);
11905 }
11906
11907 /* commit the hard disk changes */
11908 mMediaData.commit();
11909
11910 if (isSessionMachine())
11911 {
11912 /*
11913 * Update the parent machine to point to the new owner.
11914 * This is necessary because the stored parent will point to the
11915 * session machine otherwise and cause crashes or errors later
11916 * when the session machine gets invalid.
11917 */
11918 /** @todo Change the MediumAttachment class to behave like any other
11919 * class in this regard by creating peer MediumAttachment
11920 * objects for session machines and share the data with the peer
11921 * machine.
11922 */
11923 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11924 it != mMediaData->mAttachments.end();
11925 ++it)
11926 {
11927 (*it)->i_updateParentMachine(mPeer);
11928 }
11929
11930 /* attach new data to the primary machine and reshare it */
11931 mPeer->mMediaData.attach(mMediaData);
11932 }
11933
11934 return;
11935}
11936
11937/**
11938 * Perform deferred deletion of implicitly created diffs.
11939 *
11940 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11941 * backed up).
11942 *
11943 * @note Locks this object for writing!
11944 */
11945void Machine::rollbackMedia()
11946{
11947 AutoCaller autoCaller(this);
11948 AssertComRCReturnVoid(autoCaller.rc());
11949
11950 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11951 LogFlowThisFunc(("Entering rollbackMedia\n"));
11952
11953 HRESULT rc = S_OK;
11954
11955 /* no attach/detach operations -- nothing to do */
11956 if (!mMediaData.isBackedUp())
11957 return;
11958
11959 /* enumerate new attachments */
11960 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11961 it != mMediaData->mAttachments.end();
11962 ++it)
11963 {
11964 MediumAttachment *pAttach = *it;
11965 /* Fix up the backrefs for DVD/floppy media. */
11966 if (pAttach->i_getType() != DeviceType_HardDisk)
11967 {
11968 Medium* pMedium = pAttach->i_getMedium();
11969 if (pMedium)
11970 {
11971 rc = pMedium->i_removeBackReference(mData->mUuid);
11972 AssertComRC(rc);
11973 }
11974 }
11975
11976 (*it)->i_rollback();
11977
11978 pAttach = *it;
11979 /* Fix up the backrefs for DVD/floppy media. */
11980 if (pAttach->i_getType() != DeviceType_HardDisk)
11981 {
11982 Medium* pMedium = pAttach->i_getMedium();
11983 if (pMedium)
11984 {
11985 rc = pMedium->i_addBackReference(mData->mUuid);
11986 AssertComRC(rc);
11987 }
11988 }
11989 }
11990
11991 /** @todo convert all this Machine-based voodoo to MediumAttachment
11992 * based rollback logic. */
11993 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11994
11995 return;
11996}
11997
11998/**
11999 * Returns true if the settings file is located in the directory named exactly
12000 * as the machine; this means, among other things, that the machine directory
12001 * should be auto-renamed.
12002 *
12003 * @param aSettingsDir if not NULL, the full machine settings file directory
12004 * name will be assigned there.
12005 *
12006 * @note Doesn't lock anything.
12007 * @note Not thread safe (must be called from this object's lock).
12008 */
12009bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12010{
12011 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12012 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12013 if (aSettingsDir)
12014 *aSettingsDir = strMachineDirName;
12015 strMachineDirName.stripPath(); // vmname
12016 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12017 strConfigFileOnly.stripPath() // vmname.vbox
12018 .stripSuffix(); // vmname
12019 /** @todo hack, make somehow use of ComposeMachineFilename */
12020 if (mUserData->s.fDirectoryIncludesUUID)
12021 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
12022
12023 AssertReturn(!strMachineDirName.isEmpty(), false);
12024 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12025
12026 return strMachineDirName == strConfigFileOnly;
12027}
12028
12029/**
12030 * Discards all changes to machine settings.
12031 *
12032 * @param aNotify Whether to notify the direct session about changes or not.
12033 *
12034 * @note Locks objects for writing!
12035 */
12036void Machine::rollback(bool aNotify)
12037{
12038 AutoCaller autoCaller(this);
12039 AssertComRCReturn(autoCaller.rc(), (void)0);
12040
12041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12042
12043 if (!mStorageControllers.isNull())
12044 {
12045 if (mStorageControllers.isBackedUp())
12046 {
12047 /* unitialize all new devices (absent in the backed up list). */
12048 StorageControllerList::const_iterator it = mStorageControllers->begin();
12049 StorageControllerList *backedList = mStorageControllers.backedUpData();
12050 while (it != mStorageControllers->end())
12051 {
12052 if ( std::find(backedList->begin(), backedList->end(), *it)
12053 == backedList->end()
12054 )
12055 {
12056 (*it)->uninit();
12057 }
12058 ++it;
12059 }
12060
12061 /* restore the list */
12062 mStorageControllers.rollback();
12063 }
12064
12065 /* rollback any changes to devices after restoring the list */
12066 if (mData->flModifications & IsModified_Storage)
12067 {
12068 StorageControllerList::const_iterator it = mStorageControllers->begin();
12069 while (it != mStorageControllers->end())
12070 {
12071 (*it)->i_rollback();
12072 ++it;
12073 }
12074 }
12075 }
12076
12077 if (!mUSBControllers.isNull())
12078 {
12079 if (mUSBControllers.isBackedUp())
12080 {
12081 /* unitialize all new devices (absent in the backed up list). */
12082 USBControllerList::const_iterator it = mUSBControllers->begin();
12083 USBControllerList *backedList = mUSBControllers.backedUpData();
12084 while (it != mUSBControllers->end())
12085 {
12086 if ( std::find(backedList->begin(), backedList->end(), *it)
12087 == backedList->end()
12088 )
12089 {
12090 (*it)->uninit();
12091 }
12092 ++it;
12093 }
12094
12095 /* restore the list */
12096 mUSBControllers.rollback();
12097 }
12098
12099 /* rollback any changes to devices after restoring the list */
12100 if (mData->flModifications & IsModified_USB)
12101 {
12102 USBControllerList::const_iterator it = mUSBControllers->begin();
12103 while (it != mUSBControllers->end())
12104 {
12105 (*it)->i_rollback();
12106 ++it;
12107 }
12108 }
12109 }
12110
12111 mUserData.rollback();
12112
12113 mHWData.rollback();
12114
12115 if (mData->flModifications & IsModified_Storage)
12116 rollbackMedia();
12117
12118 if (mBIOSSettings)
12119 mBIOSSettings->i_rollback();
12120
12121 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12122 mVRDEServer->i_rollback();
12123
12124 if (mAudioAdapter)
12125 mAudioAdapter->i_rollback();
12126
12127 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12128 mUSBDeviceFilters->i_rollback();
12129
12130 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12131 mBandwidthControl->i_rollback();
12132
12133 if (!mHWData.isNull())
12134 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12135 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12136 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12137 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12138
12139 if (mData->flModifications & IsModified_NetworkAdapters)
12140 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12141 if ( mNetworkAdapters[slot]
12142 && mNetworkAdapters[slot]->i_isModified())
12143 {
12144 mNetworkAdapters[slot]->i_rollback();
12145 networkAdapters[slot] = mNetworkAdapters[slot];
12146 }
12147
12148 if (mData->flModifications & IsModified_SerialPorts)
12149 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12150 if ( mSerialPorts[slot]
12151 && mSerialPorts[slot]->i_isModified())
12152 {
12153 mSerialPorts[slot]->i_rollback();
12154 serialPorts[slot] = mSerialPorts[slot];
12155 }
12156
12157 if (mData->flModifications & IsModified_ParallelPorts)
12158 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12159 if ( mParallelPorts[slot]
12160 && mParallelPorts[slot]->i_isModified())
12161 {
12162 mParallelPorts[slot]->i_rollback();
12163 parallelPorts[slot] = mParallelPorts[slot];
12164 }
12165
12166 if (aNotify)
12167 {
12168 /* inform the direct session about changes */
12169
12170 ComObjPtr<Machine> that = this;
12171 uint32_t flModifications = mData->flModifications;
12172 alock.release();
12173
12174 if (flModifications & IsModified_SharedFolders)
12175 that->onSharedFolderChange();
12176
12177 if (flModifications & IsModified_VRDEServer)
12178 that->onVRDEServerChange(/* aRestart */ TRUE);
12179 if (flModifications & IsModified_USB)
12180 that->onUSBControllerChange();
12181
12182 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12183 if (networkAdapters[slot])
12184 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12185 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12186 if (serialPorts[slot])
12187 that->onSerialPortChange(serialPorts[slot]);
12188 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12189 if (parallelPorts[slot])
12190 that->onParallelPortChange(parallelPorts[slot]);
12191
12192 if (flModifications & IsModified_Storage)
12193 that->onStorageControllerChange();
12194
12195#if 0
12196 if (flModifications & IsModified_BandwidthControl)
12197 that->onBandwidthControlChange();
12198#endif
12199 }
12200}
12201
12202/**
12203 * Commits all the changes to machine settings.
12204 *
12205 * Note that this operation is supposed to never fail.
12206 *
12207 * @note Locks this object and children for writing.
12208 */
12209void Machine::commit()
12210{
12211 AutoCaller autoCaller(this);
12212 AssertComRCReturnVoid(autoCaller.rc());
12213
12214 AutoCaller peerCaller(mPeer);
12215 AssertComRCReturnVoid(peerCaller.rc());
12216
12217 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12218
12219 /*
12220 * use safe commit to ensure Snapshot machines (that share mUserData)
12221 * will still refer to a valid memory location
12222 */
12223 mUserData.commitCopy();
12224
12225 mHWData.commit();
12226
12227 if (mMediaData.isBackedUp())
12228 commitMedia(Global::IsOnline(mData->mMachineState));
12229
12230 mBIOSSettings->i_commit();
12231 mVRDEServer->i_commit();
12232 mAudioAdapter->i_commit();
12233 mUSBDeviceFilters->i_commit();
12234 mBandwidthControl->i_commit();
12235
12236 /* Since mNetworkAdapters is a list which might have been changed (resized)
12237 * without using the Backupable<> template we need to handle the copying
12238 * of the list entries manually, including the creation of peers for the
12239 * new objects. */
12240 bool commitNetworkAdapters = false;
12241 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12242 if (mPeer)
12243 {
12244 /* commit everything, even the ones which will go away */
12245 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12246 mNetworkAdapters[slot]->i_commit();
12247 /* copy over the new entries, creating a peer and uninit the original */
12248 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12249 for (size_t slot = 0; slot < newSize; slot++)
12250 {
12251 /* look if this adapter has a peer device */
12252 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12253 if (!peer)
12254 {
12255 /* no peer means the adapter is a newly created one;
12256 * create a peer owning data this data share it with */
12257 peer.createObject();
12258 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12259 }
12260 mPeer->mNetworkAdapters[slot] = peer;
12261 }
12262 /* uninit any no longer needed network adapters */
12263 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12264 mNetworkAdapters[slot]->uninit();
12265 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12266 {
12267 if (mPeer->mNetworkAdapters[slot])
12268 mPeer->mNetworkAdapters[slot]->uninit();
12269 }
12270 /* Keep the original network adapter count until this point, so that
12271 * discarding a chipset type change will not lose settings. */
12272 mNetworkAdapters.resize(newSize);
12273 mPeer->mNetworkAdapters.resize(newSize);
12274 }
12275 else
12276 {
12277 /* we have no peer (our parent is the newly created machine);
12278 * just commit changes to the network adapters */
12279 commitNetworkAdapters = true;
12280 }
12281 if (commitNetworkAdapters)
12282 {
12283 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12284 mNetworkAdapters[slot]->i_commit();
12285 }
12286
12287 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12288 mSerialPorts[slot]->i_commit();
12289 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12290 mParallelPorts[slot]->i_commit();
12291
12292 bool commitStorageControllers = false;
12293
12294 if (mStorageControllers.isBackedUp())
12295 {
12296 mStorageControllers.commit();
12297
12298 if (mPeer)
12299 {
12300 /* Commit all changes to new controllers (this will reshare data with
12301 * peers for those who have peers) */
12302 StorageControllerList *newList = new StorageControllerList();
12303 StorageControllerList::const_iterator it = mStorageControllers->begin();
12304 while (it != mStorageControllers->end())
12305 {
12306 (*it)->i_commit();
12307
12308 /* look if this controller has a peer device */
12309 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12310 if (!peer)
12311 {
12312 /* no peer means the device is a newly created one;
12313 * create a peer owning data this device share it with */
12314 peer.createObject();
12315 peer->init(mPeer, *it, true /* aReshare */);
12316 }
12317 else
12318 {
12319 /* remove peer from the old list */
12320 mPeer->mStorageControllers->remove(peer);
12321 }
12322 /* and add it to the new list */
12323 newList->push_back(peer);
12324
12325 ++it;
12326 }
12327
12328 /* uninit old peer's controllers that are left */
12329 it = mPeer->mStorageControllers->begin();
12330 while (it != mPeer->mStorageControllers->end())
12331 {
12332 (*it)->uninit();
12333 ++it;
12334 }
12335
12336 /* attach new list of controllers to our peer */
12337 mPeer->mStorageControllers.attach(newList);
12338 }
12339 else
12340 {
12341 /* we have no peer (our parent is the newly created machine);
12342 * just commit changes to devices */
12343 commitStorageControllers = true;
12344 }
12345 }
12346 else
12347 {
12348 /* the list of controllers itself is not changed,
12349 * just commit changes to controllers themselves */
12350 commitStorageControllers = true;
12351 }
12352
12353 if (commitStorageControllers)
12354 {
12355 StorageControllerList::const_iterator it = mStorageControllers->begin();
12356 while (it != mStorageControllers->end())
12357 {
12358 (*it)->i_commit();
12359 ++it;
12360 }
12361 }
12362
12363 bool commitUSBControllers = false;
12364
12365 if (mUSBControllers.isBackedUp())
12366 {
12367 mUSBControllers.commit();
12368
12369 if (mPeer)
12370 {
12371 /* Commit all changes to new controllers (this will reshare data with
12372 * peers for those who have peers) */
12373 USBControllerList *newList = new USBControllerList();
12374 USBControllerList::const_iterator it = mUSBControllers->begin();
12375 while (it != mUSBControllers->end())
12376 {
12377 (*it)->i_commit();
12378
12379 /* look if this controller has a peer device */
12380 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12381 if (!peer)
12382 {
12383 /* no peer means the device is a newly created one;
12384 * create a peer owning data this device share it with */
12385 peer.createObject();
12386 peer->init(mPeer, *it, true /* aReshare */);
12387 }
12388 else
12389 {
12390 /* remove peer from the old list */
12391 mPeer->mUSBControllers->remove(peer);
12392 }
12393 /* and add it to the new list */
12394 newList->push_back(peer);
12395
12396 ++it;
12397 }
12398
12399 /* uninit old peer's controllers that are left */
12400 it = mPeer->mUSBControllers->begin();
12401 while (it != mPeer->mUSBControllers->end())
12402 {
12403 (*it)->uninit();
12404 ++it;
12405 }
12406
12407 /* attach new list of controllers to our peer */
12408 mPeer->mUSBControllers.attach(newList);
12409 }
12410 else
12411 {
12412 /* we have no peer (our parent is the newly created machine);
12413 * just commit changes to devices */
12414 commitUSBControllers = true;
12415 }
12416 }
12417 else
12418 {
12419 /* the list of controllers itself is not changed,
12420 * just commit changes to controllers themselves */
12421 commitUSBControllers = true;
12422 }
12423
12424 if (commitUSBControllers)
12425 {
12426 USBControllerList::const_iterator it = mUSBControllers->begin();
12427 while (it != mUSBControllers->end())
12428 {
12429 (*it)->i_commit();
12430 ++it;
12431 }
12432 }
12433
12434 if (isSessionMachine())
12435 {
12436 /* attach new data to the primary machine and reshare it */
12437 mPeer->mUserData.attach(mUserData);
12438 mPeer->mHWData.attach(mHWData);
12439 /* mMediaData is reshared by fixupMedia */
12440 // mPeer->mMediaData.attach(mMediaData);
12441 Assert(mPeer->mMediaData.data() == mMediaData.data());
12442 }
12443}
12444
12445/**
12446 * Copies all the hardware data from the given machine.
12447 *
12448 * Currently, only called when the VM is being restored from a snapshot. In
12449 * particular, this implies that the VM is not running during this method's
12450 * call.
12451 *
12452 * @note This method must be called from under this object's lock.
12453 *
12454 * @note This method doesn't call #commit(), so all data remains backed up and
12455 * unsaved.
12456 */
12457void Machine::copyFrom(Machine *aThat)
12458{
12459 AssertReturnVoid(!isSnapshotMachine());
12460 AssertReturnVoid(aThat->isSnapshotMachine());
12461
12462 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12463
12464 mHWData.assignCopy(aThat->mHWData);
12465
12466 // create copies of all shared folders (mHWData after attaching a copy
12467 // contains just references to original objects)
12468 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12469 it != mHWData->mSharedFolders.end();
12470 ++it)
12471 {
12472 ComObjPtr<SharedFolder> folder;
12473 folder.createObject();
12474 HRESULT rc = folder->initCopy(getMachine(), *it);
12475 AssertComRC(rc);
12476 *it = folder;
12477 }
12478
12479 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12480 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12481 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12482 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12483 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12484
12485 /* create private copies of all controllers */
12486 mStorageControllers.backup();
12487 mStorageControllers->clear();
12488 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12489 it != aThat->mStorageControllers->end();
12490 ++it)
12491 {
12492 ComObjPtr<StorageController> ctrl;
12493 ctrl.createObject();
12494 ctrl->initCopy(this, *it);
12495 mStorageControllers->push_back(ctrl);
12496 }
12497
12498 /* create private copies of all USB controllers */
12499 mUSBControllers.backup();
12500 mUSBControllers->clear();
12501 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12502 it != aThat->mUSBControllers->end();
12503 ++it)
12504 {
12505 ComObjPtr<USBController> ctrl;
12506 ctrl.createObject();
12507 ctrl->initCopy(this, *it);
12508 mUSBControllers->push_back(ctrl);
12509 }
12510
12511 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12512 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12513 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12514 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12515 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12516 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12517 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12518}
12519
12520/**
12521 * Returns whether the given storage controller is hotplug capable.
12522 *
12523 * @returns true if the controller supports hotplugging
12524 * false otherwise.
12525 * @param enmCtrlType The controller type to check for.
12526 */
12527bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12528{
12529 switch (enmCtrlType)
12530 {
12531 case StorageControllerType_IntelAhci:
12532 case StorageControllerType_USB:
12533 return true;
12534 case StorageControllerType_LsiLogic:
12535 case StorageControllerType_LsiLogicSas:
12536 case StorageControllerType_BusLogic:
12537 case StorageControllerType_PIIX3:
12538 case StorageControllerType_PIIX4:
12539 case StorageControllerType_ICH6:
12540 case StorageControllerType_I82078:
12541 default:
12542 return false;
12543 }
12544}
12545
12546#ifdef VBOX_WITH_RESOURCE_USAGE_API
12547
12548void Machine::getDiskList(MediaList &list)
12549{
12550 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12551 it != mMediaData->mAttachments.end();
12552 ++it)
12553 {
12554 MediumAttachment* pAttach = *it;
12555 /* just in case */
12556 AssertStmt(pAttach, continue);
12557
12558 AutoCaller localAutoCallerA(pAttach);
12559 if (FAILED(localAutoCallerA.rc())) continue;
12560
12561 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12562
12563 if (pAttach->i_getType() == DeviceType_HardDisk)
12564 list.push_back(pAttach->i_getMedium());
12565 }
12566}
12567
12568void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12569{
12570 AssertReturnVoid(isWriteLockOnCurrentThread());
12571 AssertPtrReturnVoid(aCollector);
12572
12573 pm::CollectorHAL *hal = aCollector->getHAL();
12574 /* Create sub metrics */
12575 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12576 "Percentage of processor time spent in user mode by the VM process.");
12577 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12578 "Percentage of processor time spent in kernel mode by the VM process.");
12579 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12580 "Size of resident portion of VM process in memory.");
12581 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12582 "Actual size of all VM disks combined.");
12583 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12584 "Network receive rate.");
12585 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12586 "Network transmit rate.");
12587 /* Create and register base metrics */
12588 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12589 cpuLoadUser, cpuLoadKernel);
12590 aCollector->registerBaseMetric(cpuLoad);
12591 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12592 ramUsageUsed);
12593 aCollector->registerBaseMetric(ramUsage);
12594 MediaList disks;
12595 getDiskList(disks);
12596 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12597 diskUsageUsed);
12598 aCollector->registerBaseMetric(diskUsage);
12599
12600 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12601 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12602 new pm::AggregateAvg()));
12603 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12604 new pm::AggregateMin()));
12605 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12606 new pm::AggregateMax()));
12607 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12608 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12609 new pm::AggregateAvg()));
12610 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12611 new pm::AggregateMin()));
12612 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12613 new pm::AggregateMax()));
12614
12615 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12616 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12617 new pm::AggregateAvg()));
12618 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12619 new pm::AggregateMin()));
12620 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12621 new pm::AggregateMax()));
12622
12623 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12624 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12625 new pm::AggregateAvg()));
12626 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12627 new pm::AggregateMin()));
12628 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12629 new pm::AggregateMax()));
12630
12631
12632 /* Guest metrics collector */
12633 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12634 aCollector->registerGuest(mCollectorGuest);
12635 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12636 this, __PRETTY_FUNCTION__, mCollectorGuest));
12637
12638 /* Create sub metrics */
12639 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12640 "Percentage of processor time spent in user mode as seen by the guest.");
12641 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12642 "Percentage of processor time spent in kernel mode as seen by the guest.");
12643 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12644 "Percentage of processor time spent idling as seen by the guest.");
12645
12646 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12647 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12648 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12649 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12650 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12651 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12652
12653 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12654
12655 /* Create and register base metrics */
12656 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12657 machineNetRx, machineNetTx);
12658 aCollector->registerBaseMetric(machineNetRate);
12659
12660 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12661 guestLoadUser, guestLoadKernel, guestLoadIdle);
12662 aCollector->registerBaseMetric(guestCpuLoad);
12663
12664 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12665 guestMemTotal, guestMemFree,
12666 guestMemBalloon, guestMemShared,
12667 guestMemCache, guestPagedTotal);
12668 aCollector->registerBaseMetric(guestCpuMem);
12669
12670 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12671 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12672 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12673 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12674
12675 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12676 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12677 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12678 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12679
12680 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12681 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12682 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12683 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12684
12685 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12686 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12687 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12688 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12689
12690 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12691 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12692 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12693 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12694
12695 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12696 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12697 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12698 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12699
12700 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12701 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12702 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12703 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12704
12705 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12706 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12707 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12708 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12709
12710 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12711 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12712 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12713 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12714
12715 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12716 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12717 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12718 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12719
12720 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12721 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12722 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12723 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12724}
12725
12726void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12727{
12728 AssertReturnVoid(isWriteLockOnCurrentThread());
12729
12730 if (aCollector)
12731 {
12732 aCollector->unregisterMetricsFor(aMachine);
12733 aCollector->unregisterBaseMetricsFor(aMachine);
12734 }
12735}
12736
12737#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12738
12739
12740////////////////////////////////////////////////////////////////////////////////
12741
12742DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12743
12744HRESULT SessionMachine::FinalConstruct()
12745{
12746 LogFlowThisFunc(("\n"));
12747
12748 mClientToken = NULL;
12749
12750 return BaseFinalConstruct();
12751}
12752
12753void SessionMachine::FinalRelease()
12754{
12755 LogFlowThisFunc(("\n"));
12756
12757 Assert(!mClientToken);
12758 /* paranoia, should not hang around any more */
12759 if (mClientToken)
12760 {
12761 delete mClientToken;
12762 mClientToken = NULL;
12763 }
12764
12765 uninit(Uninit::Unexpected);
12766
12767 BaseFinalRelease();
12768}
12769
12770/**
12771 * @note Must be called only by Machine::LockMachine() from its own write lock.
12772 */
12773HRESULT SessionMachine::init(Machine *aMachine)
12774{
12775 LogFlowThisFuncEnter();
12776 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12777
12778 AssertReturn(aMachine, E_INVALIDARG);
12779
12780 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12781
12782 /* Enclose the state transition NotReady->InInit->Ready */
12783 AutoInitSpan autoInitSpan(this);
12784 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12785
12786 HRESULT rc = S_OK;
12787
12788 /* create the machine client token */
12789 try
12790 {
12791 mClientToken = new ClientToken(aMachine, this);
12792 if (!mClientToken->isReady())
12793 {
12794 delete mClientToken;
12795 mClientToken = NULL;
12796 rc = E_FAIL;
12797 }
12798 }
12799 catch (std::bad_alloc &)
12800 {
12801 rc = E_OUTOFMEMORY;
12802 }
12803 if (FAILED(rc))
12804 return rc;
12805
12806 /* memorize the peer Machine */
12807 unconst(mPeer) = aMachine;
12808 /* share the parent pointer */
12809 unconst(mParent) = aMachine->mParent;
12810
12811 /* take the pointers to data to share */
12812 mData.share(aMachine->mData);
12813 mSSData.share(aMachine->mSSData);
12814
12815 mUserData.share(aMachine->mUserData);
12816 mHWData.share(aMachine->mHWData);
12817 mMediaData.share(aMachine->mMediaData);
12818
12819 mStorageControllers.allocate();
12820 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12821 it != aMachine->mStorageControllers->end();
12822 ++it)
12823 {
12824 ComObjPtr<StorageController> ctl;
12825 ctl.createObject();
12826 ctl->init(this, *it);
12827 mStorageControllers->push_back(ctl);
12828 }
12829
12830 mUSBControllers.allocate();
12831 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12832 it != aMachine->mUSBControllers->end();
12833 ++it)
12834 {
12835 ComObjPtr<USBController> ctl;
12836 ctl.createObject();
12837 ctl->init(this, *it);
12838 mUSBControllers->push_back(ctl);
12839 }
12840
12841 unconst(mBIOSSettings).createObject();
12842 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12843 /* create another VRDEServer object that will be mutable */
12844 unconst(mVRDEServer).createObject();
12845 mVRDEServer->init(this, aMachine->mVRDEServer);
12846 /* create another audio adapter object that will be mutable */
12847 unconst(mAudioAdapter).createObject();
12848 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12849 /* create a list of serial ports that will be mutable */
12850 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12851 {
12852 unconst(mSerialPorts[slot]).createObject();
12853 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12854 }
12855 /* create a list of parallel ports that will be mutable */
12856 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12857 {
12858 unconst(mParallelPorts[slot]).createObject();
12859 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12860 }
12861
12862 /* create another USB device filters object that will be mutable */
12863 unconst(mUSBDeviceFilters).createObject();
12864 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12865
12866 /* create a list of network adapters that will be mutable */
12867 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12868 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12869 {
12870 unconst(mNetworkAdapters[slot]).createObject();
12871 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12872
12873 NetworkAttachmentType_T type;
12874 HRESULT hrc;
12875 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12876 if ( SUCCEEDED(hrc)
12877 && type == NetworkAttachmentType_NATNetwork)
12878 {
12879 Bstr name;
12880 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12881 if (SUCCEEDED(hrc))
12882 {
12883 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12884 mUserData->s.strName.c_str(), name.raw()));
12885 aMachine->lockHandle()->unlockWrite();
12886 mParent->natNetworkRefInc(name.raw());
12887#ifdef RT_LOCK_STRICT
12888 aMachine->lockHandle()->lockWrite(RT_SRC_POS);
12889#else
12890 aMachine->lockHandle()->lockWrite();
12891#endif
12892 }
12893 }
12894 }
12895
12896 /* create another bandwidth control object that will be mutable */
12897 unconst(mBandwidthControl).createObject();
12898 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12899
12900 /* default is to delete saved state on Saved -> PoweredOff transition */
12901 mRemoveSavedState = true;
12902
12903 /* Confirm a successful initialization when it's the case */
12904 autoInitSpan.setSucceeded();
12905
12906 LogFlowThisFuncLeave();
12907 return rc;
12908}
12909
12910/**
12911 * Uninitializes this session object. If the reason is other than
12912 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12913 * or the client watcher code.
12914 *
12915 * @param aReason uninitialization reason
12916 *
12917 * @note Locks mParent + this object for writing.
12918 */
12919void SessionMachine::uninit(Uninit::Reason aReason)
12920{
12921 LogFlowThisFuncEnter();
12922 LogFlowThisFunc(("reason=%d\n", aReason));
12923
12924 /*
12925 * Strongly reference ourselves to prevent this object deletion after
12926 * mData->mSession.mMachine.setNull() below (which can release the last
12927 * reference and call the destructor). Important: this must be done before
12928 * accessing any members (and before AutoUninitSpan that does it as well).
12929 * This self reference will be released as the very last step on return.
12930 */
12931 ComObjPtr<SessionMachine> selfRef = this;
12932
12933 /* Enclose the state transition Ready->InUninit->NotReady */
12934 AutoUninitSpan autoUninitSpan(this);
12935 if (autoUninitSpan.uninitDone())
12936 {
12937 LogFlowThisFunc(("Already uninitialized\n"));
12938 LogFlowThisFuncLeave();
12939 return;
12940 }
12941
12942 if (autoUninitSpan.initFailed())
12943 {
12944 /* We've been called by init() because it's failed. It's not really
12945 * necessary (nor it's safe) to perform the regular uninit sequence
12946 * below, the following is enough.
12947 */
12948 LogFlowThisFunc(("Initialization failed.\n"));
12949 /* destroy the machine client token */
12950 if (mClientToken)
12951 {
12952 delete mClientToken;
12953 mClientToken = NULL;
12954 }
12955 uninitDataAndChildObjects();
12956 mData.free();
12957 unconst(mParent) = NULL;
12958 unconst(mPeer) = NULL;
12959 LogFlowThisFuncLeave();
12960 return;
12961 }
12962
12963 MachineState_T lastState;
12964 {
12965 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12966 lastState = mData->mMachineState;
12967 }
12968 NOREF(lastState);
12969
12970#ifdef VBOX_WITH_USB
12971 // release all captured USB devices, but do this before requesting the locks below
12972 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12973 {
12974 /* Console::captureUSBDevices() is called in the VM process only after
12975 * setting the machine state to Starting or Restoring.
12976 * Console::detachAllUSBDevices() will be called upon successful
12977 * termination. So, we need to release USB devices only if there was
12978 * an abnormal termination of a running VM.
12979 *
12980 * This is identical to SessionMachine::DetachAllUSBDevices except
12981 * for the aAbnormal argument. */
12982 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12983 AssertComRC(rc);
12984 NOREF(rc);
12985
12986 USBProxyService *service = mParent->host()->i_usbProxyService();
12987 if (service)
12988 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12989 }
12990#endif /* VBOX_WITH_USB */
12991
12992 // we need to lock this object in uninit() because the lock is shared
12993 // with mPeer (as well as data we modify below). mParent lock is needed
12994 // by several calls to it, and USB needs host lock.
12995 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12996
12997#ifdef VBOX_WITH_RESOURCE_USAGE_API
12998 /*
12999 * It is safe to call Machine::unregisterMetrics() here because
13000 * PerformanceCollector::samplerCallback no longer accesses guest methods
13001 * holding the lock.
13002 */
13003 unregisterMetrics(mParent->performanceCollector(), mPeer);
13004 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13005 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
13006 this, __PRETTY_FUNCTION__, mCollectorGuest));
13007 if (mCollectorGuest)
13008 {
13009 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
13010 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13011 mCollectorGuest = NULL;
13012 }
13013#endif
13014
13015 if (aReason == Uninit::Abnormal)
13016 {
13017 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
13018 Global::IsOnlineOrTransient(lastState)));
13019
13020 /* reset the state to Aborted */
13021 if (mData->mMachineState != MachineState_Aborted)
13022 setMachineState(MachineState_Aborted);
13023 }
13024
13025 // any machine settings modified?
13026 if (mData->flModifications)
13027 {
13028 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
13029 rollback(false /* aNotify */);
13030 }
13031
13032 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
13033 || !mConsoleTaskData.mSnapshot);
13034 if (!mConsoleTaskData.strStateFilePath.isEmpty())
13035 {
13036 LogWarningThisFunc(("canceling failed save state request!\n"));
13037 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
13038 }
13039 else if (!mConsoleTaskData.mSnapshot.isNull())
13040 {
13041 LogWarningThisFunc(("canceling untaken snapshot!\n"));
13042
13043 /* delete all differencing hard disks created (this will also attach
13044 * their parents back by rolling back mMediaData) */
13045 rollbackMedia();
13046
13047 // delete the saved state file (it might have been already created)
13048 // AFTER killing the snapshot so that releaseSavedStateFile() won't
13049 // think it's still in use
13050 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
13051 mConsoleTaskData.mSnapshot->uninit();
13052 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
13053 }
13054
13055 mData->mSession.mPID = NIL_RTPROCESS;
13056
13057 if (aReason == Uninit::Unexpected)
13058 {
13059 /* Uninitialization didn't come from #checkForDeath(), so tell the
13060 * client watcher thread to update the set of machines that have open
13061 * sessions. */
13062 mParent->updateClientWatcher();
13063 }
13064
13065 /* uninitialize all remote controls */
13066 if (mData->mSession.mRemoteControls.size())
13067 {
13068 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13069 mData->mSession.mRemoteControls.size()));
13070
13071 Data::Session::RemoteControlList::iterator it =
13072 mData->mSession.mRemoteControls.begin();
13073 while (it != mData->mSession.mRemoteControls.end())
13074 {
13075 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13076 HRESULT rc = (*it)->Uninitialize();
13077 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13078 if (FAILED(rc))
13079 LogWarningThisFunc(("Forgot to close the remote session?\n"));
13080 ++it;
13081 }
13082 mData->mSession.mRemoteControls.clear();
13083 }
13084
13085 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
13086 {
13087 NetworkAttachmentType_T type;
13088 HRESULT hrc;
13089
13090 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13091 if ( SUCCEEDED(hrc)
13092 && type == NetworkAttachmentType_NATNetwork)
13093 {
13094 Bstr name;
13095 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13096 if (SUCCEEDED(hrc))
13097 {
13098 multilock.release();
13099 LogRel(("VM '%s' stops using NAT network '%ls'\n",
13100 mUserData->s.strName.c_str(), name.raw()));
13101 mParent->natNetworkRefDec(name.raw());
13102 multilock.acquire();
13103 }
13104 }
13105 }
13106
13107 /*
13108 * An expected uninitialization can come only from #checkForDeath().
13109 * Otherwise it means that something's gone really wrong (for example,
13110 * the Session implementation has released the VirtualBox reference
13111 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13112 * etc). However, it's also possible, that the client releases the IPC
13113 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13114 * but the VirtualBox release event comes first to the server process.
13115 * This case is practically possible, so we should not assert on an
13116 * unexpected uninit, just log a warning.
13117 */
13118
13119 if ((aReason == Uninit::Unexpected))
13120 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13121
13122 if (aReason != Uninit::Normal)
13123 {
13124 mData->mSession.mDirectControl.setNull();
13125 }
13126 else
13127 {
13128 /* this must be null here (see #OnSessionEnd()) */
13129 Assert(mData->mSession.mDirectControl.isNull());
13130 Assert(mData->mSession.mState == SessionState_Unlocking);
13131 Assert(!mData->mSession.mProgress.isNull());
13132 }
13133 if (mData->mSession.mProgress)
13134 {
13135 if (aReason == Uninit::Normal)
13136 mData->mSession.mProgress->notifyComplete(S_OK);
13137 else
13138 mData->mSession.mProgress->notifyComplete(E_FAIL,
13139 COM_IIDOF(ISession),
13140 getComponentName(),
13141 tr("The VM session was aborted"));
13142 mData->mSession.mProgress.setNull();
13143 }
13144
13145 /* remove the association between the peer machine and this session machine */
13146 Assert( (SessionMachine*)mData->mSession.mMachine == this
13147 || aReason == Uninit::Unexpected);
13148
13149 /* reset the rest of session data */
13150 mData->mSession.mMachine.setNull();
13151 mData->mSession.mState = SessionState_Unlocked;
13152 mData->mSession.mType.setNull();
13153
13154 /* destroy the machine client token before leaving the exclusive lock */
13155 if (mClientToken)
13156 {
13157 delete mClientToken;
13158 mClientToken = NULL;
13159 }
13160
13161 /* fire an event */
13162 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13163
13164 uninitDataAndChildObjects();
13165
13166 /* free the essential data structure last */
13167 mData.free();
13168
13169 /* release the exclusive lock before setting the below two to NULL */
13170 multilock.release();
13171
13172 unconst(mParent) = NULL;
13173 unconst(mPeer) = NULL;
13174
13175 LogFlowThisFuncLeave();
13176}
13177
13178// util::Lockable interface
13179////////////////////////////////////////////////////////////////////////////////
13180
13181/**
13182 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13183 * with the primary Machine instance (mPeer).
13184 */
13185RWLockHandle *SessionMachine::lockHandle() const
13186{
13187 AssertReturn(mPeer != NULL, NULL);
13188 return mPeer->lockHandle();
13189}
13190
13191// IInternalMachineControl methods
13192////////////////////////////////////////////////////////////////////////////////
13193
13194/**
13195 * Passes collected guest statistics to performance collector object
13196 */
13197STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13198 ULONG aCpuKernel, ULONG aCpuIdle,
13199 ULONG aMemTotal, ULONG aMemFree,
13200 ULONG aMemBalloon, ULONG aMemShared,
13201 ULONG aMemCache, ULONG aPageTotal,
13202 ULONG aAllocVMM, ULONG aFreeVMM,
13203 ULONG aBalloonedVMM, ULONG aSharedVMM,
13204 ULONG aVmNetRx, ULONG aVmNetTx)
13205{
13206#ifdef VBOX_WITH_RESOURCE_USAGE_API
13207 if (mCollectorGuest)
13208 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13209 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13210 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13211 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13212
13213 return S_OK;
13214#else
13215 NOREF(aValidStats);
13216 NOREF(aCpuUser);
13217 NOREF(aCpuKernel);
13218 NOREF(aCpuIdle);
13219 NOREF(aMemTotal);
13220 NOREF(aMemFree);
13221 NOREF(aMemBalloon);
13222 NOREF(aMemShared);
13223 NOREF(aMemCache);
13224 NOREF(aPageTotal);
13225 NOREF(aAllocVMM);
13226 NOREF(aFreeVMM);
13227 NOREF(aBalloonedVMM);
13228 NOREF(aSharedVMM);
13229 NOREF(aVmNetRx);
13230 NOREF(aVmNetTx);
13231 return E_NOTIMPL;
13232#endif
13233}
13234
13235/**
13236 * @note Locks this object for writing.
13237 */
13238STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13239{
13240 AutoCaller autoCaller(this);
13241 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13242
13243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13244
13245 mRemoveSavedState = aRemove;
13246
13247 return S_OK;
13248}
13249
13250/**
13251 * @note Locks the same as #setMachineState() does.
13252 */
13253STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13254{
13255 return setMachineState(aMachineState);
13256}
13257
13258/**
13259 * @note Locks this object for writing.
13260 */
13261STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13262{
13263 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13264 AutoCaller autoCaller(this);
13265 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13266
13267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13268
13269 if (mData->mSession.mState != SessionState_Locked)
13270 return VBOX_E_INVALID_OBJECT_STATE;
13271
13272 if (!mData->mSession.mProgress.isNull())
13273 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13274
13275 LogFlowThisFunc(("returns S_OK.\n"));
13276 return S_OK;
13277}
13278
13279/**
13280 * @note Locks this object for writing.
13281 */
13282STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13283{
13284 AutoCaller autoCaller(this);
13285 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13286
13287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13288
13289 if (mData->mSession.mState != SessionState_Locked)
13290 return VBOX_E_INVALID_OBJECT_STATE;
13291
13292 /* Finalize the LaunchVMProcess progress object. */
13293 if (mData->mSession.mProgress)
13294 {
13295 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13296 mData->mSession.mProgress.setNull();
13297 }
13298
13299 if (SUCCEEDED((HRESULT)iResult))
13300 {
13301#ifdef VBOX_WITH_RESOURCE_USAGE_API
13302 /* The VM has been powered up successfully, so it makes sense
13303 * now to offer the performance metrics for a running machine
13304 * object. Doing it earlier wouldn't be safe. */
13305 registerMetrics(mParent->performanceCollector(), mPeer,
13306 mData->mSession.mPID);
13307#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13308 }
13309
13310 return S_OK;
13311}
13312
13313/**
13314 * @note Locks this object for writing.
13315 */
13316STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13317{
13318 LogFlowThisFuncEnter();
13319
13320 CheckComArgOutPointerValid(aProgress);
13321
13322 AutoCaller autoCaller(this);
13323 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13324
13325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13326
13327 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13328 E_FAIL);
13329
13330 /* create a progress object to track operation completion */
13331 ComObjPtr<Progress> pProgress;
13332 pProgress.createObject();
13333 pProgress->init(getVirtualBox(),
13334 static_cast<IMachine *>(this) /* aInitiator */,
13335 Bstr(tr("Stopping the virtual machine")).raw(),
13336 FALSE /* aCancelable */);
13337
13338 /* fill in the console task data */
13339 mConsoleTaskData.mLastState = mData->mMachineState;
13340 mConsoleTaskData.mProgress = pProgress;
13341
13342 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13343 setMachineState(MachineState_Stopping);
13344
13345 pProgress.queryInterfaceTo(aProgress);
13346
13347 return S_OK;
13348}
13349
13350/**
13351 * @note Locks this object for writing.
13352 */
13353STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13354{
13355 LogFlowThisFuncEnter();
13356
13357 AutoCaller autoCaller(this);
13358 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13359
13360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13361
13362 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13363 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13364 && mConsoleTaskData.mLastState != MachineState_Null,
13365 E_FAIL);
13366
13367 /*
13368 * On failure, set the state to the state we had when BeginPoweringDown()
13369 * was called (this is expected by Console::PowerDown() and the associated
13370 * task). On success the VM process already changed the state to
13371 * MachineState_PoweredOff, so no need to do anything.
13372 */
13373 if (FAILED(iResult))
13374 setMachineState(mConsoleTaskData.mLastState);
13375
13376 /* notify the progress object about operation completion */
13377 Assert(mConsoleTaskData.mProgress);
13378 if (SUCCEEDED(iResult))
13379 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13380 else
13381 {
13382 Utf8Str strErrMsg(aErrMsg);
13383 if (strErrMsg.length())
13384 mConsoleTaskData.mProgress->notifyComplete(iResult,
13385 COM_IIDOF(ISession),
13386 getComponentName(),
13387 strErrMsg.c_str());
13388 else
13389 mConsoleTaskData.mProgress->notifyComplete(iResult);
13390 }
13391
13392 /* clear out the temporary saved state data */
13393 mConsoleTaskData.mLastState = MachineState_Null;
13394 mConsoleTaskData.mProgress.setNull();
13395
13396 LogFlowThisFuncLeave();
13397 return S_OK;
13398}
13399
13400
13401/**
13402 * Goes through the USB filters of the given machine to see if the given
13403 * device matches any filter or not.
13404 *
13405 * @note Locks the same as USBController::hasMatchingFilter() does.
13406 */
13407STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13408 BOOL *aMatched,
13409 ULONG *aMaskedIfs)
13410{
13411 LogFlowThisFunc(("\n"));
13412
13413 CheckComArgNotNull(aUSBDevice);
13414 CheckComArgOutPointerValid(aMatched);
13415
13416 AutoCaller autoCaller(this);
13417 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13418
13419#ifdef VBOX_WITH_USB
13420 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
13421#else
13422 NOREF(aUSBDevice);
13423 NOREF(aMaskedIfs);
13424 *aMatched = FALSE;
13425#endif
13426
13427 return S_OK;
13428}
13429
13430/**
13431 * @note Locks the same as Host::captureUSBDevice() does.
13432 */
13433STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13434{
13435 LogFlowThisFunc(("\n"));
13436
13437 AutoCaller autoCaller(this);
13438 AssertComRCReturnRC(autoCaller.rc());
13439
13440#ifdef VBOX_WITH_USB
13441 /* if captureDeviceForVM() fails, it must have set extended error info */
13442 clearError();
13443 MultiResult rc = mParent->host()->i_checkUSBProxyService();
13444 if (FAILED(rc)) return rc;
13445
13446 USBProxyService *service = mParent->host()->i_usbProxyService();
13447 AssertReturn(service, E_FAIL);
13448 return service->captureDeviceForVM(this, Guid(aId).ref());
13449#else
13450 NOREF(aId);
13451 return E_NOTIMPL;
13452#endif
13453}
13454
13455/**
13456 * @note Locks the same as Host::detachUSBDevice() does.
13457 */
13458STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13459{
13460 LogFlowThisFunc(("\n"));
13461
13462 AutoCaller autoCaller(this);
13463 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13464
13465#ifdef VBOX_WITH_USB
13466 USBProxyService *service = mParent->host()->i_usbProxyService();
13467 AssertReturn(service, E_FAIL);
13468 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13469#else
13470 NOREF(aId);
13471 NOREF(aDone);
13472 return E_NOTIMPL;
13473#endif
13474}
13475
13476/**
13477 * Inserts all machine filters to the USB proxy service and then calls
13478 * Host::autoCaptureUSBDevices().
13479 *
13480 * Called by Console from the VM process upon VM startup.
13481 *
13482 * @note Locks what called methods lock.
13483 */
13484STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13485{
13486 LogFlowThisFunc(("\n"));
13487
13488 AutoCaller autoCaller(this);
13489 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13490
13491#ifdef VBOX_WITH_USB
13492 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13493 AssertComRC(rc);
13494 NOREF(rc);
13495
13496 USBProxyService *service = mParent->host()->i_usbProxyService();
13497 AssertReturn(service, E_FAIL);
13498 return service->autoCaptureDevicesForVM(this);
13499#else
13500 return S_OK;
13501#endif
13502}
13503
13504/**
13505 * Removes all machine filters from the USB proxy service and then calls
13506 * Host::detachAllUSBDevices().
13507 *
13508 * Called by Console from the VM process upon normal VM termination or by
13509 * SessionMachine::uninit() upon abnormal VM termination (from under the
13510 * Machine/SessionMachine lock).
13511 *
13512 * @note Locks what called methods lock.
13513 */
13514STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13515{
13516 LogFlowThisFunc(("\n"));
13517
13518 AutoCaller autoCaller(this);
13519 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13520
13521#ifdef VBOX_WITH_USB
13522 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13523 AssertComRC(rc);
13524 NOREF(rc);
13525
13526 USBProxyService *service = mParent->host()->i_usbProxyService();
13527 AssertReturn(service, E_FAIL);
13528 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13529#else
13530 NOREF(aDone);
13531 return S_OK;
13532#endif
13533}
13534
13535/**
13536 * @note Locks this object for writing.
13537 */
13538STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13539 IProgress **aProgress)
13540{
13541 LogFlowThisFuncEnter();
13542
13543 AssertReturn(aSession, E_INVALIDARG);
13544 AssertReturn(aProgress, E_INVALIDARG);
13545
13546 AutoCaller autoCaller(this);
13547
13548 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13549 /*
13550 * We don't assert below because it might happen that a non-direct session
13551 * informs us it is closed right after we've been uninitialized -- it's ok.
13552 */
13553 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13554
13555 /* get IInternalSessionControl interface */
13556 ComPtr<IInternalSessionControl> control(aSession);
13557
13558 ComAssertRet(!control.isNull(), E_INVALIDARG);
13559
13560 /* Creating a Progress object requires the VirtualBox lock, and
13561 * thus locking it here is required by the lock order rules. */
13562 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13563
13564 if (control == mData->mSession.mDirectControl)
13565 {
13566 ComAssertRet(aProgress, E_POINTER);
13567
13568 /* The direct session is being normally closed by the client process
13569 * ----------------------------------------------------------------- */
13570
13571 /* go to the closing state (essential for all open*Session() calls and
13572 * for #checkForDeath()) */
13573 Assert(mData->mSession.mState == SessionState_Locked);
13574 mData->mSession.mState = SessionState_Unlocking;
13575
13576 /* set direct control to NULL to release the remote instance */
13577 mData->mSession.mDirectControl.setNull();
13578 LogFlowThisFunc(("Direct control is set to NULL\n"));
13579
13580 if (mData->mSession.mProgress)
13581 {
13582 /* finalize the progress, someone might wait if a frontend
13583 * closes the session before powering on the VM. */
13584 mData->mSession.mProgress->notifyComplete(E_FAIL,
13585 COM_IIDOF(ISession),
13586 getComponentName(),
13587 tr("The VM session was closed before any attempt to power it on"));
13588 mData->mSession.mProgress.setNull();
13589 }
13590
13591 /* Create the progress object the client will use to wait until
13592 * #checkForDeath() is called to uninitialize this session object after
13593 * it releases the IPC semaphore.
13594 * Note! Because we're "reusing" mProgress here, this must be a proxy
13595 * object just like for LaunchVMProcess. */
13596 Assert(mData->mSession.mProgress.isNull());
13597 ComObjPtr<ProgressProxy> progress;
13598 progress.createObject();
13599 ComPtr<IUnknown> pPeer(mPeer);
13600 progress->init(mParent, pPeer,
13601 Bstr(tr("Closing session")).raw(),
13602 FALSE /* aCancelable */);
13603 progress.queryInterfaceTo(aProgress);
13604 mData->mSession.mProgress = progress;
13605 }
13606 else
13607 {
13608 /* the remote session is being normally closed */
13609 Data::Session::RemoteControlList::iterator it =
13610 mData->mSession.mRemoteControls.begin();
13611 while (it != mData->mSession.mRemoteControls.end())
13612 {
13613 if (control == *it)
13614 break;
13615 ++it;
13616 }
13617 BOOL found = it != mData->mSession.mRemoteControls.end();
13618 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13619 E_INVALIDARG);
13620 // This MUST be erase(it), not remove(*it) as the latter triggers a
13621 // very nasty use after free due to the place where the value "lives".
13622 mData->mSession.mRemoteControls.erase(it);
13623 }
13624
13625 /* signal the client watcher thread, because the client is going away */
13626 mParent->updateClientWatcher();
13627
13628 LogFlowThisFuncLeave();
13629 return S_OK;
13630}
13631
13632/**
13633 * @note Locks this object for writing.
13634 */
13635STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13636{
13637 LogFlowThisFuncEnter();
13638
13639 CheckComArgOutPointerValid(aProgress);
13640 CheckComArgOutPointerValid(aStateFilePath);
13641
13642 AutoCaller autoCaller(this);
13643 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13644
13645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13646
13647 AssertReturn( mData->mMachineState == MachineState_Paused
13648 && mConsoleTaskData.mLastState == MachineState_Null
13649 && mConsoleTaskData.strStateFilePath.isEmpty(),
13650 E_FAIL);
13651
13652 /* create a progress object to track operation completion */
13653 ComObjPtr<Progress> pProgress;
13654 pProgress.createObject();
13655 pProgress->init(getVirtualBox(),
13656 static_cast<IMachine *>(this) /* aInitiator */,
13657 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13658 FALSE /* aCancelable */);
13659
13660 Utf8Str strStateFilePath;
13661 /* stateFilePath is null when the machine is not running */
13662 if (mData->mMachineState == MachineState_Paused)
13663 composeSavedStateFilename(strStateFilePath);
13664
13665 /* fill in the console task data */
13666 mConsoleTaskData.mLastState = mData->mMachineState;
13667 mConsoleTaskData.strStateFilePath = strStateFilePath;
13668 mConsoleTaskData.mProgress = pProgress;
13669
13670 /* set the state to Saving (this is expected by Console::SaveState()) */
13671 setMachineState(MachineState_Saving);
13672
13673 strStateFilePath.cloneTo(aStateFilePath);
13674 pProgress.queryInterfaceTo(aProgress);
13675
13676 return S_OK;
13677}
13678
13679/**
13680 * @note Locks mParent + this object for writing.
13681 */
13682STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13683{
13684 LogFlowThisFunc(("\n"));
13685
13686 AutoCaller autoCaller(this);
13687 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13688
13689 /* endSavingState() need mParent lock */
13690 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13691
13692 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13693 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13694 && mConsoleTaskData.mLastState != MachineState_Null
13695 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13696 E_FAIL);
13697
13698 /*
13699 * On failure, set the state to the state we had when BeginSavingState()
13700 * was called (this is expected by Console::SaveState() and the associated
13701 * task). On success the VM process already changed the state to
13702 * MachineState_Saved, so no need to do anything.
13703 */
13704 if (FAILED(iResult))
13705 setMachineState(mConsoleTaskData.mLastState);
13706
13707 return endSavingState(iResult, aErrMsg);
13708}
13709
13710/**
13711 * @note Locks this object for writing.
13712 */
13713STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13714{
13715 LogFlowThisFunc(("\n"));
13716
13717 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13718
13719 AutoCaller autoCaller(this);
13720 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13721
13722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13723
13724 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13725 || mData->mMachineState == MachineState_Teleported
13726 || mData->mMachineState == MachineState_Aborted
13727 , E_FAIL); /** @todo setError. */
13728
13729 Utf8Str stateFilePathFull = aSavedStateFile;
13730 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13731 if (RT_FAILURE(vrc))
13732 return setError(VBOX_E_FILE_ERROR,
13733 tr("Invalid saved state file path '%ls' (%Rrc)"),
13734 aSavedStateFile,
13735 vrc);
13736
13737 mSSData->strStateFilePath = stateFilePathFull;
13738
13739 /* The below setMachineState() will detect the state transition and will
13740 * update the settings file */
13741
13742 return setMachineState(MachineState_Saved);
13743}
13744
13745STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13746 ComSafeArrayOut(BSTR, aValues),
13747 ComSafeArrayOut(LONG64, aTimestamps),
13748 ComSafeArrayOut(BSTR, aFlags))
13749{
13750 LogFlowThisFunc(("\n"));
13751
13752#ifdef VBOX_WITH_GUEST_PROPS
13753 using namespace guestProp;
13754
13755 AutoCaller autoCaller(this);
13756 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13757
13758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13759
13760 CheckComArgOutSafeArrayPointerValid(aNames);
13761 CheckComArgOutSafeArrayPointerValid(aValues);
13762 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13763 CheckComArgOutSafeArrayPointerValid(aFlags);
13764
13765 size_t cEntries = mHWData->mGuestProperties.size();
13766 com::SafeArray<BSTR> names(cEntries);
13767 com::SafeArray<BSTR> values(cEntries);
13768 com::SafeArray<LONG64> timestamps(cEntries);
13769 com::SafeArray<BSTR> flags(cEntries);
13770 unsigned i = 0;
13771 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13772 it != mHWData->mGuestProperties.end();
13773 ++it)
13774 {
13775 char szFlags[MAX_FLAGS_LEN + 1];
13776 it->first.cloneTo(&names[i]);
13777 it->second.strValue.cloneTo(&values[i]);
13778 timestamps[i] = it->second.mTimestamp;
13779 /* If it is NULL, keep it NULL. */
13780 if (it->second.mFlags)
13781 {
13782 writeFlags(it->second.mFlags, szFlags);
13783 Bstr(szFlags).cloneTo(&flags[i]);
13784 }
13785 else
13786 flags[i] = NULL;
13787 ++i;
13788 }
13789 names.detachTo(ComSafeArrayOutArg(aNames));
13790 values.detachTo(ComSafeArrayOutArg(aValues));
13791 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13792 flags.detachTo(ComSafeArrayOutArg(aFlags));
13793 return S_OK;
13794#else
13795 ReturnComNotImplemented();
13796#endif
13797}
13798
13799STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13800 IN_BSTR aValue,
13801 LONG64 aTimestamp,
13802 IN_BSTR aFlags)
13803{
13804 LogFlowThisFunc(("\n"));
13805
13806#ifdef VBOX_WITH_GUEST_PROPS
13807 using namespace guestProp;
13808
13809 CheckComArgStrNotEmptyOrNull(aName);
13810 CheckComArgNotNull(aValue);
13811 CheckComArgNotNull(aFlags);
13812
13813 try
13814 {
13815 /*
13816 * Convert input up front.
13817 */
13818 Utf8Str utf8Name(aName);
13819 uint32_t fFlags = NILFLAG;
13820 if (aFlags)
13821 {
13822 Utf8Str utf8Flags(aFlags);
13823 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13824 AssertRCReturn(vrc, E_INVALIDARG);
13825 }
13826
13827 /*
13828 * Now grab the object lock, validate the state and do the update.
13829 */
13830 AutoCaller autoCaller(this);
13831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13832
13833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13834
13835 switch (mData->mMachineState)
13836 {
13837 case MachineState_Paused:
13838 case MachineState_Running:
13839 case MachineState_Teleporting:
13840 case MachineState_TeleportingPausedVM:
13841 case MachineState_LiveSnapshotting:
13842 case MachineState_DeletingSnapshotOnline:
13843 case MachineState_DeletingSnapshotPaused:
13844 case MachineState_Saving:
13845 case MachineState_Stopping:
13846 break;
13847
13848 default:
13849 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13850 VBOX_E_INVALID_VM_STATE);
13851 }
13852
13853 setModified(IsModified_MachineData);
13854 mHWData.backup();
13855
13856 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13857 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13858 if (it != mHWData->mGuestProperties.end())
13859 {
13860 if (!fDelete)
13861 {
13862 it->second.strValue = aValue;
13863 it->second.mTimestamp = aTimestamp;
13864 it->second.mFlags = fFlags;
13865 }
13866 else
13867 mHWData->mGuestProperties.erase(it);
13868
13869 mData->mGuestPropertiesModified = TRUE;
13870 }
13871 else if (!fDelete)
13872 {
13873 HWData::GuestProperty prop;
13874 prop.strValue = aValue;
13875 prop.mTimestamp = aTimestamp;
13876 prop.mFlags = fFlags;
13877
13878 mHWData->mGuestProperties[utf8Name] = prop;
13879 mData->mGuestPropertiesModified = TRUE;
13880 }
13881
13882 /*
13883 * Send a callback notification if appropriate
13884 */
13885 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13886 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13887 RTSTR_MAX,
13888 utf8Name.c_str(),
13889 RTSTR_MAX, NULL)
13890 )
13891 {
13892 alock.release();
13893
13894 mParent->onGuestPropertyChange(mData->mUuid,
13895 aName,
13896 aValue,
13897 aFlags);
13898 }
13899 }
13900 catch (...)
13901 {
13902 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13903 }
13904 return S_OK;
13905#else
13906 ReturnComNotImplemented();
13907#endif
13908}
13909
13910STDMETHODIMP SessionMachine::LockMedia()
13911{
13912 AutoCaller autoCaller(this);
13913 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13914
13915 AutoMultiWriteLock2 alock(this->lockHandle(),
13916 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13917
13918 AssertReturn( mData->mMachineState == MachineState_Starting
13919 || mData->mMachineState == MachineState_Restoring
13920 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13921
13922 clearError();
13923 alock.release();
13924 return lockMedia();
13925}
13926
13927STDMETHODIMP SessionMachine::UnlockMedia()
13928{
13929 unlockMedia();
13930 return S_OK;
13931}
13932
13933STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13934 IMediumAttachment **aNewAttachment)
13935{
13936 CheckComArgNotNull(aAttachment);
13937 CheckComArgOutPointerValid(aNewAttachment);
13938
13939 AutoCaller autoCaller(this);
13940 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13941
13942 // request the host lock first, since might be calling Host methods for getting host drives;
13943 // next, protect the media tree all the while we're in here, as well as our member variables
13944 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13945 this->lockHandle(),
13946 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13947
13948 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13949
13950 Bstr ctrlName;
13951 LONG lPort;
13952 LONG lDevice;
13953 bool fTempEject;
13954 {
13955 AutoCaller autoAttachCaller(this);
13956 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13957
13958 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13959
13960 /* Need to query the details first, as the IMediumAttachment reference
13961 * might be to the original settings, which we are going to change. */
13962 ctrlName = pAttach->i_getControllerName();
13963 lPort = pAttach->i_getPort();
13964 lDevice = pAttach->i_getDevice();
13965 fTempEject = pAttach->i_getTempEject();
13966 }
13967
13968 if (!fTempEject)
13969 {
13970 /* Remember previously mounted medium. The medium before taking the
13971 * backup is not necessarily the same thing. */
13972 ComObjPtr<Medium> oldmedium;
13973 oldmedium = pAttach->i_getMedium();
13974
13975 setModified(IsModified_Storage);
13976 mMediaData.backup();
13977
13978 // The backup operation makes the pAttach reference point to the
13979 // old settings. Re-get the correct reference.
13980 pAttach = findAttachment(mMediaData->mAttachments,
13981 ctrlName.raw(),
13982 lPort,
13983 lDevice);
13984
13985 {
13986 AutoCaller autoAttachCaller(this);
13987 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13988
13989 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13990 if (!oldmedium.isNull())
13991 oldmedium->i_removeBackReference(mData->mUuid);
13992
13993 pAttach->i_updateMedium(NULL);
13994 pAttach->i_updateEjected();
13995 }
13996
13997 setModified(IsModified_Storage);
13998 }
13999 else
14000 {
14001 {
14002 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14003 pAttach->i_updateEjected();
14004 }
14005 }
14006
14007 pAttach.queryInterfaceTo(aNewAttachment);
14008
14009 return S_OK;
14010}
14011
14012// public methods only for internal purposes
14013/////////////////////////////////////////////////////////////////////////////
14014
14015#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14016/**
14017 * Called from the client watcher thread to check for expected or unexpected
14018 * death of the client process that has a direct session to this machine.
14019 *
14020 * On Win32 and on OS/2, this method is called only when we've got the
14021 * mutex (i.e. the client has either died or terminated normally) so it always
14022 * returns @c true (the client is terminated, the session machine is
14023 * uninitialized).
14024 *
14025 * On other platforms, the method returns @c true if the client process has
14026 * terminated normally or abnormally and the session machine was uninitialized,
14027 * and @c false if the client process is still alive.
14028 *
14029 * @note Locks this object for writing.
14030 */
14031bool SessionMachine::checkForDeath()
14032{
14033 Uninit::Reason reason;
14034 bool terminated = false;
14035
14036 /* Enclose autoCaller with a block because calling uninit() from under it
14037 * will deadlock. */
14038 {
14039 AutoCaller autoCaller(this);
14040 if (!autoCaller.isOk())
14041 {
14042 /* return true if not ready, to cause the client watcher to exclude
14043 * the corresponding session from watching */
14044 LogFlowThisFunc(("Already uninitialized!\n"));
14045 return true;
14046 }
14047
14048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14049
14050 /* Determine the reason of death: if the session state is Closing here,
14051 * everything is fine. Otherwise it means that the client did not call
14052 * OnSessionEnd() before it released the IPC semaphore. This may happen
14053 * either because the client process has abnormally terminated, or
14054 * because it simply forgot to call ISession::Close() before exiting. We
14055 * threat the latter also as an abnormal termination (see
14056 * Session::uninit() for details). */
14057 reason = mData->mSession.mState == SessionState_Unlocking ?
14058 Uninit::Normal :
14059 Uninit::Abnormal;
14060
14061 if (mClientToken)
14062 terminated = mClientToken->release();
14063 } /* AutoCaller block */
14064
14065 if (terminated)
14066 uninit(reason);
14067
14068 return terminated;
14069}
14070
14071void SessionMachine::getTokenId(Utf8Str &strTokenId)
14072{
14073 LogFlowThisFunc(("\n"));
14074
14075 strTokenId.setNull();
14076
14077 AutoCaller autoCaller(this);
14078 AssertComRCReturnVoid(autoCaller.rc());
14079
14080 Assert(mClientToken);
14081 if (mClientToken)
14082 mClientToken->getId(strTokenId);
14083}
14084#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14085IToken *SessionMachine::getToken()
14086{
14087 LogFlowThisFunc(("\n"));
14088
14089 AutoCaller autoCaller(this);
14090 AssertComRCReturn(autoCaller.rc(), NULL);
14091
14092 Assert(mClientToken);
14093 if (mClientToken)
14094 return mClientToken->getToken();
14095 else
14096 return NULL;
14097}
14098#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14099
14100Machine::ClientToken *SessionMachine::getClientToken()
14101{
14102 LogFlowThisFunc(("\n"));
14103
14104 AutoCaller autoCaller(this);
14105 AssertComRCReturn(autoCaller.rc(), NULL);
14106
14107 return mClientToken;
14108}
14109
14110
14111/**
14112 * @note Locks this object for reading.
14113 */
14114HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14120
14121 ComPtr<IInternalSessionControl> directControl;
14122 {
14123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14124 directControl = mData->mSession.mDirectControl;
14125 }
14126
14127 /* ignore notifications sent after #OnSessionEnd() is called */
14128 if (!directControl)
14129 return S_OK;
14130
14131 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14132}
14133
14134/**
14135 * @note Locks this object for reading.
14136 */
14137HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14138 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14144
14145 ComPtr<IInternalSessionControl> directControl;
14146 {
14147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14148 directControl = mData->mSession.mDirectControl;
14149 }
14150
14151 /* ignore notifications sent after #OnSessionEnd() is called */
14152 if (!directControl)
14153 return S_OK;
14154 /*
14155 * instead acting like callback we ask IVirtualBox deliver corresponding event
14156 */
14157
14158 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14159 return S_OK;
14160}
14161
14162/**
14163 * @note Locks this object for reading.
14164 */
14165HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14166{
14167 LogFlowThisFunc(("\n"));
14168
14169 AutoCaller autoCaller(this);
14170 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14171
14172 ComPtr<IInternalSessionControl> directControl;
14173 {
14174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14175 directControl = mData->mSession.mDirectControl;
14176 }
14177
14178 /* ignore notifications sent after #OnSessionEnd() is called */
14179 if (!directControl)
14180 return S_OK;
14181
14182 return directControl->OnSerialPortChange(serialPort);
14183}
14184
14185/**
14186 * @note Locks this object for reading.
14187 */
14188HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14189{
14190 LogFlowThisFunc(("\n"));
14191
14192 AutoCaller autoCaller(this);
14193 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14194
14195 ComPtr<IInternalSessionControl> directControl;
14196 {
14197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14198 directControl = mData->mSession.mDirectControl;
14199 }
14200
14201 /* ignore notifications sent after #OnSessionEnd() is called */
14202 if (!directControl)
14203 return S_OK;
14204
14205 return directControl->OnParallelPortChange(parallelPort);
14206}
14207
14208/**
14209 * @note Locks this object for reading.
14210 */
14211HRESULT SessionMachine::onStorageControllerChange()
14212{
14213 LogFlowThisFunc(("\n"));
14214
14215 AutoCaller autoCaller(this);
14216 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14217
14218 ComPtr<IInternalSessionControl> directControl;
14219 {
14220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14221 directControl = mData->mSession.mDirectControl;
14222 }
14223
14224 /* ignore notifications sent after #OnSessionEnd() is called */
14225 if (!directControl)
14226 return S_OK;
14227
14228 return directControl->OnStorageControllerChange();
14229}
14230
14231/**
14232 * @note Locks this object for reading.
14233 */
14234HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14235{
14236 LogFlowThisFunc(("\n"));
14237
14238 AutoCaller autoCaller(this);
14239 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14240
14241 ComPtr<IInternalSessionControl> directControl;
14242 {
14243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14244 directControl = mData->mSession.mDirectControl;
14245 }
14246
14247 /* ignore notifications sent after #OnSessionEnd() is called */
14248 if (!directControl)
14249 return S_OK;
14250
14251 return directControl->OnMediumChange(aAttachment, aForce);
14252}
14253
14254/**
14255 * @note Locks this object for reading.
14256 */
14257HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14258{
14259 LogFlowThisFunc(("\n"));
14260
14261 AutoCaller autoCaller(this);
14262 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14263
14264 ComPtr<IInternalSessionControl> directControl;
14265 {
14266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14267 directControl = mData->mSession.mDirectControl;
14268 }
14269
14270 /* ignore notifications sent after #OnSessionEnd() is called */
14271 if (!directControl)
14272 return S_OK;
14273
14274 return directControl->OnCPUChange(aCPU, aRemove);
14275}
14276
14277HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14278{
14279 LogFlowThisFunc(("\n"));
14280
14281 AutoCaller autoCaller(this);
14282 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14283
14284 ComPtr<IInternalSessionControl> directControl;
14285 {
14286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14287 directControl = mData->mSession.mDirectControl;
14288 }
14289
14290 /* ignore notifications sent after #OnSessionEnd() is called */
14291 if (!directControl)
14292 return S_OK;
14293
14294 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14295}
14296
14297/**
14298 * @note Locks this object for reading.
14299 */
14300HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14301{
14302 LogFlowThisFunc(("\n"));
14303
14304 AutoCaller autoCaller(this);
14305 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14306
14307 ComPtr<IInternalSessionControl> directControl;
14308 {
14309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14310 directControl = mData->mSession.mDirectControl;
14311 }
14312
14313 /* ignore notifications sent after #OnSessionEnd() is called */
14314 if (!directControl)
14315 return S_OK;
14316
14317 return directControl->OnVRDEServerChange(aRestart);
14318}
14319
14320/**
14321 * @note Locks this object for reading.
14322 */
14323HRESULT SessionMachine::onVideoCaptureChange()
14324{
14325 LogFlowThisFunc(("\n"));
14326
14327 AutoCaller autoCaller(this);
14328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14329
14330 ComPtr<IInternalSessionControl> directControl;
14331 {
14332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14333 directControl = mData->mSession.mDirectControl;
14334 }
14335
14336 /* ignore notifications sent after #OnSessionEnd() is called */
14337 if (!directControl)
14338 return S_OK;
14339
14340 return directControl->OnVideoCaptureChange();
14341}
14342
14343/**
14344 * @note Locks this object for reading.
14345 */
14346HRESULT SessionMachine::onUSBControllerChange()
14347{
14348 LogFlowThisFunc(("\n"));
14349
14350 AutoCaller autoCaller(this);
14351 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14352
14353 ComPtr<IInternalSessionControl> directControl;
14354 {
14355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14356 directControl = mData->mSession.mDirectControl;
14357 }
14358
14359 /* ignore notifications sent after #OnSessionEnd() is called */
14360 if (!directControl)
14361 return S_OK;
14362
14363 return directControl->OnUSBControllerChange();
14364}
14365
14366/**
14367 * @note Locks this object for reading.
14368 */
14369HRESULT SessionMachine::onSharedFolderChange()
14370{
14371 LogFlowThisFunc(("\n"));
14372
14373 AutoCaller autoCaller(this);
14374 AssertComRCReturnRC(autoCaller.rc());
14375
14376 ComPtr<IInternalSessionControl> directControl;
14377 {
14378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14379 directControl = mData->mSession.mDirectControl;
14380 }
14381
14382 /* ignore notifications sent after #OnSessionEnd() is called */
14383 if (!directControl)
14384 return S_OK;
14385
14386 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14387}
14388
14389/**
14390 * @note Locks this object for reading.
14391 */
14392HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14393{
14394 LogFlowThisFunc(("\n"));
14395
14396 AutoCaller autoCaller(this);
14397 AssertComRCReturnRC(autoCaller.rc());
14398
14399 ComPtr<IInternalSessionControl> directControl;
14400 {
14401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14402 directControl = mData->mSession.mDirectControl;
14403 }
14404
14405 /* ignore notifications sent after #OnSessionEnd() is called */
14406 if (!directControl)
14407 return S_OK;
14408
14409 return directControl->OnClipboardModeChange(aClipboardMode);
14410}
14411
14412/**
14413 * @note Locks this object for reading.
14414 */
14415HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14416{
14417 LogFlowThisFunc(("\n"));
14418
14419 AutoCaller autoCaller(this);
14420 AssertComRCReturnRC(autoCaller.rc());
14421
14422 ComPtr<IInternalSessionControl> directControl;
14423 {
14424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14425 directControl = mData->mSession.mDirectControl;
14426 }
14427
14428 /* ignore notifications sent after #OnSessionEnd() is called */
14429 if (!directControl)
14430 return S_OK;
14431
14432 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14433}
14434
14435/**
14436 * @note Locks this object for reading.
14437 */
14438HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14439{
14440 LogFlowThisFunc(("\n"));
14441
14442 AutoCaller autoCaller(this);
14443 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14444
14445 ComPtr<IInternalSessionControl> directControl;
14446 {
14447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14448 directControl = mData->mSession.mDirectControl;
14449 }
14450
14451 /* ignore notifications sent after #OnSessionEnd() is called */
14452 if (!directControl)
14453 return S_OK;
14454
14455 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14456}
14457
14458/**
14459 * @note Locks this object for reading.
14460 */
14461HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14462{
14463 LogFlowThisFunc(("\n"));
14464
14465 AutoCaller autoCaller(this);
14466 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14467
14468 ComPtr<IInternalSessionControl> directControl;
14469 {
14470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14471 directControl = mData->mSession.mDirectControl;
14472 }
14473
14474 /* ignore notifications sent after #OnSessionEnd() is called */
14475 if (!directControl)
14476 return S_OK;
14477
14478 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14479}
14480
14481/**
14482 * Returns @c true if this machine's USB controller reports it has a matching
14483 * filter for the given USB device and @c false otherwise.
14484 *
14485 * @note locks this object for reading.
14486 */
14487bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14488{
14489 AutoCaller autoCaller(this);
14490 /* silently return if not ready -- this method may be called after the
14491 * direct machine session has been called */
14492 if (!autoCaller.isOk())
14493 return false;
14494
14495#ifdef VBOX_WITH_USB
14496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14497
14498 switch (mData->mMachineState)
14499 {
14500 case MachineState_Starting:
14501 case MachineState_Restoring:
14502 case MachineState_TeleportingIn:
14503 case MachineState_Paused:
14504 case MachineState_Running:
14505 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14506 * elsewhere... */
14507 alock.release();
14508 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14509 default: break;
14510 }
14511#else
14512 NOREF(aDevice);
14513 NOREF(aMaskedIfs);
14514#endif
14515 return false;
14516}
14517
14518/**
14519 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14520 */
14521HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14522 IVirtualBoxErrorInfo *aError,
14523 ULONG aMaskedIfs)
14524{
14525 LogFlowThisFunc(("\n"));
14526
14527 AutoCaller autoCaller(this);
14528
14529 /* This notification may happen after the machine object has been
14530 * uninitialized (the session was closed), so don't assert. */
14531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14532
14533 ComPtr<IInternalSessionControl> directControl;
14534 {
14535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14536 directControl = mData->mSession.mDirectControl;
14537 }
14538
14539 /* fail on notifications sent after #OnSessionEnd() is called, it is
14540 * expected by the caller */
14541 if (!directControl)
14542 return E_FAIL;
14543
14544 /* No locks should be held at this point. */
14545 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14546 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14547
14548 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14549}
14550
14551/**
14552 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14553 */
14554HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14555 IVirtualBoxErrorInfo *aError)
14556{
14557 LogFlowThisFunc(("\n"));
14558
14559 AutoCaller autoCaller(this);
14560
14561 /* This notification may happen after the machine object has been
14562 * uninitialized (the session was closed), so don't assert. */
14563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14564
14565 ComPtr<IInternalSessionControl> directControl;
14566 {
14567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14568 directControl = mData->mSession.mDirectControl;
14569 }
14570
14571 /* fail on notifications sent after #OnSessionEnd() is called, it is
14572 * expected by the caller */
14573 if (!directControl)
14574 return E_FAIL;
14575
14576 /* No locks should be held at this point. */
14577 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14578 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14579
14580 return directControl->OnUSBDeviceDetach(aId, aError);
14581}
14582
14583// protected methods
14584/////////////////////////////////////////////////////////////////////////////
14585
14586/**
14587 * Helper method to finalize saving the state.
14588 *
14589 * @note Must be called from under this object's lock.
14590 *
14591 * @param aRc S_OK if the snapshot has been taken successfully
14592 * @param aErrMsg human readable error message for failure
14593 *
14594 * @note Locks mParent + this objects for writing.
14595 */
14596HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14597{
14598 LogFlowThisFuncEnter();
14599
14600 AutoCaller autoCaller(this);
14601 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14602
14603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14604
14605 HRESULT rc = S_OK;
14606
14607 if (SUCCEEDED(aRc))
14608 {
14609 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14610
14611 /* save all VM settings */
14612 rc = saveSettings(NULL);
14613 // no need to check whether VirtualBox.xml needs saving also since
14614 // we can't have a name change pending at this point
14615 }
14616 else
14617 {
14618 // delete the saved state file (it might have been already created);
14619 // we need not check whether this is shared with a snapshot here because
14620 // we certainly created this saved state file here anew
14621 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14622 }
14623
14624 /* notify the progress object about operation completion */
14625 Assert(mConsoleTaskData.mProgress);
14626 if (SUCCEEDED(aRc))
14627 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14628 else
14629 {
14630 if (aErrMsg.length())
14631 mConsoleTaskData.mProgress->notifyComplete(aRc,
14632 COM_IIDOF(ISession),
14633 getComponentName(),
14634 aErrMsg.c_str());
14635 else
14636 mConsoleTaskData.mProgress->notifyComplete(aRc);
14637 }
14638
14639 /* clear out the temporary saved state data */
14640 mConsoleTaskData.mLastState = MachineState_Null;
14641 mConsoleTaskData.strStateFilePath.setNull();
14642 mConsoleTaskData.mProgress.setNull();
14643
14644 LogFlowThisFuncLeave();
14645 return rc;
14646}
14647
14648/**
14649 * Deletes the given file if it is no longer in use by either the current machine state
14650 * (if the machine is "saved") or any of the machine's snapshots.
14651 *
14652 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14653 * but is different for each SnapshotMachine. When calling this, the order of calling this
14654 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14655 * is therefore critical. I know, it's all rather messy.
14656 *
14657 * @param strStateFile
14658 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14659 */
14660void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14661 Snapshot *pSnapshotToIgnore)
14662{
14663 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14664 if ( (strStateFile.isNotEmpty())
14665 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14666 )
14667 // ... and it must also not be shared with other snapshots
14668 if ( !mData->mFirstSnapshot
14669 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14670 // this checks the SnapshotMachine's state file paths
14671 )
14672 RTFileDelete(strStateFile.c_str());
14673}
14674
14675/**
14676 * Locks the attached media.
14677 *
14678 * All attached hard disks are locked for writing and DVD/floppy are locked for
14679 * reading. Parents of attached hard disks (if any) are locked for reading.
14680 *
14681 * This method also performs accessibility check of all media it locks: if some
14682 * media is inaccessible, the method will return a failure and a bunch of
14683 * extended error info objects per each inaccessible medium.
14684 *
14685 * Note that this method is atomic: if it returns a success, all media are
14686 * locked as described above; on failure no media is locked at all (all
14687 * succeeded individual locks will be undone).
14688 *
14689 * The caller is responsible for doing the necessary state sanity checks.
14690 *
14691 * The locks made by this method must be undone by calling #unlockMedia() when
14692 * no more needed.
14693 */
14694HRESULT SessionMachine::lockMedia()
14695{
14696 AutoCaller autoCaller(this);
14697 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14698
14699 AutoMultiWriteLock2 alock(this->lockHandle(),
14700 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14701
14702 /* bail out if trying to lock things with already set up locking */
14703 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14704
14705 MultiResult mrc(S_OK);
14706
14707 /* Collect locking information for all medium objects attached to the VM. */
14708 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14709 it != mMediaData->mAttachments.end();
14710 ++it)
14711 {
14712 MediumAttachment* pAtt = *it;
14713 DeviceType_T devType = pAtt->i_getType();
14714 Medium *pMedium = pAtt->i_getMedium();
14715
14716 MediumLockList *pMediumLockList(new MediumLockList());
14717 // There can be attachments without a medium (floppy/dvd), and thus
14718 // it's impossible to create a medium lock list. It still makes sense
14719 // to have the empty medium lock list in the map in case a medium is
14720 // attached later.
14721 if (pMedium != NULL)
14722 {
14723 MediumType_T mediumType = pMedium->i_getType();
14724 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14725 || mediumType == MediumType_Shareable;
14726 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14727
14728 alock.release();
14729 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14730 !fIsReadOnlyLock /* fMediumLockWrite */,
14731 NULL,
14732 *pMediumLockList);
14733 alock.acquire();
14734 if (FAILED(mrc))
14735 {
14736 delete pMediumLockList;
14737 mData->mSession.mLockedMedia.Clear();
14738 break;
14739 }
14740 }
14741
14742 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14743 if (FAILED(rc))
14744 {
14745 mData->mSession.mLockedMedia.Clear();
14746 mrc = setError(rc,
14747 tr("Collecting locking information for all attached media failed"));
14748 break;
14749 }
14750 }
14751
14752 if (SUCCEEDED(mrc))
14753 {
14754 /* Now lock all media. If this fails, nothing is locked. */
14755 alock.release();
14756 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14757 alock.acquire();
14758 if (FAILED(rc))
14759 {
14760 mrc = setError(rc,
14761 tr("Locking of attached media failed"));
14762 }
14763 }
14764
14765 return mrc;
14766}
14767
14768/**
14769 * Undoes the locks made by by #lockMedia().
14770 */
14771void SessionMachine::unlockMedia()
14772{
14773 AutoCaller autoCaller(this);
14774 AssertComRCReturnVoid(autoCaller.rc());
14775
14776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14777
14778 /* we may be holding important error info on the current thread;
14779 * preserve it */
14780 ErrorInfoKeeper eik;
14781
14782 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14783 AssertComRC(rc);
14784}
14785
14786/**
14787 * Helper to change the machine state (reimplementation).
14788 *
14789 * @note Locks this object for writing.
14790 * @note This method must not call saveSettings or SaveSettings, otherwise
14791 * it can cause crashes in random places due to unexpectedly committing
14792 * the current settings. The caller is responsible for that. The call
14793 * to saveStateSettings is fine, because this method does not commit.
14794 */
14795HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14796{
14797 LogFlowThisFuncEnter();
14798 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14799
14800 AutoCaller autoCaller(this);
14801 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14802
14803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14804
14805 MachineState_T oldMachineState = mData->mMachineState;
14806
14807 AssertMsgReturn(oldMachineState != aMachineState,
14808 ("oldMachineState=%s, aMachineState=%s\n",
14809 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14810 E_FAIL);
14811
14812 HRESULT rc = S_OK;
14813
14814 int stsFlags = 0;
14815 bool deleteSavedState = false;
14816
14817 /* detect some state transitions */
14818
14819 if ( ( oldMachineState == MachineState_Saved
14820 && aMachineState == MachineState_Restoring)
14821 || ( ( oldMachineState == MachineState_PoweredOff
14822 || oldMachineState == MachineState_Teleported
14823 || oldMachineState == MachineState_Aborted
14824 )
14825 && ( aMachineState == MachineState_TeleportingIn
14826 || aMachineState == MachineState_Starting
14827 )
14828 )
14829 )
14830 {
14831 /* The EMT thread is about to start */
14832
14833 /* Nothing to do here for now... */
14834
14835 /// @todo NEWMEDIA don't let mDVDDrive and other children
14836 /// change anything when in the Starting/Restoring state
14837 }
14838 else if ( ( oldMachineState == MachineState_Running
14839 || oldMachineState == MachineState_Paused
14840 || oldMachineState == MachineState_Teleporting
14841 || oldMachineState == MachineState_LiveSnapshotting
14842 || oldMachineState == MachineState_Stuck
14843 || oldMachineState == MachineState_Starting
14844 || oldMachineState == MachineState_Stopping
14845 || oldMachineState == MachineState_Saving
14846 || oldMachineState == MachineState_Restoring
14847 || oldMachineState == MachineState_TeleportingPausedVM
14848 || oldMachineState == MachineState_TeleportingIn
14849 )
14850 && ( aMachineState == MachineState_PoweredOff
14851 || aMachineState == MachineState_Saved
14852 || aMachineState == MachineState_Teleported
14853 || aMachineState == MachineState_Aborted
14854 )
14855 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14856 * snapshot */
14857 && ( mConsoleTaskData.mSnapshot.isNull()
14858 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14859 )
14860 )
14861 {
14862 /* The EMT thread has just stopped, unlock attached media. Note that as
14863 * opposed to locking that is done from Console, we do unlocking here
14864 * because the VM process may have aborted before having a chance to
14865 * properly unlock all media it locked. */
14866
14867 unlockMedia();
14868 }
14869
14870 if (oldMachineState == MachineState_Restoring)
14871 {
14872 if (aMachineState != MachineState_Saved)
14873 {
14874 /*
14875 * delete the saved state file once the machine has finished
14876 * restoring from it (note that Console sets the state from
14877 * Restoring to Saved if the VM couldn't restore successfully,
14878 * to give the user an ability to fix an error and retry --
14879 * we keep the saved state file in this case)
14880 */
14881 deleteSavedState = true;
14882 }
14883 }
14884 else if ( oldMachineState == MachineState_Saved
14885 && ( aMachineState == MachineState_PoweredOff
14886 || aMachineState == MachineState_Aborted
14887 || aMachineState == MachineState_Teleported
14888 )
14889 )
14890 {
14891 /*
14892 * delete the saved state after Console::ForgetSavedState() is called
14893 * or if the VM process (owning a direct VM session) crashed while the
14894 * VM was Saved
14895 */
14896
14897 /// @todo (dmik)
14898 // Not sure that deleting the saved state file just because of the
14899 // client death before it attempted to restore the VM is a good
14900 // thing. But when it crashes we need to go to the Aborted state
14901 // which cannot have the saved state file associated... The only
14902 // way to fix this is to make the Aborted condition not a VM state
14903 // but a bool flag: i.e., when a crash occurs, set it to true and
14904 // change the state to PoweredOff or Saved depending on the
14905 // saved state presence.
14906
14907 deleteSavedState = true;
14908 mData->mCurrentStateModified = TRUE;
14909 stsFlags |= SaveSTS_CurStateModified;
14910 }
14911
14912 if ( aMachineState == MachineState_Starting
14913 || aMachineState == MachineState_Restoring
14914 || aMachineState == MachineState_TeleportingIn
14915 )
14916 {
14917 /* set the current state modified flag to indicate that the current
14918 * state is no more identical to the state in the
14919 * current snapshot */
14920 if (!mData->mCurrentSnapshot.isNull())
14921 {
14922 mData->mCurrentStateModified = TRUE;
14923 stsFlags |= SaveSTS_CurStateModified;
14924 }
14925 }
14926
14927 if (deleteSavedState)
14928 {
14929 if (mRemoveSavedState)
14930 {
14931 Assert(!mSSData->strStateFilePath.isEmpty());
14932
14933 // it is safe to delete the saved state file if ...
14934 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14935 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14936 // ... none of the snapshots share the saved state file
14937 )
14938 RTFileDelete(mSSData->strStateFilePath.c_str());
14939 }
14940
14941 mSSData->strStateFilePath.setNull();
14942 stsFlags |= SaveSTS_StateFilePath;
14943 }
14944
14945 /* redirect to the underlying peer machine */
14946 mPeer->setMachineState(aMachineState);
14947
14948 if ( aMachineState == MachineState_PoweredOff
14949 || aMachineState == MachineState_Teleported
14950 || aMachineState == MachineState_Aborted
14951 || aMachineState == MachineState_Saved)
14952 {
14953 /* the machine has stopped execution
14954 * (or the saved state file was adopted) */
14955 stsFlags |= SaveSTS_StateTimeStamp;
14956 }
14957
14958 if ( ( oldMachineState == MachineState_PoweredOff
14959 || oldMachineState == MachineState_Aborted
14960 || oldMachineState == MachineState_Teleported
14961 )
14962 && aMachineState == MachineState_Saved)
14963 {
14964 /* the saved state file was adopted */
14965 Assert(!mSSData->strStateFilePath.isEmpty());
14966 stsFlags |= SaveSTS_StateFilePath;
14967 }
14968
14969#ifdef VBOX_WITH_GUEST_PROPS
14970 if ( aMachineState == MachineState_PoweredOff
14971 || aMachineState == MachineState_Aborted
14972 || aMachineState == MachineState_Teleported)
14973 {
14974 /* Make sure any transient guest properties get removed from the
14975 * property store on shutdown. */
14976
14977 HWData::GuestPropertyMap::const_iterator it;
14978 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14979 if (!fNeedsSaving)
14980 for (it = mHWData->mGuestProperties.begin();
14981 it != mHWData->mGuestProperties.end(); ++it)
14982 if ( (it->second.mFlags & guestProp::TRANSIENT)
14983 || (it->second.mFlags & guestProp::TRANSRESET))
14984 {
14985 fNeedsSaving = true;
14986 break;
14987 }
14988 if (fNeedsSaving)
14989 {
14990 mData->mCurrentStateModified = TRUE;
14991 stsFlags |= SaveSTS_CurStateModified;
14992 }
14993 }
14994#endif
14995
14996 rc = saveStateSettings(stsFlags);
14997
14998 if ( ( oldMachineState != MachineState_PoweredOff
14999 && oldMachineState != MachineState_Aborted
15000 && oldMachineState != MachineState_Teleported
15001 )
15002 && ( aMachineState == MachineState_PoweredOff
15003 || aMachineState == MachineState_Aborted
15004 || aMachineState == MachineState_Teleported
15005 )
15006 )
15007 {
15008 /* we've been shut down for any reason */
15009 /* no special action so far */
15010 }
15011
15012 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15013 LogFlowThisFuncLeave();
15014 return rc;
15015}
15016
15017/**
15018 * Sends the current machine state value to the VM process.
15019 *
15020 * @note Locks this object for reading, then calls a client process.
15021 */
15022HRESULT SessionMachine::updateMachineStateOnClient()
15023{
15024 AutoCaller autoCaller(this);
15025 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15026
15027 ComPtr<IInternalSessionControl> directControl;
15028 {
15029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15030 AssertReturn(!!mData, E_FAIL);
15031 directControl = mData->mSession.mDirectControl;
15032
15033 /* directControl may be already set to NULL here in #OnSessionEnd()
15034 * called too early by the direct session process while there is still
15035 * some operation (like deleting the snapshot) in progress. The client
15036 * process in this case is waiting inside Session::close() for the
15037 * "end session" process object to complete, while #uninit() called by
15038 * #checkForDeath() on the Watcher thread is waiting for the pending
15039 * operation to complete. For now, we accept this inconsistent behavior
15040 * and simply do nothing here. */
15041
15042 if (mData->mSession.mState == SessionState_Unlocking)
15043 return S_OK;
15044
15045 AssertReturn(!directControl.isNull(), E_FAIL);
15046 }
15047
15048 return directControl->UpdateMachineState(mData->mMachineState);
15049}
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