VirtualBox

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

Last change on this file since 48366 was 48299, checked in by vboxsync, 11 years ago

SessionImpl.cpp: VBoxClient-x86.so fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 500.2 KB
Line 
1/* $Id: MachineImpl.cpp 48299 2013-09-05 11:07:08Z 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 mHPETEnabled = false;
191
192 /* default boot order: floppy - DVD - HDD */
193 mBootOrder[0] = DeviceType_Floppy;
194 mBootOrder[1] = DeviceType_DVD;
195 mBootOrder[2] = DeviceType_HardDisk;
196 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
197 mBootOrder[i] = DeviceType_Null;
198
199 mClipboardMode = ClipboardMode_Disabled;
200 mDragAndDropMode = DragAndDropMode_Disabled;
201 mGuestPropertyNotificationPatterns = "";
202
203 mFirmwareType = FirmwareType_BIOS;
204 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
205 mPointingHIDType = PointingHIDType_PS2Mouse;
206 mChipsetType = ChipsetType_PIIX3;
207 mEmulatedUSBWebcamEnabled = FALSE;
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->id();
340
341 /* Apply BIOS defaults */
342 mBIOSSettings->applyDefaults(aOsType);
343
344 /* Apply network adapters defaults */
345 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
346 mNetworkAdapters[slot]->applyDefaults(aOsType);
347
348 /* Apply serial port defaults */
349 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
350 mSerialPorts[slot]->applyDefaults(aOsType);
351
352 /* Let the OS type select 64-bit ness. */
353 mHWData->mLongMode = aOsType->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.isValid())
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(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1633{
1634#ifdef VBOX_WITH_USB_VIDEO
1635 CheckComArgOutPointerValid(aEnabled);
1636
1637 AutoCaller autoCaller(this);
1638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1639
1640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1643
1644 return S_OK;
1645#else
1646 NOREF(aEnabled);
1647 return E_NOTIMPL;
1648#endif
1649}
1650
1651STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1652{
1653#ifdef VBOX_WITH_USB_VIDEO
1654 AutoCaller autoCaller(this);
1655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1657
1658 HRESULT rc = checkStateDependency(MutableStateDep);
1659 if (FAILED(rc)) return rc;
1660
1661 setModified(IsModified_MachineData);
1662 mHWData.backup();
1663 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1664
1665 return S_OK;
1666#else
1667 NOREF(aEnabled);
1668 return E_NOTIMPL;
1669#endif
1670}
1671
1672STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1673{
1674 CheckComArgOutPointerValid(aEnabled);
1675
1676 AutoCaller autoCaller(this);
1677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 *aEnabled = mHWData->mHPETEnabled;
1681
1682 return S_OK;
1683}
1684
1685STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1686{
1687 HRESULT rc = S_OK;
1688
1689 AutoCaller autoCaller(this);
1690 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1692
1693 rc = checkStateDependency(MutableStateDep);
1694 if (FAILED(rc)) return rc;
1695
1696 setModified(IsModified_MachineData);
1697 mHWData.backup();
1698
1699 mHWData->mHPETEnabled = aEnabled;
1700
1701 return rc;
1702}
1703
1704STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1705{
1706 AutoCaller autoCaller(this);
1707 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1708
1709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1710
1711 *fEnabled = mHWData->mVideoCaptureEnabled;
1712 return S_OK;
1713}
1714
1715STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1716{
1717 HRESULT rc = S_OK;
1718
1719 AutoCaller autoCaller(this);
1720 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 setModified(IsModified_MachineData);
1724 mHWData.backup();
1725 mHWData->mVideoCaptureEnabled = fEnabled;
1726
1727 alock.release();
1728 rc = onVideoCaptureChange();
1729 alock.acquire();
1730 if (FAILED(rc))
1731 {
1732 /*
1733 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1734 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1735 * determine if it should start or stop capturing. Therefore we need to manually
1736 * undo change.
1737 */
1738 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1739 return rc;
1740 }
1741
1742 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1743 if (Global::IsOnline(mData->mMachineState))
1744 saveSettings(NULL);
1745
1746 return rc;
1747}
1748
1749STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1750{
1751 CheckComArgOutSafeArrayPointerValid(aScreens);
1752
1753 AutoCaller autoCaller(this);
1754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1755
1756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1757
1758 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1759 for (unsigned i = 0; i < screens.size(); i++)
1760 screens[i] = mHWData->maVideoCaptureScreens[i];
1761 screens.detachTo(ComSafeArrayOutArg(aScreens));
1762 return S_OK;
1763}
1764
1765STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1766{
1767 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1768 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1769 bool fChanged = false;
1770
1771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1772
1773 for (unsigned i = 0; i < screens.size(); i++)
1774 {
1775 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1776 {
1777 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1778 fChanged = true;
1779 }
1780 }
1781 if (fChanged)
1782 {
1783 alock.release();
1784 HRESULT rc = onVideoCaptureChange();
1785 alock.acquire();
1786 if (FAILED(rc)) return rc;
1787 setModified(IsModified_MachineData);
1788
1789 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1790 if (Global::IsOnline(mData->mMachineState))
1791 saveSettings(NULL);
1792 }
1793
1794 return S_OK;
1795}
1796
1797STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1798{
1799 AutoCaller autoCaller(this);
1800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1801
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803 if (mHWData->mVideoCaptureFile.isEmpty())
1804 {
1805 Utf8Str defaultFile;
1806 getDefaultVideoCaptureFile(defaultFile);
1807 defaultFile.cloneTo(apFile);
1808 }
1809 else
1810 mHWData->mVideoCaptureFile.cloneTo(apFile);
1811 return S_OK;
1812}
1813
1814STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1815{
1816 Utf8Str strFile(aFile);
1817 AutoCaller autoCaller(this);
1818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1819
1820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1821
1822 if ( Global::IsOnline(mData->mMachineState)
1823 && mHWData->mVideoCaptureEnabled)
1824 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1825
1826 if (!RTPathStartsWithRoot(strFile.c_str()))
1827 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1828
1829 if (!strFile.isEmpty())
1830 {
1831 Utf8Str defaultFile;
1832 getDefaultVideoCaptureFile(defaultFile);
1833 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1834 strFile.setNull();
1835 }
1836
1837 setModified(IsModified_MachineData);
1838 mHWData.backup();
1839 mHWData->mVideoCaptureFile = strFile;
1840
1841 return S_OK;
1842}
1843
1844STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1845{
1846 AutoCaller autoCaller(this);
1847 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1848
1849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1850 *aHorzRes = mHWData->mVideoCaptureWidth;
1851 return S_OK;
1852}
1853
1854STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1855{
1856 AutoCaller autoCaller(this);
1857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1858
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 if ( Global::IsOnline(mData->mMachineState)
1862 && mHWData->mVideoCaptureEnabled)
1863 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1864
1865 setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mVideoCaptureWidth = aHorzRes;
1868
1869 return S_OK;
1870}
1871
1872STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1873{
1874 AutoCaller autoCaller(this);
1875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1876
1877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1878 *aVertRes = mHWData->mVideoCaptureHeight;
1879 return S_OK;
1880}
1881
1882STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1883{
1884 AutoCaller autoCaller(this);
1885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1886
1887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 if ( Global::IsOnline(mData->mMachineState)
1890 && mHWData->mVideoCaptureEnabled)
1891 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1892
1893 setModified(IsModified_MachineData);
1894 mHWData.backup();
1895 mHWData->mVideoCaptureHeight = aVertRes;
1896
1897 return S_OK;
1898}
1899
1900STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1901{
1902 AutoCaller autoCaller(this);
1903 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1904
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906 *aRate = mHWData->mVideoCaptureRate;
1907 return S_OK;
1908}
1909
1910STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1911{
1912 AutoCaller autoCaller(this);
1913 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1914
1915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1916
1917 if ( Global::IsOnline(mData->mMachineState)
1918 && mHWData->mVideoCaptureEnabled)
1919 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1920
1921 setModified(IsModified_MachineData);
1922 mHWData.backup();
1923 mHWData->mVideoCaptureRate = aRate;
1924
1925 return S_OK;
1926}
1927
1928STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1929{
1930 AutoCaller autoCaller(this);
1931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1932
1933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1934 *aFPS = mHWData->mVideoCaptureFPS;
1935 return S_OK;
1936}
1937
1938STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1939{
1940 AutoCaller autoCaller(this);
1941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1942
1943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1944
1945 if ( Global::IsOnline(mData->mMachineState)
1946 && mHWData->mVideoCaptureEnabled)
1947 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1948
1949 setModified(IsModified_MachineData);
1950 mHWData.backup();
1951 mHWData->mVideoCaptureFPS = aFPS;
1952
1953 return S_OK;
1954}
1955
1956STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1957{
1958 CheckComArgOutPointerValid(aGraphicsControllerType);
1959
1960 AutoCaller autoCaller(this);
1961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1962
1963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1964
1965 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1966
1967 return S_OK;
1968}
1969
1970STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1971{
1972 switch (aGraphicsControllerType)
1973 {
1974 case GraphicsControllerType_Null:
1975 case GraphicsControllerType_VBoxVGA:
1976 break;
1977 default:
1978 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1979 }
1980
1981 AutoCaller autoCaller(this);
1982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1983
1984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1985
1986 HRESULT rc = checkStateDependency(MutableStateDep);
1987 if (FAILED(rc)) return rc;
1988
1989 setModified(IsModified_MachineData);
1990 mHWData.backup();
1991 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1992
1993 return S_OK;
1994}
1995
1996STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1997{
1998 CheckComArgOutPointerValid(memorySize);
1999
2000 AutoCaller autoCaller(this);
2001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2002
2003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2004
2005 *memorySize = mHWData->mVRAMSize;
2006
2007 return S_OK;
2008}
2009
2010STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2011{
2012 /* check VRAM limits */
2013 if (memorySize < SchemaDefs::MinGuestVRAM ||
2014 memorySize > SchemaDefs::MaxGuestVRAM)
2015 return setError(E_INVALIDARG,
2016 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2017 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2018
2019 AutoCaller autoCaller(this);
2020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2021
2022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2023
2024 HRESULT rc = checkStateDependency(MutableStateDep);
2025 if (FAILED(rc)) return rc;
2026
2027 setModified(IsModified_MachineData);
2028 mHWData.backup();
2029 mHWData->mVRAMSize = memorySize;
2030
2031 return S_OK;
2032}
2033
2034/** @todo this method should not be public */
2035STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2036{
2037 CheckComArgOutPointerValid(memoryBalloonSize);
2038
2039 AutoCaller autoCaller(this);
2040 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2041
2042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2045
2046 return S_OK;
2047}
2048
2049/**
2050 * Set the memory balloon size.
2051 *
2052 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2053 * we have to make sure that we never call IGuest from here.
2054 */
2055STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2056{
2057 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2058#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2059 /* check limits */
2060 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2061 return setError(E_INVALIDARG,
2062 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2063 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2064
2065 AutoCaller autoCaller(this);
2066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2067
2068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2069
2070 setModified(IsModified_MachineData);
2071 mHWData.backup();
2072 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2073
2074 return S_OK;
2075#else
2076 NOREF(memoryBalloonSize);
2077 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2078#endif
2079}
2080
2081STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2082{
2083 CheckComArgOutPointerValid(aEnabled);
2084
2085 AutoCaller autoCaller(this);
2086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2087
2088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2089
2090 *aEnabled = mHWData->mPageFusionEnabled;
2091 return S_OK;
2092}
2093
2094STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2095{
2096#ifdef VBOX_WITH_PAGE_SHARING
2097 AutoCaller autoCaller(this);
2098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2099
2100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2103 setModified(IsModified_MachineData);
2104 mHWData.backup();
2105 mHWData->mPageFusionEnabled = aEnabled;
2106 return S_OK;
2107#else
2108 NOREF(aEnabled);
2109 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2110#endif
2111}
2112
2113STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2114{
2115 CheckComArgOutPointerValid(aEnabled);
2116
2117 AutoCaller autoCaller(this);
2118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2119
2120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2121
2122 *aEnabled = mHWData->mAccelerate3DEnabled;
2123
2124 return S_OK;
2125}
2126
2127STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2128{
2129 AutoCaller autoCaller(this);
2130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2131
2132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 HRESULT rc = checkStateDependency(MutableStateDep);
2135 if (FAILED(rc)) return rc;
2136
2137 /** @todo check validity! */
2138
2139 setModified(IsModified_MachineData);
2140 mHWData.backup();
2141 mHWData->mAccelerate3DEnabled = enable;
2142
2143 return S_OK;
2144}
2145
2146
2147STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2148{
2149 CheckComArgOutPointerValid(aEnabled);
2150
2151 AutoCaller autoCaller(this);
2152 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2153
2154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2155
2156 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2157
2158 return S_OK;
2159}
2160
2161STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2162{
2163 AutoCaller autoCaller(this);
2164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2165
2166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2167
2168 HRESULT rc = checkStateDependency(MutableStateDep);
2169 if (FAILED(rc)) return rc;
2170
2171 /** @todo check validity! */
2172
2173 setModified(IsModified_MachineData);
2174 mHWData.backup();
2175 mHWData->mAccelerate2DVideoEnabled = enable;
2176
2177 return S_OK;
2178}
2179
2180STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2181{
2182 CheckComArgOutPointerValid(monitorCount);
2183
2184 AutoCaller autoCaller(this);
2185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2186
2187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2188
2189 *monitorCount = mHWData->mMonitorCount;
2190
2191 return S_OK;
2192}
2193
2194STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2195{
2196 /* make sure monitor count is a sensible number */
2197 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2198 return setError(E_INVALIDARG,
2199 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2200 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2201
2202 AutoCaller autoCaller(this);
2203 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2204
2205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2206
2207 HRESULT rc = checkStateDependency(MutableStateDep);
2208 if (FAILED(rc)) return rc;
2209
2210 setModified(IsModified_MachineData);
2211 mHWData.backup();
2212 mHWData->mMonitorCount = monitorCount;
2213
2214 return S_OK;
2215}
2216
2217STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2218{
2219 CheckComArgOutPointerValid(biosSettings);
2220
2221 AutoCaller autoCaller(this);
2222 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2223
2224 /* mBIOSSettings is constant during life time, no need to lock */
2225 mBIOSSettings.queryInterfaceTo(biosSettings);
2226
2227 return S_OK;
2228}
2229
2230STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2231{
2232 CheckComArgOutPointerValid(aVal);
2233
2234 AutoCaller autoCaller(this);
2235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2236
2237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2238
2239 switch (property)
2240 {
2241 case CPUPropertyType_PAE:
2242 *aVal = mHWData->mPAEEnabled;
2243 break;
2244
2245 case CPUPropertyType_Synthetic:
2246 *aVal = mHWData->mSyntheticCpu;
2247 break;
2248
2249 case CPUPropertyType_LongMode:
2250 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2251 *aVal = TRUE;
2252 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2253 *aVal = FALSE;
2254#if HC_ARCH_BITS == 64
2255 else
2256 *aVal = TRUE;
2257#else
2258 else
2259 {
2260 *aVal = FALSE;
2261
2262 ComPtr<IGuestOSType> ptrGuestOSType;
2263 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2264 if (SUCCEEDED(hrc2))
2265 {
2266 BOOL fIs64Bit = FALSE;
2267 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2268 if (SUCCEEDED(hrc2) && fIs64Bit)
2269 {
2270 ComObjPtr<Host> ptrHost = mParent->host();
2271 alock.release();
2272
2273 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2274 if (FAILED(hrc2))
2275 *aVal = FALSE;
2276 }
2277 }
2278 }
2279#endif
2280 break;
2281
2282 default:
2283 return E_INVALIDARG;
2284 }
2285 return S_OK;
2286}
2287
2288STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2289{
2290 AutoCaller autoCaller(this);
2291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2292
2293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2294
2295 HRESULT rc = checkStateDependency(MutableStateDep);
2296 if (FAILED(rc)) return rc;
2297
2298 switch (property)
2299 {
2300 case CPUPropertyType_PAE:
2301 setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mPAEEnabled = !!aVal;
2304 break;
2305
2306 case CPUPropertyType_Synthetic:
2307 setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 mHWData->mSyntheticCpu = !!aVal;
2310 break;
2311
2312 case CPUPropertyType_LongMode:
2313 setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2316 break;
2317
2318 default:
2319 return E_INVALIDARG;
2320 }
2321 return S_OK;
2322}
2323
2324STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2325{
2326 CheckComArgOutPointerValid(aValEax);
2327 CheckComArgOutPointerValid(aValEbx);
2328 CheckComArgOutPointerValid(aValEcx);
2329 CheckComArgOutPointerValid(aValEdx);
2330
2331 AutoCaller autoCaller(this);
2332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2333
2334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2335
2336 switch(aId)
2337 {
2338 case 0x0:
2339 case 0x1:
2340 case 0x2:
2341 case 0x3:
2342 case 0x4:
2343 case 0x5:
2344 case 0x6:
2345 case 0x7:
2346 case 0x8:
2347 case 0x9:
2348 case 0xA:
2349 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2350 return E_INVALIDARG;
2351
2352 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2353 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2354 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2355 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2356 break;
2357
2358 case 0x80000000:
2359 case 0x80000001:
2360 case 0x80000002:
2361 case 0x80000003:
2362 case 0x80000004:
2363 case 0x80000005:
2364 case 0x80000006:
2365 case 0x80000007:
2366 case 0x80000008:
2367 case 0x80000009:
2368 case 0x8000000A:
2369 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2370 return E_INVALIDARG;
2371
2372 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2373 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2374 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2375 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2376 break;
2377
2378 default:
2379 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2380 }
2381 return S_OK;
2382}
2383
2384STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2385{
2386 AutoCaller autoCaller(this);
2387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2388
2389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2390
2391 HRESULT rc = checkStateDependency(MutableStateDep);
2392 if (FAILED(rc)) return rc;
2393
2394 switch(aId)
2395 {
2396 case 0x0:
2397 case 0x1:
2398 case 0x2:
2399 case 0x3:
2400 case 0x4:
2401 case 0x5:
2402 case 0x6:
2403 case 0x7:
2404 case 0x8:
2405 case 0x9:
2406 case 0xA:
2407 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2408 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2409 setModified(IsModified_MachineData);
2410 mHWData.backup();
2411 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2412 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2413 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2414 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2415 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2416 break;
2417
2418 case 0x80000000:
2419 case 0x80000001:
2420 case 0x80000002:
2421 case 0x80000003:
2422 case 0x80000004:
2423 case 0x80000005:
2424 case 0x80000006:
2425 case 0x80000007:
2426 case 0x80000008:
2427 case 0x80000009:
2428 case 0x8000000A:
2429 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2430 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2431 setModified(IsModified_MachineData);
2432 mHWData.backup();
2433 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2434 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2435 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2436 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2437 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2438 break;
2439
2440 default:
2441 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2442 }
2443 return S_OK;
2444}
2445
2446STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2447{
2448 AutoCaller autoCaller(this);
2449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2450
2451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2452
2453 HRESULT rc = checkStateDependency(MutableStateDep);
2454 if (FAILED(rc)) return rc;
2455
2456 switch(aId)
2457 {
2458 case 0x0:
2459 case 0x1:
2460 case 0x2:
2461 case 0x3:
2462 case 0x4:
2463 case 0x5:
2464 case 0x6:
2465 case 0x7:
2466 case 0x8:
2467 case 0x9:
2468 case 0xA:
2469 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2470 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2471 setModified(IsModified_MachineData);
2472 mHWData.backup();
2473 /* Invalidate leaf. */
2474 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2475 break;
2476
2477 case 0x80000000:
2478 case 0x80000001:
2479 case 0x80000002:
2480 case 0x80000003:
2481 case 0x80000004:
2482 case 0x80000005:
2483 case 0x80000006:
2484 case 0x80000007:
2485 case 0x80000008:
2486 case 0x80000009:
2487 case 0x8000000A:
2488 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2489 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2490 setModified(IsModified_MachineData);
2491 mHWData.backup();
2492 /* Invalidate leaf. */
2493 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2494 break;
2495
2496 default:
2497 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2498 }
2499 return S_OK;
2500}
2501
2502STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2503{
2504 AutoCaller autoCaller(this);
2505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2506
2507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 HRESULT rc = checkStateDependency(MutableStateDep);
2510 if (FAILED(rc)) return rc;
2511
2512 setModified(IsModified_MachineData);
2513 mHWData.backup();
2514
2515 /* Invalidate all standard leafs. */
2516 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2517 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2518
2519 /* Invalidate all extended leafs. */
2520 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2521 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2522
2523 return S_OK;
2524}
2525
2526STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2527{
2528 CheckComArgOutPointerValid(aVal);
2529
2530 AutoCaller autoCaller(this);
2531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2532
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 switch(property)
2536 {
2537 case HWVirtExPropertyType_Enabled:
2538 *aVal = mHWData->mHWVirtExEnabled;
2539 break;
2540
2541 case HWVirtExPropertyType_VPID:
2542 *aVal = mHWData->mHWVirtExVPIDEnabled;
2543 break;
2544
2545 case HWVirtExPropertyType_NestedPaging:
2546 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2547 break;
2548
2549 case HWVirtExPropertyType_UnrestrictedExecution:
2550 *aVal = mHWData->mHWVirtExUXEnabled;
2551 break;
2552
2553 case HWVirtExPropertyType_LargePages:
2554 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2555#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2556 *aVal = FALSE;
2557#endif
2558 break;
2559
2560 case HWVirtExPropertyType_Force:
2561 *aVal = mHWData->mHWVirtExForceEnabled;
2562 break;
2563
2564 default:
2565 return E_INVALIDARG;
2566 }
2567 return S_OK;
2568}
2569
2570STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2571{
2572 AutoCaller autoCaller(this);
2573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2574
2575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 HRESULT rc = checkStateDependency(MutableStateDep);
2578 if (FAILED(rc)) return rc;
2579
2580 switch(property)
2581 {
2582 case HWVirtExPropertyType_Enabled:
2583 setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExEnabled = !!aVal;
2586 break;
2587
2588 case HWVirtExPropertyType_VPID:
2589 setModified(IsModified_MachineData);
2590 mHWData.backup();
2591 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2592 break;
2593
2594 case HWVirtExPropertyType_NestedPaging:
2595 setModified(IsModified_MachineData);
2596 mHWData.backup();
2597 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2598 break;
2599
2600 case HWVirtExPropertyType_UnrestrictedExecution:
2601 setModified(IsModified_MachineData);
2602 mHWData.backup();
2603 mHWData->mHWVirtExUXEnabled = !!aVal;
2604 break;
2605
2606 case HWVirtExPropertyType_LargePages:
2607 setModified(IsModified_MachineData);
2608 mHWData.backup();
2609 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2610 break;
2611
2612 case HWVirtExPropertyType_Force:
2613 setModified(IsModified_MachineData);
2614 mHWData.backup();
2615 mHWData->mHWVirtExForceEnabled = !!aVal;
2616 break;
2617
2618 default:
2619 return E_INVALIDARG;
2620 }
2621
2622 return S_OK;
2623}
2624
2625STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2626{
2627 CheckComArgOutPointerValid(aSnapshotFolder);
2628
2629 AutoCaller autoCaller(this);
2630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2631
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 Utf8Str strFullSnapshotFolder;
2635 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2636 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2637
2638 return S_OK;
2639}
2640
2641STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2642{
2643 /* @todo (r=dmik):
2644 * 1. Allow to change the name of the snapshot folder containing snapshots
2645 * 2. Rename the folder on disk instead of just changing the property
2646 * value (to be smart and not to leave garbage). Note that it cannot be
2647 * done here because the change may be rolled back. Thus, the right
2648 * place is #saveSettings().
2649 */
2650
2651 AutoCaller autoCaller(this);
2652 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2653
2654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 HRESULT rc = checkStateDependency(MutableStateDep);
2657 if (FAILED(rc)) return rc;
2658
2659 if (!mData->mCurrentSnapshot.isNull())
2660 return setError(E_FAIL,
2661 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2662
2663 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2664
2665 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2666 if (strSnapshotFolder.isEmpty())
2667 strSnapshotFolder = "Snapshots";
2668 int vrc = calculateFullPath(strSnapshotFolder,
2669 strSnapshotFolder);
2670 if (RT_FAILURE(vrc))
2671 return setError(E_FAIL,
2672 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2673 aSnapshotFolder, vrc);
2674
2675 setModified(IsModified_MachineData);
2676 mUserData.backup();
2677
2678 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2679
2680 return S_OK;
2681}
2682
2683STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2684{
2685 CheckComArgOutSafeArrayPointerValid(aAttachments);
2686
2687 AutoCaller autoCaller(this);
2688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2689
2690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2693 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2694
2695 return S_OK;
2696}
2697
2698STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2699{
2700 CheckComArgOutPointerValid(vrdeServer);
2701
2702 AutoCaller autoCaller(this);
2703 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2704
2705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
2707 Assert(!!mVRDEServer);
2708 mVRDEServer.queryInterfaceTo(vrdeServer);
2709
2710 return S_OK;
2711}
2712
2713STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2714{
2715 CheckComArgOutPointerValid(audioAdapter);
2716
2717 AutoCaller autoCaller(this);
2718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2719
2720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 mAudioAdapter.queryInterfaceTo(audioAdapter);
2723 return S_OK;
2724}
2725
2726STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2727{
2728#ifdef VBOX_WITH_VUSB
2729 CheckComArgOutPointerValid(aUSBControllers);
2730
2731 AutoCaller autoCaller(this);
2732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2733
2734 clearError();
2735 MultiResult rc(S_OK);
2736
2737# ifdef VBOX_WITH_USB
2738 rc = mParent->host()->checkUSBProxyService();
2739 if (FAILED(rc)) return rc;
2740# endif
2741
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2745 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2746 return S_OK;
2747#else
2748 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2749 * extended error info to indicate that USB is simply not available
2750 * (w/o treating it as a failure), for example, as in OSE */
2751 NOREF(aUSBControllers);
2752 ReturnComNotImplemented();
2753#endif /* VBOX_WITH_VUSB */
2754}
2755
2756STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2757{
2758#ifdef VBOX_WITH_VUSB
2759 CheckComArgOutPointerValid(aUSBDeviceFilters);
2760
2761 AutoCaller autoCaller(this);
2762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2763
2764 clearError();
2765 MultiResult rc(S_OK);
2766
2767# ifdef VBOX_WITH_USB
2768 rc = mParent->host()->checkUSBProxyService();
2769 if (FAILED(rc)) return rc;
2770# endif
2771
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2775#else
2776 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2777 * extended error info to indicate that USB is simply not available
2778 * (w/o treating it as a failure), for example, as in OSE */
2779 NOREF(aUSBDeviceFilters);
2780 ReturnComNotImplemented();
2781#endif /* VBOX_WITH_VUSB */
2782}
2783
2784STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2785{
2786 CheckComArgOutPointerValid(aFilePath);
2787
2788 AutoLimitedCaller autoCaller(this);
2789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2790
2791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2792
2793 mData->m_strConfigFileFull.cloneTo(aFilePath);
2794 return S_OK;
2795}
2796
2797STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2798{
2799 CheckComArgOutPointerValid(aModified);
2800
2801 AutoCaller autoCaller(this);
2802 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2803
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 HRESULT rc = checkStateDependency(MutableStateDep);
2807 if (FAILED(rc)) return rc;
2808
2809 if (!mData->pMachineConfigFile->fileExists())
2810 // this is a new machine, and no config file exists yet:
2811 *aModified = TRUE;
2812 else
2813 *aModified = (mData->flModifications != 0);
2814
2815 return S_OK;
2816}
2817
2818STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2819{
2820 CheckComArgOutPointerValid(aSessionState);
2821
2822 AutoCaller autoCaller(this);
2823 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2824
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 *aSessionState = mData->mSession.mState;
2828
2829 return S_OK;
2830}
2831
2832STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2833{
2834 CheckComArgOutPointerValid(aSessionType);
2835
2836 AutoCaller autoCaller(this);
2837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2838
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 mData->mSession.mType.cloneTo(aSessionType);
2842
2843 return S_OK;
2844}
2845
2846STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2847{
2848 CheckComArgOutPointerValid(aSessionPID);
2849
2850 AutoCaller autoCaller(this);
2851 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2852
2853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2854
2855 *aSessionPID = mData->mSession.mPID;
2856
2857 return S_OK;
2858}
2859
2860STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2861{
2862 CheckComArgOutPointerValid(machineState);
2863
2864 AutoCaller autoCaller(this);
2865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2866
2867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 *machineState = mData->mMachineState;
2870
2871 return S_OK;
2872}
2873
2874STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2875{
2876 CheckComArgOutPointerValid(aLastStateChange);
2877
2878 AutoCaller autoCaller(this);
2879 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2880
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882
2883 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2884
2885 return S_OK;
2886}
2887
2888STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2889{
2890 CheckComArgOutPointerValid(aStateFilePath);
2891
2892 AutoCaller autoCaller(this);
2893 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2894
2895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2896
2897 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2898
2899 return S_OK;
2900}
2901
2902STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2903{
2904 CheckComArgOutPointerValid(aLogFolder);
2905
2906 AutoCaller autoCaller(this);
2907 AssertComRCReturnRC(autoCaller.rc());
2908
2909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2910
2911 Utf8Str logFolder;
2912 getLogFolder(logFolder);
2913 logFolder.cloneTo(aLogFolder);
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2919{
2920 CheckComArgOutPointerValid(aCurrentSnapshot);
2921
2922 AutoCaller autoCaller(this);
2923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2924
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2928
2929 return S_OK;
2930}
2931
2932STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2933{
2934 CheckComArgOutPointerValid(aSnapshotCount);
2935
2936 AutoCaller autoCaller(this);
2937 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2938
2939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2940
2941 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2942 ? 0
2943 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2944
2945 return S_OK;
2946}
2947
2948STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2949{
2950 CheckComArgOutPointerValid(aCurrentStateModified);
2951
2952 AutoCaller autoCaller(this);
2953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2954
2955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2956
2957 /* Note: for machines with no snapshots, we always return FALSE
2958 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2959 * reasons :) */
2960
2961 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2962 ? FALSE
2963 : mData->mCurrentStateModified;
2964
2965 return S_OK;
2966}
2967
2968STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2969{
2970 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2971
2972 AutoCaller autoCaller(this);
2973 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2974
2975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2978 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2979
2980 return S_OK;
2981}
2982
2983STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2984{
2985 CheckComArgOutPointerValid(aClipboardMode);
2986
2987 AutoCaller autoCaller(this);
2988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2989
2990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2991
2992 *aClipboardMode = mHWData->mClipboardMode;
2993
2994 return S_OK;
2995}
2996
2997STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2998{
2999 HRESULT rc = S_OK;
3000
3001 AutoCaller autoCaller(this);
3002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3003
3004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3005
3006 alock.release();
3007 rc = onClipboardModeChange(aClipboardMode);
3008 alock.acquire();
3009 if (FAILED(rc)) return rc;
3010
3011 setModified(IsModified_MachineData);
3012 mHWData.backup();
3013 mHWData->mClipboardMode = aClipboardMode;
3014
3015 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3016 if (Global::IsOnline(mData->mMachineState))
3017 saveSettings(NULL);
3018
3019 return S_OK;
3020}
3021
3022STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3023{
3024 CheckComArgOutPointerValid(aDragAndDropMode);
3025
3026 AutoCaller autoCaller(this);
3027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3028
3029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3030
3031 *aDragAndDropMode = mHWData->mDragAndDropMode;
3032
3033 return S_OK;
3034}
3035
3036STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3037{
3038 HRESULT rc = S_OK;
3039
3040 AutoCaller autoCaller(this);
3041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3042
3043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3044
3045 alock.release();
3046 rc = onDragAndDropModeChange(aDragAndDropMode);
3047 alock.acquire();
3048 if (FAILED(rc)) return rc;
3049
3050 setModified(IsModified_MachineData);
3051 mHWData.backup();
3052 mHWData->mDragAndDropMode = aDragAndDropMode;
3053
3054 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3055 if (Global::IsOnline(mData->mMachineState))
3056 saveSettings(NULL);
3057
3058 return S_OK;
3059}
3060
3061STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3062{
3063 CheckComArgOutPointerValid(aPatterns);
3064
3065 AutoCaller autoCaller(this);
3066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3067
3068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3069
3070 try
3071 {
3072 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3073 }
3074 catch (...)
3075 {
3076 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3077 }
3078
3079 return S_OK;
3080}
3081
3082STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3083{
3084 AutoCaller autoCaller(this);
3085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3086
3087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3088
3089 HRESULT rc = checkStateDependency(MutableStateDep);
3090 if (FAILED(rc)) return rc;
3091
3092 setModified(IsModified_MachineData);
3093 mHWData.backup();
3094 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3095 return rc;
3096}
3097
3098STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3099{
3100 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3101
3102 AutoCaller autoCaller(this);
3103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3104
3105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3106
3107 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3108 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3109
3110 return S_OK;
3111}
3112
3113STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3114{
3115 CheckComArgOutPointerValid(aEnabled);
3116
3117 AutoCaller autoCaller(this);
3118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3119
3120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3121
3122 *aEnabled = mUserData->s.fTeleporterEnabled;
3123
3124 return S_OK;
3125}
3126
3127STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3128{
3129 AutoCaller autoCaller(this);
3130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3131
3132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3133
3134 /* Only allow it to be set to true when PoweredOff or Aborted.
3135 (Clearing it is always permitted.) */
3136 if ( aEnabled
3137 && mData->mRegistered
3138 && ( !isSessionMachine()
3139 || ( mData->mMachineState != MachineState_PoweredOff
3140 && mData->mMachineState != MachineState_Teleported
3141 && mData->mMachineState != MachineState_Aborted
3142 )
3143 )
3144 )
3145 return setError(VBOX_E_INVALID_VM_STATE,
3146 tr("The machine is not powered off (state is %s)"),
3147 Global::stringifyMachineState(mData->mMachineState));
3148
3149 setModified(IsModified_MachineData);
3150 mUserData.backup();
3151 mUserData->s.fTeleporterEnabled = !!aEnabled;
3152
3153 return S_OK;
3154}
3155
3156STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3157{
3158 CheckComArgOutPointerValid(aPort);
3159
3160 AutoCaller autoCaller(this);
3161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3162
3163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3164
3165 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3166
3167 return S_OK;
3168}
3169
3170STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3171{
3172 if (aPort >= _64K)
3173 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3174
3175 AutoCaller autoCaller(this);
3176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3177
3178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 HRESULT rc = checkStateDependency(MutableStateDep);
3181 if (FAILED(rc)) return rc;
3182
3183 setModified(IsModified_MachineData);
3184 mUserData.backup();
3185 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3186
3187 return S_OK;
3188}
3189
3190STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3191{
3192 CheckComArgOutPointerValid(aAddress);
3193
3194 AutoCaller autoCaller(this);
3195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3196
3197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3198
3199 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3200
3201 return S_OK;
3202}
3203
3204STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3205{
3206 AutoCaller autoCaller(this);
3207 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3208
3209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3210
3211 HRESULT rc = checkStateDependency(MutableStateDep);
3212 if (FAILED(rc)) return rc;
3213
3214 setModified(IsModified_MachineData);
3215 mUserData.backup();
3216 mUserData->s.strTeleporterAddress = aAddress;
3217
3218 return S_OK;
3219}
3220
3221STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3222{
3223 CheckComArgOutPointerValid(aPassword);
3224
3225 AutoCaller autoCaller(this);
3226 HRESULT hrc = autoCaller.rc();
3227 if (SUCCEEDED(hrc))
3228 {
3229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3230 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3231 }
3232
3233 return hrc;
3234}
3235
3236STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3237{
3238 /*
3239 * Hash the password first.
3240 */
3241 Utf8Str strPassword(aPassword);
3242 if (!strPassword.isEmpty())
3243 {
3244 if (VBoxIsPasswordHashed(&strPassword))
3245 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3246 VBoxHashPassword(&strPassword);
3247 }
3248
3249 /*
3250 * Do the update.
3251 */
3252 AutoCaller autoCaller(this);
3253 HRESULT hrc = autoCaller.rc();
3254 if (SUCCEEDED(hrc))
3255 {
3256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3257 hrc = checkStateDependency(MutableStateDep);
3258 if (SUCCEEDED(hrc))
3259 {
3260 setModified(IsModified_MachineData);
3261 mUserData.backup();
3262 mUserData->s.strTeleporterPassword = strPassword;
3263 }
3264 }
3265
3266 return hrc;
3267}
3268
3269STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3270{
3271 CheckComArgOutPointerValid(aState);
3272
3273 AutoCaller autoCaller(this);
3274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3275
3276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3277
3278 *aState = mUserData->s.enmFaultToleranceState;
3279 return S_OK;
3280}
3281
3282STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3283{
3284 AutoCaller autoCaller(this);
3285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3286
3287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3288
3289 /* @todo deal with running state change. */
3290 HRESULT rc = checkStateDependency(MutableStateDep);
3291 if (FAILED(rc)) return rc;
3292
3293 setModified(IsModified_MachineData);
3294 mUserData.backup();
3295 mUserData->s.enmFaultToleranceState = aState;
3296 return S_OK;
3297}
3298
3299STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3300{
3301 CheckComArgOutPointerValid(aAddress);
3302
3303 AutoCaller autoCaller(this);
3304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3305
3306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3307
3308 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3309 return S_OK;
3310}
3311
3312STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3313{
3314 AutoCaller autoCaller(this);
3315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3316
3317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3318
3319 /* @todo deal with running state change. */
3320 HRESULT rc = checkStateDependency(MutableStateDep);
3321 if (FAILED(rc)) return rc;
3322
3323 setModified(IsModified_MachineData);
3324 mUserData.backup();
3325 mUserData->s.strFaultToleranceAddress = aAddress;
3326 return S_OK;
3327}
3328
3329STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3330{
3331 CheckComArgOutPointerValid(aPort);
3332
3333 AutoCaller autoCaller(this);
3334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3335
3336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3337
3338 *aPort = mUserData->s.uFaultTolerancePort;
3339 return S_OK;
3340}
3341
3342STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3343{
3344 AutoCaller autoCaller(this);
3345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3346
3347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3348
3349 /* @todo deal with running state change. */
3350 HRESULT rc = checkStateDependency(MutableStateDep);
3351 if (FAILED(rc)) return rc;
3352
3353 setModified(IsModified_MachineData);
3354 mUserData.backup();
3355 mUserData->s.uFaultTolerancePort = aPort;
3356 return S_OK;
3357}
3358
3359STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3360{
3361 CheckComArgOutPointerValid(aPassword);
3362
3363 AutoCaller autoCaller(this);
3364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3365
3366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3367
3368 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3369
3370 return S_OK;
3371}
3372
3373STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3374{
3375 AutoCaller autoCaller(this);
3376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3377
3378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3379
3380 /* @todo deal with running state change. */
3381 HRESULT rc = checkStateDependency(MutableStateDep);
3382 if (FAILED(rc)) return rc;
3383
3384 setModified(IsModified_MachineData);
3385 mUserData.backup();
3386 mUserData->s.strFaultTolerancePassword = aPassword;
3387
3388 return S_OK;
3389}
3390
3391STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3392{
3393 CheckComArgOutPointerValid(aInterval);
3394
3395 AutoCaller autoCaller(this);
3396 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3397
3398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3399
3400 *aInterval = mUserData->s.uFaultToleranceInterval;
3401 return S_OK;
3402}
3403
3404STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3405{
3406 AutoCaller autoCaller(this);
3407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3408
3409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3410
3411 /* @todo deal with running state change. */
3412 HRESULT rc = checkStateDependency(MutableStateDep);
3413 if (FAILED(rc)) return rc;
3414
3415 setModified(IsModified_MachineData);
3416 mUserData.backup();
3417 mUserData->s.uFaultToleranceInterval = aInterval;
3418 return S_OK;
3419}
3420
3421STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3422{
3423 CheckComArgOutPointerValid(aEnabled);
3424
3425 AutoCaller autoCaller(this);
3426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3427
3428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3429
3430 *aEnabled = mUserData->s.fRTCUseUTC;
3431
3432 return S_OK;
3433}
3434
3435STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3436{
3437 AutoCaller autoCaller(this);
3438 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3439
3440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3441
3442 /* Only allow it to be set to true when PoweredOff or Aborted.
3443 (Clearing it is always permitted.) */
3444 if ( aEnabled
3445 && mData->mRegistered
3446 && ( !isSessionMachine()
3447 || ( mData->mMachineState != MachineState_PoweredOff
3448 && mData->mMachineState != MachineState_Teleported
3449 && mData->mMachineState != MachineState_Aborted
3450 )
3451 )
3452 )
3453 return setError(VBOX_E_INVALID_VM_STATE,
3454 tr("The machine is not powered off (state is %s)"),
3455 Global::stringifyMachineState(mData->mMachineState));
3456
3457 setModified(IsModified_MachineData);
3458 mUserData.backup();
3459 mUserData->s.fRTCUseUTC = !!aEnabled;
3460
3461 return S_OK;
3462}
3463
3464STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3465{
3466 CheckComArgOutPointerValid(aEnabled);
3467
3468 AutoCaller autoCaller(this);
3469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3470
3471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3472
3473 *aEnabled = mHWData->mIOCacheEnabled;
3474
3475 return S_OK;
3476}
3477
3478STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3479{
3480 AutoCaller autoCaller(this);
3481 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3482
3483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3484
3485 HRESULT rc = checkStateDependency(MutableStateDep);
3486 if (FAILED(rc)) return rc;
3487
3488 setModified(IsModified_MachineData);
3489 mHWData.backup();
3490 mHWData->mIOCacheEnabled = aEnabled;
3491
3492 return S_OK;
3493}
3494
3495STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3496{
3497 CheckComArgOutPointerValid(aIOCacheSize);
3498
3499 AutoCaller autoCaller(this);
3500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3501
3502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3503
3504 *aIOCacheSize = mHWData->mIOCacheSize;
3505
3506 return S_OK;
3507}
3508
3509STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3510{
3511 AutoCaller autoCaller(this);
3512 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3513
3514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3515
3516 HRESULT rc = checkStateDependency(MutableStateDep);
3517 if (FAILED(rc)) return rc;
3518
3519 setModified(IsModified_MachineData);
3520 mHWData.backup();
3521 mHWData->mIOCacheSize = aIOCacheSize;
3522
3523 return S_OK;
3524}
3525
3526
3527/**
3528 * @note Locks objects!
3529 */
3530STDMETHODIMP Machine::LockMachine(ISession *aSession,
3531 LockType_T lockType)
3532{
3533 CheckComArgNotNull(aSession);
3534
3535 AutoCaller autoCaller(this);
3536 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3537
3538 /* check the session state */
3539 SessionState_T state;
3540 HRESULT rc = aSession->COMGETTER(State)(&state);
3541 if (FAILED(rc)) return rc;
3542
3543 if (state != SessionState_Unlocked)
3544 return setError(VBOX_E_INVALID_OBJECT_STATE,
3545 tr("The given session is busy"));
3546
3547 // get the client's IInternalSessionControl interface
3548 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3549 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3550 E_INVALIDARG);
3551
3552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3553
3554 if (!mData->mRegistered)
3555 return setError(E_UNEXPECTED,
3556 tr("The machine '%s' is not registered"),
3557 mUserData->s.strName.c_str());
3558
3559 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3560
3561 SessionState_T oldState = mData->mSession.mState;
3562 /* Hack: in case the session is closing and there is a progress object
3563 * which allows waiting for the session to be closed, take the opportunity
3564 * and do a limited wait (max. 1 second). This helps a lot when the system
3565 * is busy and thus session closing can take a little while. */
3566 if ( mData->mSession.mState == SessionState_Unlocking
3567 && mData->mSession.mProgress)
3568 {
3569 alock.release();
3570 mData->mSession.mProgress->WaitForCompletion(1000);
3571 alock.acquire();
3572 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3573 }
3574
3575 // try again now
3576 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3577 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3578 )
3579 {
3580 // OK, share the session... we are now dealing with three processes:
3581 // 1) VBoxSVC (where this code runs);
3582 // 2) process C: the caller's client process (who wants a shared session);
3583 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3584
3585 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3586 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3587 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3588 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3589 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3590
3591 /*
3592 * Release the lock before calling the client process. It's safe here
3593 * since the only thing to do after we get the lock again is to add
3594 * the remote control to the list (which doesn't directly influence
3595 * anything).
3596 */
3597 alock.release();
3598
3599 // get the console of the session holding the write lock (this is a remote call)
3600 ComPtr<IConsole> pConsoleW;
3601 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3602 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3603 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3604 if (FAILED(rc))
3605 // the failure may occur w/o any error info (from RPC), so provide one
3606 return setError(VBOX_E_VM_ERROR,
3607 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3608
3609 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3610
3611 // share the session machine and W's console with the caller's session
3612 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3613 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3614 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3615
3616 if (FAILED(rc))
3617 // the failure may occur w/o any error info (from RPC), so provide one
3618 return setError(VBOX_E_VM_ERROR,
3619 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3620 alock.acquire();
3621
3622 // need to revalidate the state after acquiring the lock again
3623 if (mData->mSession.mState != SessionState_Locked)
3624 {
3625 pSessionControl->Uninitialize();
3626 return setError(VBOX_E_INVALID_SESSION_STATE,
3627 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3628 mUserData->s.strName.c_str());
3629 }
3630
3631 // add the caller's session to the list
3632 mData->mSession.mRemoteControls.push_back(pSessionControl);
3633 }
3634 else if ( mData->mSession.mState == SessionState_Locked
3635 || mData->mSession.mState == SessionState_Unlocking
3636 )
3637 {
3638 // sharing not permitted, or machine still unlocking:
3639 return setError(VBOX_E_INVALID_OBJECT_STATE,
3640 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3641 mUserData->s.strName.c_str());
3642 }
3643 else
3644 {
3645 // machine is not locked: then write-lock the machine (create the session machine)
3646
3647 // must not be busy
3648 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3649
3650 // get the caller's session PID
3651 RTPROCESS pid = NIL_RTPROCESS;
3652 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3653 pSessionControl->GetPID((ULONG*)&pid);
3654 Assert(pid != NIL_RTPROCESS);
3655
3656 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3657
3658 if (fLaunchingVMProcess)
3659 {
3660 // this machine is awaiting for a spawning session to be opened:
3661 // then the calling process must be the one that got started by
3662 // LaunchVMProcess()
3663
3664 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3665 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3666
3667 if (mData->mSession.mPID != pid)
3668 return setError(E_ACCESSDENIED,
3669 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3670 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3671 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3672 }
3673
3674 // create the mutable SessionMachine from the current machine
3675 ComObjPtr<SessionMachine> sessionMachine;
3676 sessionMachine.createObject();
3677 rc = sessionMachine->init(this);
3678 AssertComRC(rc);
3679
3680 /* NOTE: doing return from this function after this point but
3681 * before the end is forbidden since it may call SessionMachine::uninit()
3682 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3683 * lock while still holding the Machine lock in alock so that a deadlock
3684 * is possible due to the wrong lock order. */
3685
3686 if (SUCCEEDED(rc))
3687 {
3688 /*
3689 * Set the session state to Spawning to protect against subsequent
3690 * attempts to open a session and to unregister the machine after
3691 * we release the lock.
3692 */
3693 SessionState_T origState = mData->mSession.mState;
3694 mData->mSession.mState = SessionState_Spawning;
3695
3696 /* Get the client token ID to be passed to the client process */
3697 Utf8Str strTokenId;
3698 sessionMachine->getTokenId(strTokenId);
3699 Assert(!strTokenId.isEmpty());
3700
3701 /*
3702 * Release the lock before calling the client process -- it will call
3703 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3704 * because the state is Spawning, so that LaunchVMProcess() and
3705 * LockMachine() calls will fail. This method, called before we
3706 * acquire the lock again, will fail because of the wrong PID.
3707 *
3708 * Note that mData->mSession.mRemoteControls accessed outside
3709 * the lock may not be modified when state is Spawning, so it's safe.
3710 */
3711 alock.release();
3712
3713 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3714 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3715 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3716
3717 /* The failure may occur w/o any error info (from RPC), so provide one */
3718 if (FAILED(rc))
3719 setError(VBOX_E_VM_ERROR,
3720 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3721
3722 if ( SUCCEEDED(rc)
3723 && fLaunchingVMProcess
3724 )
3725 {
3726 /* complete the remote session initialization */
3727
3728 /* get the console from the direct session */
3729 ComPtr<IConsole> console;
3730 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3731 ComAssertComRC(rc);
3732
3733 if (SUCCEEDED(rc) && !console)
3734 {
3735 ComAssert(!!console);
3736 rc = E_FAIL;
3737 }
3738
3739 /* assign machine & console to the remote session */
3740 if (SUCCEEDED(rc))
3741 {
3742 /*
3743 * after LaunchVMProcess(), the first and the only
3744 * entry in remoteControls is that remote session
3745 */
3746 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3747 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3748 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3749
3750 /* The failure may occur w/o any error info (from RPC), so provide one */
3751 if (FAILED(rc))
3752 setError(VBOX_E_VM_ERROR,
3753 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3754 }
3755
3756 if (FAILED(rc))
3757 pSessionControl->Uninitialize();
3758 }
3759
3760 /* acquire the lock again */
3761 alock.acquire();
3762
3763 /* Restore the session state */
3764 mData->mSession.mState = origState;
3765 }
3766
3767 // finalize spawning anyway (this is why we don't return on errors above)
3768 if (fLaunchingVMProcess)
3769 {
3770 /* Note that the progress object is finalized later */
3771 /** @todo Consider checking mData->mSession.mProgress for cancellation
3772 * around here. */
3773
3774 /* We don't reset mSession.mPID here because it is necessary for
3775 * SessionMachine::uninit() to reap the child process later. */
3776
3777 if (FAILED(rc))
3778 {
3779 /* Close the remote session, remove the remote control from the list
3780 * and reset session state to Closed (@note keep the code in sync
3781 * with the relevant part in checkForSpawnFailure()). */
3782
3783 Assert(mData->mSession.mRemoteControls.size() == 1);
3784 if (mData->mSession.mRemoteControls.size() == 1)
3785 {
3786 ErrorInfoKeeper eik;
3787 mData->mSession.mRemoteControls.front()->Uninitialize();
3788 }
3789
3790 mData->mSession.mRemoteControls.clear();
3791 mData->mSession.mState = SessionState_Unlocked;
3792 }
3793 }
3794 else
3795 {
3796 /* memorize PID of the directly opened session */
3797 if (SUCCEEDED(rc))
3798 mData->mSession.mPID = pid;
3799 }
3800
3801 if (SUCCEEDED(rc))
3802 {
3803 /* memorize the direct session control and cache IUnknown for it */
3804 mData->mSession.mDirectControl = pSessionControl;
3805 mData->mSession.mState = SessionState_Locked;
3806 /* associate the SessionMachine with this Machine */
3807 mData->mSession.mMachine = sessionMachine;
3808
3809 /* request an IUnknown pointer early from the remote party for later
3810 * identity checks (it will be internally cached within mDirectControl
3811 * at least on XPCOM) */
3812 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3813 NOREF(unk);
3814 }
3815
3816 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3817 * would break the lock order */
3818 alock.release();
3819
3820 /* uninitialize the created session machine on failure */
3821 if (FAILED(rc))
3822 sessionMachine->uninit();
3823
3824 }
3825
3826 if (SUCCEEDED(rc))
3827 {
3828 /*
3829 * tell the client watcher thread to update the set of
3830 * machines that have open sessions
3831 */
3832 mParent->updateClientWatcher();
3833
3834 if (oldState != SessionState_Locked)
3835 /* fire an event */
3836 mParent->onSessionStateChange(getId(), SessionState_Locked);
3837 }
3838
3839 return rc;
3840}
3841
3842/**
3843 * @note Locks objects!
3844 */
3845STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3846 IN_BSTR aFrontend,
3847 IN_BSTR aEnvironment,
3848 IProgress **aProgress)
3849{
3850 CheckComArgStr(aFrontend);
3851 Utf8Str strFrontend(aFrontend);
3852 Utf8Str strEnvironment(aEnvironment);
3853 /* "emergencystop" doesn't need the session, so skip the checks/interface
3854 * retrieval. This code doesn't quite fit in here, but introducing a
3855 * special API method would be even more effort, and would require explicit
3856 * support by every API client. It's better to hide the feature a bit. */
3857 if (strFrontend != "emergencystop")
3858 CheckComArgNotNull(aSession);
3859 CheckComArgOutPointerValid(aProgress);
3860
3861 AutoCaller autoCaller(this);
3862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3863
3864 HRESULT rc = S_OK;
3865 if (strFrontend.isEmpty())
3866 {
3867 Bstr bstrFrontend;
3868 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3869 if (FAILED(rc))
3870 return rc;
3871 strFrontend = bstrFrontend;
3872 if (strFrontend.isEmpty())
3873 {
3874 ComPtr<ISystemProperties> systemProperties;
3875 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3876 if (FAILED(rc))
3877 return rc;
3878 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3879 if (FAILED(rc))
3880 return rc;
3881 strFrontend = bstrFrontend;
3882 }
3883 /* paranoia - emergencystop is not a valid default */
3884 if (strFrontend == "emergencystop")
3885 strFrontend = Utf8Str::Empty;
3886 }
3887 /* default frontend: Qt GUI */
3888 if (strFrontend.isEmpty())
3889 strFrontend = "GUI/Qt";
3890
3891 if (strFrontend != "emergencystop")
3892 {
3893 /* check the session state */
3894 SessionState_T state;
3895 rc = aSession->COMGETTER(State)(&state);
3896 if (FAILED(rc))
3897 return rc;
3898
3899 if (state != SessionState_Unlocked)
3900 return setError(VBOX_E_INVALID_OBJECT_STATE,
3901 tr("The given session is busy"));
3902
3903 /* get the IInternalSessionControl interface */
3904 ComPtr<IInternalSessionControl> control(aSession);
3905 ComAssertMsgRet(!control.isNull(),
3906 ("No IInternalSessionControl interface"),
3907 E_INVALIDARG);
3908
3909 /* get the teleporter enable state for the progress object init. */
3910 BOOL fTeleporterEnabled;
3911 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3912 if (FAILED(rc))
3913 return rc;
3914
3915 /* create a progress object */
3916 ComObjPtr<ProgressProxy> progress;
3917 progress.createObject();
3918 rc = progress->init(mParent,
3919 static_cast<IMachine*>(this),
3920 Bstr(tr("Starting VM")).raw(),
3921 TRUE /* aCancelable */,
3922 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3923 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3924 2 /* uFirstOperationWeight */,
3925 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3926
3927 if (SUCCEEDED(rc))
3928 {
3929 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3930 if (SUCCEEDED(rc))
3931 {
3932 progress.queryInterfaceTo(aProgress);
3933
3934 /* signal the client watcher thread */
3935 mParent->updateClientWatcher();
3936
3937 /* fire an event */
3938 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3939 }
3940 }
3941 }
3942 else
3943 {
3944 /* no progress object - either instant success or failure */
3945 *aProgress = NULL;
3946
3947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3948
3949 if (mData->mSession.mState != SessionState_Locked)
3950 return setError(VBOX_E_INVALID_OBJECT_STATE,
3951 tr("The machine '%s' is not locked by a session"),
3952 mUserData->s.strName.c_str());
3953
3954 /* must have a VM process associated - do not kill normal API clients
3955 * with an open session */
3956 if (!Global::IsOnline(mData->mMachineState))
3957 return setError(VBOX_E_INVALID_OBJECT_STATE,
3958 tr("The machine '%s' does not have a VM process"),
3959 mUserData->s.strName.c_str());
3960
3961 /* forcibly terminate the VM process */
3962 if (mData->mSession.mPID != NIL_RTPROCESS)
3963 RTProcTerminate(mData->mSession.mPID);
3964
3965 /* signal the client watcher thread, as most likely the client has
3966 * been terminated */
3967 mParent->updateClientWatcher();
3968 }
3969
3970 return rc;
3971}
3972
3973STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3974{
3975 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3976 return setError(E_INVALIDARG,
3977 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3978 aPosition, SchemaDefs::MaxBootPosition);
3979
3980 if (aDevice == DeviceType_USB)
3981 return setError(E_NOTIMPL,
3982 tr("Booting from USB device is currently not supported"));
3983
3984 AutoCaller autoCaller(this);
3985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3986
3987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3988
3989 HRESULT rc = checkStateDependency(MutableStateDep);
3990 if (FAILED(rc)) return rc;
3991
3992 setModified(IsModified_MachineData);
3993 mHWData.backup();
3994 mHWData->mBootOrder[aPosition - 1] = aDevice;
3995
3996 return S_OK;
3997}
3998
3999STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
4000{
4001 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4002 return setError(E_INVALIDARG,
4003 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4004 aPosition, SchemaDefs::MaxBootPosition);
4005
4006 AutoCaller autoCaller(this);
4007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4008
4009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4010
4011 *aDevice = mHWData->mBootOrder[aPosition - 1];
4012
4013 return S_OK;
4014}
4015
4016STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4017 LONG aControllerPort,
4018 LONG aDevice,
4019 DeviceType_T aType,
4020 IMedium *aMedium)
4021{
4022 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4023 aControllerName, aControllerPort, aDevice, aType, aMedium));
4024
4025 CheckComArgStrNotEmptyOrNull(aControllerName);
4026
4027 AutoCaller autoCaller(this);
4028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4029
4030 // request the host lock first, since might be calling Host methods for getting host drives;
4031 // next, protect the media tree all the while we're in here, as well as our member variables
4032 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4033 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4034
4035 HRESULT rc = checkStateDependency(MutableStateDep);
4036 if (FAILED(rc)) return rc;
4037
4038 /// @todo NEWMEDIA implicit machine registration
4039 if (!mData->mRegistered)
4040 return setError(VBOX_E_INVALID_OBJECT_STATE,
4041 tr("Cannot attach storage devices to an unregistered machine"));
4042
4043 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4044
4045 /* Check for an existing controller. */
4046 ComObjPtr<StorageController> ctl;
4047 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4048 if (FAILED(rc)) return rc;
4049
4050 StorageControllerType_T ctrlType;
4051 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4052 if (FAILED(rc))
4053 return setError(E_FAIL,
4054 tr("Could not get type of controller '%ls'"),
4055 aControllerName);
4056
4057 bool fSilent = false;
4058 Utf8Str strReconfig;
4059
4060 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4061 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4062 if ( mData->mMachineState == MachineState_Paused
4063 && strReconfig == "1")
4064 fSilent = true;
4065
4066 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4067 bool fHotplug = false;
4068 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4069 fHotplug = true;
4070
4071 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4072 return setError(VBOX_E_INVALID_VM_STATE,
4073 tr("Controller '%ls' does not support hotplugging"),
4074 aControllerName);
4075
4076 // check that the port and device are not out of range
4077 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4078 if (FAILED(rc)) return rc;
4079
4080 /* check if the device slot is already busy */
4081 MediumAttachment *pAttachTemp;
4082 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4083 aControllerName,
4084 aControllerPort,
4085 aDevice)))
4086 {
4087 Medium *pMedium = pAttachTemp->getMedium();
4088 if (pMedium)
4089 {
4090 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4091 return setError(VBOX_E_OBJECT_IN_USE,
4092 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4093 pMedium->getLocationFull().c_str(),
4094 aControllerPort,
4095 aDevice,
4096 aControllerName);
4097 }
4098 else
4099 return setError(VBOX_E_OBJECT_IN_USE,
4100 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4101 aControllerPort, aDevice, aControllerName);
4102 }
4103
4104 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4105 if (aMedium && medium.isNull())
4106 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4107
4108 AutoCaller mediumCaller(medium);
4109 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4110
4111 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4112
4113 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4114 && !medium.isNull()
4115 )
4116 return setError(VBOX_E_OBJECT_IN_USE,
4117 tr("Medium '%s' is already attached to this virtual machine"),
4118 medium->getLocationFull().c_str());
4119
4120 if (!medium.isNull())
4121 {
4122 MediumType_T mtype = medium->getType();
4123 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4124 // For DVDs it's not written to the config file, so needs no global config
4125 // version bump. For floppies it's a new attribute "type", which is ignored
4126 // by older VirtualBox version, so needs no global config version bump either.
4127 // For hard disks this type is not accepted.
4128 if (mtype == MediumType_MultiAttach)
4129 {
4130 // This type is new with VirtualBox 4.0 and therefore requires settings
4131 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4132 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4133 // two reasons: The medium type is a property of the media registry tree, which
4134 // can reside in the global config file (for pre-4.0 media); we would therefore
4135 // possibly need to bump the global config version. We don't want to do that though
4136 // because that might make downgrading to pre-4.0 impossible.
4137 // As a result, we can only use these two new types if the medium is NOT in the
4138 // global registry:
4139 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4140 if ( medium->isInRegistry(uuidGlobalRegistry)
4141 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4142 )
4143 return setError(VBOX_E_INVALID_OBJECT_STATE,
4144 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4145 "to machines that were created with VirtualBox 4.0 or later"),
4146 medium->getLocationFull().c_str());
4147 }
4148 }
4149
4150 bool fIndirect = false;
4151 if (!medium.isNull())
4152 fIndirect = medium->isReadOnly();
4153 bool associate = true;
4154
4155 do
4156 {
4157 if ( aType == DeviceType_HardDisk
4158 && mMediaData.isBackedUp())
4159 {
4160 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4161
4162 /* check if the medium was attached to the VM before we started
4163 * changing attachments in which case the attachment just needs to
4164 * be restored */
4165 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4166 {
4167 AssertReturn(!fIndirect, E_FAIL);
4168
4169 /* see if it's the same bus/channel/device */
4170 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4171 {
4172 /* the simplest case: restore the whole attachment
4173 * and return, nothing else to do */
4174 mMediaData->mAttachments.push_back(pAttachTemp);
4175
4176 /* Reattach the medium to the VM. */
4177 if (fHotplug || fSilent)
4178 {
4179 mediumLock.release();
4180 treeLock.release();
4181 alock.release();
4182
4183 MediumLockList *pMediumLockList(new MediumLockList());
4184
4185 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4186 true /* fMediumLockWrite */,
4187 NULL,
4188 *pMediumLockList);
4189 alock.acquire();
4190 if (FAILED(rc))
4191 delete pMediumLockList;
4192 else
4193 {
4194 mData->mSession.mLockedMedia.Unlock();
4195 alock.release();
4196 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4197 mData->mSession.mLockedMedia.Lock();
4198 alock.acquire();
4199 }
4200 alock.release();
4201
4202 if (SUCCEEDED(rc))
4203 {
4204 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4205 /* Remove lock list in case of error. */
4206 if (FAILED(rc))
4207 {
4208 mData->mSession.mLockedMedia.Unlock();
4209 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4210 mData->mSession.mLockedMedia.Lock();
4211 }
4212 }
4213 }
4214
4215 return S_OK;
4216 }
4217
4218 /* bus/channel/device differ; we need a new attachment object,
4219 * but don't try to associate it again */
4220 associate = false;
4221 break;
4222 }
4223 }
4224
4225 /* go further only if the attachment is to be indirect */
4226 if (!fIndirect)
4227 break;
4228
4229 /* perform the so called smart attachment logic for indirect
4230 * attachments. Note that smart attachment is only applicable to base
4231 * hard disks. */
4232
4233 if (medium->getParent().isNull())
4234 {
4235 /* first, investigate the backup copy of the current hard disk
4236 * attachments to make it possible to re-attach existing diffs to
4237 * another device slot w/o losing their contents */
4238 if (mMediaData.isBackedUp())
4239 {
4240 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4241
4242 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4243 uint32_t foundLevel = 0;
4244
4245 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4246 it != oldAtts.end();
4247 ++it)
4248 {
4249 uint32_t level = 0;
4250 MediumAttachment *pAttach = *it;
4251 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4252 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4253 if (pMedium.isNull())
4254 continue;
4255
4256 if (pMedium->getBase(&level) == medium)
4257 {
4258 /* skip the hard disk if its currently attached (we
4259 * cannot attach the same hard disk twice) */
4260 if (findAttachment(mMediaData->mAttachments,
4261 pMedium))
4262 continue;
4263
4264 /* matched device, channel and bus (i.e. attached to the
4265 * same place) will win and immediately stop the search;
4266 * otherwise the attachment that has the youngest
4267 * descendant of medium will be used
4268 */
4269 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4270 {
4271 /* the simplest case: restore the whole attachment
4272 * and return, nothing else to do */
4273 mMediaData->mAttachments.push_back(*it);
4274
4275 /* Reattach the medium to the VM. */
4276 if (fHotplug || fSilent)
4277 {
4278 mediumLock.release();
4279 treeLock.release();
4280 alock.release();
4281
4282 MediumLockList *pMediumLockList(new MediumLockList());
4283
4284 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4285 true /* fMediumLockWrite */,
4286 NULL,
4287 *pMediumLockList);
4288 alock.acquire();
4289 if (FAILED(rc))
4290 delete pMediumLockList;
4291 else
4292 {
4293 mData->mSession.mLockedMedia.Unlock();
4294 alock.release();
4295 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4296 mData->mSession.mLockedMedia.Lock();
4297 alock.acquire();
4298 }
4299 alock.release();
4300
4301 if (SUCCEEDED(rc))
4302 {
4303 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4304 /* Remove lock list in case of error. */
4305 if (FAILED(rc))
4306 {
4307 mData->mSession.mLockedMedia.Unlock();
4308 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4309 mData->mSession.mLockedMedia.Lock();
4310 }
4311 }
4312 }
4313
4314 return S_OK;
4315 }
4316 else if ( foundIt == oldAtts.end()
4317 || level > foundLevel /* prefer younger */
4318 )
4319 {
4320 foundIt = it;
4321 foundLevel = level;
4322 }
4323 }
4324 }
4325
4326 if (foundIt != oldAtts.end())
4327 {
4328 /* use the previously attached hard disk */
4329 medium = (*foundIt)->getMedium();
4330 mediumCaller.attach(medium);
4331 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4332 mediumLock.attach(medium);
4333 /* not implicit, doesn't require association with this VM */
4334 fIndirect = false;
4335 associate = false;
4336 /* go right to the MediumAttachment creation */
4337 break;
4338 }
4339 }
4340
4341 /* must give up the medium lock and medium tree lock as below we
4342 * go over snapshots, which needs a lock with higher lock order. */
4343 mediumLock.release();
4344 treeLock.release();
4345
4346 /* then, search through snapshots for the best diff in the given
4347 * hard disk's chain to base the new diff on */
4348
4349 ComObjPtr<Medium> base;
4350 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4351 while (snap)
4352 {
4353 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4354
4355 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4356
4357 MediumAttachment *pAttachFound = NULL;
4358 uint32_t foundLevel = 0;
4359
4360 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4361 it != snapAtts.end();
4362 ++it)
4363 {
4364 MediumAttachment *pAttach = *it;
4365 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4366 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4367 if (pMedium.isNull())
4368 continue;
4369
4370 uint32_t level = 0;
4371 if (pMedium->getBase(&level) == medium)
4372 {
4373 /* matched device, channel and bus (i.e. attached to the
4374 * same place) will win and immediately stop the search;
4375 * otherwise the attachment that has the youngest
4376 * descendant of medium will be used
4377 */
4378 if ( pAttach->getDevice() == aDevice
4379 && pAttach->getPort() == aControllerPort
4380 && pAttach->getControllerName() == aControllerName
4381 )
4382 {
4383 pAttachFound = pAttach;
4384 break;
4385 }
4386 else if ( !pAttachFound
4387 || level > foundLevel /* prefer younger */
4388 )
4389 {
4390 pAttachFound = pAttach;
4391 foundLevel = level;
4392 }
4393 }
4394 }
4395
4396 if (pAttachFound)
4397 {
4398 base = pAttachFound->getMedium();
4399 break;
4400 }
4401
4402 snap = snap->getParent();
4403 }
4404
4405 /* re-lock medium tree and the medium, as we need it below */
4406 treeLock.acquire();
4407 mediumLock.acquire();
4408
4409 /* found a suitable diff, use it as a base */
4410 if (!base.isNull())
4411 {
4412 medium = base;
4413 mediumCaller.attach(medium);
4414 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4415 mediumLock.attach(medium);
4416 }
4417 }
4418
4419 Utf8Str strFullSnapshotFolder;
4420 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4421
4422 ComObjPtr<Medium> diff;
4423 diff.createObject();
4424 // store this diff in the same registry as the parent
4425 Guid uuidRegistryParent;
4426 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4427 {
4428 // parent image has no registry: this can happen if we're attaching a new immutable
4429 // image that has not yet been attached (medium then points to the base and we're
4430 // creating the diff image for the immutable, and the parent is not yet registered);
4431 // put the parent in the machine registry then
4432 mediumLock.release();
4433 treeLock.release();
4434 alock.release();
4435 addMediumToRegistry(medium);
4436 alock.acquire();
4437 treeLock.acquire();
4438 mediumLock.acquire();
4439 medium->getFirstRegistryMachineId(uuidRegistryParent);
4440 }
4441 rc = diff->init(mParent,
4442 medium->getPreferredDiffFormat(),
4443 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4444 uuidRegistryParent);
4445 if (FAILED(rc)) return rc;
4446
4447 /* Apply the normal locking logic to the entire chain. */
4448 MediumLockList *pMediumLockList(new MediumLockList());
4449 mediumLock.release();
4450 treeLock.release();
4451 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4452 true /* fMediumLockWrite */,
4453 medium,
4454 *pMediumLockList);
4455 treeLock.acquire();
4456 mediumLock.acquire();
4457 if (SUCCEEDED(rc))
4458 {
4459 mediumLock.release();
4460 treeLock.release();
4461 rc = pMediumLockList->Lock();
4462 treeLock.acquire();
4463 mediumLock.acquire();
4464 if (FAILED(rc))
4465 setError(rc,
4466 tr("Could not lock medium when creating diff '%s'"),
4467 diff->getLocationFull().c_str());
4468 else
4469 {
4470 /* will release the lock before the potentially lengthy
4471 * operation, so protect with the special state */
4472 MachineState_T oldState = mData->mMachineState;
4473 setMachineState(MachineState_SettingUp);
4474
4475 mediumLock.release();
4476 treeLock.release();
4477 alock.release();
4478
4479 rc = medium->createDiffStorage(diff,
4480 MediumVariant_Standard,
4481 pMediumLockList,
4482 NULL /* aProgress */,
4483 true /* aWait */);
4484
4485 alock.acquire();
4486 treeLock.acquire();
4487 mediumLock.acquire();
4488
4489 setMachineState(oldState);
4490 }
4491 }
4492
4493 /* Unlock the media and free the associated memory. */
4494 delete pMediumLockList;
4495
4496 if (FAILED(rc)) return rc;
4497
4498 /* use the created diff for the actual attachment */
4499 medium = diff;
4500 mediumCaller.attach(medium);
4501 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4502 mediumLock.attach(medium);
4503 }
4504 while (0);
4505
4506 ComObjPtr<MediumAttachment> attachment;
4507 attachment.createObject();
4508 rc = attachment->init(this,
4509 medium,
4510 aControllerName,
4511 aControllerPort,
4512 aDevice,
4513 aType,
4514 fIndirect,
4515 false /* fPassthrough */,
4516 false /* fTempEject */,
4517 false /* fNonRotational */,
4518 false /* fDiscard */,
4519 Utf8Str::Empty);
4520 if (FAILED(rc)) return rc;
4521
4522 if (associate && !medium.isNull())
4523 {
4524 // as the last step, associate the medium to the VM
4525 rc = medium->addBackReference(mData->mUuid);
4526 // here we can fail because of Deleting, or being in process of creating a Diff
4527 if (FAILED(rc)) return rc;
4528
4529 mediumLock.release();
4530 treeLock.release();
4531 alock.release();
4532 addMediumToRegistry(medium);
4533 alock.acquire();
4534 treeLock.acquire();
4535 mediumLock.acquire();
4536 }
4537
4538 /* success: finally remember the attachment */
4539 setModified(IsModified_Storage);
4540 mMediaData.backup();
4541 mMediaData->mAttachments.push_back(attachment);
4542
4543 mediumLock.release();
4544 treeLock.release();
4545 alock.release();
4546
4547 if (fHotplug || fSilent)
4548 {
4549 if (!medium.isNull())
4550 {
4551 MediumLockList *pMediumLockList(new MediumLockList());
4552
4553 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4554 true /* fMediumLockWrite */,
4555 NULL,
4556 *pMediumLockList);
4557 alock.acquire();
4558 if (FAILED(rc))
4559 delete pMediumLockList;
4560 else
4561 {
4562 mData->mSession.mLockedMedia.Unlock();
4563 alock.release();
4564 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4565 mData->mSession.mLockedMedia.Lock();
4566 alock.acquire();
4567 }
4568 alock.release();
4569 }
4570
4571 if (SUCCEEDED(rc))
4572 {
4573 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4574 /* Remove lock list in case of error. */
4575 if (FAILED(rc))
4576 {
4577 mData->mSession.mLockedMedia.Unlock();
4578 mData->mSession.mLockedMedia.Remove(attachment);
4579 mData->mSession.mLockedMedia.Lock();
4580 }
4581 }
4582 }
4583
4584 mParent->saveModifiedRegistries();
4585
4586 return rc;
4587}
4588
4589STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4590 LONG aDevice)
4591{
4592 CheckComArgStrNotEmptyOrNull(aControllerName);
4593
4594 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4595 aControllerName, aControllerPort, aDevice));
4596
4597 AutoCaller autoCaller(this);
4598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4599
4600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4601
4602 HRESULT rc = checkStateDependency(MutableStateDep);
4603 if (FAILED(rc)) return rc;
4604
4605 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4606
4607 /* Check for an existing controller. */
4608 ComObjPtr<StorageController> ctl;
4609 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4610 if (FAILED(rc)) return rc;
4611
4612 StorageControllerType_T ctrlType;
4613 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4614 if (FAILED(rc))
4615 return setError(E_FAIL,
4616 tr("Could not get type of controller '%ls'"),
4617 aControllerName);
4618
4619 bool fSilent = false;
4620 Utf8Str strReconfig;
4621
4622 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4623 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4624 if ( mData->mMachineState == MachineState_Paused
4625 && strReconfig == "1")
4626 fSilent = true;
4627
4628 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4629 bool fHotplug = false;
4630 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4631 fHotplug = true;
4632
4633 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4634 return setError(VBOX_E_INVALID_VM_STATE,
4635 tr("Controller '%ls' does not support hotplugging"),
4636 aControllerName);
4637
4638 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4639 aControllerName,
4640 aControllerPort,
4641 aDevice);
4642 if (!pAttach)
4643 return setError(VBOX_E_OBJECT_NOT_FOUND,
4644 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4645 aDevice, aControllerPort, aControllerName);
4646
4647 /*
4648 * The VM has to detach the device before we delete any implicit diffs.
4649 * If this fails we can roll back without loosing data.
4650 */
4651 if (fHotplug || fSilent)
4652 {
4653 alock.release();
4654 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4655 alock.acquire();
4656 }
4657 if (FAILED(rc)) return rc;
4658
4659 /* If we are here everything went well and we can delete the implicit now. */
4660 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4661
4662 alock.release();
4663
4664 mParent->saveModifiedRegistries();
4665
4666 return rc;
4667}
4668
4669STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4670 LONG aDevice, BOOL aPassthrough)
4671{
4672 CheckComArgStrNotEmptyOrNull(aControllerName);
4673
4674 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4675 aControllerName, aControllerPort, aDevice, aPassthrough));
4676
4677 AutoCaller autoCaller(this);
4678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4679
4680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
4682 HRESULT rc = checkStateDependency(MutableStateDep);
4683 if (FAILED(rc)) return rc;
4684
4685 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4686
4687 if (Global::IsOnlineOrTransient(mData->mMachineState))
4688 return setError(VBOX_E_INVALID_VM_STATE,
4689 tr("Invalid machine state: %s"),
4690 Global::stringifyMachineState(mData->mMachineState));
4691
4692 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4693 aControllerName,
4694 aControllerPort,
4695 aDevice);
4696 if (!pAttach)
4697 return setError(VBOX_E_OBJECT_NOT_FOUND,
4698 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4699 aDevice, aControllerPort, aControllerName);
4700
4701
4702 setModified(IsModified_Storage);
4703 mMediaData.backup();
4704
4705 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4706
4707 if (pAttach->getType() != DeviceType_DVD)
4708 return setError(E_INVALIDARG,
4709 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4710 aDevice, aControllerPort, aControllerName);
4711 pAttach->updatePassthrough(!!aPassthrough);
4712
4713 return S_OK;
4714}
4715
4716STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4717 LONG aDevice, BOOL aTemporaryEject)
4718{
4719 CheckComArgStrNotEmptyOrNull(aControllerName);
4720
4721 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4722 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4723
4724 AutoCaller autoCaller(this);
4725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4726
4727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4728
4729 HRESULT rc = checkStateDependency(MutableStateDep);
4730 if (FAILED(rc)) return rc;
4731
4732 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4733 aControllerName,
4734 aControllerPort,
4735 aDevice);
4736 if (!pAttach)
4737 return setError(VBOX_E_OBJECT_NOT_FOUND,
4738 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4739 aDevice, aControllerPort, aControllerName);
4740
4741
4742 setModified(IsModified_Storage);
4743 mMediaData.backup();
4744
4745 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4746
4747 if (pAttach->getType() != DeviceType_DVD)
4748 return setError(E_INVALIDARG,
4749 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4750 aDevice, aControllerPort, aControllerName);
4751 pAttach->updateTempEject(!!aTemporaryEject);
4752
4753 return S_OK;
4754}
4755
4756STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4757 LONG aDevice, BOOL aNonRotational)
4758{
4759 CheckComArgStrNotEmptyOrNull(aControllerName);
4760
4761 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4762 aControllerName, aControllerPort, aDevice, aNonRotational));
4763
4764 AutoCaller autoCaller(this);
4765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4766
4767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4768
4769 HRESULT rc = checkStateDependency(MutableStateDep);
4770 if (FAILED(rc)) return rc;
4771
4772 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4773
4774 if (Global::IsOnlineOrTransient(mData->mMachineState))
4775 return setError(VBOX_E_INVALID_VM_STATE,
4776 tr("Invalid machine state: %s"),
4777 Global::stringifyMachineState(mData->mMachineState));
4778
4779 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4780 aControllerName,
4781 aControllerPort,
4782 aDevice);
4783 if (!pAttach)
4784 return setError(VBOX_E_OBJECT_NOT_FOUND,
4785 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4786 aDevice, aControllerPort, aControllerName);
4787
4788
4789 setModified(IsModified_Storage);
4790 mMediaData.backup();
4791
4792 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4793
4794 if (pAttach->getType() != DeviceType_HardDisk)
4795 return setError(E_INVALIDARG,
4796 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"),
4797 aDevice, aControllerPort, aControllerName);
4798 pAttach->updateNonRotational(!!aNonRotational);
4799
4800 return S_OK;
4801}
4802
4803STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4804 LONG aDevice, BOOL aDiscard)
4805{
4806 CheckComArgStrNotEmptyOrNull(aControllerName);
4807
4808 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4809 aControllerName, aControllerPort, aDevice, aDiscard));
4810
4811 AutoCaller autoCaller(this);
4812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4813
4814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4815
4816 HRESULT rc = checkStateDependency(MutableStateDep);
4817 if (FAILED(rc)) return rc;
4818
4819 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4820
4821 if (Global::IsOnlineOrTransient(mData->mMachineState))
4822 return setError(VBOX_E_INVALID_VM_STATE,
4823 tr("Invalid machine state: %s"),
4824 Global::stringifyMachineState(mData->mMachineState));
4825
4826 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4827 aControllerName,
4828 aControllerPort,
4829 aDevice);
4830 if (!pAttach)
4831 return setError(VBOX_E_OBJECT_NOT_FOUND,
4832 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4833 aDevice, aControllerPort, aControllerName);
4834
4835
4836 setModified(IsModified_Storage);
4837 mMediaData.backup();
4838
4839 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4840
4841 if (pAttach->getType() != DeviceType_HardDisk)
4842 return setError(E_INVALIDARG,
4843 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"),
4844 aDevice, aControllerPort, aControllerName);
4845 pAttach->updateDiscard(!!aDiscard);
4846
4847 return S_OK;
4848}
4849
4850STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4851 LONG aDevice)
4852{
4853 int rc = S_OK;
4854 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4855 aControllerName, aControllerPort, aDevice));
4856
4857 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4858
4859 return rc;
4860}
4861
4862STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4863 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4864{
4865 CheckComArgStrNotEmptyOrNull(aControllerName);
4866
4867 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4868 aControllerName, aControllerPort, aDevice));
4869
4870 AutoCaller autoCaller(this);
4871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4872
4873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4874
4875 HRESULT rc = checkStateDependency(MutableStateDep);
4876 if (FAILED(rc)) return rc;
4877
4878 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4879
4880 if (Global::IsOnlineOrTransient(mData->mMachineState))
4881 return setError(VBOX_E_INVALID_VM_STATE,
4882 tr("Invalid machine state: %s"),
4883 Global::stringifyMachineState(mData->mMachineState));
4884
4885 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4886 aControllerName,
4887 aControllerPort,
4888 aDevice);
4889 if (!pAttach)
4890 return setError(VBOX_E_OBJECT_NOT_FOUND,
4891 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4892 aDevice, aControllerPort, aControllerName);
4893
4894
4895 setModified(IsModified_Storage);
4896 mMediaData.backup();
4897
4898 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4899 if (aBandwidthGroup && group.isNull())
4900 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4901
4902 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4903
4904 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4905 if (strBandwidthGroupOld.isNotEmpty())
4906 {
4907 /* Get the bandwidth group object and release it - this must not fail. */
4908 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4909 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4910 Assert(SUCCEEDED(rc));
4911
4912 pBandwidthGroupOld->release();
4913 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4914 }
4915
4916 if (!group.isNull())
4917 {
4918 group->reference();
4919 pAttach->updateBandwidthGroup(group->getName());
4920 }
4921
4922 return S_OK;
4923}
4924
4925STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4926 LONG aControllerPort,
4927 LONG aDevice,
4928 DeviceType_T aType)
4929{
4930 HRESULT rc = S_OK;
4931
4932 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4933 aControllerName, aControllerPort, aDevice, aType));
4934
4935 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4936
4937 return rc;
4938}
4939
4940
4941
4942STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4943 LONG aControllerPort,
4944 LONG aDevice,
4945 BOOL aForce)
4946{
4947 int rc = S_OK;
4948 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4949 aControllerName, aControllerPort, aForce));
4950
4951 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4952
4953 return rc;
4954}
4955
4956STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4957 LONG aControllerPort,
4958 LONG aDevice,
4959 IMedium *aMedium,
4960 BOOL aForce)
4961{
4962 int rc = S_OK;
4963 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4964 aControllerName, aControllerPort, aDevice, aForce));
4965
4966 CheckComArgStrNotEmptyOrNull(aControllerName);
4967
4968 AutoCaller autoCaller(this);
4969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4970
4971 // request the host lock first, since might be calling Host methods for getting host drives;
4972 // next, protect the media tree all the while we're in here, as well as our member variables
4973 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4974 this->lockHandle(),
4975 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4976
4977 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4978 aControllerName,
4979 aControllerPort,
4980 aDevice);
4981 if (pAttach.isNull())
4982 return setError(VBOX_E_OBJECT_NOT_FOUND,
4983 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4984 aDevice, aControllerPort, aControllerName);
4985
4986 /* Remember previously mounted medium. The medium before taking the
4987 * backup is not necessarily the same thing. */
4988 ComObjPtr<Medium> oldmedium;
4989 oldmedium = pAttach->getMedium();
4990
4991 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4992 if (aMedium && pMedium.isNull())
4993 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4994
4995 AutoCaller mediumCaller(pMedium);
4996 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4997
4998 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4999 if (pMedium)
5000 {
5001 DeviceType_T mediumType = pAttach->getType();
5002 switch (mediumType)
5003 {
5004 case DeviceType_DVD:
5005 case DeviceType_Floppy:
5006 break;
5007
5008 default:
5009 return setError(VBOX_E_INVALID_OBJECT_STATE,
5010 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5011 aControllerPort,
5012 aDevice,
5013 aControllerName);
5014 }
5015 }
5016
5017 setModified(IsModified_Storage);
5018 mMediaData.backup();
5019
5020 {
5021 // The backup operation makes the pAttach reference point to the
5022 // old settings. Re-get the correct reference.
5023 pAttach = findAttachment(mMediaData->mAttachments,
5024 aControllerName,
5025 aControllerPort,
5026 aDevice);
5027 if (!oldmedium.isNull())
5028 oldmedium->removeBackReference(mData->mUuid);
5029 if (!pMedium.isNull())
5030 {
5031 pMedium->addBackReference(mData->mUuid);
5032
5033 mediumLock.release();
5034 multiLock.release();
5035 addMediumToRegistry(pMedium);
5036 multiLock.acquire();
5037 mediumLock.acquire();
5038 }
5039
5040 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5041 pAttach->updateMedium(pMedium);
5042 }
5043
5044 setModified(IsModified_Storage);
5045
5046 mediumLock.release();
5047 multiLock.release();
5048 rc = onMediumChange(pAttach, aForce);
5049 multiLock.acquire();
5050 mediumLock.acquire();
5051
5052 /* On error roll back this change only. */
5053 if (FAILED(rc))
5054 {
5055 if (!pMedium.isNull())
5056 pMedium->removeBackReference(mData->mUuid);
5057 pAttach = findAttachment(mMediaData->mAttachments,
5058 aControllerName,
5059 aControllerPort,
5060 aDevice);
5061 /* If the attachment is gone in the meantime, bail out. */
5062 if (pAttach.isNull())
5063 return rc;
5064 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5065 if (!oldmedium.isNull())
5066 oldmedium->addBackReference(mData->mUuid);
5067 pAttach->updateMedium(oldmedium);
5068 }
5069
5070 mediumLock.release();
5071 multiLock.release();
5072
5073 mParent->saveModifiedRegistries();
5074
5075 return rc;
5076}
5077
5078STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5079 LONG aControllerPort,
5080 LONG aDevice,
5081 IMedium **aMedium)
5082{
5083 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5084 aControllerName, aControllerPort, aDevice));
5085
5086 CheckComArgStrNotEmptyOrNull(aControllerName);
5087 CheckComArgOutPointerValid(aMedium);
5088
5089 AutoCaller autoCaller(this);
5090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5091
5092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5093
5094 *aMedium = NULL;
5095
5096 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5097 aControllerName,
5098 aControllerPort,
5099 aDevice);
5100 if (pAttach.isNull())
5101 return setError(VBOX_E_OBJECT_NOT_FOUND,
5102 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5103 aDevice, aControllerPort, aControllerName);
5104
5105 pAttach->getMedium().queryInterfaceTo(aMedium);
5106
5107 return S_OK;
5108}
5109
5110STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5111{
5112 CheckComArgOutPointerValid(port);
5113 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5114
5115 AutoCaller autoCaller(this);
5116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5117
5118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5119
5120 mSerialPorts[slot].queryInterfaceTo(port);
5121
5122 return S_OK;
5123}
5124
5125STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5126{
5127 CheckComArgOutPointerValid(port);
5128 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5129
5130 AutoCaller autoCaller(this);
5131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5132
5133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5134
5135 mParallelPorts[slot].queryInterfaceTo(port);
5136
5137 return S_OK;
5138}
5139
5140STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5141{
5142 CheckComArgOutPointerValid(adapter);
5143 /* Do not assert if slot is out of range, just return the advertised
5144 status. testdriver/vbox.py triggers this in logVmInfo. */
5145 if (slot >= mNetworkAdapters.size())
5146 return setError(E_INVALIDARG,
5147 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5148 slot, mNetworkAdapters.size());
5149
5150 AutoCaller autoCaller(this);
5151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5152
5153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5154
5155 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5156
5157 return S_OK;
5158}
5159
5160STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5161{
5162 CheckComArgOutSafeArrayPointerValid(aKeys);
5163
5164 AutoCaller autoCaller(this);
5165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5166
5167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5170 int i = 0;
5171 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5172 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5173 ++it, ++i)
5174 {
5175 const Utf8Str &strKey = it->first;
5176 strKey.cloneTo(&saKeys[i]);
5177 }
5178 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5179
5180 return S_OK;
5181 }
5182
5183 /**
5184 * @note Locks this object for reading.
5185 */
5186STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5187 BSTR *aValue)
5188{
5189 CheckComArgStrNotEmptyOrNull(aKey);
5190 CheckComArgOutPointerValid(aValue);
5191
5192 AutoCaller autoCaller(this);
5193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5194
5195 /* start with nothing found */
5196 Bstr bstrResult("");
5197
5198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5199
5200 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5201 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5202 // found:
5203 bstrResult = it->second; // source is a Utf8Str
5204
5205 /* return the result to caller (may be empty) */
5206 bstrResult.cloneTo(aValue);
5207
5208 return S_OK;
5209}
5210
5211 /**
5212 * @note Locks mParent for writing + this object for writing.
5213 */
5214STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5215{
5216 CheckComArgStrNotEmptyOrNull(aKey);
5217
5218 AutoCaller autoCaller(this);
5219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5220
5221 Utf8Str strKey(aKey);
5222 Utf8Str strValue(aValue);
5223 Utf8Str strOldValue; // empty
5224
5225 // locking note: we only hold the read lock briefly to look up the old value,
5226 // then release it and call the onExtraCanChange callbacks. There is a small
5227 // chance of a race insofar as the callback might be called twice if two callers
5228 // change the same key at the same time, but that's a much better solution
5229 // than the deadlock we had here before. The actual changing of the extradata
5230 // is then performed under the write lock and race-free.
5231
5232 // look up the old value first; if nothing has changed then we need not do anything
5233 {
5234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5235 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5236 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5237 strOldValue = it->second;
5238 }
5239
5240 bool fChanged;
5241 if ((fChanged = (strOldValue != strValue)))
5242 {
5243 // ask for permission from all listeners outside the locks;
5244 // onExtraDataCanChange() only briefly requests the VirtualBox
5245 // lock to copy the list of callbacks to invoke
5246 Bstr error;
5247 Bstr bstrValue(aValue);
5248
5249 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5250 {
5251 const char *sep = error.isEmpty() ? "" : ": ";
5252 CBSTR err = error.raw();
5253 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5254 sep, err));
5255 return setError(E_ACCESSDENIED,
5256 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5257 aKey,
5258 bstrValue.raw(),
5259 sep,
5260 err);
5261 }
5262
5263 // data is changing and change not vetoed: then write it out under the lock
5264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5265
5266 if (isSnapshotMachine())
5267 {
5268 HRESULT rc = checkStateDependency(MutableStateDep);
5269 if (FAILED(rc)) return rc;
5270 }
5271
5272 if (strValue.isEmpty())
5273 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5274 else
5275 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5276 // creates a new key if needed
5277
5278 bool fNeedsGlobalSaveSettings = false;
5279 saveSettings(&fNeedsGlobalSaveSettings);
5280
5281 if (fNeedsGlobalSaveSettings)
5282 {
5283 // save the global settings; for that we should hold only the VirtualBox lock
5284 alock.release();
5285 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5286 mParent->saveSettings();
5287 }
5288 }
5289
5290 // fire notification outside the lock
5291 if (fChanged)
5292 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5293
5294 return S_OK;
5295}
5296
5297STDMETHODIMP Machine::SaveSettings()
5298{
5299 AutoCaller autoCaller(this);
5300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5301
5302 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5303
5304 /* when there was auto-conversion, we want to save the file even if
5305 * the VM is saved */
5306 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5307 if (FAILED(rc)) return rc;
5308
5309 /* the settings file path may never be null */
5310 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5311
5312 /* save all VM data excluding snapshots */
5313 bool fNeedsGlobalSaveSettings = false;
5314 rc = saveSettings(&fNeedsGlobalSaveSettings);
5315 mlock.release();
5316
5317 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5318 {
5319 // save the global settings; for that we should hold only the VirtualBox lock
5320 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5321 rc = mParent->saveSettings();
5322 }
5323
5324 return rc;
5325}
5326
5327STDMETHODIMP Machine::DiscardSettings()
5328{
5329 AutoCaller autoCaller(this);
5330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5331
5332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5333
5334 HRESULT rc = checkStateDependency(MutableStateDep);
5335 if (FAILED(rc)) return rc;
5336
5337 /*
5338 * during this rollback, the session will be notified if data has
5339 * been actually changed
5340 */
5341 rollback(true /* aNotify */);
5342
5343 return S_OK;
5344}
5345
5346/** @note Locks objects! */
5347STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5348 ComSafeArrayOut(IMedium*, aMedia))
5349{
5350 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5351 AutoLimitedCaller autoCaller(this);
5352 AssertComRCReturnRC(autoCaller.rc());
5353
5354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5355
5356 Guid id(getId());
5357
5358 if (mData->mSession.mState != SessionState_Unlocked)
5359 return setError(VBOX_E_INVALID_OBJECT_STATE,
5360 tr("Cannot unregister the machine '%s' while it is locked"),
5361 mUserData->s.strName.c_str());
5362
5363 // wait for state dependents to drop to zero
5364 ensureNoStateDependencies();
5365
5366 if (!mData->mAccessible)
5367 {
5368 // inaccessible maschines can only be unregistered; uninitialize ourselves
5369 // here because currently there may be no unregistered that are inaccessible
5370 // (this state combination is not supported). Note releasing the caller and
5371 // leaving the lock before calling uninit()
5372 alock.release();
5373 autoCaller.release();
5374
5375 uninit();
5376
5377 mParent->unregisterMachine(this, id);
5378 // calls VirtualBox::saveSettings()
5379
5380 return S_OK;
5381 }
5382
5383 HRESULT rc = S_OK;
5384
5385 // discard saved state
5386 if (mData->mMachineState == MachineState_Saved)
5387 {
5388 // add the saved state file to the list of files the caller should delete
5389 Assert(!mSSData->strStateFilePath.isEmpty());
5390 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5391
5392 mSSData->strStateFilePath.setNull();
5393
5394 // unconditionally set the machine state to powered off, we now
5395 // know no session has locked the machine
5396 mData->mMachineState = MachineState_PoweredOff;
5397 }
5398
5399 size_t cSnapshots = 0;
5400 if (mData->mFirstSnapshot)
5401 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5402 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5403 // fail now before we start detaching media
5404 return setError(VBOX_E_INVALID_OBJECT_STATE,
5405 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5406 mUserData->s.strName.c_str(), cSnapshots);
5407
5408 // This list collects the medium objects from all medium attachments
5409 // which we will detach from the machine and its snapshots, in a specific
5410 // order which allows for closing all media without getting "media in use"
5411 // errors, simply by going through the list from the front to the back:
5412 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5413 // and must be closed before the parent media from the snapshots, or closing the parents
5414 // will fail because they still have children);
5415 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5416 // the root ("first") snapshot of the machine.
5417 MediaList llMedia;
5418
5419 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5420 && mMediaData->mAttachments.size()
5421 )
5422 {
5423 // we have media attachments: detach them all and add the Medium objects to our list
5424 if (cleanupMode != CleanupMode_UnregisterOnly)
5425 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5426 else
5427 return setError(VBOX_E_INVALID_OBJECT_STATE,
5428 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5429 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5430 }
5431
5432 if (cSnapshots)
5433 {
5434 // autoCleanup must be true here, or we would have failed above
5435
5436 // add the media from the medium attachments of the snapshots to llMedia
5437 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5438 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5439 // into the children first
5440
5441 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5442 MachineState_T oldState = mData->mMachineState;
5443 mData->mMachineState = MachineState_DeletingSnapshot;
5444
5445 // make a copy of the first snapshot so the refcount does not drop to 0
5446 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5447 // because of the AutoCaller voodoo)
5448 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5449
5450 // GO!
5451 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5452
5453 mData->mMachineState = oldState;
5454 }
5455
5456 if (FAILED(rc))
5457 {
5458 rollbackMedia();
5459 return rc;
5460 }
5461
5462 // commit all the media changes made above
5463 commitMedia();
5464
5465 mData->mRegistered = false;
5466
5467 // machine lock no longer needed
5468 alock.release();
5469
5470 // return media to caller
5471 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5472 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5473
5474 mParent->unregisterMachine(this, id);
5475 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5476
5477 return S_OK;
5478}
5479
5480struct Machine::DeleteTask
5481{
5482 ComObjPtr<Machine> pMachine;
5483 RTCList<ComPtr<IMedium> > llMediums;
5484 StringsList llFilesToDelete;
5485 ComObjPtr<Progress> pProgress;
5486};
5487
5488STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5489{
5490 LogFlowFuncEnter();
5491
5492 AutoCaller autoCaller(this);
5493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5494
5495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5496
5497 HRESULT rc = checkStateDependency(MutableStateDep);
5498 if (FAILED(rc)) return rc;
5499
5500 if (mData->mRegistered)
5501 return setError(VBOX_E_INVALID_VM_STATE,
5502 tr("Cannot delete settings of a registered machine"));
5503
5504 DeleteTask *pTask = new DeleteTask;
5505 pTask->pMachine = this;
5506 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5507
5508 // collect files to delete
5509 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5510
5511 for (size_t i = 0; i < sfaMedia.size(); ++i)
5512 {
5513 IMedium *pIMedium(sfaMedia[i]);
5514 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5515 if (pMedium.isNull())
5516 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5517 SafeArray<BSTR> ids;
5518 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5519 if (FAILED(rc)) return rc;
5520 /* At this point the medium should not have any back references
5521 * anymore. If it has it is attached to another VM and *must* not
5522 * deleted. */
5523 if (ids.size() < 1)
5524 pTask->llMediums.append(pMedium);
5525 }
5526 if (mData->pMachineConfigFile->fileExists())
5527 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5528
5529 pTask->pProgress.createObject();
5530 pTask->pProgress->init(getVirtualBox(),
5531 static_cast<IMachine*>(this) /* aInitiator */,
5532 Bstr(tr("Deleting files")).raw(),
5533 true /* fCancellable */,
5534 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5535 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5536
5537 int vrc = RTThreadCreate(NULL,
5538 Machine::deleteThread,
5539 (void*)pTask,
5540 0,
5541 RTTHREADTYPE_MAIN_WORKER,
5542 0,
5543 "MachineDelete");
5544
5545 pTask->pProgress.queryInterfaceTo(aProgress);
5546
5547 if (RT_FAILURE(vrc))
5548 {
5549 delete pTask;
5550 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5551 }
5552
5553 LogFlowFuncLeave();
5554
5555 return S_OK;
5556}
5557
5558/**
5559 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5560 * calls Machine::deleteTaskWorker() on the actual machine object.
5561 * @param Thread
5562 * @param pvUser
5563 * @return
5564 */
5565/*static*/
5566DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5567{
5568 LogFlowFuncEnter();
5569
5570 DeleteTask *pTask = (DeleteTask*)pvUser;
5571 Assert(pTask);
5572 Assert(pTask->pMachine);
5573 Assert(pTask->pProgress);
5574
5575 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5576 pTask->pProgress->notifyComplete(rc);
5577
5578 delete pTask;
5579
5580 LogFlowFuncLeave();
5581
5582 NOREF(Thread);
5583
5584 return VINF_SUCCESS;
5585}
5586
5587/**
5588 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5589 * @param task
5590 * @return
5591 */
5592HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5593{
5594 AutoCaller autoCaller(this);
5595 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5596
5597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5598
5599 HRESULT rc = S_OK;
5600
5601 try
5602 {
5603 ULONG uLogHistoryCount = 3;
5604 ComPtr<ISystemProperties> systemProperties;
5605 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5606 if (FAILED(rc)) throw rc;
5607
5608 if (!systemProperties.isNull())
5609 {
5610 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5611 if (FAILED(rc)) throw rc;
5612 }
5613
5614 MachineState_T oldState = mData->mMachineState;
5615 setMachineState(MachineState_SettingUp);
5616 alock.release();
5617 for (size_t i = 0; i < task.llMediums.size(); ++i)
5618 {
5619 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5620 {
5621 AutoCaller mac(pMedium);
5622 if (FAILED(mac.rc())) throw mac.rc();
5623 Utf8Str strLocation = pMedium->getLocationFull();
5624 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5625 if (FAILED(rc)) throw rc;
5626 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5627 }
5628 ComPtr<IProgress> pProgress2;
5629 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5630 if (FAILED(rc)) throw rc;
5631 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5632 if (FAILED(rc)) throw rc;
5633 /* Check the result of the asynchrony process. */
5634 LONG iRc;
5635 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5636 if (FAILED(rc)) throw rc;
5637 /* If the thread of the progress object has an error, then
5638 * retrieve the error info from there, or it'll be lost. */
5639 if (FAILED(iRc))
5640 throw setError(ProgressErrorInfo(pProgress2));
5641 }
5642 setMachineState(oldState);
5643 alock.acquire();
5644
5645 // delete the files pushed on the task list by Machine::Delete()
5646 // (this includes saved states of the machine and snapshots and
5647 // medium storage files from the IMedium list passed in, and the
5648 // machine XML file)
5649 StringsList::const_iterator it = task.llFilesToDelete.begin();
5650 while (it != task.llFilesToDelete.end())
5651 {
5652 const Utf8Str &strFile = *it;
5653 LogFunc(("Deleting file %s\n", strFile.c_str()));
5654 int vrc = RTFileDelete(strFile.c_str());
5655 if (RT_FAILURE(vrc))
5656 throw setError(VBOX_E_IPRT_ERROR,
5657 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5658
5659 ++it;
5660 if (it == task.llFilesToDelete.end())
5661 {
5662 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5663 if (FAILED(rc)) throw rc;
5664 break;
5665 }
5666
5667 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5668 if (FAILED(rc)) throw rc;
5669 }
5670
5671 /* delete the settings only when the file actually exists */
5672 if (mData->pMachineConfigFile->fileExists())
5673 {
5674 /* Delete any backup or uncommitted XML files. Ignore failures.
5675 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5676 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5677 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5678 RTFileDelete(otherXml.c_str());
5679 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5680 RTFileDelete(otherXml.c_str());
5681
5682 /* delete the Logs folder, nothing important should be left
5683 * there (we don't check for errors because the user might have
5684 * some private files there that we don't want to delete) */
5685 Utf8Str logFolder;
5686 getLogFolder(logFolder);
5687 Assert(logFolder.length());
5688 if (RTDirExists(logFolder.c_str()))
5689 {
5690 /* Delete all VBox.log[.N] files from the Logs folder
5691 * (this must be in sync with the rotation logic in
5692 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5693 * files that may have been created by the GUI. */
5694 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5695 logFolder.c_str(), RTPATH_DELIMITER);
5696 RTFileDelete(log.c_str());
5697 log = Utf8StrFmt("%s%cVBox.png",
5698 logFolder.c_str(), RTPATH_DELIMITER);
5699 RTFileDelete(log.c_str());
5700 for (int i = uLogHistoryCount; i > 0; i--)
5701 {
5702 log = Utf8StrFmt("%s%cVBox.log.%d",
5703 logFolder.c_str(), RTPATH_DELIMITER, i);
5704 RTFileDelete(log.c_str());
5705 log = Utf8StrFmt("%s%cVBox.png.%d",
5706 logFolder.c_str(), RTPATH_DELIMITER, i);
5707 RTFileDelete(log.c_str());
5708 }
5709
5710 RTDirRemove(logFolder.c_str());
5711 }
5712
5713 /* delete the Snapshots folder, nothing important should be left
5714 * there (we don't check for errors because the user might have
5715 * some private files there that we don't want to delete) */
5716 Utf8Str strFullSnapshotFolder;
5717 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5718 Assert(!strFullSnapshotFolder.isEmpty());
5719 if (RTDirExists(strFullSnapshotFolder.c_str()))
5720 RTDirRemove(strFullSnapshotFolder.c_str());
5721
5722 // delete the directory that contains the settings file, but only
5723 // if it matches the VM name
5724 Utf8Str settingsDir;
5725 if (isInOwnDir(&settingsDir))
5726 RTDirRemove(settingsDir.c_str());
5727 }
5728
5729 alock.release();
5730
5731 mParent->saveModifiedRegistries();
5732 }
5733 catch (HRESULT aRC) { rc = aRC; }
5734
5735 return rc;
5736}
5737
5738STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5739{
5740 CheckComArgOutPointerValid(aSnapshot);
5741
5742 AutoCaller autoCaller(this);
5743 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5744
5745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5746
5747 ComObjPtr<Snapshot> pSnapshot;
5748 HRESULT rc;
5749
5750 if (!aNameOrId || !*aNameOrId)
5751 // null case (caller wants root snapshot): findSnapshotById() handles this
5752 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5753 else
5754 {
5755 Guid uuid(aNameOrId);
5756 if (uuid.isValid())
5757 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5758 else
5759 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5760 }
5761 pSnapshot.queryInterfaceTo(aSnapshot);
5762
5763 return rc;
5764}
5765
5766STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5767{
5768 CheckComArgStrNotEmptyOrNull(aName);
5769 CheckComArgStrNotEmptyOrNull(aHostPath);
5770
5771 AutoCaller autoCaller(this);
5772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5773
5774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5775
5776 HRESULT rc = checkStateDependency(MutableStateDep);
5777 if (FAILED(rc)) return rc;
5778
5779 Utf8Str strName(aName);
5780
5781 ComObjPtr<SharedFolder> sharedFolder;
5782 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5783 if (SUCCEEDED(rc))
5784 return setError(VBOX_E_OBJECT_IN_USE,
5785 tr("Shared folder named '%s' already exists"),
5786 strName.c_str());
5787
5788 sharedFolder.createObject();
5789 rc = sharedFolder->init(getMachine(),
5790 strName,
5791 aHostPath,
5792 !!aWritable,
5793 !!aAutoMount,
5794 true /* fFailOnError */);
5795 if (FAILED(rc)) return rc;
5796
5797 setModified(IsModified_SharedFolders);
5798 mHWData.backup();
5799 mHWData->mSharedFolders.push_back(sharedFolder);
5800
5801 /* inform the direct session if any */
5802 alock.release();
5803 onSharedFolderChange();
5804
5805 return S_OK;
5806}
5807
5808STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5809{
5810 CheckComArgStrNotEmptyOrNull(aName);
5811
5812 AutoCaller autoCaller(this);
5813 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5814
5815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5816
5817 HRESULT rc = checkStateDependency(MutableStateDep);
5818 if (FAILED(rc)) return rc;
5819
5820 ComObjPtr<SharedFolder> sharedFolder;
5821 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5822 if (FAILED(rc)) return rc;
5823
5824 setModified(IsModified_SharedFolders);
5825 mHWData.backup();
5826 mHWData->mSharedFolders.remove(sharedFolder);
5827
5828 /* inform the direct session if any */
5829 alock.release();
5830 onSharedFolderChange();
5831
5832 return S_OK;
5833}
5834
5835STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5836{
5837 CheckComArgOutPointerValid(aCanShow);
5838
5839 /* start with No */
5840 *aCanShow = FALSE;
5841
5842 AutoCaller autoCaller(this);
5843 AssertComRCReturnRC(autoCaller.rc());
5844
5845 ComPtr<IInternalSessionControl> directControl;
5846 {
5847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5848
5849 if (mData->mSession.mState != SessionState_Locked)
5850 return setError(VBOX_E_INVALID_VM_STATE,
5851 tr("Machine is not locked for session (session state: %s)"),
5852 Global::stringifySessionState(mData->mSession.mState));
5853
5854 directControl = mData->mSession.mDirectControl;
5855 }
5856
5857 /* ignore calls made after #OnSessionEnd() is called */
5858 if (!directControl)
5859 return S_OK;
5860
5861 LONG64 dummy;
5862 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5863}
5864
5865STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5866{
5867 CheckComArgOutPointerValid(aWinId);
5868
5869 AutoCaller autoCaller(this);
5870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5871
5872 ComPtr<IInternalSessionControl> directControl;
5873 {
5874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5875
5876 if (mData->mSession.mState != SessionState_Locked)
5877 return setError(E_FAIL,
5878 tr("Machine is not locked for session (session state: %s)"),
5879 Global::stringifySessionState(mData->mSession.mState));
5880
5881 directControl = mData->mSession.mDirectControl;
5882 }
5883
5884 /* ignore calls made after #OnSessionEnd() is called */
5885 if (!directControl)
5886 return S_OK;
5887
5888 BOOL dummy;
5889 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5890}
5891
5892#ifdef VBOX_WITH_GUEST_PROPS
5893/**
5894 * Look up a guest property in VBoxSVC's internal structures.
5895 */
5896HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5897 BSTR *aValue,
5898 LONG64 *aTimestamp,
5899 BSTR *aFlags) const
5900{
5901 using namespace guestProp;
5902
5903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5904 Utf8Str strName(aName);
5905 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5906
5907 if (it != mHWData->mGuestProperties.end())
5908 {
5909 char szFlags[MAX_FLAGS_LEN + 1];
5910 it->second.strValue.cloneTo(aValue);
5911 *aTimestamp = it->second.mTimestamp;
5912 writeFlags(it->second.mFlags, szFlags);
5913 Bstr(szFlags).cloneTo(aFlags);
5914 }
5915
5916 return S_OK;
5917}
5918
5919/**
5920 * Query the VM that a guest property belongs to for the property.
5921 * @returns E_ACCESSDENIED if the VM process is not available or not
5922 * currently handling queries and the lookup should then be done in
5923 * VBoxSVC.
5924 */
5925HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5926 BSTR *aValue,
5927 LONG64 *aTimestamp,
5928 BSTR *aFlags) const
5929{
5930 HRESULT rc;
5931 ComPtr<IInternalSessionControl> directControl;
5932 directControl = mData->mSession.mDirectControl;
5933
5934 /* fail if we were called after #OnSessionEnd() is called. This is a
5935 * silly race condition. */
5936
5937 /** @todo This code is bothering API clients (like python script clients) with
5938 * the AccessGuestProperty call, creating unncessary IPC. Need to
5939 * have a way of figuring out which kind of direct session it is... */
5940 if (!directControl)
5941 rc = E_ACCESSDENIED;
5942 else
5943 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5944 false /* isSetter */,
5945 aValue, aTimestamp, aFlags);
5946 return rc;
5947}
5948#endif // VBOX_WITH_GUEST_PROPS
5949
5950STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5951 BSTR *aValue,
5952 LONG64 *aTimestamp,
5953 BSTR *aFlags)
5954{
5955#ifndef VBOX_WITH_GUEST_PROPS
5956 ReturnComNotImplemented();
5957#else // VBOX_WITH_GUEST_PROPS
5958 CheckComArgStrNotEmptyOrNull(aName);
5959 CheckComArgOutPointerValid(aValue);
5960 CheckComArgOutPointerValid(aTimestamp);
5961 CheckComArgOutPointerValid(aFlags);
5962
5963 AutoCaller autoCaller(this);
5964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5965
5966 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5967 if (rc == E_ACCESSDENIED)
5968 /* The VM is not running or the service is not (yet) accessible */
5969 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5970 return rc;
5971#endif // VBOX_WITH_GUEST_PROPS
5972}
5973
5974STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5975{
5976 LONG64 dummyTimestamp;
5977 Bstr dummyFlags;
5978 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5979}
5980
5981STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5982{
5983 Bstr dummyValue;
5984 Bstr dummyFlags;
5985 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5986}
5987
5988#ifdef VBOX_WITH_GUEST_PROPS
5989/**
5990 * Set a guest property in VBoxSVC's internal structures.
5991 */
5992HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5993 IN_BSTR aFlags)
5994{
5995 using namespace guestProp;
5996
5997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5998 HRESULT rc = S_OK;
5999
6000 rc = checkStateDependency(MutableStateDep);
6001 if (FAILED(rc)) return rc;
6002
6003 try
6004 {
6005 Utf8Str utf8Name(aName);
6006 Utf8Str utf8Flags(aFlags);
6007 uint32_t fFlags = NILFLAG;
6008 if ( aFlags != NULL
6009 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6010 return setError(E_INVALIDARG,
6011 tr("Invalid guest property flag values: '%ls'"),
6012 aFlags);
6013
6014 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6015 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6016 if (it == mHWData->mGuestProperties.end())
6017 {
6018 if (!fDelete)
6019 {
6020 setModified(IsModified_MachineData);
6021 mHWData.backupEx();
6022
6023 RTTIMESPEC time;
6024 HWData::GuestProperty prop;
6025 prop.strValue = aValue;
6026 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6027 prop.mFlags = fFlags;
6028 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6029 }
6030 }
6031 else
6032 {
6033 if (it->second.mFlags & (RDONLYHOST))
6034 {
6035 rc = setError(E_ACCESSDENIED,
6036 tr("The property '%ls' cannot be changed by the host"),
6037 aName);
6038 }
6039 else
6040 {
6041 setModified(IsModified_MachineData);
6042 mHWData.backupEx();
6043
6044 /* The backupEx() operation invalidates our iterator,
6045 * so get a new one. */
6046 it = mHWData->mGuestProperties.find(utf8Name);
6047 Assert(it != mHWData->mGuestProperties.end());
6048
6049 if (!fDelete)
6050 {
6051 RTTIMESPEC time;
6052 it->second.strValue = aValue;
6053 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6054 it->second.mFlags = fFlags;
6055 }
6056 else
6057 mHWData->mGuestProperties.erase(it);
6058 }
6059 }
6060
6061 if ( SUCCEEDED(rc)
6062 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6063 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6064 RTSTR_MAX,
6065 utf8Name.c_str(),
6066 RTSTR_MAX,
6067 NULL)
6068 )
6069 )
6070 {
6071 alock.release();
6072
6073 mParent->onGuestPropertyChange(mData->mUuid, aName,
6074 aValue ? aValue : Bstr("").raw(),
6075 aFlags ? aFlags : Bstr("").raw());
6076 }
6077 }
6078 catch (std::bad_alloc &)
6079 {
6080 rc = E_OUTOFMEMORY;
6081 }
6082
6083 return rc;
6084}
6085
6086/**
6087 * Set a property on the VM that that property belongs to.
6088 * @returns E_ACCESSDENIED if the VM process is not available or not
6089 * currently handling queries and the setting should then be done in
6090 * VBoxSVC.
6091 */
6092HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6093 IN_BSTR aFlags)
6094{
6095 HRESULT rc;
6096
6097 try
6098 {
6099 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6100
6101 BSTR dummy = NULL; /* will not be changed (setter) */
6102 LONG64 dummy64;
6103 if (!directControl)
6104 rc = E_ACCESSDENIED;
6105 else
6106 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6107 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6108 true /* isSetter */,
6109 &dummy, &dummy64, &dummy);
6110 }
6111 catch (std::bad_alloc &)
6112 {
6113 rc = E_OUTOFMEMORY;
6114 }
6115
6116 return rc;
6117}
6118#endif // VBOX_WITH_GUEST_PROPS
6119
6120STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6121 IN_BSTR aFlags)
6122{
6123#ifndef VBOX_WITH_GUEST_PROPS
6124 ReturnComNotImplemented();
6125#else // VBOX_WITH_GUEST_PROPS
6126 CheckComArgStrNotEmptyOrNull(aName);
6127 CheckComArgMaybeNull(aFlags);
6128 CheckComArgMaybeNull(aValue);
6129
6130 AutoCaller autoCaller(this);
6131 if (FAILED(autoCaller.rc()))
6132 return autoCaller.rc();
6133
6134 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6135 if (rc == E_ACCESSDENIED)
6136 /* The VM is not running or the service is not (yet) accessible */
6137 rc = setGuestPropertyToService(aName, aValue, aFlags);
6138 return rc;
6139#endif // VBOX_WITH_GUEST_PROPS
6140}
6141
6142STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6143{
6144 return SetGuestProperty(aName, aValue, NULL);
6145}
6146
6147STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6148{
6149 return SetGuestProperty(aName, NULL, NULL);
6150}
6151
6152#ifdef VBOX_WITH_GUEST_PROPS
6153/**
6154 * Enumerate the guest properties in VBoxSVC's internal structures.
6155 */
6156HRESULT Machine::enumerateGuestPropertiesInService
6157 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6158 ComSafeArrayOut(BSTR, aValues),
6159 ComSafeArrayOut(LONG64, aTimestamps),
6160 ComSafeArrayOut(BSTR, aFlags))
6161{
6162 using namespace guestProp;
6163
6164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6165 Utf8Str strPatterns(aPatterns);
6166
6167 HWData::GuestPropertyMap propMap;
6168
6169 /*
6170 * Look for matching patterns and build up a list.
6171 */
6172 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6173 while (it != mHWData->mGuestProperties.end())
6174 {
6175 if ( strPatterns.isEmpty()
6176 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6177 RTSTR_MAX,
6178 it->first.c_str(),
6179 RTSTR_MAX,
6180 NULL)
6181 )
6182 {
6183 propMap.insert(*it);
6184 }
6185
6186 it++;
6187 }
6188
6189 alock.release();
6190
6191 /*
6192 * And build up the arrays for returning the property information.
6193 */
6194 size_t cEntries = propMap.size();
6195 SafeArray<BSTR> names(cEntries);
6196 SafeArray<BSTR> values(cEntries);
6197 SafeArray<LONG64> timestamps(cEntries);
6198 SafeArray<BSTR> flags(cEntries);
6199 size_t iProp = 0;
6200
6201 it = propMap.begin();
6202 while (it != propMap.end())
6203 {
6204 char szFlags[MAX_FLAGS_LEN + 1];
6205 it->first.cloneTo(&names[iProp]);
6206 it->second.strValue.cloneTo(&values[iProp]);
6207 timestamps[iProp] = it->second.mTimestamp;
6208 writeFlags(it->second.mFlags, szFlags);
6209 Bstr(szFlags).cloneTo(&flags[iProp++]);
6210 it++;
6211 }
6212 names.detachTo(ComSafeArrayOutArg(aNames));
6213 values.detachTo(ComSafeArrayOutArg(aValues));
6214 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6215 flags.detachTo(ComSafeArrayOutArg(aFlags));
6216 return S_OK;
6217}
6218
6219/**
6220 * Enumerate the properties managed by a VM.
6221 * @returns E_ACCESSDENIED if the VM process is not available or not
6222 * currently handling queries and the setting should then be done in
6223 * VBoxSVC.
6224 */
6225HRESULT Machine::enumerateGuestPropertiesOnVM
6226 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6227 ComSafeArrayOut(BSTR, aValues),
6228 ComSafeArrayOut(LONG64, aTimestamps),
6229 ComSafeArrayOut(BSTR, aFlags))
6230{
6231 HRESULT rc;
6232 ComPtr<IInternalSessionControl> directControl;
6233 directControl = mData->mSession.mDirectControl;
6234
6235 if (!directControl)
6236 rc = E_ACCESSDENIED;
6237 else
6238 rc = directControl->EnumerateGuestProperties
6239 (aPatterns, ComSafeArrayOutArg(aNames),
6240 ComSafeArrayOutArg(aValues),
6241 ComSafeArrayOutArg(aTimestamps),
6242 ComSafeArrayOutArg(aFlags));
6243 return rc;
6244}
6245#endif // VBOX_WITH_GUEST_PROPS
6246
6247STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6248 ComSafeArrayOut(BSTR, aNames),
6249 ComSafeArrayOut(BSTR, aValues),
6250 ComSafeArrayOut(LONG64, aTimestamps),
6251 ComSafeArrayOut(BSTR, aFlags))
6252{
6253#ifndef VBOX_WITH_GUEST_PROPS
6254 ReturnComNotImplemented();
6255#else // VBOX_WITH_GUEST_PROPS
6256 CheckComArgMaybeNull(aPatterns);
6257 CheckComArgOutSafeArrayPointerValid(aNames);
6258 CheckComArgOutSafeArrayPointerValid(aValues);
6259 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6260 CheckComArgOutSafeArrayPointerValid(aFlags);
6261
6262 AutoCaller autoCaller(this);
6263 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6264
6265 HRESULT rc = enumerateGuestPropertiesOnVM
6266 (aPatterns, ComSafeArrayOutArg(aNames),
6267 ComSafeArrayOutArg(aValues),
6268 ComSafeArrayOutArg(aTimestamps),
6269 ComSafeArrayOutArg(aFlags));
6270 if (rc == E_ACCESSDENIED)
6271 /* The VM is not running or the service is not (yet) accessible */
6272 rc = enumerateGuestPropertiesInService
6273 (aPatterns, ComSafeArrayOutArg(aNames),
6274 ComSafeArrayOutArg(aValues),
6275 ComSafeArrayOutArg(aTimestamps),
6276 ComSafeArrayOutArg(aFlags));
6277 return rc;
6278#endif // VBOX_WITH_GUEST_PROPS
6279}
6280
6281STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6282 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6283{
6284 MediaData::AttachmentList atts;
6285
6286 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6287 if (FAILED(rc)) return rc;
6288
6289 SafeIfaceArray<IMediumAttachment> attachments(atts);
6290 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6291
6292 return S_OK;
6293}
6294
6295STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6296 LONG aControllerPort,
6297 LONG aDevice,
6298 IMediumAttachment **aAttachment)
6299{
6300 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6301 aControllerName, aControllerPort, aDevice));
6302
6303 CheckComArgStrNotEmptyOrNull(aControllerName);
6304 CheckComArgOutPointerValid(aAttachment);
6305
6306 AutoCaller autoCaller(this);
6307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6308
6309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6310
6311 *aAttachment = NULL;
6312
6313 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6314 aControllerName,
6315 aControllerPort,
6316 aDevice);
6317 if (pAttach.isNull())
6318 return setError(VBOX_E_OBJECT_NOT_FOUND,
6319 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6320 aDevice, aControllerPort, aControllerName);
6321
6322 pAttach.queryInterfaceTo(aAttachment);
6323
6324 return S_OK;
6325}
6326
6327STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6328 StorageBus_T aConnectionType,
6329 IStorageController **controller)
6330{
6331 CheckComArgStrNotEmptyOrNull(aName);
6332
6333 if ( (aConnectionType <= StorageBus_Null)
6334 || (aConnectionType > StorageBus_SAS))
6335 return setError(E_INVALIDARG,
6336 tr("Invalid connection type: %d"),
6337 aConnectionType);
6338
6339 AutoCaller autoCaller(this);
6340 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6341
6342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6343
6344 HRESULT rc = checkStateDependency(MutableStateDep);
6345 if (FAILED(rc)) return rc;
6346
6347 /* try to find one with the name first. */
6348 ComObjPtr<StorageController> ctrl;
6349
6350 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6351 if (SUCCEEDED(rc))
6352 return setError(VBOX_E_OBJECT_IN_USE,
6353 tr("Storage controller named '%ls' already exists"),
6354 aName);
6355
6356 ctrl.createObject();
6357
6358 /* get a new instance number for the storage controller */
6359 ULONG ulInstance = 0;
6360 bool fBootable = true;
6361 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6362 it != mStorageControllers->end();
6363 ++it)
6364 {
6365 if ((*it)->getStorageBus() == aConnectionType)
6366 {
6367 ULONG ulCurInst = (*it)->getInstance();
6368
6369 if (ulCurInst >= ulInstance)
6370 ulInstance = ulCurInst + 1;
6371
6372 /* Only one controller of each type can be marked as bootable. */
6373 if ((*it)->getBootable())
6374 fBootable = false;
6375 }
6376 }
6377
6378 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6379 if (FAILED(rc)) return rc;
6380
6381 setModified(IsModified_Storage);
6382 mStorageControllers.backup();
6383 mStorageControllers->push_back(ctrl);
6384
6385 ctrl.queryInterfaceTo(controller);
6386
6387 /* inform the direct session if any */
6388 alock.release();
6389 onStorageControllerChange();
6390
6391 return S_OK;
6392}
6393
6394STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6395 IStorageController **aStorageController)
6396{
6397 CheckComArgStrNotEmptyOrNull(aName);
6398
6399 AutoCaller autoCaller(this);
6400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6401
6402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6403
6404 ComObjPtr<StorageController> ctrl;
6405
6406 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6407 if (SUCCEEDED(rc))
6408 ctrl.queryInterfaceTo(aStorageController);
6409
6410 return rc;
6411}
6412
6413STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6414 IStorageController **aStorageController)
6415{
6416 AutoCaller autoCaller(this);
6417 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6418
6419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6420
6421 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6422 it != mStorageControllers->end();
6423 ++it)
6424 {
6425 if ((*it)->getInstance() == aInstance)
6426 {
6427 (*it).queryInterfaceTo(aStorageController);
6428 return S_OK;
6429 }
6430 }
6431
6432 return setError(VBOX_E_OBJECT_NOT_FOUND,
6433 tr("Could not find a storage controller with instance number '%lu'"),
6434 aInstance);
6435}
6436
6437STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6438{
6439 AutoCaller autoCaller(this);
6440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6441
6442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6443
6444 HRESULT rc = checkStateDependency(MutableStateDep);
6445 if (FAILED(rc)) return rc;
6446
6447 ComObjPtr<StorageController> ctrl;
6448
6449 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6450 if (SUCCEEDED(rc))
6451 {
6452 /* Ensure that only one controller of each type is marked as bootable. */
6453 if (fBootable == TRUE)
6454 {
6455 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6456 it != mStorageControllers->end();
6457 ++it)
6458 {
6459 ComObjPtr<StorageController> aCtrl = (*it);
6460
6461 if ( (aCtrl->getName() != Utf8Str(aName))
6462 && aCtrl->getBootable() == TRUE
6463 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6464 && aCtrl->getControllerType() == ctrl->getControllerType())
6465 {
6466 aCtrl->setBootable(FALSE);
6467 break;
6468 }
6469 }
6470 }
6471
6472 if (SUCCEEDED(rc))
6473 {
6474 ctrl->setBootable(fBootable);
6475 setModified(IsModified_Storage);
6476 }
6477 }
6478
6479 if (SUCCEEDED(rc))
6480 {
6481 /* inform the direct session if any */
6482 alock.release();
6483 onStorageControllerChange();
6484 }
6485
6486 return rc;
6487}
6488
6489STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6490{
6491 CheckComArgStrNotEmptyOrNull(aName);
6492
6493 AutoCaller autoCaller(this);
6494 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6495
6496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6497
6498 HRESULT rc = checkStateDependency(MutableStateDep);
6499 if (FAILED(rc)) return rc;
6500
6501 ComObjPtr<StorageController> ctrl;
6502 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6503 if (FAILED(rc)) return rc;
6504
6505 {
6506 /* find all attached devices to the appropriate storage controller and detach them all */
6507 // make a temporary list because detachDevice invalidates iterators into
6508 // mMediaData->mAttachments
6509 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6510
6511 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6512 it != llAttachments2.end();
6513 ++it)
6514 {
6515 MediumAttachment *pAttachTemp = *it;
6516
6517 AutoCaller localAutoCaller(pAttachTemp);
6518 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6519
6520 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6521
6522 if (pAttachTemp->getControllerName() == aName)
6523 {
6524 rc = detachDevice(pAttachTemp, alock, NULL);
6525 if (FAILED(rc)) return rc;
6526 }
6527 }
6528 }
6529
6530 /* We can remove it now. */
6531 setModified(IsModified_Storage);
6532 mStorageControllers.backup();
6533
6534 ctrl->unshare();
6535
6536 mStorageControllers->remove(ctrl);
6537
6538 /* inform the direct session if any */
6539 alock.release();
6540 onStorageControllerChange();
6541
6542 return S_OK;
6543}
6544
6545STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6546 IUSBController **controller)
6547{
6548 if ( (aType <= USBControllerType_Null)
6549 || (aType >= USBControllerType_Last))
6550 return setError(E_INVALIDARG,
6551 tr("Invalid USB controller type: %d"),
6552 aType);
6553
6554 AutoCaller autoCaller(this);
6555 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6556
6557 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6558
6559 HRESULT rc = checkStateDependency(MutableStateDep);
6560 if (FAILED(rc)) return rc;
6561
6562 /* try to find one with the same type first. */
6563 ComObjPtr<USBController> ctrl;
6564
6565 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6566 if (SUCCEEDED(rc))
6567 return setError(VBOX_E_OBJECT_IN_USE,
6568 tr("USB controller named '%ls' already exists"),
6569 aName);
6570
6571 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6572 ULONG maxInstances;
6573 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6574 if (FAILED(rc))
6575 return rc;
6576
6577 ULONG cInstances = getUSBControllerCountByType(aType);
6578 if (cInstances >= maxInstances)
6579 return setError(E_INVALIDARG,
6580 tr("Too many USB controllers of this type"));
6581
6582 ctrl.createObject();
6583
6584 rc = ctrl->init(this, aName, aType);
6585 if (FAILED(rc)) return rc;
6586
6587 setModified(IsModified_USB);
6588 mUSBControllers.backup();
6589 mUSBControllers->push_back(ctrl);
6590
6591 ctrl.queryInterfaceTo(controller);
6592
6593 /* inform the direct session if any */
6594 alock.release();
6595 onUSBControllerChange();
6596
6597 return S_OK;
6598}
6599
6600STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6601{
6602 CheckComArgStrNotEmptyOrNull(aName);
6603
6604 AutoCaller autoCaller(this);
6605 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6606
6607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6608
6609 ComObjPtr<USBController> ctrl;
6610
6611 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6612 if (SUCCEEDED(rc))
6613 ctrl.queryInterfaceTo(aUSBController);
6614
6615 return rc;
6616}
6617
6618STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6619 ULONG *aControllers)
6620{
6621 CheckComArgOutPointerValid(aControllers);
6622
6623 if ( (aType <= USBControllerType_Null)
6624 || (aType >= USBControllerType_Last))
6625 return setError(E_INVALIDARG,
6626 tr("Invalid USB controller type: %d"),
6627 aType);
6628
6629 AutoCaller autoCaller(this);
6630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6631
6632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6633
6634 ComObjPtr<USBController> ctrl;
6635
6636 *aControllers = getUSBControllerCountByType(aType);
6637
6638 return S_OK;
6639}
6640
6641STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6642{
6643 CheckComArgStrNotEmptyOrNull(aName);
6644
6645 AutoCaller autoCaller(this);
6646 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6647
6648 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6649
6650 HRESULT rc = checkStateDependency(MutableStateDep);
6651 if (FAILED(rc)) return rc;
6652
6653 ComObjPtr<USBController> ctrl;
6654 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6655 if (FAILED(rc)) return rc;
6656
6657 setModified(IsModified_USB);
6658 mUSBControllers.backup();
6659
6660 ctrl->unshare();
6661
6662 mUSBControllers->remove(ctrl);
6663
6664 /* inform the direct session if any */
6665 alock.release();
6666 onUSBControllerChange();
6667
6668 return S_OK;
6669}
6670
6671STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6672 ULONG *puOriginX,
6673 ULONG *puOriginY,
6674 ULONG *puWidth,
6675 ULONG *puHeight,
6676 BOOL *pfEnabled)
6677{
6678 LogFlowThisFunc(("\n"));
6679
6680 CheckComArgNotNull(puOriginX);
6681 CheckComArgNotNull(puOriginY);
6682 CheckComArgNotNull(puWidth);
6683 CheckComArgNotNull(puHeight);
6684 CheckComArgNotNull(pfEnabled);
6685
6686 uint32_t u32OriginX= 0;
6687 uint32_t u32OriginY= 0;
6688 uint32_t u32Width = 0;
6689 uint32_t u32Height = 0;
6690 uint16_t u16Flags = 0;
6691
6692 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6693 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6694 if (RT_FAILURE(vrc))
6695 {
6696#ifdef RT_OS_WINDOWS
6697 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6698 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6699 * So just assign fEnable to TRUE again.
6700 * The right fix would be to change GUI API wrappers to make sure that parameters
6701 * are changed only if API succeeds.
6702 */
6703 *pfEnabled = TRUE;
6704#endif
6705 return setError(VBOX_E_IPRT_ERROR,
6706 tr("Saved guest size is not available (%Rrc)"),
6707 vrc);
6708 }
6709
6710 *puOriginX = u32OriginX;
6711 *puOriginY = u32OriginY;
6712 *puWidth = u32Width;
6713 *puHeight = u32Height;
6714 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6715
6716 return S_OK;
6717}
6718
6719STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6720{
6721 LogFlowThisFunc(("\n"));
6722
6723 CheckComArgNotNull(aSize);
6724 CheckComArgNotNull(aWidth);
6725 CheckComArgNotNull(aHeight);
6726
6727 if (aScreenId != 0)
6728 return E_NOTIMPL;
6729
6730 AutoCaller autoCaller(this);
6731 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6732
6733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6734
6735 uint8_t *pu8Data = NULL;
6736 uint32_t cbData = 0;
6737 uint32_t u32Width = 0;
6738 uint32_t u32Height = 0;
6739
6740 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6741
6742 if (RT_FAILURE(vrc))
6743 return setError(VBOX_E_IPRT_ERROR,
6744 tr("Saved screenshot data is not available (%Rrc)"),
6745 vrc);
6746
6747 *aSize = cbData;
6748 *aWidth = u32Width;
6749 *aHeight = u32Height;
6750
6751 freeSavedDisplayScreenshot(pu8Data);
6752
6753 return S_OK;
6754}
6755
6756STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6757{
6758 LogFlowThisFunc(("\n"));
6759
6760 CheckComArgNotNull(aWidth);
6761 CheckComArgNotNull(aHeight);
6762 CheckComArgOutSafeArrayPointerValid(aData);
6763
6764 if (aScreenId != 0)
6765 return E_NOTIMPL;
6766
6767 AutoCaller autoCaller(this);
6768 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6769
6770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6771
6772 uint8_t *pu8Data = NULL;
6773 uint32_t cbData = 0;
6774 uint32_t u32Width = 0;
6775 uint32_t u32Height = 0;
6776
6777 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6778
6779 if (RT_FAILURE(vrc))
6780 return setError(VBOX_E_IPRT_ERROR,
6781 tr("Saved screenshot data is not available (%Rrc)"),
6782 vrc);
6783
6784 *aWidth = u32Width;
6785 *aHeight = u32Height;
6786
6787 com::SafeArray<BYTE> bitmap(cbData);
6788 /* Convert pixels to format expected by the API caller. */
6789 if (aBGR)
6790 {
6791 /* [0] B, [1] G, [2] R, [3] A. */
6792 for (unsigned i = 0; i < cbData; i += 4)
6793 {
6794 bitmap[i] = pu8Data[i];
6795 bitmap[i + 1] = pu8Data[i + 1];
6796 bitmap[i + 2] = pu8Data[i + 2];
6797 bitmap[i + 3] = 0xff;
6798 }
6799 }
6800 else
6801 {
6802 /* [0] R, [1] G, [2] B, [3] A. */
6803 for (unsigned i = 0; i < cbData; i += 4)
6804 {
6805 bitmap[i] = pu8Data[i + 2];
6806 bitmap[i + 1] = pu8Data[i + 1];
6807 bitmap[i + 2] = pu8Data[i];
6808 bitmap[i + 3] = 0xff;
6809 }
6810 }
6811 bitmap.detachTo(ComSafeArrayOutArg(aData));
6812
6813 freeSavedDisplayScreenshot(pu8Data);
6814
6815 return S_OK;
6816}
6817
6818
6819STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6820{
6821 LogFlowThisFunc(("\n"));
6822
6823 CheckComArgNotNull(aWidth);
6824 CheckComArgNotNull(aHeight);
6825 CheckComArgOutSafeArrayPointerValid(aData);
6826
6827 if (aScreenId != 0)
6828 return E_NOTIMPL;
6829
6830 AutoCaller autoCaller(this);
6831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6832
6833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6834
6835 uint8_t *pu8Data = NULL;
6836 uint32_t cbData = 0;
6837 uint32_t u32Width = 0;
6838 uint32_t u32Height = 0;
6839
6840 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6841
6842 if (RT_FAILURE(vrc))
6843 return setError(VBOX_E_IPRT_ERROR,
6844 tr("Saved screenshot data is not available (%Rrc)"),
6845 vrc);
6846
6847 *aWidth = u32Width;
6848 *aHeight = u32Height;
6849
6850 HRESULT rc = S_OK;
6851 uint8_t *pu8PNG = NULL;
6852 uint32_t cbPNG = 0;
6853 uint32_t cxPNG = 0;
6854 uint32_t cyPNG = 0;
6855
6856 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6857
6858 if (RT_SUCCESS(vrc))
6859 {
6860 com::SafeArray<BYTE> screenData(cbPNG);
6861 screenData.initFrom(pu8PNG, cbPNG);
6862 if (pu8PNG)
6863 RTMemFree(pu8PNG);
6864 screenData.detachTo(ComSafeArrayOutArg(aData));
6865 }
6866 else
6867 {
6868 if (pu8PNG)
6869 RTMemFree(pu8PNG);
6870 return setError(VBOX_E_IPRT_ERROR,
6871 tr("Could not convert screenshot to PNG (%Rrc)"),
6872 vrc);
6873 }
6874
6875 freeSavedDisplayScreenshot(pu8Data);
6876
6877 return rc;
6878}
6879
6880STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6881{
6882 LogFlowThisFunc(("\n"));
6883
6884 CheckComArgNotNull(aSize);
6885 CheckComArgNotNull(aWidth);
6886 CheckComArgNotNull(aHeight);
6887
6888 if (aScreenId != 0)
6889 return E_NOTIMPL;
6890
6891 AutoCaller autoCaller(this);
6892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6893
6894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6895
6896 uint8_t *pu8Data = NULL;
6897 uint32_t cbData = 0;
6898 uint32_t u32Width = 0;
6899 uint32_t u32Height = 0;
6900
6901 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6902
6903 if (RT_FAILURE(vrc))
6904 return setError(VBOX_E_IPRT_ERROR,
6905 tr("Saved screenshot data is not available (%Rrc)"),
6906 vrc);
6907
6908 *aSize = cbData;
6909 *aWidth = u32Width;
6910 *aHeight = u32Height;
6911
6912 freeSavedDisplayScreenshot(pu8Data);
6913
6914 return S_OK;
6915}
6916
6917STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6918{
6919 LogFlowThisFunc(("\n"));
6920
6921 CheckComArgNotNull(aWidth);
6922 CheckComArgNotNull(aHeight);
6923 CheckComArgOutSafeArrayPointerValid(aData);
6924
6925 if (aScreenId != 0)
6926 return E_NOTIMPL;
6927
6928 AutoCaller autoCaller(this);
6929 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6930
6931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6932
6933 uint8_t *pu8Data = NULL;
6934 uint32_t cbData = 0;
6935 uint32_t u32Width = 0;
6936 uint32_t u32Height = 0;
6937
6938 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6939
6940 if (RT_FAILURE(vrc))
6941 return setError(VBOX_E_IPRT_ERROR,
6942 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6943 vrc);
6944
6945 *aWidth = u32Width;
6946 *aHeight = u32Height;
6947
6948 com::SafeArray<BYTE> png(cbData);
6949 png.initFrom(pu8Data, cbData);
6950 png.detachTo(ComSafeArrayOutArg(aData));
6951
6952 freeSavedDisplayScreenshot(pu8Data);
6953
6954 return S_OK;
6955}
6956
6957STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6958{
6959 HRESULT rc = S_OK;
6960 LogFlowThisFunc(("\n"));
6961
6962 AutoCaller autoCaller(this);
6963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6964
6965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6966
6967 if (!mHWData->mCPUHotPlugEnabled)
6968 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6969
6970 if (aCpu >= mHWData->mCPUCount)
6971 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6972
6973 if (mHWData->mCPUAttached[aCpu])
6974 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6975
6976 alock.release();
6977 rc = onCPUChange(aCpu, false);
6978 alock.acquire();
6979 if (FAILED(rc)) return rc;
6980
6981 setModified(IsModified_MachineData);
6982 mHWData.backup();
6983 mHWData->mCPUAttached[aCpu] = true;
6984
6985 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6986 if (Global::IsOnline(mData->mMachineState))
6987 saveSettings(NULL);
6988
6989 return S_OK;
6990}
6991
6992STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6993{
6994 HRESULT rc = S_OK;
6995 LogFlowThisFunc(("\n"));
6996
6997 AutoCaller autoCaller(this);
6998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6999
7000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7001
7002 if (!mHWData->mCPUHotPlugEnabled)
7003 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7004
7005 if (aCpu >= SchemaDefs::MaxCPUCount)
7006 return setError(E_INVALIDARG,
7007 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7008 SchemaDefs::MaxCPUCount);
7009
7010 if (!mHWData->mCPUAttached[aCpu])
7011 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7012
7013 /* CPU 0 can't be detached */
7014 if (aCpu == 0)
7015 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7016
7017 alock.release();
7018 rc = onCPUChange(aCpu, true);
7019 alock.acquire();
7020 if (FAILED(rc)) return rc;
7021
7022 setModified(IsModified_MachineData);
7023 mHWData.backup();
7024 mHWData->mCPUAttached[aCpu] = false;
7025
7026 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7027 if (Global::IsOnline(mData->mMachineState))
7028 saveSettings(NULL);
7029
7030 return S_OK;
7031}
7032
7033STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7034{
7035 LogFlowThisFunc(("\n"));
7036
7037 CheckComArgNotNull(aCpuAttached);
7038
7039 *aCpuAttached = false;
7040
7041 AutoCaller autoCaller(this);
7042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7043
7044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7045
7046 /* If hotplug is enabled the CPU is always enabled. */
7047 if (!mHWData->mCPUHotPlugEnabled)
7048 {
7049 if (aCpu < mHWData->mCPUCount)
7050 *aCpuAttached = true;
7051 }
7052 else
7053 {
7054 if (aCpu < SchemaDefs::MaxCPUCount)
7055 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7056 }
7057
7058 return S_OK;
7059}
7060
7061STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7062{
7063 CheckComArgOutPointerValid(aName);
7064
7065 AutoCaller autoCaller(this);
7066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7067
7068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7069
7070 Utf8Str log = queryLogFilename(aIdx);
7071 if (!RTFileExists(log.c_str()))
7072 log.setNull();
7073 log.cloneTo(aName);
7074
7075 return S_OK;
7076}
7077
7078STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7079{
7080 LogFlowThisFunc(("\n"));
7081 CheckComArgOutSafeArrayPointerValid(aData);
7082 if (aSize < 0)
7083 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7084
7085 AutoCaller autoCaller(this);
7086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7087
7088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7089
7090 HRESULT rc = S_OK;
7091 Utf8Str log = queryLogFilename(aIdx);
7092
7093 /* do not unnecessarily hold the lock while doing something which does
7094 * not need the lock and potentially takes a long time. */
7095 alock.release();
7096
7097 /* Limit the chunk size to 32K for now, as that gives better performance
7098 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7099 * One byte expands to approx. 25 bytes of breathtaking XML. */
7100 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7101 com::SafeArray<BYTE> logData(cbData);
7102
7103 RTFILE LogFile;
7104 int vrc = RTFileOpen(&LogFile, log.c_str(),
7105 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7106 if (RT_SUCCESS(vrc))
7107 {
7108 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7109 if (RT_SUCCESS(vrc))
7110 logData.resize(cbData);
7111 else
7112 rc = setError(VBOX_E_IPRT_ERROR,
7113 tr("Could not read log file '%s' (%Rrc)"),
7114 log.c_str(), vrc);
7115 RTFileClose(LogFile);
7116 }
7117 else
7118 rc = setError(VBOX_E_IPRT_ERROR,
7119 tr("Could not open log file '%s' (%Rrc)"),
7120 log.c_str(), vrc);
7121
7122 if (FAILED(rc))
7123 logData.resize(0);
7124 logData.detachTo(ComSafeArrayOutArg(aData));
7125
7126 return rc;
7127}
7128
7129
7130/**
7131 * Currently this method doesn't attach device to the running VM,
7132 * just makes sure it's plugged on next VM start.
7133 */
7134STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7135{
7136 AutoCaller autoCaller(this);
7137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7138
7139 // lock scope
7140 {
7141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7142
7143 HRESULT rc = checkStateDependency(MutableStateDep);
7144 if (FAILED(rc)) return rc;
7145
7146 ChipsetType_T aChipset = ChipsetType_PIIX3;
7147 COMGETTER(ChipsetType)(&aChipset);
7148
7149 if (aChipset != ChipsetType_ICH9)
7150 {
7151 return setError(E_INVALIDARG,
7152 tr("Host PCI attachment only supported with ICH9 chipset"));
7153 }
7154
7155 // check if device with this host PCI address already attached
7156 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7157 it != mHWData->mPCIDeviceAssignments.end();
7158 ++it)
7159 {
7160 LONG iHostAddress = -1;
7161 ComPtr<PCIDeviceAttachment> pAttach;
7162 pAttach = *it;
7163 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7164 if (iHostAddress == hostAddress)
7165 return setError(E_INVALIDARG,
7166 tr("Device with host PCI address already attached to this VM"));
7167 }
7168
7169 ComObjPtr<PCIDeviceAttachment> pda;
7170 char name[32];
7171
7172 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7173 Bstr bname(name);
7174 pda.createObject();
7175 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7176 setModified(IsModified_MachineData);
7177 mHWData.backup();
7178 mHWData->mPCIDeviceAssignments.push_back(pda);
7179 }
7180
7181 return S_OK;
7182}
7183
7184/**
7185 * Currently this method doesn't detach device from the running VM,
7186 * just makes sure it's not plugged on next VM start.
7187 */
7188STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7189{
7190 AutoCaller autoCaller(this);
7191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7192
7193 ComObjPtr<PCIDeviceAttachment> pAttach;
7194 bool fRemoved = false;
7195 HRESULT rc;
7196
7197 // lock scope
7198 {
7199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7200
7201 rc = checkStateDependency(MutableStateDep);
7202 if (FAILED(rc)) return rc;
7203
7204 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7205 it != mHWData->mPCIDeviceAssignments.end();
7206 ++it)
7207 {
7208 LONG iHostAddress = -1;
7209 pAttach = *it;
7210 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7211 if (iHostAddress != -1 && iHostAddress == hostAddress)
7212 {
7213 setModified(IsModified_MachineData);
7214 mHWData.backup();
7215 mHWData->mPCIDeviceAssignments.remove(pAttach);
7216 fRemoved = true;
7217 break;
7218 }
7219 }
7220 }
7221
7222
7223 /* Fire event outside of the lock */
7224 if (fRemoved)
7225 {
7226 Assert(!pAttach.isNull());
7227 ComPtr<IEventSource> es;
7228 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7229 Assert(SUCCEEDED(rc));
7230 Bstr mid;
7231 rc = this->COMGETTER(Id)(mid.asOutParam());
7232 Assert(SUCCEEDED(rc));
7233 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7234 }
7235
7236 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7237 tr("No host PCI device %08x attached"),
7238 hostAddress
7239 );
7240}
7241
7242STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7243{
7244 CheckComArgOutSafeArrayPointerValid(aAssignments);
7245
7246 AutoCaller autoCaller(this);
7247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7248
7249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7250
7251 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7252 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7253
7254 return S_OK;
7255}
7256
7257STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7258{
7259 CheckComArgOutPointerValid(aBandwidthControl);
7260
7261 AutoCaller autoCaller(this);
7262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7263
7264 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7265
7266 return S_OK;
7267}
7268
7269STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7270{
7271 CheckComArgOutPointerValid(pfEnabled);
7272 AutoCaller autoCaller(this);
7273 HRESULT hrc = autoCaller.rc();
7274 if (SUCCEEDED(hrc))
7275 {
7276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7277 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7278 }
7279 return hrc;
7280}
7281
7282STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7283{
7284 AutoCaller autoCaller(this);
7285 HRESULT hrc = autoCaller.rc();
7286 if (SUCCEEDED(hrc))
7287 {
7288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7289 hrc = checkStateDependency(MutableStateDep);
7290 if (SUCCEEDED(hrc))
7291 {
7292 hrc = mHWData.backupEx();
7293 if (SUCCEEDED(hrc))
7294 {
7295 setModified(IsModified_MachineData);
7296 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7297 }
7298 }
7299 }
7300 return hrc;
7301}
7302
7303STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7304{
7305 CheckComArgOutPointerValid(pbstrConfig);
7306 AutoCaller autoCaller(this);
7307 HRESULT hrc = autoCaller.rc();
7308 if (SUCCEEDED(hrc))
7309 {
7310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7311 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7312 }
7313 return hrc;
7314}
7315
7316STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7317{
7318 CheckComArgStr(bstrConfig);
7319 AutoCaller autoCaller(this);
7320 HRESULT hrc = autoCaller.rc();
7321 if (SUCCEEDED(hrc))
7322 {
7323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7324 hrc = checkStateDependency(MutableStateDep);
7325 if (SUCCEEDED(hrc))
7326 {
7327 hrc = mHWData.backupEx();
7328 if (SUCCEEDED(hrc))
7329 {
7330 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7331 if (SUCCEEDED(hrc))
7332 setModified(IsModified_MachineData);
7333 }
7334 }
7335 }
7336 return hrc;
7337
7338}
7339
7340STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7341{
7342 CheckComArgOutPointerValid(pfAllow);
7343 AutoCaller autoCaller(this);
7344 HRESULT hrc = autoCaller.rc();
7345 if (SUCCEEDED(hrc))
7346 {
7347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7348 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7349 }
7350 return hrc;
7351}
7352
7353STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7354{
7355 AutoCaller autoCaller(this);
7356 HRESULT hrc = autoCaller.rc();
7357 if (SUCCEEDED(hrc))
7358 {
7359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7360 hrc = checkStateDependency(MutableStateDep);
7361 if (SUCCEEDED(hrc))
7362 {
7363 hrc = mHWData.backupEx();
7364 if (SUCCEEDED(hrc))
7365 {
7366 setModified(IsModified_MachineData);
7367 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7368 }
7369 }
7370 }
7371 return hrc;
7372}
7373
7374STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7375{
7376 CheckComArgOutPointerValid(pfEnabled);
7377 AutoCaller autoCaller(this);
7378 HRESULT hrc = autoCaller.rc();
7379 if (SUCCEEDED(hrc))
7380 {
7381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7382 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7383 }
7384 return hrc;
7385}
7386
7387STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7388{
7389 AutoCaller autoCaller(this);
7390 HRESULT hrc = autoCaller.rc();
7391 if (SUCCEEDED(hrc))
7392 {
7393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7394 hrc = checkStateDependency(MutableStateDep);
7395 if ( SUCCEEDED(hrc)
7396 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7397 {
7398 AutostartDb *autostartDb = mParent->getAutostartDb();
7399 int vrc;
7400
7401 if (fEnabled)
7402 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7403 else
7404 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7405
7406 if (RT_SUCCESS(vrc))
7407 {
7408 hrc = mHWData.backupEx();
7409 if (SUCCEEDED(hrc))
7410 {
7411 setModified(IsModified_MachineData);
7412 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7413 }
7414 }
7415 else if (vrc == VERR_NOT_SUPPORTED)
7416 hrc = setError(VBOX_E_NOT_SUPPORTED,
7417 tr("The VM autostart feature is not supported on this platform"));
7418 else if (vrc == VERR_PATH_NOT_FOUND)
7419 hrc = setError(E_FAIL,
7420 tr("The path to the autostart database is not set"));
7421 else
7422 hrc = setError(E_UNEXPECTED,
7423 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7424 fEnabled ? "Adding" : "Removing",
7425 mUserData->s.strName.c_str(), vrc);
7426 }
7427 }
7428 return hrc;
7429}
7430
7431STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7432{
7433 CheckComArgOutPointerValid(puDelay);
7434 AutoCaller autoCaller(this);
7435 HRESULT hrc = autoCaller.rc();
7436 if (SUCCEEDED(hrc))
7437 {
7438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7439 *puDelay = mHWData->mAutostart.uAutostartDelay;
7440 }
7441 return hrc;
7442}
7443
7444STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7445{
7446 AutoCaller autoCaller(this);
7447 HRESULT hrc = autoCaller.rc();
7448 if (SUCCEEDED(hrc))
7449 {
7450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7451 hrc = checkStateDependency(MutableStateDep);
7452 if (SUCCEEDED(hrc))
7453 {
7454 hrc = mHWData.backupEx();
7455 if (SUCCEEDED(hrc))
7456 {
7457 setModified(IsModified_MachineData);
7458 mHWData->mAutostart.uAutostartDelay = uDelay;
7459 }
7460 }
7461 }
7462 return hrc;
7463}
7464
7465STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7466{
7467 CheckComArgOutPointerValid(penmAutostopType);
7468 AutoCaller autoCaller(this);
7469 HRESULT hrc = autoCaller.rc();
7470 if (SUCCEEDED(hrc))
7471 {
7472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7473 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7474 }
7475 return hrc;
7476}
7477
7478STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7479{
7480 AutoCaller autoCaller(this);
7481 HRESULT hrc = autoCaller.rc();
7482 if (SUCCEEDED(hrc))
7483 {
7484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7485 hrc = checkStateDependency(MutableStateDep);
7486 if ( SUCCEEDED(hrc)
7487 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7488 {
7489 AutostartDb *autostartDb = mParent->getAutostartDb();
7490 int vrc;
7491
7492 if (enmAutostopType != AutostopType_Disabled)
7493 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7494 else
7495 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7496
7497 if (RT_SUCCESS(vrc))
7498 {
7499 hrc = mHWData.backupEx();
7500 if (SUCCEEDED(hrc))
7501 {
7502 setModified(IsModified_MachineData);
7503 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7504 }
7505 }
7506 else if (vrc == VERR_NOT_SUPPORTED)
7507 hrc = setError(VBOX_E_NOT_SUPPORTED,
7508 tr("The VM autostop feature is not supported on this platform"));
7509 else if (vrc == VERR_PATH_NOT_FOUND)
7510 hrc = setError(E_FAIL,
7511 tr("The path to the autostart database is not set"));
7512 else
7513 hrc = setError(E_UNEXPECTED,
7514 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7515 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7516 mUserData->s.strName.c_str(), vrc);
7517 }
7518 }
7519 return hrc;
7520}
7521
7522STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7523{
7524 CheckComArgOutPointerValid(aDefaultFrontend);
7525 AutoCaller autoCaller(this);
7526 HRESULT hrc = autoCaller.rc();
7527 if (SUCCEEDED(hrc))
7528 {
7529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7530 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7531 }
7532 return hrc;
7533}
7534
7535STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7536{
7537 CheckComArgStr(aDefaultFrontend);
7538 AutoCaller autoCaller(this);
7539 HRESULT hrc = autoCaller.rc();
7540 if (SUCCEEDED(hrc))
7541 {
7542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7543 hrc = checkStateDependency(MutableOrSavedStateDep);
7544 if (SUCCEEDED(hrc))
7545 {
7546 hrc = mHWData.backupEx();
7547 if (SUCCEEDED(hrc))
7548 {
7549 setModified(IsModified_MachineData);
7550 mHWData->mDefaultFrontend = aDefaultFrontend;
7551 }
7552 }
7553 }
7554 return hrc;
7555}
7556
7557STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7558{
7559 CheckComArgSafeArrayNotNull(aIcon);
7560 CheckComArgOutSafeArrayPointerValid(aIcon);
7561 AutoCaller autoCaller(this);
7562 HRESULT hrc = autoCaller.rc();
7563 if (SUCCEEDED(hrc))
7564 {
7565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7566 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7567 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7568 icon.detachTo(ComSafeArrayOutArg(aIcon));
7569 }
7570 return hrc;
7571}
7572
7573STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7574{
7575 CheckComArgSafeArrayNotNull(aIcon);
7576 AutoCaller autoCaller(this);
7577 HRESULT hrc = autoCaller.rc();
7578 if (SUCCEEDED(hrc))
7579 {
7580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7581 hrc = checkStateDependency(MutableOrSavedStateDep);
7582 if (SUCCEEDED(hrc))
7583 {
7584 setModified(IsModified_MachineData);
7585 mUserData.backup();
7586 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7587 mUserData->mIcon.resize(icon.size());
7588 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7589 }
7590 }
7591 return hrc;
7592}
7593
7594STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7595{
7596 CheckComArgOutPointerValid(aAvailable);
7597
7598 AutoCaller autoCaller(this);
7599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7600
7601#ifdef VBOX_WITH_USB
7602 *aAvailable = true;
7603#else
7604 *aAvailable = false;
7605#endif
7606 return S_OK;
7607}
7608
7609STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7610{
7611 LogFlowFuncEnter();
7612
7613 CheckComArgNotNull(pTarget);
7614 CheckComArgOutPointerValid(pProgress);
7615
7616 /* Convert the options. */
7617 RTCList<CloneOptions_T> optList;
7618 if (options != NULL)
7619 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7620
7621 if (optList.contains(CloneOptions_Link))
7622 {
7623 if (!isSnapshotMachine())
7624 return setError(E_INVALIDARG,
7625 tr("Linked clone can only be created from a snapshot"));
7626 if (mode != CloneMode_MachineState)
7627 return setError(E_INVALIDARG,
7628 tr("Linked clone can only be created for a single machine state"));
7629 }
7630 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7631
7632 AutoCaller autoCaller(this);
7633 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7634
7635
7636 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7637
7638 HRESULT rc = pWorker->start(pProgress);
7639
7640 LogFlowFuncLeave();
7641
7642 return rc;
7643}
7644
7645// public methods for internal purposes
7646/////////////////////////////////////////////////////////////////////////////
7647
7648/**
7649 * Adds the given IsModified_* flag to the dirty flags of the machine.
7650 * This must be called either during loadSettings or under the machine write lock.
7651 * @param fl
7652 */
7653void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7654{
7655 mData->flModifications |= fl;
7656 if (fAllowStateModification && isStateModificationAllowed())
7657 mData->mCurrentStateModified = true;
7658}
7659
7660/**
7661 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7662 * care of the write locking.
7663 *
7664 * @param fModifications The flag to add.
7665 */
7666void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7667{
7668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7669 setModified(fModification, fAllowStateModification);
7670}
7671
7672/**
7673 * Saves the registry entry of this machine to the given configuration node.
7674 *
7675 * @param aEntryNode Node to save the registry entry to.
7676 *
7677 * @note locks this object for reading.
7678 */
7679HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7680{
7681 AutoLimitedCaller autoCaller(this);
7682 AssertComRCReturnRC(autoCaller.rc());
7683
7684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7685
7686 data.uuid = mData->mUuid;
7687 data.strSettingsFile = mData->m_strConfigFile;
7688
7689 return S_OK;
7690}
7691
7692/**
7693 * Calculates the absolute path of the given path taking the directory of the
7694 * machine settings file as the current directory.
7695 *
7696 * @param aPath Path to calculate the absolute path for.
7697 * @param aResult Where to put the result (used only on success, can be the
7698 * same Utf8Str instance as passed in @a aPath).
7699 * @return IPRT result.
7700 *
7701 * @note Locks this object for reading.
7702 */
7703int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7704{
7705 AutoCaller autoCaller(this);
7706 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7707
7708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7709
7710 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7711
7712 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7713
7714 strSettingsDir.stripFilename();
7715 char folder[RTPATH_MAX];
7716 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7717 if (RT_SUCCESS(vrc))
7718 aResult = folder;
7719
7720 return vrc;
7721}
7722
7723/**
7724 * Copies strSource to strTarget, making it relative to the machine folder
7725 * if it is a subdirectory thereof, or simply copying it otherwise.
7726 *
7727 * @param strSource Path to evaluate and copy.
7728 * @param strTarget Buffer to receive target path.
7729 *
7730 * @note Locks this object for reading.
7731 */
7732void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7733 Utf8Str &strTarget)
7734{
7735 AutoCaller autoCaller(this);
7736 AssertComRCReturn(autoCaller.rc(), (void)0);
7737
7738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7739
7740 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7741 // use strTarget as a temporary buffer to hold the machine settings dir
7742 strTarget = mData->m_strConfigFileFull;
7743 strTarget.stripFilename();
7744 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7745 {
7746 // is relative: then append what's left
7747 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7748 // for empty paths (only possible for subdirs) use "." to avoid
7749 // triggering default settings for not present config attributes.
7750 if (strTarget.isEmpty())
7751 strTarget = ".";
7752 }
7753 else
7754 // is not relative: then overwrite
7755 strTarget = strSource;
7756}
7757
7758/**
7759 * Returns the full path to the machine's log folder in the
7760 * \a aLogFolder argument.
7761 */
7762void Machine::getLogFolder(Utf8Str &aLogFolder)
7763{
7764 AutoCaller autoCaller(this);
7765 AssertComRCReturnVoid(autoCaller.rc());
7766
7767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7768
7769 char szTmp[RTPATH_MAX];
7770 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7771 if (RT_SUCCESS(vrc))
7772 {
7773 if (szTmp[0] && !mUserData.isNull())
7774 {
7775 char szTmp2[RTPATH_MAX];
7776 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7777 if (RT_SUCCESS(vrc))
7778 aLogFolder = BstrFmt("%s%c%s",
7779 szTmp2,
7780 RTPATH_DELIMITER,
7781 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7782 }
7783 else
7784 vrc = VERR_PATH_IS_RELATIVE;
7785 }
7786
7787 if (RT_FAILURE(vrc))
7788 {
7789 // fallback if VBOX_USER_LOGHOME is not set or invalid
7790 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7791 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7792 aLogFolder.append(RTPATH_DELIMITER);
7793 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7794 }
7795}
7796
7797/**
7798 * Returns the full path to the machine's log file for an given index.
7799 */
7800Utf8Str Machine::queryLogFilename(ULONG idx)
7801{
7802 Utf8Str logFolder;
7803 getLogFolder(logFolder);
7804 Assert(logFolder.length());
7805 Utf8Str log;
7806 if (idx == 0)
7807 log = Utf8StrFmt("%s%cVBox.log",
7808 logFolder.c_str(), RTPATH_DELIMITER);
7809 else
7810 log = Utf8StrFmt("%s%cVBox.log.%d",
7811 logFolder.c_str(), RTPATH_DELIMITER, idx);
7812 return log;
7813}
7814
7815/**
7816 * Composes a unique saved state filename based on the current system time. The filename is
7817 * granular to the second so this will work so long as no more than one snapshot is taken on
7818 * a machine per second.
7819 *
7820 * Before version 4.1, we used this formula for saved state files:
7821 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7822 * which no longer works because saved state files can now be shared between the saved state of the
7823 * "saved" machine and an online snapshot, and the following would cause problems:
7824 * 1) save machine
7825 * 2) create online snapshot from that machine state --> reusing saved state file
7826 * 3) save machine again --> filename would be reused, breaking the online snapshot
7827 *
7828 * So instead we now use a timestamp.
7829 *
7830 * @param str
7831 */
7832void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7833{
7834 AutoCaller autoCaller(this);
7835 AssertComRCReturnVoid(autoCaller.rc());
7836
7837 {
7838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7839 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7840 }
7841
7842 RTTIMESPEC ts;
7843 RTTimeNow(&ts);
7844 RTTIME time;
7845 RTTimeExplode(&time, &ts);
7846
7847 strStateFilePath += RTPATH_DELIMITER;
7848 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7849 time.i32Year, time.u8Month, time.u8MonthDay,
7850 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7851}
7852
7853/**
7854 * Returns the full path to the default video capture file.
7855 */
7856void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7857{
7858 AutoCaller autoCaller(this);
7859 AssertComRCReturnVoid(autoCaller.rc());
7860
7861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7862
7863 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7864 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7865 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7866}
7867
7868/**
7869 * Returns whether at least one USB controller is present for the VM.
7870 */
7871bool Machine::isUSBControllerPresent()
7872{
7873 AutoCaller autoCaller(this);
7874 AssertComRCReturn(autoCaller.rc(), false);
7875
7876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7877
7878 return (mUSBControllers->size() > 0);
7879}
7880
7881/**
7882 * @note Locks this object for writing, calls the client process
7883 * (inside the lock).
7884 */
7885HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7886 const Utf8Str &strFrontend,
7887 const Utf8Str &strEnvironment,
7888 ProgressProxy *aProgress)
7889{
7890 LogFlowThisFuncEnter();
7891
7892 AssertReturn(aControl, E_FAIL);
7893 AssertReturn(aProgress, E_FAIL);
7894 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7895
7896 AutoCaller autoCaller(this);
7897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7898
7899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7900
7901 if (!mData->mRegistered)
7902 return setError(E_UNEXPECTED,
7903 tr("The machine '%s' is not registered"),
7904 mUserData->s.strName.c_str());
7905
7906 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7907
7908 if ( mData->mSession.mState == SessionState_Locked
7909 || mData->mSession.mState == SessionState_Spawning
7910 || mData->mSession.mState == SessionState_Unlocking)
7911 return setError(VBOX_E_INVALID_OBJECT_STATE,
7912 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7913 mUserData->s.strName.c_str());
7914
7915 /* may not be busy */
7916 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7917
7918 /* get the path to the executable */
7919 char szPath[RTPATH_MAX];
7920 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7921 size_t sz = strlen(szPath);
7922 szPath[sz++] = RTPATH_DELIMITER;
7923 szPath[sz] = 0;
7924 char *cmd = szPath + sz;
7925 sz = sizeof(szPath) - sz;
7926
7927 int vrc = VINF_SUCCESS;
7928 RTPROCESS pid = NIL_RTPROCESS;
7929
7930 RTENV env = RTENV_DEFAULT;
7931
7932 if (!strEnvironment.isEmpty())
7933 {
7934 char *newEnvStr = NULL;
7935
7936 do
7937 {
7938 /* clone the current environment */
7939 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7940 AssertRCBreakStmt(vrc2, vrc = vrc2);
7941
7942 newEnvStr = RTStrDup(strEnvironment.c_str());
7943 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7944
7945 /* put new variables to the environment
7946 * (ignore empty variable names here since RTEnv API
7947 * intentionally doesn't do that) */
7948 char *var = newEnvStr;
7949 for (char *p = newEnvStr; *p; ++p)
7950 {
7951 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7952 {
7953 *p = '\0';
7954 if (*var)
7955 {
7956 char *val = strchr(var, '=');
7957 if (val)
7958 {
7959 *val++ = '\0';
7960 vrc2 = RTEnvSetEx(env, var, val);
7961 }
7962 else
7963 vrc2 = RTEnvUnsetEx(env, var);
7964 if (RT_FAILURE(vrc2))
7965 break;
7966 }
7967 var = p + 1;
7968 }
7969 }
7970 if (RT_SUCCESS(vrc2) && *var)
7971 vrc2 = RTEnvPutEx(env, var);
7972
7973 AssertRCBreakStmt(vrc2, vrc = vrc2);
7974 }
7975 while (0);
7976
7977 if (newEnvStr != NULL)
7978 RTStrFree(newEnvStr);
7979 }
7980
7981#ifdef VBOX_WITH_QTGUI
7982 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7983 {
7984# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7985 /* Modify the base path so that we don't need to use ".." below. */
7986 RTPathStripTrailingSlash(szPath);
7987 RTPathStripFilename(szPath);
7988 sz = strlen(szPath);
7989 cmd = szPath + sz;
7990 sz = sizeof(szPath) - sz;
7991
7992#define OSX_APP_NAME "VirtualBoxVM"
7993#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7994
7995 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7996 if ( strAppOverride.contains(".")
7997 || strAppOverride.contains("/")
7998 || strAppOverride.contains("\\")
7999 || strAppOverride.contains(":"))
8000 strAppOverride.setNull();
8001 Utf8Str strAppPath;
8002 if (!strAppOverride.isEmpty())
8003 {
8004 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8005 Utf8Str strFullPath(szPath);
8006 strFullPath.append(strAppPath);
8007 /* there is a race, but people using this deserve the failure */
8008 if (!RTFileExists(strFullPath.c_str()))
8009 strAppOverride.setNull();
8010 }
8011 if (strAppOverride.isEmpty())
8012 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8013 const char *VirtualBox_exe = strAppPath.c_str();
8014 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8015# else
8016 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8017 Assert(sz >= sizeof(VirtualBox_exe));
8018# endif
8019 strcpy(cmd, VirtualBox_exe);
8020
8021 Utf8Str idStr = mData->mUuid.toString();
8022 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8023 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8024 }
8025#else /* !VBOX_WITH_QTGUI */
8026 if (0)
8027 ;
8028#endif /* VBOX_WITH_QTGUI */
8029
8030 else
8031
8032#ifdef VBOX_WITH_VBOXSDL
8033 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8034 {
8035 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8036 Assert(sz >= sizeof(VBoxSDL_exe));
8037 strcpy(cmd, VBoxSDL_exe);
8038
8039 Utf8Str idStr = mData->mUuid.toString();
8040 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8041 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8042 }
8043#else /* !VBOX_WITH_VBOXSDL */
8044 if (0)
8045 ;
8046#endif /* !VBOX_WITH_VBOXSDL */
8047
8048 else
8049
8050#ifdef VBOX_WITH_HEADLESS
8051 if ( strFrontend == "headless"
8052 || strFrontend == "capture"
8053 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8054 )
8055 {
8056 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8057 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8058 * and a VM works even if the server has not been installed.
8059 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8060 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8061 * differently in 4.0 and 3.x.
8062 */
8063 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8064 Assert(sz >= sizeof(VBoxHeadless_exe));
8065 strcpy(cmd, VBoxHeadless_exe);
8066
8067 Utf8Str idStr = mData->mUuid.toString();
8068 /* Leave space for "--capture" arg. */
8069 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8070 "--startvm", idStr.c_str(),
8071 "--vrde", "config",
8072 0, /* For "--capture". */
8073 0 };
8074 if (strFrontend == "capture")
8075 {
8076 unsigned pos = RT_ELEMENTS(args) - 2;
8077 args[pos] = "--capture";
8078 }
8079 vrc = RTProcCreate(szPath, args, env,
8080#ifdef RT_OS_WINDOWS
8081 RTPROC_FLAGS_NO_WINDOW
8082#else
8083 0
8084#endif
8085 , &pid);
8086 }
8087#else /* !VBOX_WITH_HEADLESS */
8088 if (0)
8089 ;
8090#endif /* !VBOX_WITH_HEADLESS */
8091 else
8092 {
8093 RTEnvDestroy(env);
8094 return setError(E_INVALIDARG,
8095 tr("Invalid frontend name: '%s'"),
8096 strFrontend.c_str());
8097 }
8098
8099 RTEnvDestroy(env);
8100
8101 if (RT_FAILURE(vrc))
8102 return setError(VBOX_E_IPRT_ERROR,
8103 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8104 mUserData->s.strName.c_str(), vrc);
8105
8106 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8107
8108 /*
8109 * Note that we don't release the lock here before calling the client,
8110 * because it doesn't need to call us back if called with a NULL argument.
8111 * Releasing the lock here is dangerous because we didn't prepare the
8112 * launch data yet, but the client we've just started may happen to be
8113 * too fast and call LockMachine() that will fail (because of PID, etc.),
8114 * so that the Machine will never get out of the Spawning session state.
8115 */
8116
8117 /* inform the session that it will be a remote one */
8118 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8119 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8120 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8121
8122 if (FAILED(rc))
8123 {
8124 /* restore the session state */
8125 mData->mSession.mState = SessionState_Unlocked;
8126 /* The failure may occur w/o any error info (from RPC), so provide one */
8127 return setError(VBOX_E_VM_ERROR,
8128 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8129 }
8130
8131 /* attach launch data to the machine */
8132 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8133 mData->mSession.mRemoteControls.push_back(aControl);
8134 mData->mSession.mProgress = aProgress;
8135 mData->mSession.mPID = pid;
8136 mData->mSession.mState = SessionState_Spawning;
8137 mData->mSession.mType = strFrontend;
8138
8139 LogFlowThisFuncLeave();
8140 return S_OK;
8141}
8142
8143/**
8144 * Returns @c true if the given session machine instance has an open direct
8145 * session (and optionally also for direct sessions which are closing) and
8146 * returns the session control machine instance if so.
8147 *
8148 * Note that when the method returns @c false, the arguments remain unchanged.
8149 *
8150 * @param aMachine Session machine object.
8151 * @param aControl Direct session control object (optional).
8152 * @param aAllowClosing If true then additionally a session which is currently
8153 * being closed will also be allowed.
8154 *
8155 * @note locks this object for reading.
8156 */
8157bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8158 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8159 bool aAllowClosing /*= false*/)
8160{
8161 AutoLimitedCaller autoCaller(this);
8162 AssertComRCReturn(autoCaller.rc(), false);
8163
8164 /* just return false for inaccessible machines */
8165 if (autoCaller.state() != Ready)
8166 return false;
8167
8168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8169
8170 if ( mData->mSession.mState == SessionState_Locked
8171 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8172 )
8173 {
8174 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8175
8176 aMachine = mData->mSession.mMachine;
8177
8178 if (aControl != NULL)
8179 *aControl = mData->mSession.mDirectControl;
8180
8181 return true;
8182 }
8183
8184 return false;
8185}
8186
8187/**
8188 * Returns @c true if the given machine has an spawning direct session.
8189 *
8190 * @note locks this object for reading.
8191 */
8192bool Machine::isSessionSpawning()
8193{
8194 AutoLimitedCaller autoCaller(this);
8195 AssertComRCReturn(autoCaller.rc(), false);
8196
8197 /* just return false for inaccessible machines */
8198 if (autoCaller.state() != Ready)
8199 return false;
8200
8201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8202
8203 if (mData->mSession.mState == SessionState_Spawning)
8204 return true;
8205
8206 return false;
8207}
8208
8209/**
8210 * Called from the client watcher thread to check for unexpected client process
8211 * death during Session_Spawning state (e.g. before it successfully opened a
8212 * direct session).
8213 *
8214 * On Win32 and on OS/2, this method is called only when we've got the
8215 * direct client's process termination notification, so it always returns @c
8216 * true.
8217 *
8218 * On other platforms, this method returns @c true if the client process is
8219 * terminated and @c false if it's still alive.
8220 *
8221 * @note Locks this object for writing.
8222 */
8223bool Machine::checkForSpawnFailure()
8224{
8225 AutoCaller autoCaller(this);
8226 if (!autoCaller.isOk())
8227 {
8228 /* nothing to do */
8229 LogFlowThisFunc(("Already uninitialized!\n"));
8230 return true;
8231 }
8232
8233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8234
8235 if (mData->mSession.mState != SessionState_Spawning)
8236 {
8237 /* nothing to do */
8238 LogFlowThisFunc(("Not spawning any more!\n"));
8239 return true;
8240 }
8241
8242 HRESULT rc = S_OK;
8243
8244 /* PID not yet initialized, skip check. */
8245 if (mData->mSession.mPID == NIL_RTPROCESS)
8246 return false;
8247
8248 RTPROCSTATUS status;
8249 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8250
8251 if (vrc != VERR_PROCESS_RUNNING)
8252 {
8253 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8254 rc = setError(E_FAIL,
8255 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8256 getName().c_str(), status.iStatus);
8257 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8258 rc = setError(E_FAIL,
8259 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8260 getName().c_str(), status.iStatus);
8261 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8262 rc = setError(E_FAIL,
8263 tr("The virtual machine '%s' has terminated abnormally"),
8264 getName().c_str(), status.iStatus);
8265 else
8266 rc = setError(E_FAIL,
8267 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8268 getName().c_str(), vrc);
8269 }
8270
8271 if (FAILED(rc))
8272 {
8273 /* Close the remote session, remove the remote control from the list
8274 * and reset session state to Closed (@note keep the code in sync with
8275 * the relevant part in LockMachine()). */
8276
8277 Assert(mData->mSession.mRemoteControls.size() == 1);
8278 if (mData->mSession.mRemoteControls.size() == 1)
8279 {
8280 ErrorInfoKeeper eik;
8281 mData->mSession.mRemoteControls.front()->Uninitialize();
8282 }
8283
8284 mData->mSession.mRemoteControls.clear();
8285 mData->mSession.mState = SessionState_Unlocked;
8286
8287 /* finalize the progress after setting the state */
8288 if (!mData->mSession.mProgress.isNull())
8289 {
8290 mData->mSession.mProgress->notifyComplete(rc);
8291 mData->mSession.mProgress.setNull();
8292 }
8293
8294 mData->mSession.mPID = NIL_RTPROCESS;
8295
8296 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8297 return true;
8298 }
8299
8300 return false;
8301}
8302
8303/**
8304 * Checks whether the machine can be registered. If so, commits and saves
8305 * all settings.
8306 *
8307 * @note Must be called from mParent's write lock. Locks this object and
8308 * children for writing.
8309 */
8310HRESULT Machine::prepareRegister()
8311{
8312 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8313
8314 AutoLimitedCaller autoCaller(this);
8315 AssertComRCReturnRC(autoCaller.rc());
8316
8317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8318
8319 /* wait for state dependents to drop to zero */
8320 ensureNoStateDependencies();
8321
8322 if (!mData->mAccessible)
8323 return setError(VBOX_E_INVALID_OBJECT_STATE,
8324 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8325 mUserData->s.strName.c_str(),
8326 mData->mUuid.toString().c_str());
8327
8328 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8329
8330 if (mData->mRegistered)
8331 return setError(VBOX_E_INVALID_OBJECT_STATE,
8332 tr("The machine '%s' with UUID {%s} is already registered"),
8333 mUserData->s.strName.c_str(),
8334 mData->mUuid.toString().c_str());
8335
8336 HRESULT rc = S_OK;
8337
8338 // Ensure the settings are saved. If we are going to be registered and
8339 // no config file exists yet, create it by calling saveSettings() too.
8340 if ( (mData->flModifications)
8341 || (!mData->pMachineConfigFile->fileExists())
8342 )
8343 {
8344 rc = saveSettings(NULL);
8345 // no need to check whether VirtualBox.xml needs saving too since
8346 // we can't have a machine XML file rename pending
8347 if (FAILED(rc)) return rc;
8348 }
8349
8350 /* more config checking goes here */
8351
8352 if (SUCCEEDED(rc))
8353 {
8354 /* we may have had implicit modifications we want to fix on success */
8355 commit();
8356
8357 mData->mRegistered = true;
8358 }
8359 else
8360 {
8361 /* we may have had implicit modifications we want to cancel on failure*/
8362 rollback(false /* aNotify */);
8363 }
8364
8365 return rc;
8366}
8367
8368/**
8369 * Increases the number of objects dependent on the machine state or on the
8370 * registered state. Guarantees that these two states will not change at least
8371 * until #releaseStateDependency() is called.
8372 *
8373 * Depending on the @a aDepType value, additional state checks may be made.
8374 * These checks will set extended error info on failure. See
8375 * #checkStateDependency() for more info.
8376 *
8377 * If this method returns a failure, the dependency is not added and the caller
8378 * is not allowed to rely on any particular machine state or registration state
8379 * value and may return the failed result code to the upper level.
8380 *
8381 * @param aDepType Dependency type to add.
8382 * @param aState Current machine state (NULL if not interested).
8383 * @param aRegistered Current registered state (NULL if not interested).
8384 *
8385 * @note Locks this object for writing.
8386 */
8387HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8388 MachineState_T *aState /* = NULL */,
8389 BOOL *aRegistered /* = NULL */)
8390{
8391 AutoCaller autoCaller(this);
8392 AssertComRCReturnRC(autoCaller.rc());
8393
8394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8395
8396 HRESULT rc = checkStateDependency(aDepType);
8397 if (FAILED(rc)) return rc;
8398
8399 {
8400 if (mData->mMachineStateChangePending != 0)
8401 {
8402 /* ensureNoStateDependencies() is waiting for state dependencies to
8403 * drop to zero so don't add more. It may make sense to wait a bit
8404 * and retry before reporting an error (since the pending state
8405 * transition should be really quick) but let's just assert for
8406 * now to see if it ever happens on practice. */
8407
8408 AssertFailed();
8409
8410 return setError(E_ACCESSDENIED,
8411 tr("Machine state change is in progress. Please retry the operation later."));
8412 }
8413
8414 ++mData->mMachineStateDeps;
8415 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8416 }
8417
8418 if (aState)
8419 *aState = mData->mMachineState;
8420 if (aRegistered)
8421 *aRegistered = mData->mRegistered;
8422
8423 return S_OK;
8424}
8425
8426/**
8427 * Decreases the number of objects dependent on the machine state.
8428 * Must always complete the #addStateDependency() call after the state
8429 * dependency is no more necessary.
8430 */
8431void Machine::releaseStateDependency()
8432{
8433 AutoCaller autoCaller(this);
8434 AssertComRCReturnVoid(autoCaller.rc());
8435
8436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8437
8438 /* releaseStateDependency() w/o addStateDependency()? */
8439 AssertReturnVoid(mData->mMachineStateDeps != 0);
8440 -- mData->mMachineStateDeps;
8441
8442 if (mData->mMachineStateDeps == 0)
8443 {
8444 /* inform ensureNoStateDependencies() that there are no more deps */
8445 if (mData->mMachineStateChangePending != 0)
8446 {
8447 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8448 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8449 }
8450 }
8451}
8452
8453Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8454{
8455 /* start with nothing found */
8456 Utf8Str strResult("");
8457
8458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8459
8460 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8461 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8462 // found:
8463 strResult = it->second; // source is a Utf8Str
8464
8465 return strResult;
8466}
8467
8468// protected methods
8469/////////////////////////////////////////////////////////////////////////////
8470
8471/**
8472 * Performs machine state checks based on the @a aDepType value. If a check
8473 * fails, this method will set extended error info, otherwise it will return
8474 * S_OK. It is supposed, that on failure, the caller will immediately return
8475 * the return value of this method to the upper level.
8476 *
8477 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8478 *
8479 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8480 * current state of this machine object allows to change settings of the
8481 * machine (i.e. the machine is not registered, or registered but not running
8482 * and not saved). It is useful to call this method from Machine setters
8483 * before performing any change.
8484 *
8485 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8486 * as for MutableStateDep except that if the machine is saved, S_OK is also
8487 * returned. This is useful in setters which allow changing machine
8488 * properties when it is in the saved state.
8489 *
8490 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8491 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8492 * Aborted).
8493 *
8494 * @param aDepType Dependency type to check.
8495 *
8496 * @note Non Machine based classes should use #addStateDependency() and
8497 * #releaseStateDependency() methods or the smart AutoStateDependency
8498 * template.
8499 *
8500 * @note This method must be called from under this object's read or write
8501 * lock.
8502 */
8503HRESULT Machine::checkStateDependency(StateDependency aDepType)
8504{
8505 switch (aDepType)
8506 {
8507 case AnyStateDep:
8508 {
8509 break;
8510 }
8511 case MutableStateDep:
8512 {
8513 if ( mData->mRegistered
8514 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8515 || ( mData->mMachineState != MachineState_Paused
8516 && mData->mMachineState != MachineState_Running
8517 && mData->mMachineState != MachineState_Aborted
8518 && mData->mMachineState != MachineState_Teleported
8519 && mData->mMachineState != MachineState_PoweredOff
8520 )
8521 )
8522 )
8523 return setError(VBOX_E_INVALID_VM_STATE,
8524 tr("The machine is not mutable (state is %s)"),
8525 Global::stringifyMachineState(mData->mMachineState));
8526 break;
8527 }
8528 case MutableOrSavedStateDep:
8529 {
8530 if ( mData->mRegistered
8531 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8532 || ( mData->mMachineState != MachineState_Paused
8533 && mData->mMachineState != MachineState_Running
8534 && mData->mMachineState != MachineState_Aborted
8535 && mData->mMachineState != MachineState_Teleported
8536 && mData->mMachineState != MachineState_Saved
8537 && mData->mMachineState != MachineState_PoweredOff
8538 )
8539 )
8540 )
8541 return setError(VBOX_E_INVALID_VM_STATE,
8542 tr("The machine is not mutable (state is %s)"),
8543 Global::stringifyMachineState(mData->mMachineState));
8544 break;
8545 }
8546 case OfflineStateDep:
8547 {
8548 if ( mData->mRegistered
8549 && ( !isSessionMachine()
8550 || ( mData->mMachineState != MachineState_PoweredOff
8551 && mData->mMachineState != MachineState_Saved
8552 && mData->mMachineState != MachineState_Aborted
8553 && mData->mMachineState != MachineState_Teleported
8554 )
8555 )
8556 )
8557 return setError(VBOX_E_INVALID_VM_STATE,
8558 tr("The machine is not offline (state is %s)"),
8559 Global::stringifyMachineState(mData->mMachineState));
8560 break;
8561 }
8562 }
8563
8564 return S_OK;
8565}
8566
8567/**
8568 * Helper to initialize all associated child objects and allocate data
8569 * structures.
8570 *
8571 * This method must be called as a part of the object's initialization procedure
8572 * (usually done in the #init() method).
8573 *
8574 * @note Must be called only from #init() or from #registeredInit().
8575 */
8576HRESULT Machine::initDataAndChildObjects()
8577{
8578 AutoCaller autoCaller(this);
8579 AssertComRCReturnRC(autoCaller.rc());
8580 AssertComRCReturn(autoCaller.state() == InInit ||
8581 autoCaller.state() == Limited, E_FAIL);
8582
8583 AssertReturn(!mData->mAccessible, E_FAIL);
8584
8585 /* allocate data structures */
8586 mSSData.allocate();
8587 mUserData.allocate();
8588 mHWData.allocate();
8589 mMediaData.allocate();
8590 mStorageControllers.allocate();
8591 mUSBControllers.allocate();
8592
8593 /* initialize mOSTypeId */
8594 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8595
8596 /* create associated BIOS settings object */
8597 unconst(mBIOSSettings).createObject();
8598 mBIOSSettings->init(this);
8599
8600 /* create an associated VRDE object (default is disabled) */
8601 unconst(mVRDEServer).createObject();
8602 mVRDEServer->init(this);
8603
8604 /* create associated serial port objects */
8605 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8606 {
8607 unconst(mSerialPorts[slot]).createObject();
8608 mSerialPorts[slot]->init(this, slot);
8609 }
8610
8611 /* create associated parallel port objects */
8612 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8613 {
8614 unconst(mParallelPorts[slot]).createObject();
8615 mParallelPorts[slot]->init(this, slot);
8616 }
8617
8618 /* create the audio adapter object (always present, default is disabled) */
8619 unconst(mAudioAdapter).createObject();
8620 mAudioAdapter->init(this);
8621
8622 /* create the USB device filters object (always present) */
8623 unconst(mUSBDeviceFilters).createObject();
8624 mUSBDeviceFilters->init(this);
8625
8626 /* create associated network adapter objects */
8627 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8628 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8629 {
8630 unconst(mNetworkAdapters[slot]).createObject();
8631 mNetworkAdapters[slot]->init(this, slot);
8632 }
8633
8634 /* create the bandwidth control */
8635 unconst(mBandwidthControl).createObject();
8636 mBandwidthControl->init(this);
8637
8638 return S_OK;
8639}
8640
8641/**
8642 * Helper to uninitialize all associated child objects and to free all data
8643 * structures.
8644 *
8645 * This method must be called as a part of the object's uninitialization
8646 * procedure (usually done in the #uninit() method).
8647 *
8648 * @note Must be called only from #uninit() or from #registeredInit().
8649 */
8650void Machine::uninitDataAndChildObjects()
8651{
8652 AutoCaller autoCaller(this);
8653 AssertComRCReturnVoid(autoCaller.rc());
8654 AssertComRCReturnVoid( autoCaller.state() == InUninit
8655 || autoCaller.state() == Limited);
8656
8657 /* tell all our other child objects we've been uninitialized */
8658 if (mBandwidthControl)
8659 {
8660 mBandwidthControl->uninit();
8661 unconst(mBandwidthControl).setNull();
8662 }
8663
8664 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8665 {
8666 if (mNetworkAdapters[slot])
8667 {
8668 mNetworkAdapters[slot]->uninit();
8669 unconst(mNetworkAdapters[slot]).setNull();
8670 }
8671 }
8672
8673 if (mUSBDeviceFilters)
8674 {
8675 mUSBDeviceFilters->uninit();
8676 unconst(mUSBDeviceFilters).setNull();
8677 }
8678
8679 if (mAudioAdapter)
8680 {
8681 mAudioAdapter->uninit();
8682 unconst(mAudioAdapter).setNull();
8683 }
8684
8685 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8686 {
8687 if (mParallelPorts[slot])
8688 {
8689 mParallelPorts[slot]->uninit();
8690 unconst(mParallelPorts[slot]).setNull();
8691 }
8692 }
8693
8694 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8695 {
8696 if (mSerialPorts[slot])
8697 {
8698 mSerialPorts[slot]->uninit();
8699 unconst(mSerialPorts[slot]).setNull();
8700 }
8701 }
8702
8703 if (mVRDEServer)
8704 {
8705 mVRDEServer->uninit();
8706 unconst(mVRDEServer).setNull();
8707 }
8708
8709 if (mBIOSSettings)
8710 {
8711 mBIOSSettings->uninit();
8712 unconst(mBIOSSettings).setNull();
8713 }
8714
8715 /* Deassociate media (only when a real Machine or a SnapshotMachine
8716 * instance is uninitialized; SessionMachine instances refer to real
8717 * Machine media). This is necessary for a clean re-initialization of
8718 * the VM after successfully re-checking the accessibility state. Note
8719 * that in case of normal Machine or SnapshotMachine uninitialization (as
8720 * a result of unregistering or deleting the snapshot), outdated media
8721 * attachments will already be uninitialized and deleted, so this
8722 * code will not affect them. */
8723 if ( !!mMediaData
8724 && (!isSessionMachine())
8725 )
8726 {
8727 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8728 it != mMediaData->mAttachments.end();
8729 ++it)
8730 {
8731 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8732 if (pMedium.isNull())
8733 continue;
8734 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8735 AssertComRC(rc);
8736 }
8737 }
8738
8739 if (!isSessionMachine() && !isSnapshotMachine())
8740 {
8741 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8742 if (mData->mFirstSnapshot)
8743 {
8744 // snapshots tree is protected by machine write lock; strictly
8745 // this isn't necessary here since we're deleting the entire
8746 // machine, but otherwise we assert in Snapshot::uninit()
8747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8748 mData->mFirstSnapshot->uninit();
8749 mData->mFirstSnapshot.setNull();
8750 }
8751
8752 mData->mCurrentSnapshot.setNull();
8753 }
8754
8755 /* free data structures (the essential mData structure is not freed here
8756 * since it may be still in use) */
8757 mMediaData.free();
8758 mStorageControllers.free();
8759 mUSBControllers.free();
8760 mHWData.free();
8761 mUserData.free();
8762 mSSData.free();
8763}
8764
8765/**
8766 * Returns a pointer to the Machine object for this machine that acts like a
8767 * parent for complex machine data objects such as shared folders, etc.
8768 *
8769 * For primary Machine objects and for SnapshotMachine objects, returns this
8770 * object's pointer itself. For SessionMachine objects, returns the peer
8771 * (primary) machine pointer.
8772 */
8773Machine* Machine::getMachine()
8774{
8775 if (isSessionMachine())
8776 return (Machine*)mPeer;
8777 return this;
8778}
8779
8780/**
8781 * Makes sure that there are no machine state dependents. If necessary, waits
8782 * for the number of dependents to drop to zero.
8783 *
8784 * Make sure this method is called from under this object's write lock to
8785 * guarantee that no new dependents may be added when this method returns
8786 * control to the caller.
8787 *
8788 * @note Locks this object for writing. The lock will be released while waiting
8789 * (if necessary).
8790 *
8791 * @warning To be used only in methods that change the machine state!
8792 */
8793void Machine::ensureNoStateDependencies()
8794{
8795 AssertReturnVoid(isWriteLockOnCurrentThread());
8796
8797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8798
8799 /* Wait for all state dependents if necessary */
8800 if (mData->mMachineStateDeps != 0)
8801 {
8802 /* lazy semaphore creation */
8803 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8804 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8805
8806 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8807 mData->mMachineStateDeps));
8808
8809 ++mData->mMachineStateChangePending;
8810
8811 /* reset the semaphore before waiting, the last dependent will signal
8812 * it */
8813 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8814
8815 alock.release();
8816
8817 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8818
8819 alock.acquire();
8820
8821 -- mData->mMachineStateChangePending;
8822 }
8823}
8824
8825/**
8826 * Changes the machine state and informs callbacks.
8827 *
8828 * This method is not intended to fail so it either returns S_OK or asserts (and
8829 * returns a failure).
8830 *
8831 * @note Locks this object for writing.
8832 */
8833HRESULT Machine::setMachineState(MachineState_T aMachineState)
8834{
8835 LogFlowThisFuncEnter();
8836 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8837
8838 AutoCaller autoCaller(this);
8839 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8840
8841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8842
8843 /* wait for state dependents to drop to zero */
8844 ensureNoStateDependencies();
8845
8846 if (mData->mMachineState != aMachineState)
8847 {
8848 mData->mMachineState = aMachineState;
8849
8850 RTTimeNow(&mData->mLastStateChange);
8851
8852 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8853 }
8854
8855 LogFlowThisFuncLeave();
8856 return S_OK;
8857}
8858
8859/**
8860 * Searches for a shared folder with the given logical name
8861 * in the collection of shared folders.
8862 *
8863 * @param aName logical name of the shared folder
8864 * @param aSharedFolder where to return the found object
8865 * @param aSetError whether to set the error info if the folder is
8866 * not found
8867 * @return
8868 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8869 *
8870 * @note
8871 * must be called from under the object's lock!
8872 */
8873HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8874 ComObjPtr<SharedFolder> &aSharedFolder,
8875 bool aSetError /* = false */)
8876{
8877 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8878 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8879 it != mHWData->mSharedFolders.end();
8880 ++it)
8881 {
8882 SharedFolder *pSF = *it;
8883 AutoCaller autoCaller(pSF);
8884 if (pSF->getName() == aName)
8885 {
8886 aSharedFolder = pSF;
8887 rc = S_OK;
8888 break;
8889 }
8890 }
8891
8892 if (aSetError && FAILED(rc))
8893 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8894
8895 return rc;
8896}
8897
8898/**
8899 * Initializes all machine instance data from the given settings structures
8900 * from XML. The exception is the machine UUID which needs special handling
8901 * depending on the caller's use case, so the caller needs to set that herself.
8902 *
8903 * This gets called in several contexts during machine initialization:
8904 *
8905 * -- When machine XML exists on disk already and needs to be loaded into memory,
8906 * for example, from registeredInit() to load all registered machines on
8907 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8908 * attached to the machine should be part of some media registry already.
8909 *
8910 * -- During OVF import, when a machine config has been constructed from an
8911 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8912 * ensure that the media listed as attachments in the config (which have
8913 * been imported from the OVF) receive the correct registry ID.
8914 *
8915 * -- During VM cloning.
8916 *
8917 * @param config Machine settings from XML.
8918 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8919 * @return
8920 */
8921HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8922 const Guid *puuidRegistry)
8923{
8924 // copy name, description, OS type, teleporter, UTC etc.
8925 mUserData->s = config.machineUserData;
8926
8927 // Decode the Icon overide data from config userdata and set onto Machine.
8928 #define DECODE_STR_MAX _1M
8929 const char* pszStr = config.machineUserData.ovIcon.c_str();
8930 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8931 if (cbOut > DECODE_STR_MAX)
8932 return setError(E_FAIL,
8933 tr("Icon Data too long.'%d' > '%d'"),
8934 cbOut,
8935 DECODE_STR_MAX);
8936 com::SafeArray<BYTE> iconByte(cbOut);
8937 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8938 if (FAILED(rc))
8939 return setError(E_FAIL,
8940 tr("Failure to Decode Icon Data. '%s' (%d)"),
8941 pszStr,
8942 rc);
8943 mUserData->mIcon.resize(iconByte.size());
8944 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8945
8946 // look up the object by Id to check it is valid
8947 ComPtr<IGuestOSType> guestOSType;
8948 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8949 guestOSType.asOutParam());
8950 if (FAILED(rc)) return rc;
8951
8952 // stateFile (optional)
8953 if (config.strStateFile.isEmpty())
8954 mSSData->strStateFilePath.setNull();
8955 else
8956 {
8957 Utf8Str stateFilePathFull(config.strStateFile);
8958 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8959 if (RT_FAILURE(vrc))
8960 return setError(E_FAIL,
8961 tr("Invalid saved state file path '%s' (%Rrc)"),
8962 config.strStateFile.c_str(),
8963 vrc);
8964 mSSData->strStateFilePath = stateFilePathFull;
8965 }
8966
8967 // snapshot folder needs special processing so set it again
8968 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8969 if (FAILED(rc)) return rc;
8970
8971 /* Copy the extra data items (Not in any case config is already the same as
8972 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8973 * make sure the extra data map is copied). */
8974 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8975
8976 /* currentStateModified (optional, default is true) */
8977 mData->mCurrentStateModified = config.fCurrentStateModified;
8978
8979 mData->mLastStateChange = config.timeLastStateChange;
8980
8981 /*
8982 * note: all mUserData members must be assigned prior this point because
8983 * we need to commit changes in order to let mUserData be shared by all
8984 * snapshot machine instances.
8985 */
8986 mUserData.commitCopy();
8987
8988 // machine registry, if present (must be loaded before snapshots)
8989 if (config.canHaveOwnMediaRegistry())
8990 {
8991 // determine machine folder
8992 Utf8Str strMachineFolder = getSettingsFileFull();
8993 strMachineFolder.stripFilename();
8994 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8995 config.mediaRegistry,
8996 strMachineFolder);
8997 if (FAILED(rc)) return rc;
8998 }
8999
9000 /* Snapshot node (optional) */
9001 size_t cRootSnapshots;
9002 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9003 {
9004 // there must be only one root snapshot
9005 Assert(cRootSnapshots == 1);
9006
9007 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9008
9009 rc = loadSnapshot(snap,
9010 config.uuidCurrentSnapshot,
9011 NULL); // no parent == first snapshot
9012 if (FAILED(rc)) return rc;
9013 }
9014
9015 // hardware data
9016 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9017 if (FAILED(rc)) return rc;
9018
9019 // load storage controllers
9020 rc = loadStorageControllers(config.storageMachine,
9021 puuidRegistry,
9022 NULL /* puuidSnapshot */);
9023 if (FAILED(rc)) return rc;
9024
9025 /*
9026 * NOTE: the assignment below must be the last thing to do,
9027 * otherwise it will be not possible to change the settings
9028 * somewhere in the code above because all setters will be
9029 * blocked by checkStateDependency(MutableStateDep).
9030 */
9031
9032 /* set the machine state to Aborted or Saved when appropriate */
9033 if (config.fAborted)
9034 {
9035 mSSData->strStateFilePath.setNull();
9036
9037 /* no need to use setMachineState() during init() */
9038 mData->mMachineState = MachineState_Aborted;
9039 }
9040 else if (!mSSData->strStateFilePath.isEmpty())
9041 {
9042 /* no need to use setMachineState() during init() */
9043 mData->mMachineState = MachineState_Saved;
9044 }
9045
9046 // after loading settings, we are no longer different from the XML on disk
9047 mData->flModifications = 0;
9048
9049 return S_OK;
9050}
9051
9052/**
9053 * Recursively loads all snapshots starting from the given.
9054 *
9055 * @param aNode <Snapshot> node.
9056 * @param aCurSnapshotId Current snapshot ID from the settings file.
9057 * @param aParentSnapshot Parent snapshot.
9058 */
9059HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9060 const Guid &aCurSnapshotId,
9061 Snapshot *aParentSnapshot)
9062{
9063 AssertReturn(!isSnapshotMachine(), E_FAIL);
9064 AssertReturn(!isSessionMachine(), E_FAIL);
9065
9066 HRESULT rc = S_OK;
9067
9068 Utf8Str strStateFile;
9069 if (!data.strStateFile.isEmpty())
9070 {
9071 /* optional */
9072 strStateFile = data.strStateFile;
9073 int vrc = calculateFullPath(strStateFile, strStateFile);
9074 if (RT_FAILURE(vrc))
9075 return setError(E_FAIL,
9076 tr("Invalid saved state file path '%s' (%Rrc)"),
9077 strStateFile.c_str(),
9078 vrc);
9079 }
9080
9081 /* create a snapshot machine object */
9082 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9083 pSnapshotMachine.createObject();
9084 rc = pSnapshotMachine->initFromSettings(this,
9085 data.hardware,
9086 &data.debugging,
9087 &data.autostart,
9088 data.storage,
9089 data.uuid.ref(),
9090 strStateFile);
9091 if (FAILED(rc)) return rc;
9092
9093 /* create a snapshot object */
9094 ComObjPtr<Snapshot> pSnapshot;
9095 pSnapshot.createObject();
9096 /* initialize the snapshot */
9097 rc = pSnapshot->init(mParent, // VirtualBox object
9098 data.uuid,
9099 data.strName,
9100 data.strDescription,
9101 data.timestamp,
9102 pSnapshotMachine,
9103 aParentSnapshot);
9104 if (FAILED(rc)) return rc;
9105
9106 /* memorize the first snapshot if necessary */
9107 if (!mData->mFirstSnapshot)
9108 mData->mFirstSnapshot = pSnapshot;
9109
9110 /* memorize the current snapshot when appropriate */
9111 if ( !mData->mCurrentSnapshot
9112 && pSnapshot->getId() == aCurSnapshotId
9113 )
9114 mData->mCurrentSnapshot = pSnapshot;
9115
9116 // now create the children
9117 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9118 it != data.llChildSnapshots.end();
9119 ++it)
9120 {
9121 const settings::Snapshot &childData = *it;
9122 // recurse
9123 rc = loadSnapshot(childData,
9124 aCurSnapshotId,
9125 pSnapshot); // parent = the one we created above
9126 if (FAILED(rc)) return rc;
9127 }
9128
9129 return rc;
9130}
9131
9132/**
9133 * Loads settings into mHWData.
9134 *
9135 * @param data Reference to the hardware settings.
9136 * @param pDbg Pointer to the debugging settings.
9137 * @param pAutostart Pointer to the autostart settings.
9138 */
9139HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9140 const settings::Autostart *pAutostart)
9141{
9142 AssertReturn(!isSessionMachine(), E_FAIL);
9143
9144 HRESULT rc = S_OK;
9145
9146 try
9147 {
9148 /* The hardware version attribute (optional). */
9149 mHWData->mHWVersion = data.strVersion;
9150 mHWData->mHardwareUUID = data.uuid;
9151
9152 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9153 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9154 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9155 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9156 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9157 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9158 mHWData->mPAEEnabled = data.fPAE;
9159 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9160 mHWData->mLongMode = data.enmLongMode;
9161 mHWData->mCPUCount = data.cCPUs;
9162 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9163 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9164
9165 // cpu
9166 if (mHWData->mCPUHotPlugEnabled)
9167 {
9168 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9169 it != data.llCpus.end();
9170 ++it)
9171 {
9172 const settings::Cpu &cpu = *it;
9173
9174 mHWData->mCPUAttached[cpu.ulId] = true;
9175 }
9176 }
9177
9178 // cpuid leafs
9179 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9180 it != data.llCpuIdLeafs.end();
9181 ++it)
9182 {
9183 const settings::CpuIdLeaf &leaf = *it;
9184
9185 switch (leaf.ulId)
9186 {
9187 case 0x0:
9188 case 0x1:
9189 case 0x2:
9190 case 0x3:
9191 case 0x4:
9192 case 0x5:
9193 case 0x6:
9194 case 0x7:
9195 case 0x8:
9196 case 0x9:
9197 case 0xA:
9198 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9199 break;
9200
9201 case 0x80000000:
9202 case 0x80000001:
9203 case 0x80000002:
9204 case 0x80000003:
9205 case 0x80000004:
9206 case 0x80000005:
9207 case 0x80000006:
9208 case 0x80000007:
9209 case 0x80000008:
9210 case 0x80000009:
9211 case 0x8000000A:
9212 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9213 break;
9214
9215 default:
9216 /* just ignore */
9217 break;
9218 }
9219 }
9220
9221 mHWData->mMemorySize = data.ulMemorySizeMB;
9222 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9223
9224 // boot order
9225 for (size_t i = 0;
9226 i < RT_ELEMENTS(mHWData->mBootOrder);
9227 i++)
9228 {
9229 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9230 if (it == data.mapBootOrder.end())
9231 mHWData->mBootOrder[i] = DeviceType_Null;
9232 else
9233 mHWData->mBootOrder[i] = it->second;
9234 }
9235
9236 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9237 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9238 mHWData->mMonitorCount = data.cMonitors;
9239 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9240 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9241 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9242 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9243 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9244 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9245 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9246 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9247 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9248 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9249 if (!data.strVideoCaptureFile.isEmpty())
9250 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9251 else
9252 mHWData->mVideoCaptureFile.setNull();
9253 mHWData->mFirmwareType = data.firmwareType;
9254 mHWData->mPointingHIDType = data.pointingHIDType;
9255 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9256 mHWData->mChipsetType = data.chipsetType;
9257 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9258 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9259 mHWData->mHPETEnabled = data.fHPETEnabled;
9260
9261 /* VRDEServer */
9262 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9263 if (FAILED(rc)) return rc;
9264
9265 /* BIOS */
9266 rc = mBIOSSettings->loadSettings(data.biosSettings);
9267 if (FAILED(rc)) return rc;
9268
9269 // Bandwidth control (must come before network adapters)
9270 rc = mBandwidthControl->loadSettings(data.ioSettings);
9271 if (FAILED(rc)) return rc;
9272
9273 /* Shared folders */
9274 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9275 it != data.usbSettings.llUSBControllers.end();
9276 ++it)
9277 {
9278 const settings::USBController &settingsCtrl = *it;
9279 ComObjPtr<USBController> newCtrl;
9280
9281 newCtrl.createObject();
9282 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9283 mUSBControllers->push_back(newCtrl);
9284 }
9285
9286 /* USB device filters */
9287 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9288 if (FAILED(rc)) return rc;
9289
9290 // network adapters
9291 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9292 uint32_t oldCount = mNetworkAdapters.size();
9293 if (newCount > oldCount)
9294 {
9295 mNetworkAdapters.resize(newCount);
9296 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9297 {
9298 unconst(mNetworkAdapters[slot]).createObject();
9299 mNetworkAdapters[slot]->init(this, slot);
9300 }
9301 }
9302 else if (newCount < oldCount)
9303 mNetworkAdapters.resize(newCount);
9304 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9305 it != data.llNetworkAdapters.end();
9306 ++it)
9307 {
9308 const settings::NetworkAdapter &nic = *it;
9309
9310 /* slot unicity is guaranteed by XML Schema */
9311 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9312 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9313 if (FAILED(rc)) return rc;
9314 }
9315
9316 // serial ports
9317 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9318 it != data.llSerialPorts.end();
9319 ++it)
9320 {
9321 const settings::SerialPort &s = *it;
9322
9323 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9324 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9325 if (FAILED(rc)) return rc;
9326 }
9327
9328 // parallel ports (optional)
9329 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9330 it != data.llParallelPorts.end();
9331 ++it)
9332 {
9333 const settings::ParallelPort &p = *it;
9334
9335 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9336 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9337 if (FAILED(rc)) return rc;
9338 }
9339
9340 /* AudioAdapter */
9341 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9342 if (FAILED(rc)) return rc;
9343
9344 /* Shared folders */
9345 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9346 it != data.llSharedFolders.end();
9347 ++it)
9348 {
9349 const settings::SharedFolder &sf = *it;
9350
9351 ComObjPtr<SharedFolder> sharedFolder;
9352 /* Check for double entries. Not allowed! */
9353 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9354 if (SUCCEEDED(rc))
9355 return setError(VBOX_E_OBJECT_IN_USE,
9356 tr("Shared folder named '%s' already exists"),
9357 sf.strName.c_str());
9358
9359 /* Create the new shared folder. Don't break on error. This will be
9360 * reported when the machine starts. */
9361 sharedFolder.createObject();
9362 rc = sharedFolder->init(getMachine(),
9363 sf.strName,
9364 sf.strHostPath,
9365 RT_BOOL(sf.fWritable),
9366 RT_BOOL(sf.fAutoMount),
9367 false /* fFailOnError */);
9368 if (FAILED(rc)) return rc;
9369 mHWData->mSharedFolders.push_back(sharedFolder);
9370 }
9371
9372 // Clipboard
9373 mHWData->mClipboardMode = data.clipboardMode;
9374
9375 // drag'n'drop
9376 mHWData->mDragAndDropMode = data.dragAndDropMode;
9377
9378 // guest settings
9379 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9380
9381 // IO settings
9382 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9383 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9384
9385 // Host PCI devices
9386 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9387 it != data.pciAttachments.end();
9388 ++it)
9389 {
9390 const settings::HostPCIDeviceAttachment &hpda = *it;
9391 ComObjPtr<PCIDeviceAttachment> pda;
9392
9393 pda.createObject();
9394 pda->loadSettings(this, hpda);
9395 mHWData->mPCIDeviceAssignments.push_back(pda);
9396 }
9397
9398 /*
9399 * (The following isn't really real hardware, but it lives in HWData
9400 * for reasons of convenience.)
9401 */
9402
9403#ifdef VBOX_WITH_GUEST_PROPS
9404 /* Guest properties (optional) */
9405 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9406 it != data.llGuestProperties.end();
9407 ++it)
9408 {
9409 const settings::GuestProperty &prop = *it;
9410 uint32_t fFlags = guestProp::NILFLAG;
9411 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9412 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9413 mHWData->mGuestProperties[prop.strName] = property;
9414 }
9415
9416 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9417#endif /* VBOX_WITH_GUEST_PROPS defined */
9418
9419 rc = loadDebugging(pDbg);
9420 if (FAILED(rc))
9421 return rc;
9422
9423 mHWData->mAutostart = *pAutostart;
9424
9425 /* default frontend */
9426 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9427 }
9428 catch(std::bad_alloc &)
9429 {
9430 return E_OUTOFMEMORY;
9431 }
9432
9433 AssertComRC(rc);
9434 return rc;
9435}
9436
9437/**
9438 * Called from Machine::loadHardware() to load the debugging settings of the
9439 * machine.
9440 *
9441 * @param pDbg Pointer to the settings.
9442 */
9443HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9444{
9445 mHWData->mDebugging = *pDbg;
9446 /* no more processing currently required, this will probably change. */
9447 return S_OK;
9448}
9449
9450/**
9451 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9452 *
9453 * @param data
9454 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9455 * @param puuidSnapshot
9456 * @return
9457 */
9458HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9459 const Guid *puuidRegistry,
9460 const Guid *puuidSnapshot)
9461{
9462 AssertReturn(!isSessionMachine(), E_FAIL);
9463
9464 HRESULT rc = S_OK;
9465
9466 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9467 it != data.llStorageControllers.end();
9468 ++it)
9469 {
9470 const settings::StorageController &ctlData = *it;
9471
9472 ComObjPtr<StorageController> pCtl;
9473 /* Try to find one with the name first. */
9474 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9475 if (SUCCEEDED(rc))
9476 return setError(VBOX_E_OBJECT_IN_USE,
9477 tr("Storage controller named '%s' already exists"),
9478 ctlData.strName.c_str());
9479
9480 pCtl.createObject();
9481 rc = pCtl->init(this,
9482 ctlData.strName,
9483 ctlData.storageBus,
9484 ctlData.ulInstance,
9485 ctlData.fBootable);
9486 if (FAILED(rc)) return rc;
9487
9488 mStorageControllers->push_back(pCtl);
9489
9490 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9491 if (FAILED(rc)) return rc;
9492
9493 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9494 if (FAILED(rc)) return rc;
9495
9496 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9497 if (FAILED(rc)) return rc;
9498
9499 /* Set IDE emulation settings (only for AHCI controller). */
9500 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9501 {
9502 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9503 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9504 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9505 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9506 )
9507 return rc;
9508 }
9509
9510 /* Load the attached devices now. */
9511 rc = loadStorageDevices(pCtl,
9512 ctlData,
9513 puuidRegistry,
9514 puuidSnapshot);
9515 if (FAILED(rc)) return rc;
9516 }
9517
9518 return S_OK;
9519}
9520
9521/**
9522 * Called from loadStorageControllers for a controller's devices.
9523 *
9524 * @param aStorageController
9525 * @param data
9526 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9527 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9528 * @return
9529 */
9530HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9531 const settings::StorageController &data,
9532 const Guid *puuidRegistry,
9533 const Guid *puuidSnapshot)
9534{
9535 HRESULT rc = S_OK;
9536
9537 /* paranoia: detect duplicate attachments */
9538 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9539 it != data.llAttachedDevices.end();
9540 ++it)
9541 {
9542 const settings::AttachedDevice &ad = *it;
9543
9544 for (settings::AttachedDevicesList::const_iterator it2 = it;
9545 it2 != data.llAttachedDevices.end();
9546 ++it2)
9547 {
9548 if (it == it2)
9549 continue;
9550
9551 const settings::AttachedDevice &ad2 = *it2;
9552
9553 if ( ad.lPort == ad2.lPort
9554 && ad.lDevice == ad2.lDevice)
9555 {
9556 return setError(E_FAIL,
9557 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9558 aStorageController->getName().c_str(),
9559 ad.lPort,
9560 ad.lDevice,
9561 mUserData->s.strName.c_str());
9562 }
9563 }
9564 }
9565
9566 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9567 it != data.llAttachedDevices.end();
9568 ++it)
9569 {
9570 const settings::AttachedDevice &dev = *it;
9571 ComObjPtr<Medium> medium;
9572
9573 switch (dev.deviceType)
9574 {
9575 case DeviceType_Floppy:
9576 case DeviceType_DVD:
9577 if (dev.strHostDriveSrc.isNotEmpty())
9578 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9579 else
9580 rc = mParent->findRemoveableMedium(dev.deviceType,
9581 dev.uuid,
9582 false /* fRefresh */,
9583 false /* aSetError */,
9584 medium);
9585 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9586 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9587 rc = S_OK;
9588 break;
9589
9590 case DeviceType_HardDisk:
9591 {
9592 /* find a hard disk by UUID */
9593 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9594 if (FAILED(rc))
9595 {
9596 if (isSnapshotMachine())
9597 {
9598 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9599 // so the user knows that the bad disk is in a snapshot somewhere
9600 com::ErrorInfo info;
9601 return setError(E_FAIL,
9602 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9603 puuidSnapshot->raw(),
9604 info.getText().raw());
9605 }
9606 else
9607 return rc;
9608 }
9609
9610 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9611
9612 if (medium->getType() == MediumType_Immutable)
9613 {
9614 if (isSnapshotMachine())
9615 return setError(E_FAIL,
9616 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9617 "of the virtual machine '%s' ('%s')"),
9618 medium->getLocationFull().c_str(),
9619 dev.uuid.raw(),
9620 puuidSnapshot->raw(),
9621 mUserData->s.strName.c_str(),
9622 mData->m_strConfigFileFull.c_str());
9623
9624 return setError(E_FAIL,
9625 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9626 medium->getLocationFull().c_str(),
9627 dev.uuid.raw(),
9628 mUserData->s.strName.c_str(),
9629 mData->m_strConfigFileFull.c_str());
9630 }
9631
9632 if (medium->getType() == MediumType_MultiAttach)
9633 {
9634 if (isSnapshotMachine())
9635 return setError(E_FAIL,
9636 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9637 "of the virtual machine '%s' ('%s')"),
9638 medium->getLocationFull().c_str(),
9639 dev.uuid.raw(),
9640 puuidSnapshot->raw(),
9641 mUserData->s.strName.c_str(),
9642 mData->m_strConfigFileFull.c_str());
9643
9644 return setError(E_FAIL,
9645 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9646 medium->getLocationFull().c_str(),
9647 dev.uuid.raw(),
9648 mUserData->s.strName.c_str(),
9649 mData->m_strConfigFileFull.c_str());
9650 }
9651
9652 if ( !isSnapshotMachine()
9653 && medium->getChildren().size() != 0
9654 )
9655 return setError(E_FAIL,
9656 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9657 "because it has %d differencing child hard disks"),
9658 medium->getLocationFull().c_str(),
9659 dev.uuid.raw(),
9660 mUserData->s.strName.c_str(),
9661 mData->m_strConfigFileFull.c_str(),
9662 medium->getChildren().size());
9663
9664 if (findAttachment(mMediaData->mAttachments,
9665 medium))
9666 return setError(E_FAIL,
9667 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9668 medium->getLocationFull().c_str(),
9669 dev.uuid.raw(),
9670 mUserData->s.strName.c_str(),
9671 mData->m_strConfigFileFull.c_str());
9672
9673 break;
9674 }
9675
9676 default:
9677 return setError(E_FAIL,
9678 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9679 medium->getLocationFull().c_str(),
9680 mUserData->s.strName.c_str(),
9681 mData->m_strConfigFileFull.c_str());
9682 }
9683
9684 if (FAILED(rc))
9685 break;
9686
9687 /* Bandwidth groups are loaded at this point. */
9688 ComObjPtr<BandwidthGroup> pBwGroup;
9689
9690 if (!dev.strBwGroup.isEmpty())
9691 {
9692 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9693 if (FAILED(rc))
9694 return setError(E_FAIL,
9695 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9696 medium->getLocationFull().c_str(),
9697 dev.strBwGroup.c_str(),
9698 mUserData->s.strName.c_str(),
9699 mData->m_strConfigFileFull.c_str());
9700 pBwGroup->reference();
9701 }
9702
9703 const Bstr controllerName = aStorageController->getName();
9704 ComObjPtr<MediumAttachment> pAttachment;
9705 pAttachment.createObject();
9706 rc = pAttachment->init(this,
9707 medium,
9708 controllerName,
9709 dev.lPort,
9710 dev.lDevice,
9711 dev.deviceType,
9712 false,
9713 dev.fPassThrough,
9714 dev.fTempEject,
9715 dev.fNonRotational,
9716 dev.fDiscard,
9717 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9718 if (FAILED(rc)) break;
9719
9720 /* associate the medium with this machine and snapshot */
9721 if (!medium.isNull())
9722 {
9723 AutoCaller medCaller(medium);
9724 if (FAILED(medCaller.rc())) return medCaller.rc();
9725 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9726
9727 if (isSnapshotMachine())
9728 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9729 else
9730 rc = medium->addBackReference(mData->mUuid);
9731 /* If the medium->addBackReference fails it sets an appropriate
9732 * error message, so no need to do any guesswork here. */
9733
9734 if (puuidRegistry)
9735 // caller wants registry ID to be set on all attached media (OVF import case)
9736 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9737 }
9738
9739 if (FAILED(rc))
9740 break;
9741
9742 /* back up mMediaData to let registeredInit() properly rollback on failure
9743 * (= limited accessibility) */
9744 setModified(IsModified_Storage);
9745 mMediaData.backup();
9746 mMediaData->mAttachments.push_back(pAttachment);
9747 }
9748
9749 return rc;
9750}
9751
9752/**
9753 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9754 *
9755 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9756 * @param aSnapshot where to return the found snapshot
9757 * @param aSetError true to set extended error info on failure
9758 */
9759HRESULT Machine::findSnapshotById(const Guid &aId,
9760 ComObjPtr<Snapshot> &aSnapshot,
9761 bool aSetError /* = false */)
9762{
9763 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9764
9765 if (!mData->mFirstSnapshot)
9766 {
9767 if (aSetError)
9768 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9769 return E_FAIL;
9770 }
9771
9772 if (aId.isZero())
9773 aSnapshot = mData->mFirstSnapshot;
9774 else
9775 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9776
9777 if (!aSnapshot)
9778 {
9779 if (aSetError)
9780 return setError(E_FAIL,
9781 tr("Could not find a snapshot with UUID {%s}"),
9782 aId.toString().c_str());
9783 return E_FAIL;
9784 }
9785
9786 return S_OK;
9787}
9788
9789/**
9790 * Returns the snapshot with the given name or fails of no such snapshot.
9791 *
9792 * @param aName snapshot name to find
9793 * @param aSnapshot where to return the found snapshot
9794 * @param aSetError true to set extended error info on failure
9795 */
9796HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9797 ComObjPtr<Snapshot> &aSnapshot,
9798 bool aSetError /* = false */)
9799{
9800 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9801
9802 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9803
9804 if (!mData->mFirstSnapshot)
9805 {
9806 if (aSetError)
9807 return setError(VBOX_E_OBJECT_NOT_FOUND,
9808 tr("This machine does not have any snapshots"));
9809 return VBOX_E_OBJECT_NOT_FOUND;
9810 }
9811
9812 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9813
9814 if (!aSnapshot)
9815 {
9816 if (aSetError)
9817 return setError(VBOX_E_OBJECT_NOT_FOUND,
9818 tr("Could not find a snapshot named '%s'"), strName.c_str());
9819 return VBOX_E_OBJECT_NOT_FOUND;
9820 }
9821
9822 return S_OK;
9823}
9824
9825/**
9826 * Returns a storage controller object with the given name.
9827 *
9828 * @param aName storage controller name to find
9829 * @param aStorageController where to return the found storage controller
9830 * @param aSetError true to set extended error info on failure
9831 */
9832HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9833 ComObjPtr<StorageController> &aStorageController,
9834 bool aSetError /* = false */)
9835{
9836 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9837
9838 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9839 it != mStorageControllers->end();
9840 ++it)
9841 {
9842 if ((*it)->getName() == aName)
9843 {
9844 aStorageController = (*it);
9845 return S_OK;
9846 }
9847 }
9848
9849 if (aSetError)
9850 return setError(VBOX_E_OBJECT_NOT_FOUND,
9851 tr("Could not find a storage controller named '%s'"),
9852 aName.c_str());
9853 return VBOX_E_OBJECT_NOT_FOUND;
9854}
9855
9856/**
9857 * Returns a USB controller object with the given name.
9858 *
9859 * @param aName USB controller name to find
9860 * @param aUSBController where to return the found USB controller
9861 * @param aSetError true to set extended error info on failure
9862 */
9863HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9864 ComObjPtr<USBController> &aUSBController,
9865 bool aSetError /* = false */)
9866{
9867 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9868
9869 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9870 it != mUSBControllers->end();
9871 ++it)
9872 {
9873 if ((*it)->getName() == aName)
9874 {
9875 aUSBController = (*it);
9876 return S_OK;
9877 }
9878 }
9879
9880 if (aSetError)
9881 return setError(VBOX_E_OBJECT_NOT_FOUND,
9882 tr("Could not find a storage controller named '%s'"),
9883 aName.c_str());
9884 return VBOX_E_OBJECT_NOT_FOUND;
9885}
9886
9887/**
9888 * Returns the number of USB controller instance of the given type.
9889 *
9890 * @param enmType USB controller type.
9891 */
9892ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9893{
9894 ULONG cCtrls = 0;
9895
9896 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9897 it != mUSBControllers->end();
9898 ++it)
9899 {
9900 if ((*it)->getControllerType() == enmType)
9901 cCtrls++;
9902 }
9903
9904 return cCtrls;
9905}
9906
9907HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9908 MediaData::AttachmentList &atts)
9909{
9910 AutoCaller autoCaller(this);
9911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9912
9913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9914
9915 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9916 it != mMediaData->mAttachments.end();
9917 ++it)
9918 {
9919 const ComObjPtr<MediumAttachment> &pAtt = *it;
9920
9921 // should never happen, but deal with NULL pointers in the list.
9922 AssertStmt(!pAtt.isNull(), continue);
9923
9924 // getControllerName() needs caller+read lock
9925 AutoCaller autoAttCaller(pAtt);
9926 if (FAILED(autoAttCaller.rc()))
9927 {
9928 atts.clear();
9929 return autoAttCaller.rc();
9930 }
9931 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9932
9933 if (pAtt->getControllerName() == aName)
9934 atts.push_back(pAtt);
9935 }
9936
9937 return S_OK;
9938}
9939
9940/**
9941 * Helper for #saveSettings. Cares about renaming the settings directory and
9942 * file if the machine name was changed and about creating a new settings file
9943 * if this is a new machine.
9944 *
9945 * @note Must be never called directly but only from #saveSettings().
9946 */
9947HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9948{
9949 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9950
9951 HRESULT rc = S_OK;
9952
9953 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9954
9955 /// @todo need to handle primary group change, too
9956
9957 /* attempt to rename the settings file if machine name is changed */
9958 if ( mUserData->s.fNameSync
9959 && mUserData.isBackedUp()
9960 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9961 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9962 )
9963 {
9964 bool dirRenamed = false;
9965 bool fileRenamed = false;
9966
9967 Utf8Str configFile, newConfigFile;
9968 Utf8Str configFilePrev, newConfigFilePrev;
9969 Utf8Str configDir, newConfigDir;
9970
9971 do
9972 {
9973 int vrc = VINF_SUCCESS;
9974
9975 Utf8Str name = mUserData.backedUpData()->s.strName;
9976 Utf8Str newName = mUserData->s.strName;
9977 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9978 if (group == "/")
9979 group.setNull();
9980 Utf8Str newGroup = mUserData->s.llGroups.front();
9981 if (newGroup == "/")
9982 newGroup.setNull();
9983
9984 configFile = mData->m_strConfigFileFull;
9985
9986 /* first, rename the directory if it matches the group and machine name */
9987 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9988 group.c_str(), RTPATH_DELIMITER, name.c_str());
9989 /** @todo hack, make somehow use of ComposeMachineFilename */
9990 if (mUserData->s.fDirectoryIncludesUUID)
9991 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9992 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9993 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9994 /** @todo hack, make somehow use of ComposeMachineFilename */
9995 if (mUserData->s.fDirectoryIncludesUUID)
9996 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9997 configDir = configFile;
9998 configDir.stripFilename();
9999 newConfigDir = configDir;
10000 if ( configDir.length() >= groupPlusName.length()
10001 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10002 {
10003 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10004 Utf8Str newConfigBaseDir(newConfigDir);
10005 newConfigDir.append(newGroupPlusName);
10006 /* consistency: use \ if appropriate on the platform */
10007 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10008 /* new dir and old dir cannot be equal here because of 'if'
10009 * above and because name != newName */
10010 Assert(configDir != newConfigDir);
10011 if (!fSettingsFileIsNew)
10012 {
10013 /* perform real rename only if the machine is not new */
10014 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10015 if ( vrc == VERR_FILE_NOT_FOUND
10016 || vrc == VERR_PATH_NOT_FOUND)
10017 {
10018 /* create the parent directory, then retry renaming */
10019 Utf8Str parent(newConfigDir);
10020 parent.stripFilename();
10021 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10022 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10023 }
10024 if (RT_FAILURE(vrc))
10025 {
10026 rc = setError(E_FAIL,
10027 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10028 configDir.c_str(),
10029 newConfigDir.c_str(),
10030 vrc);
10031 break;
10032 }
10033 /* delete subdirectories which are no longer needed */
10034 Utf8Str dir(configDir);
10035 dir.stripFilename();
10036 while (dir != newConfigBaseDir && dir != ".")
10037 {
10038 vrc = RTDirRemove(dir.c_str());
10039 if (RT_FAILURE(vrc))
10040 break;
10041 dir.stripFilename();
10042 }
10043 dirRenamed = true;
10044 }
10045 }
10046
10047 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10048 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10049
10050 /* then try to rename the settings file itself */
10051 if (newConfigFile != configFile)
10052 {
10053 /* get the path to old settings file in renamed directory */
10054 configFile = Utf8StrFmt("%s%c%s",
10055 newConfigDir.c_str(),
10056 RTPATH_DELIMITER,
10057 RTPathFilename(configFile.c_str()));
10058 if (!fSettingsFileIsNew)
10059 {
10060 /* perform real rename only if the machine is not new */
10061 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10062 if (RT_FAILURE(vrc))
10063 {
10064 rc = setError(E_FAIL,
10065 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10066 configFile.c_str(),
10067 newConfigFile.c_str(),
10068 vrc);
10069 break;
10070 }
10071 fileRenamed = true;
10072 configFilePrev = configFile;
10073 configFilePrev += "-prev";
10074 newConfigFilePrev = newConfigFile;
10075 newConfigFilePrev += "-prev";
10076 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10077 }
10078 }
10079
10080 // update m_strConfigFileFull amd mConfigFile
10081 mData->m_strConfigFileFull = newConfigFile;
10082 // compute the relative path too
10083 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10084
10085 // store the old and new so that VirtualBox::saveSettings() can update
10086 // the media registry
10087 if ( mData->mRegistered
10088 && configDir != newConfigDir)
10089 {
10090 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10091
10092 if (pfNeedsGlobalSaveSettings)
10093 *pfNeedsGlobalSaveSettings = true;
10094 }
10095
10096 // in the saved state file path, replace the old directory with the new directory
10097 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10098 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10099
10100 // and do the same thing for the saved state file paths of all the online snapshots
10101 if (mData->mFirstSnapshot)
10102 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10103 newConfigDir.c_str());
10104 }
10105 while (0);
10106
10107 if (FAILED(rc))
10108 {
10109 /* silently try to rename everything back */
10110 if (fileRenamed)
10111 {
10112 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10113 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10114 }
10115 if (dirRenamed)
10116 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10117 }
10118
10119 if (FAILED(rc)) return rc;
10120 }
10121
10122 if (fSettingsFileIsNew)
10123 {
10124 /* create a virgin config file */
10125 int vrc = VINF_SUCCESS;
10126
10127 /* ensure the settings directory exists */
10128 Utf8Str path(mData->m_strConfigFileFull);
10129 path.stripFilename();
10130 if (!RTDirExists(path.c_str()))
10131 {
10132 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10133 if (RT_FAILURE(vrc))
10134 {
10135 return setError(E_FAIL,
10136 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10137 path.c_str(),
10138 vrc);
10139 }
10140 }
10141
10142 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10143 path = Utf8Str(mData->m_strConfigFileFull);
10144 RTFILE f = NIL_RTFILE;
10145 vrc = RTFileOpen(&f, path.c_str(),
10146 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10147 if (RT_FAILURE(vrc))
10148 return setError(E_FAIL,
10149 tr("Could not create the settings file '%s' (%Rrc)"),
10150 path.c_str(),
10151 vrc);
10152 RTFileClose(f);
10153 }
10154
10155 return rc;
10156}
10157
10158/**
10159 * Saves and commits machine data, user data and hardware data.
10160 *
10161 * Note that on failure, the data remains uncommitted.
10162 *
10163 * @a aFlags may combine the following flags:
10164 *
10165 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10166 * Used when saving settings after an operation that makes them 100%
10167 * correspond to the settings from the current snapshot.
10168 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10169 * #isReallyModified() returns false. This is necessary for cases when we
10170 * change machine data directly, not through the backup()/commit() mechanism.
10171 * - SaveS_Force: settings will be saved without doing a deep compare of the
10172 * settings structures. This is used when this is called because snapshots
10173 * have changed to avoid the overhead of the deep compare.
10174 *
10175 * @note Must be called from under this object's write lock. Locks children for
10176 * writing.
10177 *
10178 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10179 * initialized to false and that will be set to true by this function if
10180 * the caller must invoke VirtualBox::saveSettings() because the global
10181 * settings have changed. This will happen if a machine rename has been
10182 * saved and the global machine and media registries will therefore need
10183 * updating.
10184 */
10185HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10186 int aFlags /*= 0*/)
10187{
10188 LogFlowThisFuncEnter();
10189
10190 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10191
10192 /* make sure child objects are unable to modify the settings while we are
10193 * saving them */
10194 ensureNoStateDependencies();
10195
10196 AssertReturn(!isSnapshotMachine(),
10197 E_FAIL);
10198
10199 HRESULT rc = S_OK;
10200 bool fNeedsWrite = false;
10201
10202 /* First, prepare to save settings. It will care about renaming the
10203 * settings directory and file if the machine name was changed and about
10204 * creating a new settings file if this is a new machine. */
10205 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10206 if (FAILED(rc)) return rc;
10207
10208 // keep a pointer to the current settings structures
10209 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10210 settings::MachineConfigFile *pNewConfig = NULL;
10211
10212 try
10213 {
10214 // make a fresh one to have everyone write stuff into
10215 pNewConfig = new settings::MachineConfigFile(NULL);
10216 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10217
10218 // now go and copy all the settings data from COM to the settings structures
10219 // (this calles saveSettings() on all the COM objects in the machine)
10220 copyMachineDataToSettings(*pNewConfig);
10221
10222 if (aFlags & SaveS_ResetCurStateModified)
10223 {
10224 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10225 mData->mCurrentStateModified = FALSE;
10226 fNeedsWrite = true; // always, no need to compare
10227 }
10228 else if (aFlags & SaveS_Force)
10229 {
10230 fNeedsWrite = true; // always, no need to compare
10231 }
10232 else
10233 {
10234 if (!mData->mCurrentStateModified)
10235 {
10236 // do a deep compare of the settings that we just saved with the settings
10237 // previously stored in the config file; this invokes MachineConfigFile::operator==
10238 // which does a deep compare of all the settings, which is expensive but less expensive
10239 // than writing out XML in vain
10240 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10241
10242 // could still be modified if any settings changed
10243 mData->mCurrentStateModified = fAnySettingsChanged;
10244
10245 fNeedsWrite = fAnySettingsChanged;
10246 }
10247 else
10248 fNeedsWrite = true;
10249 }
10250
10251 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10252
10253 if (fNeedsWrite)
10254 // now spit it all out!
10255 pNewConfig->write(mData->m_strConfigFileFull);
10256
10257 mData->pMachineConfigFile = pNewConfig;
10258 delete pOldConfig;
10259 commit();
10260
10261 // after saving settings, we are no longer different from the XML on disk
10262 mData->flModifications = 0;
10263 }
10264 catch (HRESULT err)
10265 {
10266 // we assume that error info is set by the thrower
10267 rc = err;
10268
10269 // restore old config
10270 delete pNewConfig;
10271 mData->pMachineConfigFile = pOldConfig;
10272 }
10273 catch (...)
10274 {
10275 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10276 }
10277
10278 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10279 {
10280 /* Fire the data change event, even on failure (since we've already
10281 * committed all data). This is done only for SessionMachines because
10282 * mutable Machine instances are always not registered (i.e. private
10283 * to the client process that creates them) and thus don't need to
10284 * inform callbacks. */
10285 if (isSessionMachine())
10286 mParent->onMachineDataChange(mData->mUuid);
10287 }
10288
10289 LogFlowThisFunc(("rc=%08X\n", rc));
10290 LogFlowThisFuncLeave();
10291 return rc;
10292}
10293
10294/**
10295 * Implementation for saving the machine settings into the given
10296 * settings::MachineConfigFile instance. This copies machine extradata
10297 * from the previous machine config file in the instance data, if any.
10298 *
10299 * This gets called from two locations:
10300 *
10301 * -- Machine::saveSettings(), during the regular XML writing;
10302 *
10303 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10304 * exported to OVF and we write the VirtualBox proprietary XML
10305 * into a <vbox:Machine> tag.
10306 *
10307 * This routine fills all the fields in there, including snapshots, *except*
10308 * for the following:
10309 *
10310 * -- fCurrentStateModified. There is some special logic associated with that.
10311 *
10312 * The caller can then call MachineConfigFile::write() or do something else
10313 * with it.
10314 *
10315 * Caller must hold the machine lock!
10316 *
10317 * This throws XML errors and HRESULT, so the caller must have a catch block!
10318 */
10319void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10320{
10321 // deep copy extradata
10322 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10323
10324 config.uuid = mData->mUuid;
10325
10326 // copy name, description, OS type, teleport, UTC etc.
10327 config.machineUserData = mUserData->s;
10328
10329 // Encode the Icon Override data from Machine and store on config userdata.
10330 com::SafeArray<BYTE> iconByte;
10331 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10332 ssize_t cbData = iconByte.size();
10333 if (cbData > 0)
10334 {
10335 ssize_t cchOut = RTBase64EncodedLength(cbData);
10336 Utf8Str strIconData;
10337 strIconData.reserve(cchOut+1);
10338 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10339 strIconData.mutableRaw(), strIconData.capacity(),
10340 NULL);
10341 if (RT_FAILURE(vrc))
10342 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10343 strIconData.jolt();
10344 config.machineUserData.ovIcon = strIconData;
10345 }
10346 else
10347 config.machineUserData.ovIcon.setNull();
10348
10349 if ( mData->mMachineState == MachineState_Saved
10350 || mData->mMachineState == MachineState_Restoring
10351 // when deleting a snapshot we may or may not have a saved state in the current state,
10352 // so let's not assert here please
10353 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10354 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10355 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10356 && (!mSSData->strStateFilePath.isEmpty())
10357 )
10358 )
10359 {
10360 Assert(!mSSData->strStateFilePath.isEmpty());
10361 /* try to make the file name relative to the settings file dir */
10362 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10363 }
10364 else
10365 {
10366 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10367 config.strStateFile.setNull();
10368 }
10369
10370 if (mData->mCurrentSnapshot)
10371 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10372 else
10373 config.uuidCurrentSnapshot.clear();
10374
10375 config.timeLastStateChange = mData->mLastStateChange;
10376 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10377 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10378
10379 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10380 if (FAILED(rc)) throw rc;
10381
10382 rc = saveStorageControllers(config.storageMachine);
10383 if (FAILED(rc)) throw rc;
10384
10385 // save machine's media registry if this is VirtualBox 4.0 or later
10386 if (config.canHaveOwnMediaRegistry())
10387 {
10388 // determine machine folder
10389 Utf8Str strMachineFolder = getSettingsFileFull();
10390 strMachineFolder.stripFilename();
10391 mParent->saveMediaRegistry(config.mediaRegistry,
10392 getId(), // only media with registry ID == machine UUID
10393 strMachineFolder);
10394 // this throws HRESULT
10395 }
10396
10397 // save snapshots
10398 rc = saveAllSnapshots(config);
10399 if (FAILED(rc)) throw rc;
10400}
10401
10402/**
10403 * Saves all snapshots of the machine into the given machine config file. Called
10404 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10405 * @param config
10406 * @return
10407 */
10408HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10409{
10410 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10411
10412 HRESULT rc = S_OK;
10413
10414 try
10415 {
10416 config.llFirstSnapshot.clear();
10417
10418 if (mData->mFirstSnapshot)
10419 {
10420 settings::Snapshot snapNew;
10421 config.llFirstSnapshot.push_back(snapNew);
10422
10423 // get reference to the fresh copy of the snapshot on the list and
10424 // work on that copy directly to avoid excessive copying later
10425 settings::Snapshot &snap = config.llFirstSnapshot.front();
10426
10427 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10428 if (FAILED(rc)) throw rc;
10429 }
10430
10431// if (mType == IsSessionMachine)
10432// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10433
10434 }
10435 catch (HRESULT err)
10436 {
10437 /* we assume that error info is set by the thrower */
10438 rc = err;
10439 }
10440 catch (...)
10441 {
10442 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10443 }
10444
10445 return rc;
10446}
10447
10448/**
10449 * Saves the VM hardware configuration. It is assumed that the
10450 * given node is empty.
10451 *
10452 * @param data Reference to the settings object for the hardware config.
10453 * @param pDbg Pointer to the settings object for the debugging config
10454 * which happens to live in mHWData.
10455 * @param pAutostart Pointer to the settings object for the autostart config
10456 * which happens to live in mHWData.
10457 */
10458HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10459 settings::Autostart *pAutostart)
10460{
10461 HRESULT rc = S_OK;
10462
10463 try
10464 {
10465 /* The hardware version attribute (optional).
10466 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10467 if ( mHWData->mHWVersion == "1"
10468 && mSSData->strStateFilePath.isEmpty()
10469 )
10470 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. */
10471
10472 data.strVersion = mHWData->mHWVersion;
10473 data.uuid = mHWData->mHardwareUUID;
10474
10475 // CPU
10476 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10477 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10478 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10479 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10480 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10481 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10482 data.fPAE = !!mHWData->mPAEEnabled;
10483 data.enmLongMode = mHWData->mLongMode;
10484 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10485
10486 /* Standard and Extended CPUID leafs. */
10487 data.llCpuIdLeafs.clear();
10488 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10489 {
10490 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10491 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10492 }
10493 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10494 {
10495 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10496 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10497 }
10498
10499 data.cCPUs = mHWData->mCPUCount;
10500 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10501 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10502
10503 data.llCpus.clear();
10504 if (data.fCpuHotPlug)
10505 {
10506 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10507 {
10508 if (mHWData->mCPUAttached[idx])
10509 {
10510 settings::Cpu cpu;
10511 cpu.ulId = idx;
10512 data.llCpus.push_back(cpu);
10513 }
10514 }
10515 }
10516
10517 // memory
10518 data.ulMemorySizeMB = mHWData->mMemorySize;
10519 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10520
10521 // firmware
10522 data.firmwareType = mHWData->mFirmwareType;
10523
10524 // HID
10525 data.pointingHIDType = mHWData->mPointingHIDType;
10526 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10527
10528 // chipset
10529 data.chipsetType = mHWData->mChipsetType;
10530
10531 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10532 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10533
10534 // HPET
10535 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10536
10537 // boot order
10538 data.mapBootOrder.clear();
10539 for (size_t i = 0;
10540 i < RT_ELEMENTS(mHWData->mBootOrder);
10541 ++i)
10542 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10543
10544 // display
10545 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10546 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10547 data.cMonitors = mHWData->mMonitorCount;
10548 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10549 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10550 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10551 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10552 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10553 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10554 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10555 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10556 {
10557 if (mHWData->maVideoCaptureScreens[i])
10558 ASMBitSet(&data.u64VideoCaptureScreens, i);
10559 else
10560 ASMBitClear(&data.u64VideoCaptureScreens, i);
10561 }
10562 /* store relative video capture file if possible */
10563 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10564
10565 /* VRDEServer settings (optional) */
10566 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10567 if (FAILED(rc)) throw rc;
10568
10569 /* BIOS (required) */
10570 rc = mBIOSSettings->saveSettings(data.biosSettings);
10571 if (FAILED(rc)) throw rc;
10572
10573 /* USB Controller (required) */
10574 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10575 it != mUSBControllers->end();
10576 ++it)
10577 {
10578 ComObjPtr<USBController> ctrl = *it;
10579 settings::USBController settingsCtrl;
10580
10581 settingsCtrl.strName = ctrl->getName();
10582 settingsCtrl.enmType = ctrl->getControllerType();
10583
10584 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10585 }
10586
10587 /* USB device filters (required) */
10588 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10589 if (FAILED(rc)) throw rc;
10590
10591 /* Network adapters (required) */
10592 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10593 data.llNetworkAdapters.clear();
10594 /* Write out only the nominal number of network adapters for this
10595 * chipset type. Since Machine::commit() hasn't been called there
10596 * may be extra NIC settings in the vector. */
10597 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10598 {
10599 settings::NetworkAdapter nic;
10600 nic.ulSlot = slot;
10601 /* paranoia check... must not be NULL, but must not crash either. */
10602 if (mNetworkAdapters[slot])
10603 {
10604 rc = mNetworkAdapters[slot]->saveSettings(nic);
10605 if (FAILED(rc)) throw rc;
10606
10607 data.llNetworkAdapters.push_back(nic);
10608 }
10609 }
10610
10611 /* Serial ports */
10612 data.llSerialPorts.clear();
10613 for (ULONG slot = 0;
10614 slot < RT_ELEMENTS(mSerialPorts);
10615 ++slot)
10616 {
10617 settings::SerialPort s;
10618 s.ulSlot = slot;
10619 rc = mSerialPorts[slot]->saveSettings(s);
10620 if (FAILED(rc)) return rc;
10621
10622 data.llSerialPorts.push_back(s);
10623 }
10624
10625 /* Parallel ports */
10626 data.llParallelPorts.clear();
10627 for (ULONG slot = 0;
10628 slot < RT_ELEMENTS(mParallelPorts);
10629 ++slot)
10630 {
10631 settings::ParallelPort p;
10632 p.ulSlot = slot;
10633 rc = mParallelPorts[slot]->saveSettings(p);
10634 if (FAILED(rc)) return rc;
10635
10636 data.llParallelPorts.push_back(p);
10637 }
10638
10639 /* Audio adapter */
10640 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10641 if (FAILED(rc)) return rc;
10642
10643 /* Shared folders */
10644 data.llSharedFolders.clear();
10645 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10646 it != mHWData->mSharedFolders.end();
10647 ++it)
10648 {
10649 SharedFolder *pSF = *it;
10650 AutoCaller sfCaller(pSF);
10651 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10652 settings::SharedFolder sf;
10653 sf.strName = pSF->getName();
10654 sf.strHostPath = pSF->getHostPath();
10655 sf.fWritable = !!pSF->isWritable();
10656 sf.fAutoMount = !!pSF->isAutoMounted();
10657
10658 data.llSharedFolders.push_back(sf);
10659 }
10660
10661 // clipboard
10662 data.clipboardMode = mHWData->mClipboardMode;
10663
10664 // drag'n'drop
10665 data.dragAndDropMode = mHWData->mDragAndDropMode;
10666
10667 /* Guest */
10668 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10669
10670 // IO settings
10671 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10672 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10673
10674 /* BandwidthControl (required) */
10675 rc = mBandwidthControl->saveSettings(data.ioSettings);
10676 if (FAILED(rc)) throw rc;
10677
10678 /* Host PCI devices */
10679 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10680 it != mHWData->mPCIDeviceAssignments.end();
10681 ++it)
10682 {
10683 ComObjPtr<PCIDeviceAttachment> pda = *it;
10684 settings::HostPCIDeviceAttachment hpda;
10685
10686 rc = pda->saveSettings(hpda);
10687 if (FAILED(rc)) throw rc;
10688
10689 data.pciAttachments.push_back(hpda);
10690 }
10691
10692
10693 // guest properties
10694 data.llGuestProperties.clear();
10695#ifdef VBOX_WITH_GUEST_PROPS
10696 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10697 it != mHWData->mGuestProperties.end();
10698 ++it)
10699 {
10700 HWData::GuestProperty property = it->second;
10701
10702 /* Remove transient guest properties at shutdown unless we
10703 * are saving state */
10704 if ( ( mData->mMachineState == MachineState_PoweredOff
10705 || mData->mMachineState == MachineState_Aborted
10706 || mData->mMachineState == MachineState_Teleported)
10707 && ( property.mFlags & guestProp::TRANSIENT
10708 || property.mFlags & guestProp::TRANSRESET))
10709 continue;
10710 settings::GuestProperty prop;
10711 prop.strName = it->first;
10712 prop.strValue = property.strValue;
10713 prop.timestamp = property.mTimestamp;
10714 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10715 guestProp::writeFlags(property.mFlags, szFlags);
10716 prop.strFlags = szFlags;
10717
10718 data.llGuestProperties.push_back(prop);
10719 }
10720
10721 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10722 /* I presume this doesn't require a backup(). */
10723 mData->mGuestPropertiesModified = FALSE;
10724#endif /* VBOX_WITH_GUEST_PROPS defined */
10725
10726 *pDbg = mHWData->mDebugging;
10727 *pAutostart = mHWData->mAutostart;
10728
10729 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10730 }
10731 catch(std::bad_alloc &)
10732 {
10733 return E_OUTOFMEMORY;
10734 }
10735
10736 AssertComRC(rc);
10737 return rc;
10738}
10739
10740/**
10741 * Saves the storage controller configuration.
10742 *
10743 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10744 */
10745HRESULT Machine::saveStorageControllers(settings::Storage &data)
10746{
10747 data.llStorageControllers.clear();
10748
10749 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10750 it != mStorageControllers->end();
10751 ++it)
10752 {
10753 HRESULT rc;
10754 ComObjPtr<StorageController> pCtl = *it;
10755
10756 settings::StorageController ctl;
10757 ctl.strName = pCtl->getName();
10758 ctl.controllerType = pCtl->getControllerType();
10759 ctl.storageBus = pCtl->getStorageBus();
10760 ctl.ulInstance = pCtl->getInstance();
10761 ctl.fBootable = pCtl->getBootable();
10762
10763 /* Save the port count. */
10764 ULONG portCount;
10765 rc = pCtl->COMGETTER(PortCount)(&portCount);
10766 ComAssertComRCRet(rc, rc);
10767 ctl.ulPortCount = portCount;
10768
10769 /* Save fUseHostIOCache */
10770 BOOL fUseHostIOCache;
10771 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10772 ComAssertComRCRet(rc, rc);
10773 ctl.fUseHostIOCache = !!fUseHostIOCache;
10774
10775 /* Save IDE emulation settings. */
10776 if (ctl.controllerType == StorageControllerType_IntelAhci)
10777 {
10778 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10779 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10780 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10781 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10782 )
10783 ComAssertComRCRet(rc, rc);
10784 }
10785
10786 /* save the devices now. */
10787 rc = saveStorageDevices(pCtl, ctl);
10788 ComAssertComRCRet(rc, rc);
10789
10790 data.llStorageControllers.push_back(ctl);
10791 }
10792
10793 return S_OK;
10794}
10795
10796/**
10797 * Saves the hard disk configuration.
10798 */
10799HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10800 settings::StorageController &data)
10801{
10802 MediaData::AttachmentList atts;
10803
10804 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10805 if (FAILED(rc)) return rc;
10806
10807 data.llAttachedDevices.clear();
10808 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10809 it != atts.end();
10810 ++it)
10811 {
10812 settings::AttachedDevice dev;
10813
10814 MediumAttachment *pAttach = *it;
10815 Medium *pMedium = pAttach->getMedium();
10816
10817 dev.deviceType = pAttach->getType();
10818 dev.lPort = pAttach->getPort();
10819 dev.lDevice = pAttach->getDevice();
10820 if (pMedium)
10821 {
10822 if (pMedium->isHostDrive())
10823 dev.strHostDriveSrc = pMedium->getLocationFull();
10824 else
10825 dev.uuid = pMedium->getId();
10826 dev.fPassThrough = pAttach->getPassthrough();
10827 dev.fTempEject = pAttach->getTempEject();
10828 dev.fNonRotational = pAttach->getNonRotational();
10829 dev.fDiscard = pAttach->getDiscard();
10830 }
10831
10832 dev.strBwGroup = pAttach->getBandwidthGroup();
10833
10834 data.llAttachedDevices.push_back(dev);
10835 }
10836
10837 return S_OK;
10838}
10839
10840/**
10841 * Saves machine state settings as defined by aFlags
10842 * (SaveSTS_* values).
10843 *
10844 * @param aFlags Combination of SaveSTS_* flags.
10845 *
10846 * @note Locks objects for writing.
10847 */
10848HRESULT Machine::saveStateSettings(int aFlags)
10849{
10850 if (aFlags == 0)
10851 return S_OK;
10852
10853 AutoCaller autoCaller(this);
10854 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10855
10856 /* This object's write lock is also necessary to serialize file access
10857 * (prevent concurrent reads and writes) */
10858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10859
10860 HRESULT rc = S_OK;
10861
10862 Assert(mData->pMachineConfigFile);
10863
10864 try
10865 {
10866 if (aFlags & SaveSTS_CurStateModified)
10867 mData->pMachineConfigFile->fCurrentStateModified = true;
10868
10869 if (aFlags & SaveSTS_StateFilePath)
10870 {
10871 if (!mSSData->strStateFilePath.isEmpty())
10872 /* try to make the file name relative to the settings file dir */
10873 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10874 else
10875 mData->pMachineConfigFile->strStateFile.setNull();
10876 }
10877
10878 if (aFlags & SaveSTS_StateTimeStamp)
10879 {
10880 Assert( mData->mMachineState != MachineState_Aborted
10881 || mSSData->strStateFilePath.isEmpty());
10882
10883 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10884
10885 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10886//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10887 }
10888
10889 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10890 }
10891 catch (...)
10892 {
10893 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10894 }
10895
10896 return rc;
10897}
10898
10899/**
10900 * Ensures that the given medium is added to a media registry. If this machine
10901 * was created with 4.0 or later, then the machine registry is used. Otherwise
10902 * the global VirtualBox media registry is used.
10903 *
10904 * Caller must NOT hold machine lock, media tree or any medium locks!
10905 *
10906 * @param pMedium
10907 */
10908void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10909{
10910 /* Paranoia checks: do not hold machine or media tree locks. */
10911 AssertReturnVoid(!isWriteLockOnCurrentThread());
10912 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10913
10914 ComObjPtr<Medium> pBase;
10915 {
10916 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10917 pBase = pMedium->getBase();
10918 }
10919
10920 /* Paranoia checks: do not hold medium locks. */
10921 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10922 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10923
10924 // decide which medium registry to use now that the medium is attached:
10925 Guid uuid;
10926 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10927 // machine XML is VirtualBox 4.0 or higher:
10928 uuid = getId(); // machine UUID
10929 else
10930 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10931
10932 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10933 mParent->markRegistryModified(uuid);
10934
10935 /* For more complex hard disk structures it can happen that the base
10936 * medium isn't yet associated with any medium registry. Do that now. */
10937 if (pMedium != pBase)
10938 {
10939 if (pBase->addRegistry(uuid, true /* fRecurse */))
10940 mParent->markRegistryModified(uuid);
10941 }
10942}
10943
10944/**
10945 * Creates differencing hard disks for all normal hard disks attached to this
10946 * machine and a new set of attachments to refer to created disks.
10947 *
10948 * Used when taking a snapshot or when deleting the current state. Gets called
10949 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10950 *
10951 * This method assumes that mMediaData contains the original hard disk attachments
10952 * it needs to create diffs for. On success, these attachments will be replaced
10953 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10954 * called to delete created diffs which will also rollback mMediaData and restore
10955 * whatever was backed up before calling this method.
10956 *
10957 * Attachments with non-normal hard disks are left as is.
10958 *
10959 * If @a aOnline is @c false then the original hard disks that require implicit
10960 * diffs will be locked for reading. Otherwise it is assumed that they are
10961 * already locked for writing (when the VM was started). Note that in the latter
10962 * case it is responsibility of the caller to lock the newly created diffs for
10963 * writing if this method succeeds.
10964 *
10965 * @param aProgress Progress object to run (must contain at least as
10966 * many operations left as the number of hard disks
10967 * attached).
10968 * @param aOnline Whether the VM was online prior to this operation.
10969 *
10970 * @note The progress object is not marked as completed, neither on success nor
10971 * on failure. This is a responsibility of the caller.
10972 *
10973 * @note Locks this object and the media tree for writing.
10974 */
10975HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10976 ULONG aWeight,
10977 bool aOnline)
10978{
10979 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10980
10981 AutoCaller autoCaller(this);
10982 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10983
10984 AutoMultiWriteLock2 alock(this->lockHandle(),
10985 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10986
10987 /* must be in a protective state because we release the lock below */
10988 AssertReturn( mData->mMachineState == MachineState_Saving
10989 || mData->mMachineState == MachineState_LiveSnapshotting
10990 || mData->mMachineState == MachineState_RestoringSnapshot
10991 || mData->mMachineState == MachineState_DeletingSnapshot
10992 , E_FAIL);
10993
10994 HRESULT rc = S_OK;
10995
10996 // use appropriate locked media map (online or offline)
10997 MediumLockListMap lockedMediaOffline;
10998 MediumLockListMap *lockedMediaMap;
10999 if (aOnline)
11000 lockedMediaMap = &mData->mSession.mLockedMedia;
11001 else
11002 lockedMediaMap = &lockedMediaOffline;
11003
11004 try
11005 {
11006 if (!aOnline)
11007 {
11008 /* lock all attached hard disks early to detect "in use"
11009 * situations before creating actual diffs */
11010 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11011 it != mMediaData->mAttachments.end();
11012 ++it)
11013 {
11014 MediumAttachment* pAtt = *it;
11015 if (pAtt->getType() == DeviceType_HardDisk)
11016 {
11017 Medium* pMedium = pAtt->getMedium();
11018 Assert(pMedium);
11019
11020 MediumLockList *pMediumLockList(new MediumLockList());
11021 alock.release();
11022 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11023 false /* fMediumLockWrite */,
11024 NULL,
11025 *pMediumLockList);
11026 alock.acquire();
11027 if (FAILED(rc))
11028 {
11029 delete pMediumLockList;
11030 throw rc;
11031 }
11032 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11033 if (FAILED(rc))
11034 {
11035 throw setError(rc,
11036 tr("Collecting locking information for all attached media failed"));
11037 }
11038 }
11039 }
11040
11041 /* Now lock all media. If this fails, nothing is locked. */
11042 alock.release();
11043 rc = lockedMediaMap->Lock();
11044 alock.acquire();
11045 if (FAILED(rc))
11046 {
11047 throw setError(rc,
11048 tr("Locking of attached media failed"));
11049 }
11050 }
11051
11052 /* remember the current list (note that we don't use backup() since
11053 * mMediaData may be already backed up) */
11054 MediaData::AttachmentList atts = mMediaData->mAttachments;
11055
11056 /* start from scratch */
11057 mMediaData->mAttachments.clear();
11058
11059 /* go through remembered attachments and create diffs for normal hard
11060 * disks and attach them */
11061 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11062 it != atts.end();
11063 ++it)
11064 {
11065 MediumAttachment* pAtt = *it;
11066
11067 DeviceType_T devType = pAtt->getType();
11068 Medium* pMedium = pAtt->getMedium();
11069
11070 if ( devType != DeviceType_HardDisk
11071 || pMedium == NULL
11072 || pMedium->getType() != MediumType_Normal)
11073 {
11074 /* copy the attachment as is */
11075
11076 /** @todo the progress object created in Console::TakeSnaphot
11077 * only expects operations for hard disks. Later other
11078 * device types need to show up in the progress as well. */
11079 if (devType == DeviceType_HardDisk)
11080 {
11081 if (pMedium == NULL)
11082 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11083 aWeight); // weight
11084 else
11085 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11086 pMedium->getBase()->getName().c_str()).raw(),
11087 aWeight); // weight
11088 }
11089
11090 mMediaData->mAttachments.push_back(pAtt);
11091 continue;
11092 }
11093
11094 /* need a diff */
11095 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11096 pMedium->getBase()->getName().c_str()).raw(),
11097 aWeight); // weight
11098
11099 Utf8Str strFullSnapshotFolder;
11100 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11101
11102 ComObjPtr<Medium> diff;
11103 diff.createObject();
11104 // store the diff in the same registry as the parent
11105 // (this cannot fail here because we can't create implicit diffs for
11106 // unregistered images)
11107 Guid uuidRegistryParent;
11108 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11109 Assert(fInRegistry); NOREF(fInRegistry);
11110 rc = diff->init(mParent,
11111 pMedium->getPreferredDiffFormat(),
11112 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11113 uuidRegistryParent);
11114 if (FAILED(rc)) throw rc;
11115
11116 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11117 * the push_back? Looks like we're going to release medium with the
11118 * wrong kind of lock (general issue with if we fail anywhere at all)
11119 * and an orphaned VDI in the snapshots folder. */
11120
11121 /* update the appropriate lock list */
11122 MediumLockList *pMediumLockList;
11123 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11124 AssertComRCThrowRC(rc);
11125 if (aOnline)
11126 {
11127 alock.release();
11128 /* The currently attached medium will be read-only, change
11129 * the lock type to read. */
11130 rc = pMediumLockList->Update(pMedium, false);
11131 alock.acquire();
11132 AssertComRCThrowRC(rc);
11133 }
11134
11135 /* release the locks before the potentially lengthy operation */
11136 alock.release();
11137 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11138 pMediumLockList,
11139 NULL /* aProgress */,
11140 true /* aWait */);
11141 alock.acquire();
11142 if (FAILED(rc)) throw rc;
11143
11144 /* actual lock list update is done in Medium::commitMedia */
11145
11146 rc = diff->addBackReference(mData->mUuid);
11147 AssertComRCThrowRC(rc);
11148
11149 /* add a new attachment */
11150 ComObjPtr<MediumAttachment> attachment;
11151 attachment.createObject();
11152 rc = attachment->init(this,
11153 diff,
11154 pAtt->getControllerName(),
11155 pAtt->getPort(),
11156 pAtt->getDevice(),
11157 DeviceType_HardDisk,
11158 true /* aImplicit */,
11159 false /* aPassthrough */,
11160 false /* aTempEject */,
11161 pAtt->getNonRotational(),
11162 pAtt->getDiscard(),
11163 pAtt->getBandwidthGroup());
11164 if (FAILED(rc)) throw rc;
11165
11166 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11167 AssertComRCThrowRC(rc);
11168 mMediaData->mAttachments.push_back(attachment);
11169 }
11170 }
11171 catch (HRESULT aRC) { rc = aRC; }
11172
11173 /* unlock all hard disks we locked when there is no VM */
11174 if (!aOnline)
11175 {
11176 ErrorInfoKeeper eik;
11177
11178 HRESULT rc1 = lockedMediaMap->Clear();
11179 AssertComRC(rc1);
11180 }
11181
11182 return rc;
11183}
11184
11185/**
11186 * Deletes implicit differencing hard disks created either by
11187 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11188 *
11189 * Note that to delete hard disks created by #AttachDevice() this method is
11190 * called from #fixupMedia() when the changes are rolled back.
11191 *
11192 * @note Locks this object and the media tree for writing.
11193 */
11194HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11195{
11196 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11197
11198 AutoCaller autoCaller(this);
11199 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11200
11201 AutoMultiWriteLock2 alock(this->lockHandle(),
11202 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11203
11204 /* We absolutely must have backed up state. */
11205 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11206
11207 /* Check if there are any implicitly created diff images. */
11208 bool fImplicitDiffs = false;
11209 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11210 it != mMediaData->mAttachments.end();
11211 ++it)
11212 {
11213 const ComObjPtr<MediumAttachment> &pAtt = *it;
11214 if (pAtt->isImplicit())
11215 {
11216 fImplicitDiffs = true;
11217 break;
11218 }
11219 }
11220 /* If there is nothing to do, leave early. This saves lots of image locking
11221 * effort. It also avoids a MachineStateChanged event without real reason.
11222 * This is important e.g. when loading a VM config, because there should be
11223 * no events. Otherwise API clients can become thoroughly confused for
11224 * inaccessible VMs (the code for loading VM configs uses this method for
11225 * cleanup if the config makes no sense), as they take such events as an
11226 * indication that the VM is alive, and they would force the VM config to
11227 * be reread, leading to an endless loop. */
11228 if (!fImplicitDiffs)
11229 return S_OK;
11230
11231 HRESULT rc = S_OK;
11232 MachineState_T oldState = mData->mMachineState;
11233
11234 /* will release the lock before the potentially lengthy operation,
11235 * so protect with the special state (unless already protected) */
11236 if ( oldState != MachineState_Saving
11237 && oldState != MachineState_LiveSnapshotting
11238 && oldState != MachineState_RestoringSnapshot
11239 && oldState != MachineState_DeletingSnapshot
11240 && oldState != MachineState_DeletingSnapshotOnline
11241 && oldState != MachineState_DeletingSnapshotPaused
11242 )
11243 setMachineState(MachineState_SettingUp);
11244
11245 // use appropriate locked media map (online or offline)
11246 MediumLockListMap lockedMediaOffline;
11247 MediumLockListMap *lockedMediaMap;
11248 if (aOnline)
11249 lockedMediaMap = &mData->mSession.mLockedMedia;
11250 else
11251 lockedMediaMap = &lockedMediaOffline;
11252
11253 try
11254 {
11255 if (!aOnline)
11256 {
11257 /* lock all attached hard disks early to detect "in use"
11258 * situations before deleting actual diffs */
11259 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11260 it != mMediaData->mAttachments.end();
11261 ++it)
11262 {
11263 MediumAttachment* pAtt = *it;
11264 if (pAtt->getType() == DeviceType_HardDisk)
11265 {
11266 Medium* pMedium = pAtt->getMedium();
11267 Assert(pMedium);
11268
11269 MediumLockList *pMediumLockList(new MediumLockList());
11270 alock.release();
11271 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11272 false /* fMediumLockWrite */,
11273 NULL,
11274 *pMediumLockList);
11275 alock.acquire();
11276
11277 if (FAILED(rc))
11278 {
11279 delete pMediumLockList;
11280 throw rc;
11281 }
11282
11283 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11284 if (FAILED(rc))
11285 throw rc;
11286 }
11287 }
11288
11289 if (FAILED(rc))
11290 throw rc;
11291 } // end of offline
11292
11293 /* Lock lists are now up to date and include implicitly created media */
11294
11295 /* Go through remembered attachments and delete all implicitly created
11296 * diffs and fix up the attachment information */
11297 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11298 MediaData::AttachmentList implicitAtts;
11299 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11300 it != mMediaData->mAttachments.end();
11301 ++it)
11302 {
11303 ComObjPtr<MediumAttachment> pAtt = *it;
11304 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11305 if (pMedium.isNull())
11306 continue;
11307
11308 // Implicit attachments go on the list for deletion and back references are removed.
11309 if (pAtt->isImplicit())
11310 {
11311 /* Deassociate and mark for deletion */
11312 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11313 rc = pMedium->removeBackReference(mData->mUuid);
11314 if (FAILED(rc))
11315 throw rc;
11316 implicitAtts.push_back(pAtt);
11317 continue;
11318 }
11319
11320 /* Was this medium attached before? */
11321 if (!findAttachment(oldAtts, pMedium))
11322 {
11323 /* no: de-associate */
11324 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11325 rc = pMedium->removeBackReference(mData->mUuid);
11326 if (FAILED(rc))
11327 throw rc;
11328 continue;
11329 }
11330 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11331 }
11332
11333 /* If there are implicit attachments to delete, throw away the lock
11334 * map contents (which will unlock all media) since the medium
11335 * attachments will be rolled back. Below we need to completely
11336 * recreate the lock map anyway since it is infinitely complex to
11337 * do this incrementally (would need reconstructing each attachment
11338 * change, which would be extremely hairy). */
11339 if (implicitAtts.size() != 0)
11340 {
11341 ErrorInfoKeeper eik;
11342
11343 HRESULT rc1 = lockedMediaMap->Clear();
11344 AssertComRC(rc1);
11345 }
11346
11347 /* rollback hard disk changes */
11348 mMediaData.rollback();
11349
11350 MultiResult mrc(S_OK);
11351
11352 // Delete unused implicit diffs.
11353 if (implicitAtts.size() != 0)
11354 {
11355 alock.release();
11356
11357 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11358 it != implicitAtts.end();
11359 ++it)
11360 {
11361 // Remove medium associated with this attachment.
11362 ComObjPtr<MediumAttachment> pAtt = *it;
11363 Assert(pAtt);
11364 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11365 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11366 Assert(pMedium);
11367
11368 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11369 // continue on delete failure, just collect error messages
11370 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11371 mrc = rc;
11372 }
11373
11374 alock.acquire();
11375
11376 /* if there is a VM recreate media lock map as mentioned above,
11377 * otherwise it is a waste of time and we leave things unlocked */
11378 if (aOnline)
11379 {
11380 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11381 /* must never be NULL, but better safe than sorry */
11382 if (!pMachine.isNull())
11383 {
11384 alock.release();
11385 rc = mData->mSession.mMachine->lockMedia();
11386 alock.acquire();
11387 if (FAILED(rc))
11388 throw rc;
11389 }
11390 }
11391 }
11392 }
11393 catch (HRESULT aRC) {rc = aRC;}
11394
11395 if (mData->mMachineState == MachineState_SettingUp)
11396 setMachineState(oldState);
11397
11398 /* unlock all hard disks we locked when there is no VM */
11399 if (!aOnline)
11400 {
11401 ErrorInfoKeeper eik;
11402
11403 HRESULT rc1 = lockedMediaMap->Clear();
11404 AssertComRC(rc1);
11405 }
11406
11407 return rc;
11408}
11409
11410
11411/**
11412 * Looks through the given list of media attachments for one with the given parameters
11413 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11414 * can be searched as well if needed.
11415 *
11416 * @param list
11417 * @param aControllerName
11418 * @param aControllerPort
11419 * @param aDevice
11420 * @return
11421 */
11422MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11423 IN_BSTR aControllerName,
11424 LONG aControllerPort,
11425 LONG aDevice)
11426{
11427 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11428 it != ll.end();
11429 ++it)
11430 {
11431 MediumAttachment *pAttach = *it;
11432 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11433 return pAttach;
11434 }
11435
11436 return NULL;
11437}
11438
11439/**
11440 * Looks through the given list of media attachments for one with the given parameters
11441 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11442 * can be searched as well if needed.
11443 *
11444 * @param list
11445 * @param aControllerName
11446 * @param aControllerPort
11447 * @param aDevice
11448 * @return
11449 */
11450MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11451 ComObjPtr<Medium> pMedium)
11452{
11453 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11454 it != ll.end();
11455 ++it)
11456 {
11457 MediumAttachment *pAttach = *it;
11458 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11459 if (pMediumThis == pMedium)
11460 return pAttach;
11461 }
11462
11463 return NULL;
11464}
11465
11466/**
11467 * Looks through the given list of media attachments for one with the given parameters
11468 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11469 * can be searched as well if needed.
11470 *
11471 * @param list
11472 * @param aControllerName
11473 * @param aControllerPort
11474 * @param aDevice
11475 * @return
11476 */
11477MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11478 Guid &id)
11479{
11480 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11481 it != ll.end();
11482 ++it)
11483 {
11484 MediumAttachment *pAttach = *it;
11485 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11486 if (pMediumThis->getId() == id)
11487 return pAttach;
11488 }
11489
11490 return NULL;
11491}
11492
11493/**
11494 * Main implementation for Machine::DetachDevice. This also gets called
11495 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11496 *
11497 * @param pAttach Medium attachment to detach.
11498 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11499 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11500 * @return
11501 */
11502HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11503 AutoWriteLock &writeLock,
11504 Snapshot *pSnapshot)
11505{
11506 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11507 DeviceType_T mediumType = pAttach->getType();
11508
11509 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11510
11511 if (pAttach->isImplicit())
11512 {
11513 /* attempt to implicitly delete the implicitly created diff */
11514
11515 /// @todo move the implicit flag from MediumAttachment to Medium
11516 /// and forbid any hard disk operation when it is implicit. Or maybe
11517 /// a special media state for it to make it even more simple.
11518
11519 Assert(mMediaData.isBackedUp());
11520
11521 /* will release the lock before the potentially lengthy operation, so
11522 * protect with the special state */
11523 MachineState_T oldState = mData->mMachineState;
11524 setMachineState(MachineState_SettingUp);
11525
11526 writeLock.release();
11527
11528 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11529 true /*aWait*/);
11530
11531 writeLock.acquire();
11532
11533 setMachineState(oldState);
11534
11535 if (FAILED(rc)) return rc;
11536 }
11537
11538 setModified(IsModified_Storage);
11539 mMediaData.backup();
11540 mMediaData->mAttachments.remove(pAttach);
11541
11542 if (!oldmedium.isNull())
11543 {
11544 // if this is from a snapshot, do not defer detachment to commitMedia()
11545 if (pSnapshot)
11546 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11547 // else if non-hard disk media, do not defer detachment to commitMedia() either
11548 else if (mediumType != DeviceType_HardDisk)
11549 oldmedium->removeBackReference(mData->mUuid);
11550 }
11551
11552 return S_OK;
11553}
11554
11555/**
11556 * Goes thru all media of the given list and
11557 *
11558 * 1) calls detachDevice() on each of them for this machine and
11559 * 2) adds all Medium objects found in the process to the given list,
11560 * depending on cleanupMode.
11561 *
11562 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11563 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11564 * media to the list.
11565 *
11566 * This gets called from Machine::Unregister, both for the actual Machine and
11567 * the SnapshotMachine objects that might be found in the snapshots.
11568 *
11569 * Requires caller and locking. The machine lock must be passed in because it
11570 * will be passed on to detachDevice which needs it for temporary unlocking.
11571 *
11572 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11573 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11574 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11575 * otherwise no media get added.
11576 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11577 * @return
11578 */
11579HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11580 Snapshot *pSnapshot,
11581 CleanupMode_T cleanupMode,
11582 MediaList &llMedia)
11583{
11584 Assert(isWriteLockOnCurrentThread());
11585
11586 HRESULT rc;
11587
11588 // make a temporary list because detachDevice invalidates iterators into
11589 // mMediaData->mAttachments
11590 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11591
11592 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11593 it != llAttachments2.end();
11594 ++it)
11595 {
11596 ComObjPtr<MediumAttachment> &pAttach = *it;
11597 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11598
11599 if (!pMedium.isNull())
11600 {
11601 AutoCaller mac(pMedium);
11602 if (FAILED(mac.rc())) return mac.rc();
11603 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11604 DeviceType_T devType = pMedium->getDeviceType();
11605 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11606 && devType == DeviceType_HardDisk)
11607 || (cleanupMode == CleanupMode_Full)
11608 )
11609 {
11610 llMedia.push_back(pMedium);
11611 ComObjPtr<Medium> pParent = pMedium->getParent();
11612 /*
11613 * Search for medias which are not attached to any machine, but
11614 * in the chain to an attached disk. Mediums are only consided
11615 * if they are:
11616 * - have only one child
11617 * - no references to any machines
11618 * - are of normal medium type
11619 */
11620 while (!pParent.isNull())
11621 {
11622 AutoCaller mac1(pParent);
11623 if (FAILED(mac1.rc())) return mac1.rc();
11624 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11625 if (pParent->getChildren().size() == 1)
11626 {
11627 if ( pParent->getMachineBackRefCount() == 0
11628 && pParent->getType() == MediumType_Normal
11629 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11630 llMedia.push_back(pParent);
11631 }
11632 else
11633 break;
11634 pParent = pParent->getParent();
11635 }
11636 }
11637 }
11638
11639 // real machine: then we need to use the proper method
11640 rc = detachDevice(pAttach, writeLock, pSnapshot);
11641
11642 if (FAILED(rc))
11643 return rc;
11644 }
11645
11646 return S_OK;
11647}
11648
11649/**
11650 * Perform deferred hard disk detachments.
11651 *
11652 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11653 * backed up).
11654 *
11655 * If @a aOnline is @c true then this method will also unlock the old hard disks
11656 * for which the new implicit diffs were created and will lock these new diffs for
11657 * writing.
11658 *
11659 * @param aOnline Whether the VM was online prior to this operation.
11660 *
11661 * @note Locks this object for writing!
11662 */
11663void Machine::commitMedia(bool aOnline /*= false*/)
11664{
11665 AutoCaller autoCaller(this);
11666 AssertComRCReturnVoid(autoCaller.rc());
11667
11668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11669
11670 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11671
11672 HRESULT rc = S_OK;
11673
11674 /* no attach/detach operations -- nothing to do */
11675 if (!mMediaData.isBackedUp())
11676 return;
11677
11678 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11679 bool fMediaNeedsLocking = false;
11680
11681 /* enumerate new attachments */
11682 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11683 it != mMediaData->mAttachments.end();
11684 ++it)
11685 {
11686 MediumAttachment *pAttach = *it;
11687
11688 pAttach->commit();
11689
11690 Medium* pMedium = pAttach->getMedium();
11691 bool fImplicit = pAttach->isImplicit();
11692
11693 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11694 (pMedium) ? pMedium->getName().c_str() : "NULL",
11695 fImplicit));
11696
11697 /** @todo convert all this Machine-based voodoo to MediumAttachment
11698 * based commit logic. */
11699 if (fImplicit)
11700 {
11701 /* convert implicit attachment to normal */
11702 pAttach->setImplicit(false);
11703
11704 if ( aOnline
11705 && pMedium
11706 && pAttach->getType() == DeviceType_HardDisk
11707 )
11708 {
11709 ComObjPtr<Medium> parent = pMedium->getParent();
11710 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11711
11712 /* update the appropriate lock list */
11713 MediumLockList *pMediumLockList;
11714 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11715 AssertComRC(rc);
11716 if (pMediumLockList)
11717 {
11718 /* unlock if there's a need to change the locking */
11719 if (!fMediaNeedsLocking)
11720 {
11721 rc = mData->mSession.mLockedMedia.Unlock();
11722 AssertComRC(rc);
11723 fMediaNeedsLocking = true;
11724 }
11725 rc = pMediumLockList->Update(parent, false);
11726 AssertComRC(rc);
11727 rc = pMediumLockList->Append(pMedium, true);
11728 AssertComRC(rc);
11729 }
11730 }
11731
11732 continue;
11733 }
11734
11735 if (pMedium)
11736 {
11737 /* was this medium attached before? */
11738 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11739 oldIt != oldAtts.end();
11740 ++oldIt)
11741 {
11742 MediumAttachment *pOldAttach = *oldIt;
11743 if (pOldAttach->getMedium() == pMedium)
11744 {
11745 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11746
11747 /* yes: remove from old to avoid de-association */
11748 oldAtts.erase(oldIt);
11749 break;
11750 }
11751 }
11752 }
11753 }
11754
11755 /* enumerate remaining old attachments and de-associate from the
11756 * current machine state */
11757 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11758 it != oldAtts.end();
11759 ++it)
11760 {
11761 MediumAttachment *pAttach = *it;
11762 Medium* pMedium = pAttach->getMedium();
11763
11764 /* Detach only hard disks, since DVD/floppy media is detached
11765 * instantly in MountMedium. */
11766 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11767 {
11768 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11769
11770 /* now de-associate from the current machine state */
11771 rc = pMedium->removeBackReference(mData->mUuid);
11772 AssertComRC(rc);
11773
11774 if (aOnline)
11775 {
11776 /* unlock since medium is not used anymore */
11777 MediumLockList *pMediumLockList;
11778 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11779 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11780 {
11781 /* this happens for online snapshots, there the attachment
11782 * is changing, but only to a diff image created under
11783 * the old one, so there is no separate lock list */
11784 Assert(!pMediumLockList);
11785 }
11786 else
11787 {
11788 AssertComRC(rc);
11789 if (pMediumLockList)
11790 {
11791 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11792 AssertComRC(rc);
11793 }
11794 }
11795 }
11796 }
11797 }
11798
11799 /* take media locks again so that the locking state is consistent */
11800 if (fMediaNeedsLocking)
11801 {
11802 Assert(aOnline);
11803 rc = mData->mSession.mLockedMedia.Lock();
11804 AssertComRC(rc);
11805 }
11806
11807 /* commit the hard disk changes */
11808 mMediaData.commit();
11809
11810 if (isSessionMachine())
11811 {
11812 /*
11813 * Update the parent machine to point to the new owner.
11814 * This is necessary because the stored parent will point to the
11815 * session machine otherwise and cause crashes or errors later
11816 * when the session machine gets invalid.
11817 */
11818 /** @todo Change the MediumAttachment class to behave like any other
11819 * class in this regard by creating peer MediumAttachment
11820 * objects for session machines and share the data with the peer
11821 * machine.
11822 */
11823 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11824 it != mMediaData->mAttachments.end();
11825 ++it)
11826 {
11827 (*it)->updateParentMachine(mPeer);
11828 }
11829
11830 /* attach new data to the primary machine and reshare it */
11831 mPeer->mMediaData.attach(mMediaData);
11832 }
11833
11834 return;
11835}
11836
11837/**
11838 * Perform deferred deletion of implicitly created diffs.
11839 *
11840 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11841 * backed up).
11842 *
11843 * @note Locks this object for writing!
11844 */
11845void Machine::rollbackMedia()
11846{
11847 AutoCaller autoCaller(this);
11848 AssertComRCReturnVoid(autoCaller.rc());
11849
11850 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11851 LogFlowThisFunc(("Entering rollbackMedia\n"));
11852
11853 HRESULT rc = S_OK;
11854
11855 /* no attach/detach operations -- nothing to do */
11856 if (!mMediaData.isBackedUp())
11857 return;
11858
11859 /* enumerate new attachments */
11860 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11861 it != mMediaData->mAttachments.end();
11862 ++it)
11863 {
11864 MediumAttachment *pAttach = *it;
11865 /* Fix up the backrefs for DVD/floppy media. */
11866 if (pAttach->getType() != DeviceType_HardDisk)
11867 {
11868 Medium* pMedium = pAttach->getMedium();
11869 if (pMedium)
11870 {
11871 rc = pMedium->removeBackReference(mData->mUuid);
11872 AssertComRC(rc);
11873 }
11874 }
11875
11876 (*it)->rollback();
11877
11878 pAttach = *it;
11879 /* Fix up the backrefs for DVD/floppy media. */
11880 if (pAttach->getType() != DeviceType_HardDisk)
11881 {
11882 Medium* pMedium = pAttach->getMedium();
11883 if (pMedium)
11884 {
11885 rc = pMedium->addBackReference(mData->mUuid);
11886 AssertComRC(rc);
11887 }
11888 }
11889 }
11890
11891 /** @todo convert all this Machine-based voodoo to MediumAttachment
11892 * based rollback logic. */
11893 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11894
11895 return;
11896}
11897
11898/**
11899 * Returns true if the settings file is located in the directory named exactly
11900 * as the machine; this means, among other things, that the machine directory
11901 * should be auto-renamed.
11902 *
11903 * @param aSettingsDir if not NULL, the full machine settings file directory
11904 * name will be assigned there.
11905 *
11906 * @note Doesn't lock anything.
11907 * @note Not thread safe (must be called from this object's lock).
11908 */
11909bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11910{
11911 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11912 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11913 if (aSettingsDir)
11914 *aSettingsDir = strMachineDirName;
11915 strMachineDirName.stripPath(); // vmname
11916 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11917 strConfigFileOnly.stripPath() // vmname.vbox
11918 .stripExt(); // vmname
11919 /** @todo hack, make somehow use of ComposeMachineFilename */
11920 if (mUserData->s.fDirectoryIncludesUUID)
11921 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11922
11923 AssertReturn(!strMachineDirName.isEmpty(), false);
11924 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11925
11926 return strMachineDirName == strConfigFileOnly;
11927}
11928
11929/**
11930 * Discards all changes to machine settings.
11931 *
11932 * @param aNotify Whether to notify the direct session about changes or not.
11933 *
11934 * @note Locks objects for writing!
11935 */
11936void Machine::rollback(bool aNotify)
11937{
11938 AutoCaller autoCaller(this);
11939 AssertComRCReturn(autoCaller.rc(), (void)0);
11940
11941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11942
11943 if (!mStorageControllers.isNull())
11944 {
11945 if (mStorageControllers.isBackedUp())
11946 {
11947 /* unitialize all new devices (absent in the backed up list). */
11948 StorageControllerList::const_iterator it = mStorageControllers->begin();
11949 StorageControllerList *backedList = mStorageControllers.backedUpData();
11950 while (it != mStorageControllers->end())
11951 {
11952 if ( std::find(backedList->begin(), backedList->end(), *it)
11953 == backedList->end()
11954 )
11955 {
11956 (*it)->uninit();
11957 }
11958 ++it;
11959 }
11960
11961 /* restore the list */
11962 mStorageControllers.rollback();
11963 }
11964
11965 /* rollback any changes to devices after restoring the list */
11966 if (mData->flModifications & IsModified_Storage)
11967 {
11968 StorageControllerList::const_iterator it = mStorageControllers->begin();
11969 while (it != mStorageControllers->end())
11970 {
11971 (*it)->rollback();
11972 ++it;
11973 }
11974 }
11975 }
11976
11977 if (!mUSBControllers.isNull())
11978 {
11979 if (mUSBControllers.isBackedUp())
11980 {
11981 /* unitialize all new devices (absent in the backed up list). */
11982 USBControllerList::const_iterator it = mUSBControllers->begin();
11983 USBControllerList *backedList = mUSBControllers.backedUpData();
11984 while (it != mUSBControllers->end())
11985 {
11986 if ( std::find(backedList->begin(), backedList->end(), *it)
11987 == backedList->end()
11988 )
11989 {
11990 (*it)->uninit();
11991 }
11992 ++it;
11993 }
11994
11995 /* restore the list */
11996 mUSBControllers.rollback();
11997 }
11998
11999 /* rollback any changes to devices after restoring the list */
12000 if (mData->flModifications & IsModified_USB)
12001 {
12002 USBControllerList::const_iterator it = mUSBControllers->begin();
12003 while (it != mUSBControllers->end())
12004 {
12005 (*it)->rollback();
12006 ++it;
12007 }
12008 }
12009 }
12010
12011 mUserData.rollback();
12012
12013 mHWData.rollback();
12014
12015 if (mData->flModifications & IsModified_Storage)
12016 rollbackMedia();
12017
12018 if (mBIOSSettings)
12019 mBIOSSettings->rollback();
12020
12021 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12022 mVRDEServer->rollback();
12023
12024 if (mAudioAdapter)
12025 mAudioAdapter->rollback();
12026
12027 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12028 mUSBDeviceFilters->rollback();
12029
12030 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12031 mBandwidthControl->rollback();
12032
12033 if (!mHWData.isNull())
12034 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12035 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12036 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12037 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12038
12039 if (mData->flModifications & IsModified_NetworkAdapters)
12040 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12041 if ( mNetworkAdapters[slot]
12042 && mNetworkAdapters[slot]->isModified())
12043 {
12044 mNetworkAdapters[slot]->rollback();
12045 networkAdapters[slot] = mNetworkAdapters[slot];
12046 }
12047
12048 if (mData->flModifications & IsModified_SerialPorts)
12049 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12050 if ( mSerialPorts[slot]
12051 && mSerialPorts[slot]->isModified())
12052 {
12053 mSerialPorts[slot]->rollback();
12054 serialPorts[slot] = mSerialPorts[slot];
12055 }
12056
12057 if (mData->flModifications & IsModified_ParallelPorts)
12058 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12059 if ( mParallelPorts[slot]
12060 && mParallelPorts[slot]->isModified())
12061 {
12062 mParallelPorts[slot]->rollback();
12063 parallelPorts[slot] = mParallelPorts[slot];
12064 }
12065
12066 if (aNotify)
12067 {
12068 /* inform the direct session about changes */
12069
12070 ComObjPtr<Machine> that = this;
12071 uint32_t flModifications = mData->flModifications;
12072 alock.release();
12073
12074 if (flModifications & IsModified_SharedFolders)
12075 that->onSharedFolderChange();
12076
12077 if (flModifications & IsModified_VRDEServer)
12078 that->onVRDEServerChange(/* aRestart */ TRUE);
12079 if (flModifications & IsModified_USB)
12080 that->onUSBControllerChange();
12081
12082 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12083 if (networkAdapters[slot])
12084 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12085 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12086 if (serialPorts[slot])
12087 that->onSerialPortChange(serialPorts[slot]);
12088 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12089 if (parallelPorts[slot])
12090 that->onParallelPortChange(parallelPorts[slot]);
12091
12092 if (flModifications & IsModified_Storage)
12093 that->onStorageControllerChange();
12094
12095#if 0
12096 if (flModifications & IsModified_BandwidthControl)
12097 that->onBandwidthControlChange();
12098#endif
12099 }
12100}
12101
12102/**
12103 * Commits all the changes to machine settings.
12104 *
12105 * Note that this operation is supposed to never fail.
12106 *
12107 * @note Locks this object and children for writing.
12108 */
12109void Machine::commit()
12110{
12111 AutoCaller autoCaller(this);
12112 AssertComRCReturnVoid(autoCaller.rc());
12113
12114 AutoCaller peerCaller(mPeer);
12115 AssertComRCReturnVoid(peerCaller.rc());
12116
12117 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12118
12119 /*
12120 * use safe commit to ensure Snapshot machines (that share mUserData)
12121 * will still refer to a valid memory location
12122 */
12123 mUserData.commitCopy();
12124
12125 mHWData.commit();
12126
12127 if (mMediaData.isBackedUp())
12128 commitMedia(Global::IsOnline(mData->mMachineState));
12129
12130 mBIOSSettings->commit();
12131 mVRDEServer->commit();
12132 mAudioAdapter->commit();
12133 mUSBDeviceFilters->commit();
12134 mBandwidthControl->commit();
12135
12136 /* Since mNetworkAdapters is a list which might have been changed (resized)
12137 * without using the Backupable<> template we need to handle the copying
12138 * of the list entries manually, including the creation of peers for the
12139 * new objects. */
12140 bool commitNetworkAdapters = false;
12141 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12142 if (mPeer)
12143 {
12144 /* commit everything, even the ones which will go away */
12145 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12146 mNetworkAdapters[slot]->commit();
12147 /* copy over the new entries, creating a peer and uninit the original */
12148 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12149 for (size_t slot = 0; slot < newSize; slot++)
12150 {
12151 /* look if this adapter has a peer device */
12152 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12153 if (!peer)
12154 {
12155 /* no peer means the adapter is a newly created one;
12156 * create a peer owning data this data share it with */
12157 peer.createObject();
12158 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12159 }
12160 mPeer->mNetworkAdapters[slot] = peer;
12161 }
12162 /* uninit any no longer needed network adapters */
12163 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12164 mNetworkAdapters[slot]->uninit();
12165 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12166 {
12167 if (mPeer->mNetworkAdapters[slot])
12168 mPeer->mNetworkAdapters[slot]->uninit();
12169 }
12170 /* Keep the original network adapter count until this point, so that
12171 * discarding a chipset type change will not lose settings. */
12172 mNetworkAdapters.resize(newSize);
12173 mPeer->mNetworkAdapters.resize(newSize);
12174 }
12175 else
12176 {
12177 /* we have no peer (our parent is the newly created machine);
12178 * just commit changes to the network adapters */
12179 commitNetworkAdapters = true;
12180 }
12181 if (commitNetworkAdapters)
12182 {
12183 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12184 mNetworkAdapters[slot]->commit();
12185 }
12186
12187 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12188 mSerialPorts[slot]->commit();
12189 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12190 mParallelPorts[slot]->commit();
12191
12192 bool commitStorageControllers = false;
12193
12194 if (mStorageControllers.isBackedUp())
12195 {
12196 mStorageControllers.commit();
12197
12198 if (mPeer)
12199 {
12200 /* Commit all changes to new controllers (this will reshare data with
12201 * peers for those who have peers) */
12202 StorageControllerList *newList = new StorageControllerList();
12203 StorageControllerList::const_iterator it = mStorageControllers->begin();
12204 while (it != mStorageControllers->end())
12205 {
12206 (*it)->commit();
12207
12208 /* look if this controller has a peer device */
12209 ComObjPtr<StorageController> peer = (*it)->getPeer();
12210 if (!peer)
12211 {
12212 /* no peer means the device is a newly created one;
12213 * create a peer owning data this device share it with */
12214 peer.createObject();
12215 peer->init(mPeer, *it, true /* aReshare */);
12216 }
12217 else
12218 {
12219 /* remove peer from the old list */
12220 mPeer->mStorageControllers->remove(peer);
12221 }
12222 /* and add it to the new list */
12223 newList->push_back(peer);
12224
12225 ++it;
12226 }
12227
12228 /* uninit old peer's controllers that are left */
12229 it = mPeer->mStorageControllers->begin();
12230 while (it != mPeer->mStorageControllers->end())
12231 {
12232 (*it)->uninit();
12233 ++it;
12234 }
12235
12236 /* attach new list of controllers to our peer */
12237 mPeer->mStorageControllers.attach(newList);
12238 }
12239 else
12240 {
12241 /* we have no peer (our parent is the newly created machine);
12242 * just commit changes to devices */
12243 commitStorageControllers = true;
12244 }
12245 }
12246 else
12247 {
12248 /* the list of controllers itself is not changed,
12249 * just commit changes to controllers themselves */
12250 commitStorageControllers = true;
12251 }
12252
12253 if (commitStorageControllers)
12254 {
12255 StorageControllerList::const_iterator it = mStorageControllers->begin();
12256 while (it != mStorageControllers->end())
12257 {
12258 (*it)->commit();
12259 ++it;
12260 }
12261 }
12262
12263 bool commitUSBControllers = false;
12264
12265 if (mUSBControllers.isBackedUp())
12266 {
12267 mUSBControllers.commit();
12268
12269 if (mPeer)
12270 {
12271 /* Commit all changes to new controllers (this will reshare data with
12272 * peers for those who have peers) */
12273 USBControllerList *newList = new USBControllerList();
12274 USBControllerList::const_iterator it = mUSBControllers->begin();
12275 while (it != mUSBControllers->end())
12276 {
12277 (*it)->commit();
12278
12279 /* look if this controller has a peer device */
12280 ComObjPtr<USBController> peer = (*it)->getPeer();
12281 if (!peer)
12282 {
12283 /* no peer means the device is a newly created one;
12284 * create a peer owning data this device share it with */
12285 peer.createObject();
12286 peer->init(mPeer, *it, true /* aReshare */);
12287 }
12288 else
12289 {
12290 /* remove peer from the old list */
12291 mPeer->mUSBControllers->remove(peer);
12292 }
12293 /* and add it to the new list */
12294 newList->push_back(peer);
12295
12296 ++it;
12297 }
12298
12299 /* uninit old peer's controllers that are left */
12300 it = mPeer->mUSBControllers->begin();
12301 while (it != mPeer->mUSBControllers->end())
12302 {
12303 (*it)->uninit();
12304 ++it;
12305 }
12306
12307 /* attach new list of controllers to our peer */
12308 mPeer->mUSBControllers.attach(newList);
12309 }
12310 else
12311 {
12312 /* we have no peer (our parent is the newly created machine);
12313 * just commit changes to devices */
12314 commitUSBControllers = true;
12315 }
12316 }
12317 else
12318 {
12319 /* the list of controllers itself is not changed,
12320 * just commit changes to controllers themselves */
12321 commitUSBControllers = true;
12322 }
12323
12324 if (commitUSBControllers)
12325 {
12326 USBControllerList::const_iterator it = mUSBControllers->begin();
12327 while (it != mUSBControllers->end())
12328 {
12329 (*it)->commit();
12330 ++it;
12331 }
12332 }
12333
12334 if (isSessionMachine())
12335 {
12336 /* attach new data to the primary machine and reshare it */
12337 mPeer->mUserData.attach(mUserData);
12338 mPeer->mHWData.attach(mHWData);
12339 /* mMediaData is reshared by fixupMedia */
12340 // mPeer->mMediaData.attach(mMediaData);
12341 Assert(mPeer->mMediaData.data() == mMediaData.data());
12342 }
12343}
12344
12345/**
12346 * Copies all the hardware data from the given machine.
12347 *
12348 * Currently, only called when the VM is being restored from a snapshot. In
12349 * particular, this implies that the VM is not running during this method's
12350 * call.
12351 *
12352 * @note This method must be called from under this object's lock.
12353 *
12354 * @note This method doesn't call #commit(), so all data remains backed up and
12355 * unsaved.
12356 */
12357void Machine::copyFrom(Machine *aThat)
12358{
12359 AssertReturnVoid(!isSnapshotMachine());
12360 AssertReturnVoid(aThat->isSnapshotMachine());
12361
12362 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12363
12364 mHWData.assignCopy(aThat->mHWData);
12365
12366 // create copies of all shared folders (mHWData after attaching a copy
12367 // contains just references to original objects)
12368 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12369 it != mHWData->mSharedFolders.end();
12370 ++it)
12371 {
12372 ComObjPtr<SharedFolder> folder;
12373 folder.createObject();
12374 HRESULT rc = folder->initCopy(getMachine(), *it);
12375 AssertComRC(rc);
12376 *it = folder;
12377 }
12378
12379 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12380 mVRDEServer->copyFrom(aThat->mVRDEServer);
12381 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12382 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12383 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12384
12385 /* create private copies of all controllers */
12386 mStorageControllers.backup();
12387 mStorageControllers->clear();
12388 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12389 it != aThat->mStorageControllers->end();
12390 ++it)
12391 {
12392 ComObjPtr<StorageController> ctrl;
12393 ctrl.createObject();
12394 ctrl->initCopy(this, *it);
12395 mStorageControllers->push_back(ctrl);
12396 }
12397
12398 /* create private copies of all USB controllers */
12399 mUSBControllers.backup();
12400 mUSBControllers->clear();
12401 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12402 it != aThat->mUSBControllers->end();
12403 ++it)
12404 {
12405 ComObjPtr<USBController> ctrl;
12406 ctrl.createObject();
12407 ctrl->initCopy(this, *it);
12408 mUSBControllers->push_back(ctrl);
12409 }
12410
12411 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12412 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12413 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12414 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12415 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12416 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12417 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12418}
12419
12420/**
12421 * Returns whether the given storage controller is hotplug capable.
12422 *
12423 * @returns true if the controller supports hotplugging
12424 * false otherwise.
12425 * @param enmCtrlType The controller type to check for.
12426 */
12427bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12428{
12429 switch (enmCtrlType)
12430 {
12431 case StorageControllerType_IntelAhci:
12432 return true;
12433 case StorageControllerType_LsiLogic:
12434 case StorageControllerType_LsiLogicSas:
12435 case StorageControllerType_BusLogic:
12436 case StorageControllerType_PIIX3:
12437 case StorageControllerType_PIIX4:
12438 case StorageControllerType_ICH6:
12439 case StorageControllerType_I82078:
12440 default:
12441 return false;
12442 }
12443}
12444
12445#ifdef VBOX_WITH_RESOURCE_USAGE_API
12446
12447void Machine::getDiskList(MediaList &list)
12448{
12449 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12450 it != mMediaData->mAttachments.end();
12451 ++it)
12452 {
12453 MediumAttachment* pAttach = *it;
12454 /* just in case */
12455 AssertStmt(pAttach, continue);
12456
12457 AutoCaller localAutoCallerA(pAttach);
12458 if (FAILED(localAutoCallerA.rc())) continue;
12459
12460 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12461
12462 if (pAttach->getType() == DeviceType_HardDisk)
12463 list.push_back(pAttach->getMedium());
12464 }
12465}
12466
12467void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12468{
12469 AssertReturnVoid(isWriteLockOnCurrentThread());
12470 AssertPtrReturnVoid(aCollector);
12471
12472 pm::CollectorHAL *hal = aCollector->getHAL();
12473 /* Create sub metrics */
12474 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12475 "Percentage of processor time spent in user mode by the VM process.");
12476 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12477 "Percentage of processor time spent in kernel mode by the VM process.");
12478 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12479 "Size of resident portion of VM process in memory.");
12480 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12481 "Actual size of all VM disks combined.");
12482 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12483 "Network receive rate.");
12484 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12485 "Network transmit rate.");
12486 /* Create and register base metrics */
12487 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12488 cpuLoadUser, cpuLoadKernel);
12489 aCollector->registerBaseMetric(cpuLoad);
12490 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12491 ramUsageUsed);
12492 aCollector->registerBaseMetric(ramUsage);
12493 MediaList disks;
12494 getDiskList(disks);
12495 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12496 diskUsageUsed);
12497 aCollector->registerBaseMetric(diskUsage);
12498
12499 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12500 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12501 new pm::AggregateAvg()));
12502 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12503 new pm::AggregateMin()));
12504 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12505 new pm::AggregateMax()));
12506 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12507 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12508 new pm::AggregateAvg()));
12509 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12510 new pm::AggregateMin()));
12511 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12512 new pm::AggregateMax()));
12513
12514 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12515 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12516 new pm::AggregateAvg()));
12517 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12518 new pm::AggregateMin()));
12519 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12520 new pm::AggregateMax()));
12521
12522 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12523 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12524 new pm::AggregateAvg()));
12525 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12526 new pm::AggregateMin()));
12527 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12528 new pm::AggregateMax()));
12529
12530
12531 /* Guest metrics collector */
12532 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12533 aCollector->registerGuest(mCollectorGuest);
12534 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12535 this, __PRETTY_FUNCTION__, mCollectorGuest));
12536
12537 /* Create sub metrics */
12538 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12539 "Percentage of processor time spent in user mode as seen by the guest.");
12540 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12541 "Percentage of processor time spent in kernel mode as seen by the guest.");
12542 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12543 "Percentage of processor time spent idling as seen by the guest.");
12544
12545 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12546 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12547 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12548 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12549 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12550 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12551
12552 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12553
12554 /* Create and register base metrics */
12555 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12556 machineNetRx, machineNetTx);
12557 aCollector->registerBaseMetric(machineNetRate);
12558
12559 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12560 guestLoadUser, guestLoadKernel, guestLoadIdle);
12561 aCollector->registerBaseMetric(guestCpuLoad);
12562
12563 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12564 guestMemTotal, guestMemFree,
12565 guestMemBalloon, guestMemShared,
12566 guestMemCache, guestPagedTotal);
12567 aCollector->registerBaseMetric(guestCpuMem);
12568
12569 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12570 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12571 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12572 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12573
12574 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12575 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12576 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12577 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12578
12579 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12580 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12581 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12582 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12583
12584 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12585 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12586 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12587 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12588
12589 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12590 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12591 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12592 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12593
12594 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12595 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12596 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12597 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12598
12599 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12600 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12601 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12602 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12603
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12608
12609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12613
12614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12618
12619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12623}
12624
12625void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12626{
12627 AssertReturnVoid(isWriteLockOnCurrentThread());
12628
12629 if (aCollector)
12630 {
12631 aCollector->unregisterMetricsFor(aMachine);
12632 aCollector->unregisterBaseMetricsFor(aMachine);
12633 }
12634}
12635
12636#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12637
12638
12639////////////////////////////////////////////////////////////////////////////////
12640
12641DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12642
12643HRESULT SessionMachine::FinalConstruct()
12644{
12645 LogFlowThisFunc(("\n"));
12646
12647 mClientToken = NULL;
12648
12649 return BaseFinalConstruct();
12650}
12651
12652void SessionMachine::FinalRelease()
12653{
12654 LogFlowThisFunc(("\n"));
12655
12656 Assert(!mClientToken);
12657 /* paranoia, should not hang around any more */
12658 if (mClientToken)
12659 {
12660 delete mClientToken;
12661 mClientToken = NULL;
12662 }
12663
12664 uninit(Uninit::Unexpected);
12665
12666 BaseFinalRelease();
12667}
12668
12669/**
12670 * @note Must be called only by Machine::LockMachine() from its own write lock.
12671 */
12672HRESULT SessionMachine::init(Machine *aMachine)
12673{
12674 LogFlowThisFuncEnter();
12675 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12676
12677 AssertReturn(aMachine, E_INVALIDARG);
12678
12679 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12680
12681 /* Enclose the state transition NotReady->InInit->Ready */
12682 AutoInitSpan autoInitSpan(this);
12683 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12684
12685 HRESULT rc = S_OK;
12686
12687 /* create the machine client token */
12688 try
12689 {
12690 mClientToken = new ClientToken(aMachine);
12691 if (!mClientToken->isReady())
12692 {
12693 delete mClientToken;
12694 mClientToken = NULL;
12695 rc = E_FAIL;
12696 }
12697 }
12698 catch (std::bad_alloc &)
12699 {
12700 rc = E_OUTOFMEMORY;
12701 }
12702 if (FAILED(rc))
12703 return rc;
12704
12705 /* memorize the peer Machine */
12706 unconst(mPeer) = aMachine;
12707 /* share the parent pointer */
12708 unconst(mParent) = aMachine->mParent;
12709
12710 /* take the pointers to data to share */
12711 mData.share(aMachine->mData);
12712 mSSData.share(aMachine->mSSData);
12713
12714 mUserData.share(aMachine->mUserData);
12715 mHWData.share(aMachine->mHWData);
12716 mMediaData.share(aMachine->mMediaData);
12717
12718 mStorageControllers.allocate();
12719 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12720 it != aMachine->mStorageControllers->end();
12721 ++it)
12722 {
12723 ComObjPtr<StorageController> ctl;
12724 ctl.createObject();
12725 ctl->init(this, *it);
12726 mStorageControllers->push_back(ctl);
12727 }
12728
12729 mUSBControllers.allocate();
12730 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12731 it != aMachine->mUSBControllers->end();
12732 ++it)
12733 {
12734 ComObjPtr<USBController> ctl;
12735 ctl.createObject();
12736 ctl->init(this, *it);
12737 mUSBControllers->push_back(ctl);
12738 }
12739
12740 unconst(mBIOSSettings).createObject();
12741 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12742 /* create another VRDEServer object that will be mutable */
12743 unconst(mVRDEServer).createObject();
12744 mVRDEServer->init(this, aMachine->mVRDEServer);
12745 /* create another audio adapter object that will be mutable */
12746 unconst(mAudioAdapter).createObject();
12747 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12748 /* create a list of serial ports that will be mutable */
12749 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12750 {
12751 unconst(mSerialPorts[slot]).createObject();
12752 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12753 }
12754 /* create a list of parallel ports that will be mutable */
12755 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12756 {
12757 unconst(mParallelPorts[slot]).createObject();
12758 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12759 }
12760
12761 /* create another USB device filters object that will be mutable */
12762 unconst(mUSBDeviceFilters).createObject();
12763 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12764
12765 /* create a list of network adapters that will be mutable */
12766 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12767 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12768 {
12769 unconst(mNetworkAdapters[slot]).createObject();
12770 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12771 }
12772
12773 /* create another bandwidth control object that will be mutable */
12774 unconst(mBandwidthControl).createObject();
12775 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12776
12777 /* default is to delete saved state on Saved -> PoweredOff transition */
12778 mRemoveSavedState = true;
12779
12780 /* Confirm a successful initialization when it's the case */
12781 autoInitSpan.setSucceeded();
12782
12783 LogFlowThisFuncLeave();
12784 return rc;
12785}
12786
12787/**
12788 * Uninitializes this session object. If the reason is other than
12789 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12790 *
12791 * @param aReason uninitialization reason
12792 *
12793 * @note Locks mParent + this object for writing.
12794 */
12795void SessionMachine::uninit(Uninit::Reason aReason)
12796{
12797 LogFlowThisFuncEnter();
12798 LogFlowThisFunc(("reason=%d\n", aReason));
12799
12800 /*
12801 * Strongly reference ourselves to prevent this object deletion after
12802 * mData->mSession.mMachine.setNull() below (which can release the last
12803 * reference and call the destructor). Important: this must be done before
12804 * accessing any members (and before AutoUninitSpan that does it as well).
12805 * This self reference will be released as the very last step on return.
12806 */
12807 ComObjPtr<SessionMachine> selfRef = this;
12808
12809 /* Enclose the state transition Ready->InUninit->NotReady */
12810 AutoUninitSpan autoUninitSpan(this);
12811 if (autoUninitSpan.uninitDone())
12812 {
12813 LogFlowThisFunc(("Already uninitialized\n"));
12814 LogFlowThisFuncLeave();
12815 return;
12816 }
12817
12818 if (autoUninitSpan.initFailed())
12819 {
12820 /* We've been called by init() because it's failed. It's not really
12821 * necessary (nor it's safe) to perform the regular uninit sequence
12822 * below, the following is enough.
12823 */
12824 LogFlowThisFunc(("Initialization failed.\n"));
12825 /* destroy the machine client token */
12826 if (mClientToken)
12827 {
12828 delete mClientToken;
12829 mClientToken = NULL;
12830 }
12831 uninitDataAndChildObjects();
12832 mData.free();
12833 unconst(mParent) = NULL;
12834 unconst(mPeer) = NULL;
12835 LogFlowThisFuncLeave();
12836 return;
12837 }
12838
12839 MachineState_T lastState;
12840 {
12841 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12842 lastState = mData->mMachineState;
12843 }
12844 NOREF(lastState);
12845
12846#ifdef VBOX_WITH_USB
12847 // release all captured USB devices, but do this before requesting the locks below
12848 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12849 {
12850 /* Console::captureUSBDevices() is called in the VM process only after
12851 * setting the machine state to Starting or Restoring.
12852 * Console::detachAllUSBDevices() will be called upon successful
12853 * termination. So, we need to release USB devices only if there was
12854 * an abnormal termination of a running VM.
12855 *
12856 * This is identical to SessionMachine::DetachAllUSBDevices except
12857 * for the aAbnormal argument. */
12858 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12859 AssertComRC(rc);
12860 NOREF(rc);
12861
12862 USBProxyService *service = mParent->host()->usbProxyService();
12863 if (service)
12864 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12865 }
12866#endif /* VBOX_WITH_USB */
12867
12868 // we need to lock this object in uninit() because the lock is shared
12869 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12870 // and others need mParent lock, and USB needs host lock.
12871 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12872
12873#ifdef VBOX_WITH_RESOURCE_USAGE_API
12874 /*
12875 * It is safe to call Machine::unregisterMetrics() here because
12876 * PerformanceCollector::samplerCallback no longer accesses guest methods
12877 * holding the lock.
12878 */
12879 unregisterMetrics(mParent->performanceCollector(), mPeer);
12880 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12881 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12882 this, __PRETTY_FUNCTION__, mCollectorGuest));
12883 if (mCollectorGuest)
12884 {
12885 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12886 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12887 mCollectorGuest = NULL;
12888 }
12889#endif
12890
12891 if (aReason == Uninit::Abnormal)
12892 {
12893 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12894 Global::IsOnlineOrTransient(lastState)));
12895
12896 /* reset the state to Aborted */
12897 if (mData->mMachineState != MachineState_Aborted)
12898 setMachineState(MachineState_Aborted);
12899 }
12900
12901 // any machine settings modified?
12902 if (mData->flModifications)
12903 {
12904 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12905 rollback(false /* aNotify */);
12906 }
12907
12908 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12909 || !mConsoleTaskData.mSnapshot);
12910 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12911 {
12912 LogWarningThisFunc(("canceling failed save state request!\n"));
12913 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12914 }
12915 else if (!mConsoleTaskData.mSnapshot.isNull())
12916 {
12917 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12918
12919 /* delete all differencing hard disks created (this will also attach
12920 * their parents back by rolling back mMediaData) */
12921 rollbackMedia();
12922
12923 // delete the saved state file (it might have been already created)
12924 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12925 // think it's still in use
12926 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12927 mConsoleTaskData.mSnapshot->uninit();
12928 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12929 }
12930
12931 if (!mData->mSession.mType.isEmpty())
12932 {
12933 /* mType is not null when this machine's process has been started by
12934 * Machine::LaunchVMProcess(), therefore it is our child. We
12935 * need to queue the PID to reap the process (and avoid zombies on
12936 * Linux). */
12937 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12938 mParent->addProcessToReap(mData->mSession.mPID);
12939 }
12940
12941 mData->mSession.mPID = NIL_RTPROCESS;
12942
12943 if (aReason == Uninit::Unexpected)
12944 {
12945 /* Uninitialization didn't come from #checkForDeath(), so tell the
12946 * client watcher thread to update the set of machines that have open
12947 * sessions. */
12948 mParent->updateClientWatcher();
12949 }
12950
12951 /* uninitialize all remote controls */
12952 if (mData->mSession.mRemoteControls.size())
12953 {
12954 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12955 mData->mSession.mRemoteControls.size()));
12956
12957 Data::Session::RemoteControlList::iterator it =
12958 mData->mSession.mRemoteControls.begin();
12959 while (it != mData->mSession.mRemoteControls.end())
12960 {
12961 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12962 HRESULT rc = (*it)->Uninitialize();
12963 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12964 if (FAILED(rc))
12965 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12966 ++it;
12967 }
12968 mData->mSession.mRemoteControls.clear();
12969 }
12970
12971 /*
12972 * An expected uninitialization can come only from #checkForDeath().
12973 * Otherwise it means that something's gone really wrong (for example,
12974 * the Session implementation has released the VirtualBox reference
12975 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12976 * etc). However, it's also possible, that the client releases the IPC
12977 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12978 * but the VirtualBox release event comes first to the server process.
12979 * This case is practically possible, so we should not assert on an
12980 * unexpected uninit, just log a warning.
12981 */
12982
12983 if ((aReason == Uninit::Unexpected))
12984 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12985
12986 if (aReason != Uninit::Normal)
12987 {
12988 mData->mSession.mDirectControl.setNull();
12989 }
12990 else
12991 {
12992 /* this must be null here (see #OnSessionEnd()) */
12993 Assert(mData->mSession.mDirectControl.isNull());
12994 Assert(mData->mSession.mState == SessionState_Unlocking);
12995 Assert(!mData->mSession.mProgress.isNull());
12996 }
12997 if (mData->mSession.mProgress)
12998 {
12999 if (aReason == Uninit::Normal)
13000 mData->mSession.mProgress->notifyComplete(S_OK);
13001 else
13002 mData->mSession.mProgress->notifyComplete(E_FAIL,
13003 COM_IIDOF(ISession),
13004 getComponentName(),
13005 tr("The VM session was aborted"));
13006 mData->mSession.mProgress.setNull();
13007 }
13008
13009 /* remove the association between the peer machine and this session machine */
13010 Assert( (SessionMachine*)mData->mSession.mMachine == this
13011 || aReason == Uninit::Unexpected);
13012
13013 /* reset the rest of session data */
13014 mData->mSession.mMachine.setNull();
13015 mData->mSession.mState = SessionState_Unlocked;
13016 mData->mSession.mType.setNull();
13017
13018 /* destroy the machine client token before leaving the exclusive lock */
13019 if (mClientToken)
13020 {
13021 delete mClientToken;
13022 mClientToken = NULL;
13023 }
13024
13025 /* fire an event */
13026 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13027
13028 uninitDataAndChildObjects();
13029
13030 /* free the essential data structure last */
13031 mData.free();
13032
13033 /* release the exclusive lock before setting the below two to NULL */
13034 multilock.release();
13035
13036 RTThreadSleep(500);
13037 mParent->AddRef();
13038 LONG c = mParent->Release();
13039 LogFlowThisFunc(("vbox ref=%d\n", c)); NOREF(c);
13040 unconst(mParent) = NULL;
13041 unconst(mPeer) = NULL;
13042
13043 LogFlowThisFuncLeave();
13044}
13045
13046// util::Lockable interface
13047////////////////////////////////////////////////////////////////////////////////
13048
13049/**
13050 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13051 * with the primary Machine instance (mPeer).
13052 */
13053RWLockHandle *SessionMachine::lockHandle() const
13054{
13055 AssertReturn(mPeer != NULL, NULL);
13056 return mPeer->lockHandle();
13057}
13058
13059// IInternalMachineControl methods
13060////////////////////////////////////////////////////////////////////////////////
13061
13062/**
13063 * Passes collected guest statistics to performance collector object
13064 */
13065STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13066 ULONG aCpuKernel, ULONG aCpuIdle,
13067 ULONG aMemTotal, ULONG aMemFree,
13068 ULONG aMemBalloon, ULONG aMemShared,
13069 ULONG aMemCache, ULONG aPageTotal,
13070 ULONG aAllocVMM, ULONG aFreeVMM,
13071 ULONG aBalloonedVMM, ULONG aSharedVMM,
13072 ULONG aVmNetRx, ULONG aVmNetTx)
13073{
13074#ifdef VBOX_WITH_RESOURCE_USAGE_API
13075 if (mCollectorGuest)
13076 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13077 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13078 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13079 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13080
13081 return S_OK;
13082#else
13083 NOREF(aValidStats);
13084 NOREF(aCpuUser);
13085 NOREF(aCpuKernel);
13086 NOREF(aCpuIdle);
13087 NOREF(aMemTotal);
13088 NOREF(aMemFree);
13089 NOREF(aMemBalloon);
13090 NOREF(aMemShared);
13091 NOREF(aMemCache);
13092 NOREF(aPageTotal);
13093 NOREF(aAllocVMM);
13094 NOREF(aFreeVMM);
13095 NOREF(aBalloonedVMM);
13096 NOREF(aSharedVMM);
13097 NOREF(aVmNetRx);
13098 NOREF(aVmNetTx);
13099 return E_NOTIMPL;
13100#endif
13101}
13102
13103/**
13104 * @note Locks this object for writing.
13105 */
13106STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13107{
13108 AutoCaller autoCaller(this);
13109 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13110
13111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13112
13113 mRemoveSavedState = aRemove;
13114
13115 return S_OK;
13116}
13117
13118/**
13119 * @note Locks the same as #setMachineState() does.
13120 */
13121STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13122{
13123 return setMachineState(aMachineState);
13124}
13125
13126/**
13127 * @note Locks this object for writing.
13128 */
13129STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13130{
13131 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13132 AutoCaller autoCaller(this);
13133 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13134
13135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13136
13137 if (mData->mSession.mState != SessionState_Locked)
13138 return VBOX_E_INVALID_OBJECT_STATE;
13139
13140 if (!mData->mSession.mProgress.isNull())
13141 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13142
13143 LogFlowThisFunc(("returns S_OK.\n"));
13144 return S_OK;
13145}
13146
13147/**
13148 * @note Locks this object for writing.
13149 */
13150STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13151{
13152 AutoCaller autoCaller(this);
13153 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13154
13155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13156
13157 if (mData->mSession.mState != SessionState_Locked)
13158 return VBOX_E_INVALID_OBJECT_STATE;
13159
13160 /* Finalize the LaunchVMProcess progress object. */
13161 if (mData->mSession.mProgress)
13162 {
13163 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13164 mData->mSession.mProgress.setNull();
13165 }
13166
13167 if (SUCCEEDED((HRESULT)iResult))
13168 {
13169#ifdef VBOX_WITH_RESOURCE_USAGE_API
13170 /* The VM has been powered up successfully, so it makes sense
13171 * now to offer the performance metrics for a running machine
13172 * object. Doing it earlier wouldn't be safe. */
13173 registerMetrics(mParent->performanceCollector(), mPeer,
13174 mData->mSession.mPID);
13175#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13176 }
13177
13178 return S_OK;
13179}
13180
13181/**
13182 * @note Locks this object for writing.
13183 */
13184STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13185{
13186 LogFlowThisFuncEnter();
13187
13188 CheckComArgOutPointerValid(aProgress);
13189
13190 AutoCaller autoCaller(this);
13191 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13192
13193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13194
13195 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13196 E_FAIL);
13197
13198 /* create a progress object to track operation completion */
13199 ComObjPtr<Progress> pProgress;
13200 pProgress.createObject();
13201 pProgress->init(getVirtualBox(),
13202 static_cast<IMachine *>(this) /* aInitiator */,
13203 Bstr(tr("Stopping the virtual machine")).raw(),
13204 FALSE /* aCancelable */);
13205
13206 /* fill in the console task data */
13207 mConsoleTaskData.mLastState = mData->mMachineState;
13208 mConsoleTaskData.mProgress = pProgress;
13209
13210 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13211 setMachineState(MachineState_Stopping);
13212
13213 pProgress.queryInterfaceTo(aProgress);
13214
13215 return S_OK;
13216}
13217
13218/**
13219 * @note Locks this object for writing.
13220 */
13221STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13222{
13223 LogFlowThisFuncEnter();
13224
13225 AutoCaller autoCaller(this);
13226 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13227
13228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13229
13230 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13231 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13232 && mConsoleTaskData.mLastState != MachineState_Null,
13233 E_FAIL);
13234
13235 /*
13236 * On failure, set the state to the state we had when BeginPoweringDown()
13237 * was called (this is expected by Console::PowerDown() and the associated
13238 * task). On success the VM process already changed the state to
13239 * MachineState_PoweredOff, so no need to do anything.
13240 */
13241 if (FAILED(iResult))
13242 setMachineState(mConsoleTaskData.mLastState);
13243
13244 /* notify the progress object about operation completion */
13245 Assert(mConsoleTaskData.mProgress);
13246 if (SUCCEEDED(iResult))
13247 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13248 else
13249 {
13250 Utf8Str strErrMsg(aErrMsg);
13251 if (strErrMsg.length())
13252 mConsoleTaskData.mProgress->notifyComplete(iResult,
13253 COM_IIDOF(ISession),
13254 getComponentName(),
13255 strErrMsg.c_str());
13256 else
13257 mConsoleTaskData.mProgress->notifyComplete(iResult);
13258 }
13259
13260 /* clear out the temporary saved state data */
13261 mConsoleTaskData.mLastState = MachineState_Null;
13262 mConsoleTaskData.mProgress.setNull();
13263
13264 LogFlowThisFuncLeave();
13265 return S_OK;
13266}
13267
13268
13269/**
13270 * Goes through the USB filters of the given machine to see if the given
13271 * device matches any filter or not.
13272 *
13273 * @note Locks the same as USBController::hasMatchingFilter() does.
13274 */
13275STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13276 BOOL *aMatched,
13277 ULONG *aMaskedIfs)
13278{
13279 LogFlowThisFunc(("\n"));
13280
13281 CheckComArgNotNull(aUSBDevice);
13282 CheckComArgOutPointerValid(aMatched);
13283
13284 AutoCaller autoCaller(this);
13285 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13286
13287#ifdef VBOX_WITH_USB
13288 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13289#else
13290 NOREF(aUSBDevice);
13291 NOREF(aMaskedIfs);
13292 *aMatched = FALSE;
13293#endif
13294
13295 return S_OK;
13296}
13297
13298/**
13299 * @note Locks the same as Host::captureUSBDevice() does.
13300 */
13301STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13302{
13303 LogFlowThisFunc(("\n"));
13304
13305 AutoCaller autoCaller(this);
13306 AssertComRCReturnRC(autoCaller.rc());
13307
13308#ifdef VBOX_WITH_USB
13309 /* if captureDeviceForVM() fails, it must have set extended error info */
13310 clearError();
13311 MultiResult rc = mParent->host()->checkUSBProxyService();
13312 if (FAILED(rc)) return rc;
13313
13314 USBProxyService *service = mParent->host()->usbProxyService();
13315 AssertReturn(service, E_FAIL);
13316 return service->captureDeviceForVM(this, Guid(aId).ref());
13317#else
13318 NOREF(aId);
13319 return E_NOTIMPL;
13320#endif
13321}
13322
13323/**
13324 * @note Locks the same as Host::detachUSBDevice() does.
13325 */
13326STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13327{
13328 LogFlowThisFunc(("\n"));
13329
13330 AutoCaller autoCaller(this);
13331 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13332
13333#ifdef VBOX_WITH_USB
13334 USBProxyService *service = mParent->host()->usbProxyService();
13335 AssertReturn(service, E_FAIL);
13336 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13337#else
13338 NOREF(aId);
13339 NOREF(aDone);
13340 return E_NOTIMPL;
13341#endif
13342}
13343
13344/**
13345 * Inserts all machine filters to the USB proxy service and then calls
13346 * Host::autoCaptureUSBDevices().
13347 *
13348 * Called by Console from the VM process upon VM startup.
13349 *
13350 * @note Locks what called methods lock.
13351 */
13352STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13353{
13354 LogFlowThisFunc(("\n"));
13355
13356 AutoCaller autoCaller(this);
13357 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13358
13359#ifdef VBOX_WITH_USB
13360 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13361 AssertComRC(rc);
13362 NOREF(rc);
13363
13364 USBProxyService *service = mParent->host()->usbProxyService();
13365 AssertReturn(service, E_FAIL);
13366 return service->autoCaptureDevicesForVM(this);
13367#else
13368 return S_OK;
13369#endif
13370}
13371
13372/**
13373 * Removes all machine filters from the USB proxy service and then calls
13374 * Host::detachAllUSBDevices().
13375 *
13376 * Called by Console from the VM process upon normal VM termination or by
13377 * SessionMachine::uninit() upon abnormal VM termination (from under the
13378 * Machine/SessionMachine lock).
13379 *
13380 * @note Locks what called methods lock.
13381 */
13382STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13383{
13384 LogFlowThisFunc(("\n"));
13385
13386 AutoCaller autoCaller(this);
13387 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13388
13389#ifdef VBOX_WITH_USB
13390 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13391 AssertComRC(rc);
13392 NOREF(rc);
13393
13394 USBProxyService *service = mParent->host()->usbProxyService();
13395 AssertReturn(service, E_FAIL);
13396 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13397#else
13398 NOREF(aDone);
13399 return S_OK;
13400#endif
13401}
13402
13403/**
13404 * @note Locks this object for writing.
13405 */
13406STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13407 IProgress **aProgress)
13408{
13409 LogFlowThisFuncEnter();
13410
13411 AssertReturn(aSession, E_INVALIDARG);
13412 AssertReturn(aProgress, E_INVALIDARG);
13413
13414 AutoCaller autoCaller(this);
13415
13416 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13417 /*
13418 * We don't assert below because it might happen that a non-direct session
13419 * informs us it is closed right after we've been uninitialized -- it's ok.
13420 */
13421 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13422
13423 /* get IInternalSessionControl interface */
13424 ComPtr<IInternalSessionControl> control(aSession);
13425
13426 ComAssertRet(!control.isNull(), E_INVALIDARG);
13427
13428 /* Creating a Progress object requires the VirtualBox lock, and
13429 * thus locking it here is required by the lock order rules. */
13430 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13431
13432 if (control == mData->mSession.mDirectControl)
13433 {
13434 ComAssertRet(aProgress, E_POINTER);
13435
13436 /* The direct session is being normally closed by the client process
13437 * ----------------------------------------------------------------- */
13438
13439 /* go to the closing state (essential for all open*Session() calls and
13440 * for #checkForDeath()) */
13441 Assert(mData->mSession.mState == SessionState_Locked);
13442 mData->mSession.mState = SessionState_Unlocking;
13443
13444 /* set direct control to NULL to release the remote instance */
13445 mData->mSession.mDirectControl.setNull();
13446 LogFlowThisFunc(("Direct control is set to NULL\n"));
13447
13448 if (mData->mSession.mProgress)
13449 {
13450 /* finalize the progress, someone might wait if a frontend
13451 * closes the session before powering on the VM. */
13452 mData->mSession.mProgress->notifyComplete(E_FAIL,
13453 COM_IIDOF(ISession),
13454 getComponentName(),
13455 tr("The VM session was closed before any attempt to power it on"));
13456 mData->mSession.mProgress.setNull();
13457 }
13458
13459 /* Create the progress object the client will use to wait until
13460 * #checkForDeath() is called to uninitialize this session object after
13461 * it releases the IPC semaphore.
13462 * Note! Because we're "reusing" mProgress here, this must be a proxy
13463 * object just like for LaunchVMProcess. */
13464 Assert(mData->mSession.mProgress.isNull());
13465 ComObjPtr<ProgressProxy> progress;
13466 progress.createObject();
13467 ComPtr<IUnknown> pPeer(mPeer);
13468 progress->init(mParent, pPeer,
13469 Bstr(tr("Closing session")).raw(),
13470 FALSE /* aCancelable */);
13471 progress.queryInterfaceTo(aProgress);
13472 mData->mSession.mProgress = progress;
13473 }
13474 else
13475 {
13476 /* the remote session is being normally closed */
13477 Data::Session::RemoteControlList::iterator it =
13478 mData->mSession.mRemoteControls.begin();
13479 while (it != mData->mSession.mRemoteControls.end())
13480 {
13481 if (control == *it)
13482 break;
13483 ++it;
13484 }
13485 BOOL found = it != mData->mSession.mRemoteControls.end();
13486 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13487 E_INVALIDARG);
13488 // This MUST be erase(it), not remove(*it) as the latter triggers a
13489 // very nasty use after free due to the place where the value "lives".
13490 mData->mSession.mRemoteControls.erase(it);
13491 }
13492
13493 /* signal the client watcher thread, because the client is going away */
13494 mParent->updateClientWatcher();
13495
13496 LogFlowThisFuncLeave();
13497 return S_OK;
13498}
13499
13500/**
13501 * @note Locks this object for writing.
13502 */
13503STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13504{
13505 LogFlowThisFuncEnter();
13506
13507 CheckComArgOutPointerValid(aProgress);
13508 CheckComArgOutPointerValid(aStateFilePath);
13509
13510 AutoCaller autoCaller(this);
13511 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13512
13513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13514
13515 AssertReturn( mData->mMachineState == MachineState_Paused
13516 && mConsoleTaskData.mLastState == MachineState_Null
13517 && mConsoleTaskData.strStateFilePath.isEmpty(),
13518 E_FAIL);
13519
13520 /* create a progress object to track operation completion */
13521 ComObjPtr<Progress> pProgress;
13522 pProgress.createObject();
13523 pProgress->init(getVirtualBox(),
13524 static_cast<IMachine *>(this) /* aInitiator */,
13525 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13526 FALSE /* aCancelable */);
13527
13528 Utf8Str strStateFilePath;
13529 /* stateFilePath is null when the machine is not running */
13530 if (mData->mMachineState == MachineState_Paused)
13531 composeSavedStateFilename(strStateFilePath);
13532
13533 /* fill in the console task data */
13534 mConsoleTaskData.mLastState = mData->mMachineState;
13535 mConsoleTaskData.strStateFilePath = strStateFilePath;
13536 mConsoleTaskData.mProgress = pProgress;
13537
13538 /* set the state to Saving (this is expected by Console::SaveState()) */
13539 setMachineState(MachineState_Saving);
13540
13541 strStateFilePath.cloneTo(aStateFilePath);
13542 pProgress.queryInterfaceTo(aProgress);
13543
13544 return S_OK;
13545}
13546
13547/**
13548 * @note Locks mParent + this object for writing.
13549 */
13550STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13551{
13552 LogFlowThisFunc(("\n"));
13553
13554 AutoCaller autoCaller(this);
13555 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13556
13557 /* endSavingState() need mParent lock */
13558 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13559
13560 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13561 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13562 && mConsoleTaskData.mLastState != MachineState_Null
13563 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13564 E_FAIL);
13565
13566 /*
13567 * On failure, set the state to the state we had when BeginSavingState()
13568 * was called (this is expected by Console::SaveState() and the associated
13569 * task). On success the VM process already changed the state to
13570 * MachineState_Saved, so no need to do anything.
13571 */
13572 if (FAILED(iResult))
13573 setMachineState(mConsoleTaskData.mLastState);
13574
13575 return endSavingState(iResult, aErrMsg);
13576}
13577
13578/**
13579 * @note Locks this object for writing.
13580 */
13581STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13582{
13583 LogFlowThisFunc(("\n"));
13584
13585 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13586
13587 AutoCaller autoCaller(this);
13588 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13589
13590 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13591
13592 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13593 || mData->mMachineState == MachineState_Teleported
13594 || mData->mMachineState == MachineState_Aborted
13595 , E_FAIL); /** @todo setError. */
13596
13597 Utf8Str stateFilePathFull = aSavedStateFile;
13598 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13599 if (RT_FAILURE(vrc))
13600 return setError(VBOX_E_FILE_ERROR,
13601 tr("Invalid saved state file path '%ls' (%Rrc)"),
13602 aSavedStateFile,
13603 vrc);
13604
13605 mSSData->strStateFilePath = stateFilePathFull;
13606
13607 /* The below setMachineState() will detect the state transition and will
13608 * update the settings file */
13609
13610 return setMachineState(MachineState_Saved);
13611}
13612
13613STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13614 ComSafeArrayOut(BSTR, aValues),
13615 ComSafeArrayOut(LONG64, aTimestamps),
13616 ComSafeArrayOut(BSTR, aFlags))
13617{
13618 LogFlowThisFunc(("\n"));
13619
13620#ifdef VBOX_WITH_GUEST_PROPS
13621 using namespace guestProp;
13622
13623 AutoCaller autoCaller(this);
13624 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13625
13626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13627
13628 CheckComArgOutSafeArrayPointerValid(aNames);
13629 CheckComArgOutSafeArrayPointerValid(aValues);
13630 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13631 CheckComArgOutSafeArrayPointerValid(aFlags);
13632
13633 size_t cEntries = mHWData->mGuestProperties.size();
13634 com::SafeArray<BSTR> names(cEntries);
13635 com::SafeArray<BSTR> values(cEntries);
13636 com::SafeArray<LONG64> timestamps(cEntries);
13637 com::SafeArray<BSTR> flags(cEntries);
13638 unsigned i = 0;
13639 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13640 it != mHWData->mGuestProperties.end();
13641 ++it)
13642 {
13643 char szFlags[MAX_FLAGS_LEN + 1];
13644 it->first.cloneTo(&names[i]);
13645 it->second.strValue.cloneTo(&values[i]);
13646 timestamps[i] = it->second.mTimestamp;
13647 /* If it is NULL, keep it NULL. */
13648 if (it->second.mFlags)
13649 {
13650 writeFlags(it->second.mFlags, szFlags);
13651 Bstr(szFlags).cloneTo(&flags[i]);
13652 }
13653 else
13654 flags[i] = NULL;
13655 ++i;
13656 }
13657 names.detachTo(ComSafeArrayOutArg(aNames));
13658 values.detachTo(ComSafeArrayOutArg(aValues));
13659 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13660 flags.detachTo(ComSafeArrayOutArg(aFlags));
13661 return S_OK;
13662#else
13663 ReturnComNotImplemented();
13664#endif
13665}
13666
13667STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13668 IN_BSTR aValue,
13669 LONG64 aTimestamp,
13670 IN_BSTR aFlags)
13671{
13672 LogFlowThisFunc(("\n"));
13673
13674#ifdef VBOX_WITH_GUEST_PROPS
13675 using namespace guestProp;
13676
13677 CheckComArgStrNotEmptyOrNull(aName);
13678 CheckComArgNotNull(aValue);
13679 CheckComArgNotNull(aFlags);
13680
13681 try
13682 {
13683 /*
13684 * Convert input up front.
13685 */
13686 Utf8Str utf8Name(aName);
13687 uint32_t fFlags = NILFLAG;
13688 if (aFlags)
13689 {
13690 Utf8Str utf8Flags(aFlags);
13691 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13692 AssertRCReturn(vrc, E_INVALIDARG);
13693 }
13694
13695 /*
13696 * Now grab the object lock, validate the state and do the update.
13697 */
13698 AutoCaller autoCaller(this);
13699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13700
13701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13702
13703 switch (mData->mMachineState)
13704 {
13705 case MachineState_Paused:
13706 case MachineState_Running:
13707 case MachineState_Teleporting:
13708 case MachineState_TeleportingPausedVM:
13709 case MachineState_LiveSnapshotting:
13710 case MachineState_DeletingSnapshotOnline:
13711 case MachineState_DeletingSnapshotPaused:
13712 case MachineState_Saving:
13713 case MachineState_Stopping:
13714 break;
13715
13716 default:
13717 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13718 VBOX_E_INVALID_VM_STATE);
13719 }
13720
13721 setModified(IsModified_MachineData);
13722 mHWData.backup();
13723
13724 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13725 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13726 if (it != mHWData->mGuestProperties.end())
13727 {
13728 if (!fDelete)
13729 {
13730 it->second.strValue = aValue;
13731 it->second.mTimestamp = aTimestamp;
13732 it->second.mFlags = fFlags;
13733 }
13734 else
13735 mHWData->mGuestProperties.erase(it);
13736
13737 mData->mGuestPropertiesModified = TRUE;
13738 }
13739 else if (!fDelete)
13740 {
13741 HWData::GuestProperty prop;
13742 prop.strValue = aValue;
13743 prop.mTimestamp = aTimestamp;
13744 prop.mFlags = fFlags;
13745
13746 mHWData->mGuestProperties[utf8Name] = prop;
13747 mData->mGuestPropertiesModified = TRUE;
13748 }
13749
13750 /*
13751 * Send a callback notification if appropriate
13752 */
13753 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13754 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13755 RTSTR_MAX,
13756 utf8Name.c_str(),
13757 RTSTR_MAX, NULL)
13758 )
13759 {
13760 alock.release();
13761
13762 mParent->onGuestPropertyChange(mData->mUuid,
13763 aName,
13764 aValue,
13765 aFlags);
13766 }
13767 }
13768 catch (...)
13769 {
13770 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13771 }
13772 return S_OK;
13773#else
13774 ReturnComNotImplemented();
13775#endif
13776}
13777
13778STDMETHODIMP SessionMachine::LockMedia()
13779{
13780 AutoCaller autoCaller(this);
13781 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13782
13783 AutoMultiWriteLock2 alock(this->lockHandle(),
13784 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13785
13786 AssertReturn( mData->mMachineState == MachineState_Starting
13787 || mData->mMachineState == MachineState_Restoring
13788 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13789
13790 clearError();
13791 alock.release();
13792 return lockMedia();
13793}
13794
13795STDMETHODIMP SessionMachine::UnlockMedia()
13796{
13797 unlockMedia();
13798 return S_OK;
13799}
13800
13801STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13802 IMediumAttachment **aNewAttachment)
13803{
13804 CheckComArgNotNull(aAttachment);
13805 CheckComArgOutPointerValid(aNewAttachment);
13806
13807 AutoCaller autoCaller(this);
13808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13809
13810 // request the host lock first, since might be calling Host methods for getting host drives;
13811 // next, protect the media tree all the while we're in here, as well as our member variables
13812 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13813 this->lockHandle(),
13814 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13815
13816 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13817
13818 Bstr ctrlName;
13819 LONG lPort;
13820 LONG lDevice;
13821 bool fTempEject;
13822 {
13823 AutoCaller autoAttachCaller(this);
13824 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13825
13826 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13827
13828 /* Need to query the details first, as the IMediumAttachment reference
13829 * might be to the original settings, which we are going to change. */
13830 ctrlName = pAttach->getControllerName();
13831 lPort = pAttach->getPort();
13832 lDevice = pAttach->getDevice();
13833 fTempEject = pAttach->getTempEject();
13834 }
13835
13836 if (!fTempEject)
13837 {
13838 /* Remember previously mounted medium. The medium before taking the
13839 * backup is not necessarily the same thing. */
13840 ComObjPtr<Medium> oldmedium;
13841 oldmedium = pAttach->getMedium();
13842
13843 setModified(IsModified_Storage);
13844 mMediaData.backup();
13845
13846 // The backup operation makes the pAttach reference point to the
13847 // old settings. Re-get the correct reference.
13848 pAttach = findAttachment(mMediaData->mAttachments,
13849 ctrlName.raw(),
13850 lPort,
13851 lDevice);
13852
13853 {
13854 AutoCaller autoAttachCaller(this);
13855 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13856
13857 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13858 if (!oldmedium.isNull())
13859 oldmedium->removeBackReference(mData->mUuid);
13860
13861 pAttach->updateMedium(NULL);
13862 pAttach->updateEjected();
13863 }
13864
13865 setModified(IsModified_Storage);
13866 }
13867 else
13868 {
13869 {
13870 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13871 pAttach->updateEjected();
13872 }
13873 }
13874
13875 pAttach.queryInterfaceTo(aNewAttachment);
13876
13877 return S_OK;
13878}
13879
13880// public methods only for internal purposes
13881/////////////////////////////////////////////////////////////////////////////
13882
13883/**
13884 * Called from the client watcher thread to check for expected or unexpected
13885 * death of the client process that has a direct session to this machine.
13886 *
13887 * On Win32 and on OS/2, this method is called only when we've got the
13888 * mutex (i.e. the client has either died or terminated normally) so it always
13889 * returns @c true (the client is terminated, the session machine is
13890 * uninitialized).
13891 *
13892 * On other platforms, the method returns @c true if the client process has
13893 * terminated normally or abnormally and the session machine was uninitialized,
13894 * and @c false if the client process is still alive.
13895 *
13896 * @note Locks this object for writing.
13897 */
13898bool SessionMachine::checkForDeath()
13899{
13900 Uninit::Reason reason;
13901 bool terminated = false;
13902
13903 /* Enclose autoCaller with a block because calling uninit() from under it
13904 * will deadlock. */
13905 {
13906 AutoCaller autoCaller(this);
13907 if (!autoCaller.isOk())
13908 {
13909 /* return true if not ready, to cause the client watcher to exclude
13910 * the corresponding session from watching */
13911 LogFlowThisFunc(("Already uninitialized!\n"));
13912 return true;
13913 }
13914
13915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13916
13917 /* Determine the reason of death: if the session state is Closing here,
13918 * everything is fine. Otherwise it means that the client did not call
13919 * OnSessionEnd() before it released the IPC semaphore. This may happen
13920 * either because the client process has abnormally terminated, or
13921 * because it simply forgot to call ISession::Close() before exiting. We
13922 * threat the latter also as an abnormal termination (see
13923 * Session::uninit() for details). */
13924 reason = mData->mSession.mState == SessionState_Unlocking ?
13925 Uninit::Normal :
13926 Uninit::Abnormal;
13927
13928 if (mClientToken)
13929 terminated = mClientToken->release();
13930 } /* AutoCaller block */
13931
13932 if (terminated)
13933 uninit(reason);
13934
13935 return terminated;
13936}
13937
13938void SessionMachine::getTokenId(Utf8Str &strTokenId)
13939{
13940 LogFlowThisFunc(("\n"));
13941
13942 strTokenId.setNull();
13943
13944 AutoCaller autoCaller(this);
13945 AssertComRCReturnVoid(autoCaller.rc());
13946
13947 Assert(mClientToken);
13948 if (mClientToken)
13949 mClientToken->getId(strTokenId);
13950}
13951
13952Machine::ClientToken *SessionMachine::getClientToken()
13953{
13954 LogFlowThisFunc(("\n"));
13955
13956 AutoCaller autoCaller(this);
13957 AssertComRCReturn(autoCaller.rc(), NULL);
13958
13959 return mClientToken;
13960}
13961
13962
13963/**
13964 * @note Locks this object for reading.
13965 */
13966HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13967{
13968 LogFlowThisFunc(("\n"));
13969
13970 AutoCaller autoCaller(this);
13971 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13972
13973 ComPtr<IInternalSessionControl> directControl;
13974 {
13975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13976 directControl = mData->mSession.mDirectControl;
13977 }
13978
13979 /* ignore notifications sent after #OnSessionEnd() is called */
13980 if (!directControl)
13981 return S_OK;
13982
13983 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13984}
13985
13986/**
13987 * @note Locks this object for reading.
13988 */
13989HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13990 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13991{
13992 LogFlowThisFunc(("\n"));
13993
13994 AutoCaller autoCaller(this);
13995 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13996
13997 ComPtr<IInternalSessionControl> directControl;
13998 {
13999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14000 directControl = mData->mSession.mDirectControl;
14001 }
14002
14003 /* ignore notifications sent after #OnSessionEnd() is called */
14004 if (!directControl)
14005 return S_OK;
14006 /*
14007 * instead acting like callback we ask IVirtualBox deliver corresponding event
14008 */
14009
14010 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14011 return S_OK;
14012}
14013
14014/**
14015 * @note Locks this object for reading.
14016 */
14017HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14018{
14019 LogFlowThisFunc(("\n"));
14020
14021 AutoCaller autoCaller(this);
14022 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14023
14024 ComPtr<IInternalSessionControl> directControl;
14025 {
14026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14027 directControl = mData->mSession.mDirectControl;
14028 }
14029
14030 /* ignore notifications sent after #OnSessionEnd() is called */
14031 if (!directControl)
14032 return S_OK;
14033
14034 return directControl->OnSerialPortChange(serialPort);
14035}
14036
14037/**
14038 * @note Locks this object for reading.
14039 */
14040HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14041{
14042 LogFlowThisFunc(("\n"));
14043
14044 AutoCaller autoCaller(this);
14045 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14046
14047 ComPtr<IInternalSessionControl> directControl;
14048 {
14049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14050 directControl = mData->mSession.mDirectControl;
14051 }
14052
14053 /* ignore notifications sent after #OnSessionEnd() is called */
14054 if (!directControl)
14055 return S_OK;
14056
14057 return directControl->OnParallelPortChange(parallelPort);
14058}
14059
14060/**
14061 * @note Locks this object for reading.
14062 */
14063HRESULT SessionMachine::onStorageControllerChange()
14064{
14065 LogFlowThisFunc(("\n"));
14066
14067 AutoCaller autoCaller(this);
14068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14069
14070 ComPtr<IInternalSessionControl> directControl;
14071 {
14072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14073 directControl = mData->mSession.mDirectControl;
14074 }
14075
14076 /* ignore notifications sent after #OnSessionEnd() is called */
14077 if (!directControl)
14078 return S_OK;
14079
14080 return directControl->OnStorageControllerChange();
14081}
14082
14083/**
14084 * @note Locks this object for reading.
14085 */
14086HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14087{
14088 LogFlowThisFunc(("\n"));
14089
14090 AutoCaller autoCaller(this);
14091 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14092
14093 ComPtr<IInternalSessionControl> directControl;
14094 {
14095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14096 directControl = mData->mSession.mDirectControl;
14097 }
14098
14099 /* ignore notifications sent after #OnSessionEnd() is called */
14100 if (!directControl)
14101 return S_OK;
14102
14103 return directControl->OnMediumChange(aAttachment, aForce);
14104}
14105
14106/**
14107 * @note Locks this object for reading.
14108 */
14109HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14110{
14111 LogFlowThisFunc(("\n"));
14112
14113 AutoCaller autoCaller(this);
14114 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14115
14116 ComPtr<IInternalSessionControl> directControl;
14117 {
14118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14119 directControl = mData->mSession.mDirectControl;
14120 }
14121
14122 /* ignore notifications sent after #OnSessionEnd() is called */
14123 if (!directControl)
14124 return S_OK;
14125
14126 return directControl->OnCPUChange(aCPU, aRemove);
14127}
14128
14129HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14130{
14131 LogFlowThisFunc(("\n"));
14132
14133 AutoCaller autoCaller(this);
14134 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14135
14136 ComPtr<IInternalSessionControl> directControl;
14137 {
14138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14139 directControl = mData->mSession.mDirectControl;
14140 }
14141
14142 /* ignore notifications sent after #OnSessionEnd() is called */
14143 if (!directControl)
14144 return S_OK;
14145
14146 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14147}
14148
14149/**
14150 * @note Locks this object for reading.
14151 */
14152HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14153{
14154 LogFlowThisFunc(("\n"));
14155
14156 AutoCaller autoCaller(this);
14157 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14158
14159 ComPtr<IInternalSessionControl> directControl;
14160 {
14161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14162 directControl = mData->mSession.mDirectControl;
14163 }
14164
14165 /* ignore notifications sent after #OnSessionEnd() is called */
14166 if (!directControl)
14167 return S_OK;
14168
14169 return directControl->OnVRDEServerChange(aRestart);
14170}
14171
14172/**
14173 * @note Locks this object for reading.
14174 */
14175HRESULT SessionMachine::onVideoCaptureChange()
14176{
14177 LogFlowThisFunc(("\n"));
14178
14179 AutoCaller autoCaller(this);
14180 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14181
14182 ComPtr<IInternalSessionControl> directControl;
14183 {
14184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14185 directControl = mData->mSession.mDirectControl;
14186 }
14187
14188 /* ignore notifications sent after #OnSessionEnd() is called */
14189 if (!directControl)
14190 return S_OK;
14191
14192 return directControl->OnVideoCaptureChange();
14193}
14194
14195/**
14196 * @note Locks this object for reading.
14197 */
14198HRESULT SessionMachine::onUSBControllerChange()
14199{
14200 LogFlowThisFunc(("\n"));
14201
14202 AutoCaller autoCaller(this);
14203 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14204
14205 ComPtr<IInternalSessionControl> directControl;
14206 {
14207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14208 directControl = mData->mSession.mDirectControl;
14209 }
14210
14211 /* ignore notifications sent after #OnSessionEnd() is called */
14212 if (!directControl)
14213 return S_OK;
14214
14215 return directControl->OnUSBControllerChange();
14216}
14217
14218/**
14219 * @note Locks this object for reading.
14220 */
14221HRESULT SessionMachine::onSharedFolderChange()
14222{
14223 LogFlowThisFunc(("\n"));
14224
14225 AutoCaller autoCaller(this);
14226 AssertComRCReturnRC(autoCaller.rc());
14227
14228 ComPtr<IInternalSessionControl> directControl;
14229 {
14230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14231 directControl = mData->mSession.mDirectControl;
14232 }
14233
14234 /* ignore notifications sent after #OnSessionEnd() is called */
14235 if (!directControl)
14236 return S_OK;
14237
14238 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14239}
14240
14241/**
14242 * @note Locks this object for reading.
14243 */
14244HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14245{
14246 LogFlowThisFunc(("\n"));
14247
14248 AutoCaller autoCaller(this);
14249 AssertComRCReturnRC(autoCaller.rc());
14250
14251 ComPtr<IInternalSessionControl> directControl;
14252 {
14253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14254 directControl = mData->mSession.mDirectControl;
14255 }
14256
14257 /* ignore notifications sent after #OnSessionEnd() is called */
14258 if (!directControl)
14259 return S_OK;
14260
14261 return directControl->OnClipboardModeChange(aClipboardMode);
14262}
14263
14264/**
14265 * @note Locks this object for reading.
14266 */
14267HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14268{
14269 LogFlowThisFunc(("\n"));
14270
14271 AutoCaller autoCaller(this);
14272 AssertComRCReturnRC(autoCaller.rc());
14273
14274 ComPtr<IInternalSessionControl> directControl;
14275 {
14276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14277 directControl = mData->mSession.mDirectControl;
14278 }
14279
14280 /* ignore notifications sent after #OnSessionEnd() is called */
14281 if (!directControl)
14282 return S_OK;
14283
14284 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14285}
14286
14287/**
14288 * @note Locks this object for reading.
14289 */
14290HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14291{
14292 LogFlowThisFunc(("\n"));
14293
14294 AutoCaller autoCaller(this);
14295 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14296
14297 ComPtr<IInternalSessionControl> directControl;
14298 {
14299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14300 directControl = mData->mSession.mDirectControl;
14301 }
14302
14303 /* ignore notifications sent after #OnSessionEnd() is called */
14304 if (!directControl)
14305 return S_OK;
14306
14307 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14308}
14309
14310/**
14311 * @note Locks this object for reading.
14312 */
14313HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14314{
14315 LogFlowThisFunc(("\n"));
14316
14317 AutoCaller autoCaller(this);
14318 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14319
14320 ComPtr<IInternalSessionControl> directControl;
14321 {
14322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14323 directControl = mData->mSession.mDirectControl;
14324 }
14325
14326 /* ignore notifications sent after #OnSessionEnd() is called */
14327 if (!directControl)
14328 return S_OK;
14329
14330 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14331}
14332
14333/**
14334 * Returns @c true if this machine's USB controller reports it has a matching
14335 * filter for the given USB device and @c false otherwise.
14336 *
14337 * @note locks this object for reading.
14338 */
14339bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14340{
14341 AutoCaller autoCaller(this);
14342 /* silently return if not ready -- this method may be called after the
14343 * direct machine session has been called */
14344 if (!autoCaller.isOk())
14345 return false;
14346
14347#ifdef VBOX_WITH_USB
14348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14349
14350 switch (mData->mMachineState)
14351 {
14352 case MachineState_Starting:
14353 case MachineState_Restoring:
14354 case MachineState_TeleportingIn:
14355 case MachineState_Paused:
14356 case MachineState_Running:
14357 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14358 * elsewhere... */
14359 alock.release();
14360 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14361 default: break;
14362 }
14363#else
14364 NOREF(aDevice);
14365 NOREF(aMaskedIfs);
14366#endif
14367 return false;
14368}
14369
14370/**
14371 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14372 */
14373HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14374 IVirtualBoxErrorInfo *aError,
14375 ULONG aMaskedIfs)
14376{
14377 LogFlowThisFunc(("\n"));
14378
14379 AutoCaller autoCaller(this);
14380
14381 /* This notification may happen after the machine object has been
14382 * uninitialized (the session was closed), so don't assert. */
14383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14384
14385 ComPtr<IInternalSessionControl> directControl;
14386 {
14387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14388 directControl = mData->mSession.mDirectControl;
14389 }
14390
14391 /* fail on notifications sent after #OnSessionEnd() is called, it is
14392 * expected by the caller */
14393 if (!directControl)
14394 return E_FAIL;
14395
14396 /* No locks should be held at this point. */
14397 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14398 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14399
14400 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14401}
14402
14403/**
14404 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14405 */
14406HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14407 IVirtualBoxErrorInfo *aError)
14408{
14409 LogFlowThisFunc(("\n"));
14410
14411 AutoCaller autoCaller(this);
14412
14413 /* This notification may happen after the machine object has been
14414 * uninitialized (the session was closed), so don't assert. */
14415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14416
14417 ComPtr<IInternalSessionControl> directControl;
14418 {
14419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14420 directControl = mData->mSession.mDirectControl;
14421 }
14422
14423 /* fail on notifications sent after #OnSessionEnd() is called, it is
14424 * expected by the caller */
14425 if (!directControl)
14426 return E_FAIL;
14427
14428 /* No locks should be held at this point. */
14429 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14430 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14431
14432 return directControl->OnUSBDeviceDetach(aId, aError);
14433}
14434
14435// protected methods
14436/////////////////////////////////////////////////////////////////////////////
14437
14438/**
14439 * Helper method to finalize saving the state.
14440 *
14441 * @note Must be called from under this object's lock.
14442 *
14443 * @param aRc S_OK if the snapshot has been taken successfully
14444 * @param aErrMsg human readable error message for failure
14445 *
14446 * @note Locks mParent + this objects for writing.
14447 */
14448HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14449{
14450 LogFlowThisFuncEnter();
14451
14452 AutoCaller autoCaller(this);
14453 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14454
14455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14456
14457 HRESULT rc = S_OK;
14458
14459 if (SUCCEEDED(aRc))
14460 {
14461 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14462
14463 /* save all VM settings */
14464 rc = saveSettings(NULL);
14465 // no need to check whether VirtualBox.xml needs saving also since
14466 // we can't have a name change pending at this point
14467 }
14468 else
14469 {
14470 // delete the saved state file (it might have been already created);
14471 // we need not check whether this is shared with a snapshot here because
14472 // we certainly created this saved state file here anew
14473 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14474 }
14475
14476 /* notify the progress object about operation completion */
14477 Assert(mConsoleTaskData.mProgress);
14478 if (SUCCEEDED(aRc))
14479 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14480 else
14481 {
14482 if (aErrMsg.length())
14483 mConsoleTaskData.mProgress->notifyComplete(aRc,
14484 COM_IIDOF(ISession),
14485 getComponentName(),
14486 aErrMsg.c_str());
14487 else
14488 mConsoleTaskData.mProgress->notifyComplete(aRc);
14489 }
14490
14491 /* clear out the temporary saved state data */
14492 mConsoleTaskData.mLastState = MachineState_Null;
14493 mConsoleTaskData.strStateFilePath.setNull();
14494 mConsoleTaskData.mProgress.setNull();
14495
14496 LogFlowThisFuncLeave();
14497 return rc;
14498}
14499
14500/**
14501 * Deletes the given file if it is no longer in use by either the current machine state
14502 * (if the machine is "saved") or any of the machine's snapshots.
14503 *
14504 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14505 * but is different for each SnapshotMachine. When calling this, the order of calling this
14506 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14507 * is therefore critical. I know, it's all rather messy.
14508 *
14509 * @param strStateFile
14510 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14511 */
14512void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14513 Snapshot *pSnapshotToIgnore)
14514{
14515 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14516 if ( (strStateFile.isNotEmpty())
14517 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14518 )
14519 // ... and it must also not be shared with other snapshots
14520 if ( !mData->mFirstSnapshot
14521 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14522 // this checks the SnapshotMachine's state file paths
14523 )
14524 RTFileDelete(strStateFile.c_str());
14525}
14526
14527/**
14528 * Locks the attached media.
14529 *
14530 * All attached hard disks are locked for writing and DVD/floppy are locked for
14531 * reading. Parents of attached hard disks (if any) are locked for reading.
14532 *
14533 * This method also performs accessibility check of all media it locks: if some
14534 * media is inaccessible, the method will return a failure and a bunch of
14535 * extended error info objects per each inaccessible medium.
14536 *
14537 * Note that this method is atomic: if it returns a success, all media are
14538 * locked as described above; on failure no media is locked at all (all
14539 * succeeded individual locks will be undone).
14540 *
14541 * The caller is responsible for doing the necessary state sanity checks.
14542 *
14543 * The locks made by this method must be undone by calling #unlockMedia() when
14544 * no more needed.
14545 */
14546HRESULT SessionMachine::lockMedia()
14547{
14548 AutoCaller autoCaller(this);
14549 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14550
14551 AutoMultiWriteLock2 alock(this->lockHandle(),
14552 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14553
14554 /* bail out if trying to lock things with already set up locking */
14555 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14556
14557 MultiResult mrc(S_OK);
14558
14559 /* Collect locking information for all medium objects attached to the VM. */
14560 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14561 it != mMediaData->mAttachments.end();
14562 ++it)
14563 {
14564 MediumAttachment* pAtt = *it;
14565 DeviceType_T devType = pAtt->getType();
14566 Medium *pMedium = pAtt->getMedium();
14567
14568 MediumLockList *pMediumLockList(new MediumLockList());
14569 // There can be attachments without a medium (floppy/dvd), and thus
14570 // it's impossible to create a medium lock list. It still makes sense
14571 // to have the empty medium lock list in the map in case a medium is
14572 // attached later.
14573 if (pMedium != NULL)
14574 {
14575 MediumType_T mediumType = pMedium->getType();
14576 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14577 || mediumType == MediumType_Shareable;
14578 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14579
14580 alock.release();
14581 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14582 !fIsReadOnlyLock /* fMediumLockWrite */,
14583 NULL,
14584 *pMediumLockList);
14585 alock.acquire();
14586 if (FAILED(mrc))
14587 {
14588 delete pMediumLockList;
14589 mData->mSession.mLockedMedia.Clear();
14590 break;
14591 }
14592 }
14593
14594 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14595 if (FAILED(rc))
14596 {
14597 mData->mSession.mLockedMedia.Clear();
14598 mrc = setError(rc,
14599 tr("Collecting locking information for all attached media failed"));
14600 break;
14601 }
14602 }
14603
14604 if (SUCCEEDED(mrc))
14605 {
14606 /* Now lock all media. If this fails, nothing is locked. */
14607 alock.release();
14608 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14609 alock.acquire();
14610 if (FAILED(rc))
14611 {
14612 mrc = setError(rc,
14613 tr("Locking of attached media failed"));
14614 }
14615 }
14616
14617 return mrc;
14618}
14619
14620/**
14621 * Undoes the locks made by by #lockMedia().
14622 */
14623void SessionMachine::unlockMedia()
14624{
14625 AutoCaller autoCaller(this);
14626 AssertComRCReturnVoid(autoCaller.rc());
14627
14628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14629
14630 /* we may be holding important error info on the current thread;
14631 * preserve it */
14632 ErrorInfoKeeper eik;
14633
14634 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14635 AssertComRC(rc);
14636}
14637
14638/**
14639 * Helper to change the machine state (reimplementation).
14640 *
14641 * @note Locks this object for writing.
14642 * @note This method must not call saveSettings or SaveSettings, otherwise
14643 * it can cause crashes in random places due to unexpectedly committing
14644 * the current settings. The caller is responsible for that. The call
14645 * to saveStateSettings is fine, because this method does not commit.
14646 */
14647HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14648{
14649 LogFlowThisFuncEnter();
14650 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14651
14652 AutoCaller autoCaller(this);
14653 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14654
14655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14656
14657 MachineState_T oldMachineState = mData->mMachineState;
14658
14659 AssertMsgReturn(oldMachineState != aMachineState,
14660 ("oldMachineState=%s, aMachineState=%s\n",
14661 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14662 E_FAIL);
14663
14664 HRESULT rc = S_OK;
14665
14666 int stsFlags = 0;
14667 bool deleteSavedState = false;
14668
14669 /* detect some state transitions */
14670
14671 if ( ( oldMachineState == MachineState_Saved
14672 && aMachineState == MachineState_Restoring)
14673 || ( ( oldMachineState == MachineState_PoweredOff
14674 || oldMachineState == MachineState_Teleported
14675 || oldMachineState == MachineState_Aborted
14676 )
14677 && ( aMachineState == MachineState_TeleportingIn
14678 || aMachineState == MachineState_Starting
14679 )
14680 )
14681 )
14682 {
14683 /* The EMT thread is about to start */
14684
14685 /* Nothing to do here for now... */
14686
14687 /// @todo NEWMEDIA don't let mDVDDrive and other children
14688 /// change anything when in the Starting/Restoring state
14689 }
14690 else if ( ( oldMachineState == MachineState_Running
14691 || oldMachineState == MachineState_Paused
14692 || oldMachineState == MachineState_Teleporting
14693 || oldMachineState == MachineState_LiveSnapshotting
14694 || oldMachineState == MachineState_Stuck
14695 || oldMachineState == MachineState_Starting
14696 || oldMachineState == MachineState_Stopping
14697 || oldMachineState == MachineState_Saving
14698 || oldMachineState == MachineState_Restoring
14699 || oldMachineState == MachineState_TeleportingPausedVM
14700 || oldMachineState == MachineState_TeleportingIn
14701 )
14702 && ( aMachineState == MachineState_PoweredOff
14703 || aMachineState == MachineState_Saved
14704 || aMachineState == MachineState_Teleported
14705 || aMachineState == MachineState_Aborted
14706 )
14707 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14708 * snapshot */
14709 && ( mConsoleTaskData.mSnapshot.isNull()
14710 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14711 )
14712 )
14713 {
14714 /* The EMT thread has just stopped, unlock attached media. Note that as
14715 * opposed to locking that is done from Console, we do unlocking here
14716 * because the VM process may have aborted before having a chance to
14717 * properly unlock all media it locked. */
14718
14719 unlockMedia();
14720 }
14721
14722 if (oldMachineState == MachineState_Restoring)
14723 {
14724 if (aMachineState != MachineState_Saved)
14725 {
14726 /*
14727 * delete the saved state file once the machine has finished
14728 * restoring from it (note that Console sets the state from
14729 * Restoring to Saved if the VM couldn't restore successfully,
14730 * to give the user an ability to fix an error and retry --
14731 * we keep the saved state file in this case)
14732 */
14733 deleteSavedState = true;
14734 }
14735 }
14736 else if ( oldMachineState == MachineState_Saved
14737 && ( aMachineState == MachineState_PoweredOff
14738 || aMachineState == MachineState_Aborted
14739 || aMachineState == MachineState_Teleported
14740 )
14741 )
14742 {
14743 /*
14744 * delete the saved state after Console::ForgetSavedState() is called
14745 * or if the VM process (owning a direct VM session) crashed while the
14746 * VM was Saved
14747 */
14748
14749 /// @todo (dmik)
14750 // Not sure that deleting the saved state file just because of the
14751 // client death before it attempted to restore the VM is a good
14752 // thing. But when it crashes we need to go to the Aborted state
14753 // which cannot have the saved state file associated... The only
14754 // way to fix this is to make the Aborted condition not a VM state
14755 // but a bool flag: i.e., when a crash occurs, set it to true and
14756 // change the state to PoweredOff or Saved depending on the
14757 // saved state presence.
14758
14759 deleteSavedState = true;
14760 mData->mCurrentStateModified = TRUE;
14761 stsFlags |= SaveSTS_CurStateModified;
14762 }
14763
14764 if ( aMachineState == MachineState_Starting
14765 || aMachineState == MachineState_Restoring
14766 || aMachineState == MachineState_TeleportingIn
14767 )
14768 {
14769 /* set the current state modified flag to indicate that the current
14770 * state is no more identical to the state in the
14771 * current snapshot */
14772 if (!mData->mCurrentSnapshot.isNull())
14773 {
14774 mData->mCurrentStateModified = TRUE;
14775 stsFlags |= SaveSTS_CurStateModified;
14776 }
14777 }
14778
14779 if (deleteSavedState)
14780 {
14781 if (mRemoveSavedState)
14782 {
14783 Assert(!mSSData->strStateFilePath.isEmpty());
14784
14785 // it is safe to delete the saved state file if ...
14786 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14787 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14788 // ... none of the snapshots share the saved state file
14789 )
14790 RTFileDelete(mSSData->strStateFilePath.c_str());
14791 }
14792
14793 mSSData->strStateFilePath.setNull();
14794 stsFlags |= SaveSTS_StateFilePath;
14795 }
14796
14797 /* redirect to the underlying peer machine */
14798 mPeer->setMachineState(aMachineState);
14799
14800 if ( aMachineState == MachineState_PoweredOff
14801 || aMachineState == MachineState_Teleported
14802 || aMachineState == MachineState_Aborted
14803 || aMachineState == MachineState_Saved)
14804 {
14805 /* the machine has stopped execution
14806 * (or the saved state file was adopted) */
14807 stsFlags |= SaveSTS_StateTimeStamp;
14808 }
14809
14810 if ( ( oldMachineState == MachineState_PoweredOff
14811 || oldMachineState == MachineState_Aborted
14812 || oldMachineState == MachineState_Teleported
14813 )
14814 && aMachineState == MachineState_Saved)
14815 {
14816 /* the saved state file was adopted */
14817 Assert(!mSSData->strStateFilePath.isEmpty());
14818 stsFlags |= SaveSTS_StateFilePath;
14819 }
14820
14821#ifdef VBOX_WITH_GUEST_PROPS
14822 if ( aMachineState == MachineState_PoweredOff
14823 || aMachineState == MachineState_Aborted
14824 || aMachineState == MachineState_Teleported)
14825 {
14826 /* Make sure any transient guest properties get removed from the
14827 * property store on shutdown. */
14828
14829 HWData::GuestPropertyMap::const_iterator it;
14830 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14831 if (!fNeedsSaving)
14832 for (it = mHWData->mGuestProperties.begin();
14833 it != mHWData->mGuestProperties.end(); ++it)
14834 if ( (it->second.mFlags & guestProp::TRANSIENT)
14835 || (it->second.mFlags & guestProp::TRANSRESET))
14836 {
14837 fNeedsSaving = true;
14838 break;
14839 }
14840 if (fNeedsSaving)
14841 {
14842 mData->mCurrentStateModified = TRUE;
14843 stsFlags |= SaveSTS_CurStateModified;
14844 }
14845 }
14846#endif
14847
14848 rc = saveStateSettings(stsFlags);
14849
14850 if ( ( oldMachineState != MachineState_PoweredOff
14851 && oldMachineState != MachineState_Aborted
14852 && oldMachineState != MachineState_Teleported
14853 )
14854 && ( aMachineState == MachineState_PoweredOff
14855 || aMachineState == MachineState_Aborted
14856 || aMachineState == MachineState_Teleported
14857 )
14858 )
14859 {
14860 /* we've been shut down for any reason */
14861 /* no special action so far */
14862 }
14863
14864 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14865 LogFlowThisFuncLeave();
14866 return rc;
14867}
14868
14869/**
14870 * Sends the current machine state value to the VM process.
14871 *
14872 * @note Locks this object for reading, then calls a client process.
14873 */
14874HRESULT SessionMachine::updateMachineStateOnClient()
14875{
14876 AutoCaller autoCaller(this);
14877 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14878
14879 ComPtr<IInternalSessionControl> directControl;
14880 {
14881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14882 AssertReturn(!!mData, E_FAIL);
14883 directControl = mData->mSession.mDirectControl;
14884
14885 /* directControl may be already set to NULL here in #OnSessionEnd()
14886 * called too early by the direct session process while there is still
14887 * some operation (like deleting the snapshot) in progress. The client
14888 * process in this case is waiting inside Session::close() for the
14889 * "end session" process object to complete, while #uninit() called by
14890 * #checkForDeath() on the Watcher thread is waiting for the pending
14891 * operation to complete. For now, we accept this inconsistent behavior
14892 * and simply do nothing here. */
14893
14894 if (mData->mSession.mState == SessionState_Unlocking)
14895 return S_OK;
14896
14897 AssertReturn(!directControl.isNull(), E_FAIL);
14898 }
14899
14900 return directControl->UpdateMachineState(mData->mMachineState);
14901}
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