VirtualBox

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

Last change on this file since 46775 was 46734, checked in by vboxsync, 12 years ago

Main: Don't create a medium lock list if there is no medium to attach during hotplug (empty DVD drive)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 492.7 KB
Line 
1/* $Id: MachineImpl.cpp 46734 2013-06-22 19:39:58Z 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#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76#include <iprt/base64.h>
77
78#include <VBox/com/array.h>
79#include <VBox/com/list.h>
80
81#include <VBox/err.h>
82#include <VBox/param.h>
83#include <VBox/settings.h>
84#include <VBox/vmm/ssm.h>
85
86#ifdef VBOX_WITH_GUEST_PROPS
87# include <VBox/HostServices/GuestPropertySvc.h>
88# include <VBox/com/array.h>
89#endif
90
91#include "VBox/com/MultiResult.h"
92
93#include <algorithm>
94
95#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
96# define HOSTSUFF_EXE ".exe"
97#else /* !RT_OS_WINDOWS */
98# define HOSTSUFF_EXE ""
99#endif /* !RT_OS_WINDOWS */
100
101// defines / prototypes
102/////////////////////////////////////////////////////////////////////////////
103
104/////////////////////////////////////////////////////////////////////////////
105// Machine::Data structure
106/////////////////////////////////////////////////////////////////////////////
107
108Machine::Data::Data()
109{
110 mRegistered = FALSE;
111 pMachineConfigFile = NULL;
112 /* Contains hints on what has changed when the user is using the VM (config
113 * changes, running the VM, ...). This is used to decide if a config needs
114 * to be written to disk. */
115 flModifications = 0;
116 /* VM modification usually also trigger setting the current state to
117 * "Modified". Although this is not always the case. An e.g. is the VM
118 * initialization phase or when snapshot related data is changed. The
119 * actually behavior is controlled by the following flag. */
120 m_fAllowStateModification = false;
121 mAccessible = FALSE;
122 /* mUuid is initialized in Machine::init() */
123
124 mMachineState = MachineState_PoweredOff;
125 RTTimeNow(&mLastStateChange);
126
127 mMachineStateDeps = 0;
128 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
129 mMachineStateChangePending = 0;
130
131 mCurrentStateModified = TRUE;
132 mGuestPropertiesModified = FALSE;
133
134 mSession.mPID = NIL_RTPROCESS;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureRate = 512;
173 mVideoCaptureFPS = 25;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
190 mHWVirtExExclusive = false;
191#else
192 mHWVirtExExclusive = true;
193#endif
194#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
195 mPAEEnabled = true;
196#else
197 mPAEEnabled = false;
198#endif
199 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
200 mSyntheticCpu = false;
201 mHPETEnabled = false;
202
203 /* default boot order: floppy - DVD - HDD */
204 mBootOrder[0] = DeviceType_Floppy;
205 mBootOrder[1] = DeviceType_DVD;
206 mBootOrder[2] = DeviceType_HardDisk;
207 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
208 mBootOrder[i] = DeviceType_Null;
209
210 mClipboardMode = ClipboardMode_Disabled;
211 mDragAndDropMode = DragAndDropMode_Disabled;
212 mGuestPropertyNotificationPatterns = "";
213
214 mFirmwareType = FirmwareType_BIOS;
215 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
216 mPointingHIDType = PointingHIDType_PS2Mouse;
217 mChipsetType = ChipsetType_PIIX3;
218 mEmulatedUSBWebcamEnabled = FALSE;
219 mEmulatedUSBCardReaderEnabled = FALSE;
220
221 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
222 mCPUAttached[i] = false;
223
224 mIOCacheEnabled = true;
225 mIOCacheSize = 5; /* 5MB */
226
227 /* Maximum CPU execution cap by default. */
228 mCpuExecutionCap = 100;
229}
230
231Machine::HWData::~HWData()
232{
233}
234
235/////////////////////////////////////////////////////////////////////////////
236// Machine::HDData structure
237/////////////////////////////////////////////////////////////////////////////
238
239Machine::MediaData::MediaData()
240{
241}
242
243Machine::MediaData::~MediaData()
244{
245}
246
247/////////////////////////////////////////////////////////////////////////////
248// Machine class
249/////////////////////////////////////////////////////////////////////////////
250
251// constructor / destructor
252/////////////////////////////////////////////////////////////////////////////
253
254Machine::Machine()
255 : mCollectorGuest(NULL),
256 mPeer(NULL),
257 mParent(NULL),
258 mSerialPorts(),
259 mParallelPorts(),
260 uRegistryNeedsSaving(0)
261{}
262
263Machine::~Machine()
264{}
265
266HRESULT Machine::FinalConstruct()
267{
268 LogFlowThisFunc(("\n"));
269 return BaseFinalConstruct();
270}
271
272void Machine::FinalRelease()
273{
274 LogFlowThisFunc(("\n"));
275 uninit();
276 BaseFinalRelease();
277}
278
279/**
280 * Initializes a new machine instance; this init() variant creates a new, empty machine.
281 * This gets called from VirtualBox::CreateMachine().
282 *
283 * @param aParent Associated parent object
284 * @param strConfigFile Local file system path to the VM settings file (can
285 * be relative to the VirtualBox config directory).
286 * @param strName name for the machine
287 * @param llGroups list of groups for the machine
288 * @param aOsType OS Type of this machine or NULL.
289 * @param aId UUID for the new machine.
290 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
291 *
292 * @return Success indicator. if not S_OK, the machine object is invalid
293 */
294HRESULT Machine::init(VirtualBox *aParent,
295 const Utf8Str &strConfigFile,
296 const Utf8Str &strName,
297 const StringsList &llGroups,
298 GuestOSType *aOsType,
299 const Guid &aId,
300 bool fForceOverwrite,
301 bool fDirectoryIncludesUUID)
302{
303 LogFlowThisFuncEnter();
304 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
305
306 /* Enclose the state transition NotReady->InInit->Ready */
307 AutoInitSpan autoInitSpan(this);
308 AssertReturn(autoInitSpan.isOk(), E_FAIL);
309
310 HRESULT rc = initImpl(aParent, strConfigFile);
311 if (FAILED(rc)) return rc;
312
313 rc = tryCreateMachineConfigFile(fForceOverwrite);
314 if (FAILED(rc)) return rc;
315
316 if (SUCCEEDED(rc))
317 {
318 // create an empty machine config
319 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
320
321 rc = initDataAndChildObjects();
322 }
323
324 if (SUCCEEDED(rc))
325 {
326 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
327 mData->mAccessible = TRUE;
328
329 unconst(mData->mUuid) = aId;
330
331 mUserData->s.strName = strName;
332
333 mUserData->s.llGroups = llGroups;
334
335 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
336 // the "name sync" flag determines whether the machine directory gets renamed along
337 // with the machine file; say so if the settings file name is the same as the
338 // settings file parent directory (machine directory)
339 mUserData->s.fNameSync = isInOwnDir();
340
341 // initialize the default snapshots folder
342 rc = COMSETTER(SnapshotFolder)(NULL);
343 AssertComRC(rc);
344
345 if (aOsType)
346 {
347 /* Store OS type */
348 mUserData->s.strOsType = aOsType->id();
349
350 /* Apply BIOS defaults */
351 mBIOSSettings->applyDefaults(aOsType);
352
353 /* Apply network adapters defaults */
354 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
355 mNetworkAdapters[slot]->applyDefaults(aOsType);
356
357 /* Apply serial port defaults */
358 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
359 mSerialPorts[slot]->applyDefaults(aOsType);
360
361 /* Let the OS type select 64-bit ness. */
362 mHWData->mLongMode = aOsType->is64Bit()
363 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
364 }
365
366 /* At this point the changing of the current state modification
367 * flag is allowed. */
368 allowStateModification();
369
370 /* commit all changes made during the initialization */
371 commit();
372 }
373
374 /* Confirm a successful initialization when it's the case */
375 if (SUCCEEDED(rc))
376 {
377 if (mData->mAccessible)
378 autoInitSpan.setSucceeded();
379 else
380 autoInitSpan.setLimited();
381 }
382
383 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
384 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
385 mData->mRegistered,
386 mData->mAccessible,
387 rc));
388
389 LogFlowThisFuncLeave();
390
391 return rc;
392}
393
394/**
395 * Initializes a new instance with data from machine XML (formerly Init_Registered).
396 * Gets called in two modes:
397 *
398 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
399 * UUID is specified and we mark the machine as "registered";
400 *
401 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
402 * and the machine remains unregistered until RegisterMachine() is called.
403 *
404 * @param aParent Associated parent object
405 * @param aConfigFile Local file system path to the VM settings file (can
406 * be relative to the VirtualBox config directory).
407 * @param aId UUID of the machine or NULL (see above).
408 *
409 * @return Success indicator. if not S_OK, the machine object is invalid
410 */
411HRESULT Machine::initFromSettings(VirtualBox *aParent,
412 const Utf8Str &strConfigFile,
413 const Guid *aId)
414{
415 LogFlowThisFuncEnter();
416 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
417
418 /* Enclose the state transition NotReady->InInit->Ready */
419 AutoInitSpan autoInitSpan(this);
420 AssertReturn(autoInitSpan.isOk(), E_FAIL);
421
422 HRESULT rc = initImpl(aParent, strConfigFile);
423 if (FAILED(rc)) return rc;
424
425 if (aId)
426 {
427 // loading a registered VM:
428 unconst(mData->mUuid) = *aId;
429 mData->mRegistered = TRUE;
430 // now load the settings from XML:
431 rc = registeredInit();
432 // this calls initDataAndChildObjects() and loadSettings()
433 }
434 else
435 {
436 // opening an unregistered VM (VirtualBox::OpenMachine()):
437 rc = initDataAndChildObjects();
438
439 if (SUCCEEDED(rc))
440 {
441 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
442 mData->mAccessible = TRUE;
443
444 try
445 {
446 // load and parse machine XML; this will throw on XML or logic errors
447 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
448
449 // reject VM UUID duplicates, they can happen if someone
450 // tries to register an already known VM config again
451 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
452 true /* fPermitInaccessible */,
453 false /* aDoSetError */,
454 NULL) != VBOX_E_OBJECT_NOT_FOUND)
455 {
456 throw setError(E_FAIL,
457 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
458 mData->m_strConfigFile.c_str());
459 }
460
461 // use UUID from machine config
462 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
463
464 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
465 NULL /* puuidRegistry */);
466 if (FAILED(rc)) throw rc;
467
468 /* At this point the changing of the current state modification
469 * flag is allowed. */
470 allowStateModification();
471
472 commit();
473 }
474 catch (HRESULT err)
475 {
476 /* we assume that error info is set by the thrower */
477 rc = err;
478 }
479 catch (...)
480 {
481 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
482 }
483 }
484 }
485
486 /* Confirm a successful initialization when it's the case */
487 if (SUCCEEDED(rc))
488 {
489 if (mData->mAccessible)
490 autoInitSpan.setSucceeded();
491 else
492 {
493 autoInitSpan.setLimited();
494
495 // uninit media from this machine's media registry, or else
496 // reloading the settings will fail
497 mParent->unregisterMachineMedia(getId());
498 }
499 }
500
501 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
502 "rc=%08X\n",
503 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
504 mData->mRegistered, mData->mAccessible, rc));
505
506 LogFlowThisFuncLeave();
507
508 return rc;
509}
510
511/**
512 * Initializes a new instance from a machine config that is already in memory
513 * (import OVF case). Since we are importing, the UUID in the machine
514 * config is ignored and we always generate a fresh one.
515 *
516 * @param strName Name for the new machine; this overrides what is specified in config and is used
517 * for the settings file as well.
518 * @param config Machine configuration loaded and parsed from XML.
519 *
520 * @return Success indicator. if not S_OK, the machine object is invalid
521 */
522HRESULT Machine::init(VirtualBox *aParent,
523 const Utf8Str &strName,
524 const settings::MachineConfigFile &config)
525{
526 LogFlowThisFuncEnter();
527
528 /* Enclose the state transition NotReady->InInit->Ready */
529 AutoInitSpan autoInitSpan(this);
530 AssertReturn(autoInitSpan.isOk(), E_FAIL);
531
532 Utf8Str strConfigFile;
533 aParent->getDefaultMachineFolder(strConfigFile);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(RTPATH_DELIMITER);
537 strConfigFile.append(strName);
538 strConfigFile.append(".vbox");
539
540 HRESULT rc = initImpl(aParent, strConfigFile);
541 if (FAILED(rc)) return rc;
542
543 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
544 if (FAILED(rc)) return rc;
545
546 rc = initDataAndChildObjects();
547
548 if (SUCCEEDED(rc))
549 {
550 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
551 mData->mAccessible = TRUE;
552
553 // create empty machine config for instance data
554 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
555
556 // generate fresh UUID, ignore machine config
557 unconst(mData->mUuid).create();
558
559 rc = loadMachineDataFromSettings(config,
560 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
561
562 // override VM name as well, it may be different
563 mUserData->s.strName = strName;
564
565 if (SUCCEEDED(rc))
566 {
567 /* At this point the changing of the current state modification
568 * flag is allowed. */
569 allowStateModification();
570
571 /* commit all changes made during the initialization */
572 commit();
573 }
574 }
575
576 /* Confirm a successful initialization when it's the case */
577 if (SUCCEEDED(rc))
578 {
579 if (mData->mAccessible)
580 autoInitSpan.setSucceeded();
581 else
582 {
583 autoInitSpan.setLimited();
584
585 // uninit media from this machine's media registry, or else
586 // reloading the settings will fail
587 mParent->unregisterMachineMedia(getId());
588 }
589 }
590
591 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
592 "rc=%08X\n",
593 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
594 mData->mRegistered, mData->mAccessible, rc));
595
596 LogFlowThisFuncLeave();
597
598 return rc;
599}
600
601/**
602 * Shared code between the various init() implementations.
603 * @param aParent
604 * @return
605 */
606HRESULT Machine::initImpl(VirtualBox *aParent,
607 const Utf8Str &strConfigFile)
608{
609 LogFlowThisFuncEnter();
610
611 AssertReturn(aParent, E_INVALIDARG);
612 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
613
614 HRESULT rc = S_OK;
615
616 /* share the parent weakly */
617 unconst(mParent) = aParent;
618
619 /* allocate the essential machine data structure (the rest will be
620 * allocated later by initDataAndChildObjects() */
621 mData.allocate();
622
623 /* memorize the config file name (as provided) */
624 mData->m_strConfigFile = strConfigFile;
625
626 /* get the full file name */
627 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
628 if (RT_FAILURE(vrc1))
629 return setError(VBOX_E_FILE_ERROR,
630 tr("Invalid machine settings file name '%s' (%Rrc)"),
631 strConfigFile.c_str(),
632 vrc1);
633
634 LogFlowThisFuncLeave();
635
636 return rc;
637}
638
639/**
640 * Tries to create a machine settings file in the path stored in the machine
641 * instance data. Used when a new machine is created to fail gracefully if
642 * the settings file could not be written (e.g. because machine dir is read-only).
643 * @return
644 */
645HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
646{
647 HRESULT rc = S_OK;
648
649 // when we create a new machine, we must be able to create the settings file
650 RTFILE f = NIL_RTFILE;
651 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
652 if ( RT_SUCCESS(vrc)
653 || vrc == VERR_SHARING_VIOLATION
654 )
655 {
656 if (RT_SUCCESS(vrc))
657 RTFileClose(f);
658 if (!fForceOverwrite)
659 rc = setError(VBOX_E_FILE_ERROR,
660 tr("Machine settings file '%s' already exists"),
661 mData->m_strConfigFileFull.c_str());
662 else
663 {
664 /* try to delete the config file, as otherwise the creation
665 * of a new settings file will fail. */
666 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
667 if (RT_FAILURE(vrc2))
668 rc = setError(VBOX_E_FILE_ERROR,
669 tr("Could not delete the existing settings file '%s' (%Rrc)"),
670 mData->m_strConfigFileFull.c_str(), vrc2);
671 }
672 }
673 else if ( vrc != VERR_FILE_NOT_FOUND
674 && vrc != VERR_PATH_NOT_FOUND
675 )
676 rc = setError(VBOX_E_FILE_ERROR,
677 tr("Invalid machine settings file name '%s' (%Rrc)"),
678 mData->m_strConfigFileFull.c_str(),
679 vrc);
680 return rc;
681}
682
683/**
684 * Initializes the registered machine by loading the settings file.
685 * This method is separated from #init() in order to make it possible to
686 * retry the operation after VirtualBox startup instead of refusing to
687 * startup the whole VirtualBox server in case if the settings file of some
688 * registered VM is invalid or inaccessible.
689 *
690 * @note Must be always called from this object's write lock
691 * (unless called from #init() that doesn't need any locking).
692 * @note Locks the mUSBController method for writing.
693 * @note Subclasses must not call this method.
694 */
695HRESULT Machine::registeredInit()
696{
697 AssertReturn(!isSessionMachine(), E_FAIL);
698 AssertReturn(!isSnapshotMachine(), E_FAIL);
699 AssertReturn(mData->mUuid.isValid(), E_FAIL);
700 AssertReturn(!mData->mAccessible, E_FAIL);
701
702 HRESULT rc = initDataAndChildObjects();
703
704 if (SUCCEEDED(rc))
705 {
706 /* Temporarily reset the registered flag in order to let setters
707 * potentially called from loadSettings() succeed (isMutable() used in
708 * all setters will return FALSE for a Machine instance if mRegistered
709 * is TRUE). */
710 mData->mRegistered = FALSE;
711
712 try
713 {
714 // load and parse machine XML; this will throw on XML or logic errors
715 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
716
717 if (mData->mUuid != mData->pMachineConfigFile->uuid)
718 throw setError(E_FAIL,
719 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
720 mData->pMachineConfigFile->uuid.raw(),
721 mData->m_strConfigFileFull.c_str(),
722 mData->mUuid.toString().c_str(),
723 mParent->settingsFilePath().c_str());
724
725 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
726 NULL /* const Guid *puuidRegistry */);
727 if (FAILED(rc)) throw rc;
728 }
729 catch (HRESULT err)
730 {
731 /* we assume that error info is set by the thrower */
732 rc = err;
733 }
734 catch (...)
735 {
736 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
737 }
738
739 /* Restore the registered flag (even on failure) */
740 mData->mRegistered = TRUE;
741 }
742
743 if (SUCCEEDED(rc))
744 {
745 /* Set mAccessible to TRUE only if we successfully locked and loaded
746 * the settings file */
747 mData->mAccessible = TRUE;
748
749 /* commit all changes made during loading the settings file */
750 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
751 /// @todo r=klaus for some reason the settings loading logic backs up
752 // the settings, and therefore a commit is needed. Should probably be changed.
753 }
754 else
755 {
756 /* If the machine is registered, then, instead of returning a
757 * failure, we mark it as inaccessible and set the result to
758 * success to give it a try later */
759
760 /* fetch the current error info */
761 mData->mAccessError = com::ErrorInfo();
762 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
763 mData->mUuid.raw(),
764 mData->mAccessError.getText().raw()));
765
766 /* rollback all changes */
767 rollback(false /* aNotify */);
768
769 // uninit media from this machine's media registry, or else
770 // reloading the settings will fail
771 mParent->unregisterMachineMedia(getId());
772
773 /* uninitialize the common part to make sure all data is reset to
774 * default (null) values */
775 uninitDataAndChildObjects();
776
777 rc = S_OK;
778 }
779
780 return rc;
781}
782
783/**
784 * Uninitializes the instance.
785 * Called either from FinalRelease() or by the parent when it gets destroyed.
786 *
787 * @note The caller of this method must make sure that this object
788 * a) doesn't have active callers on the current thread and b) is not locked
789 * by the current thread; otherwise uninit() will hang either a) due to
790 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
791 * a dead-lock caused by this thread waiting for all callers on the other
792 * threads are done but preventing them from doing so by holding a lock.
793 */
794void Machine::uninit()
795{
796 LogFlowThisFuncEnter();
797
798 Assert(!isWriteLockOnCurrentThread());
799
800 Assert(!uRegistryNeedsSaving);
801 if (uRegistryNeedsSaving)
802 {
803 AutoCaller autoCaller(this);
804 if (SUCCEEDED(autoCaller.rc()))
805 {
806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
807 saveSettings(NULL, Machine::SaveS_Force);
808 }
809 }
810
811 /* Enclose the state transition Ready->InUninit->NotReady */
812 AutoUninitSpan autoUninitSpan(this);
813 if (autoUninitSpan.uninitDone())
814 return;
815
816 Assert(!isSnapshotMachine());
817 Assert(!isSessionMachine());
818 Assert(!!mData);
819
820 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
821 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
822
823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
824
825 if (!mData->mSession.mMachine.isNull())
826 {
827 /* Theoretically, this can only happen if the VirtualBox server has been
828 * terminated while there were clients running that owned open direct
829 * sessions. Since in this case we are definitely called by
830 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
831 * won't happen on the client watcher thread (because it does
832 * VirtualBox::addCaller() for the duration of the
833 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
834 * cannot happen until the VirtualBox caller is released). This is
835 * important, because SessionMachine::uninit() cannot correctly operate
836 * after we return from this method (it expects the Machine instance is
837 * still valid). We'll call it ourselves below.
838 */
839 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
840 (SessionMachine*)mData->mSession.mMachine));
841
842 if (Global::IsOnlineOrTransient(mData->mMachineState))
843 {
844 LogWarningThisFunc(("Setting state to Aborted!\n"));
845 /* set machine state using SessionMachine reimplementation */
846 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
847 }
848
849 /*
850 * Uninitialize SessionMachine using public uninit() to indicate
851 * an unexpected uninitialization.
852 */
853 mData->mSession.mMachine->uninit();
854 /* SessionMachine::uninit() must set mSession.mMachine to null */
855 Assert(mData->mSession.mMachine.isNull());
856 }
857
858 // uninit media from this machine's media registry, if they're still there
859 Guid uuidMachine(getId());
860
861 /* the lock is no more necessary (SessionMachine is uninitialized) */
862 alock.release();
863
864 /* XXX This will fail with
865 * "cannot be closed because it is still attached to 1 virtual machines"
866 * because at this point we did not call uninitDataAndChildObjects() yet
867 * and therefore also removeBackReference() for all these mediums was not called! */
868
869 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
870 mParent->unregisterMachineMedia(uuidMachine);
871
872 // has machine been modified?
873 if (mData->flModifications)
874 {
875 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
876 rollback(false /* aNotify */);
877 }
878
879 if (mData->mAccessible)
880 uninitDataAndChildObjects();
881
882 /* free the essential data structure last */
883 mData.free();
884
885 LogFlowThisFuncLeave();
886}
887
888// IMachine properties
889/////////////////////////////////////////////////////////////////////////////
890
891STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
892{
893 CheckComArgOutPointerValid(aParent);
894
895 AutoLimitedCaller autoCaller(this);
896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
897
898 /* mParent is constant during life time, no need to lock */
899 ComObjPtr<VirtualBox> pVirtualBox(mParent);
900 pVirtualBox.queryInterfaceTo(aParent);
901
902 return S_OK;
903}
904
905STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
906{
907 CheckComArgOutPointerValid(aAccessible);
908
909 AutoLimitedCaller autoCaller(this);
910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
911
912 LogFlowThisFunc(("ENTER\n"));
913
914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
915
916 HRESULT rc = S_OK;
917
918 if (!mData->mAccessible)
919 {
920 /* try to initialize the VM once more if not accessible */
921
922 AutoReinitSpan autoReinitSpan(this);
923 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
924
925#ifdef DEBUG
926 LogFlowThisFunc(("Dumping media backreferences\n"));
927 mParent->dumpAllBackRefs();
928#endif
929
930 if (mData->pMachineConfigFile)
931 {
932 // reset the XML file to force loadSettings() (called from registeredInit())
933 // to parse it again; the file might have changed
934 delete mData->pMachineConfigFile;
935 mData->pMachineConfigFile = NULL;
936 }
937
938 rc = registeredInit();
939
940 if (SUCCEEDED(rc) && mData->mAccessible)
941 {
942 autoReinitSpan.setSucceeded();
943
944 /* make sure interesting parties will notice the accessibility
945 * state change */
946 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
947 mParent->onMachineDataChange(mData->mUuid);
948 }
949 }
950
951 if (SUCCEEDED(rc))
952 *aAccessible = mData->mAccessible;
953
954 LogFlowThisFuncLeave();
955
956 return rc;
957}
958
959STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
960{
961 CheckComArgOutPointerValid(aAccessError);
962
963 AutoLimitedCaller autoCaller(this);
964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
965
966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
967
968 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
969 {
970 /* return shortly */
971 aAccessError = NULL;
972 return S_OK;
973 }
974
975 HRESULT rc = S_OK;
976
977 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
978 rc = errorInfo.createObject();
979 if (SUCCEEDED(rc))
980 {
981 errorInfo->init(mData->mAccessError.getResultCode(),
982 mData->mAccessError.getInterfaceID().ref(),
983 Utf8Str(mData->mAccessError.getComponent()).c_str(),
984 Utf8Str(mData->mAccessError.getText()));
985 rc = errorInfo.queryInterfaceTo(aAccessError);
986 }
987
988 return rc;
989}
990
991STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
992{
993 CheckComArgOutPointerValid(aName);
994
995 AutoCaller autoCaller(this);
996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
997
998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 mUserData->s.strName.cloneTo(aName);
1001
1002 return S_OK;
1003}
1004
1005STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1006{
1007 CheckComArgStrNotEmptyOrNull(aName);
1008
1009 AutoCaller autoCaller(this);
1010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1011
1012 // prohibit setting a UUID only as the machine name, or else it can
1013 // never be found by findMachine()
1014 Guid test(aName);
1015
1016 if (test.isValid())
1017 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1018
1019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1020
1021 HRESULT rc = checkStateDependency(MutableStateDep);
1022 if (FAILED(rc)) return rc;
1023
1024 setModified(IsModified_MachineData);
1025 mUserData.backup();
1026 mUserData->s.strName = aName;
1027
1028 return S_OK;
1029}
1030
1031STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1032{
1033 CheckComArgOutPointerValid(aDescription);
1034
1035 AutoCaller autoCaller(this);
1036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1037
1038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 mUserData->s.strDescription.cloneTo(aDescription);
1041
1042 return S_OK;
1043}
1044
1045STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1046{
1047 AutoCaller autoCaller(this);
1048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1049
1050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1051
1052 // this can be done in principle in any state as it doesn't affect the VM
1053 // significantly, but play safe by not messing around while complex
1054 // activities are going on
1055 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1056 if (FAILED(rc)) return rc;
1057
1058 setModified(IsModified_MachineData);
1059 mUserData.backup();
1060 mUserData->s.strDescription = aDescription;
1061
1062 return S_OK;
1063}
1064
1065STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1066{
1067 CheckComArgOutPointerValid(aId);
1068
1069 AutoLimitedCaller autoCaller(this);
1070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1071
1072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1073
1074 mData->mUuid.toUtf16().cloneTo(aId);
1075
1076 return S_OK;
1077}
1078
1079STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1080{
1081 CheckComArgOutSafeArrayPointerValid(aGroups);
1082
1083 AutoCaller autoCaller(this);
1084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1085
1086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1087 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1088 size_t i = 0;
1089 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1090 it != mUserData->s.llGroups.end();
1091 ++it, i++)
1092 {
1093 Bstr tmp = *it;
1094 tmp.cloneTo(&groups[i]);
1095 }
1096 groups.detachTo(ComSafeArrayOutArg(aGroups));
1097
1098 return S_OK;
1099}
1100
1101STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1102{
1103 AutoCaller autoCaller(this);
1104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1105
1106 StringsList llGroups;
1107 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1108 if (FAILED(rc))
1109 return rc;
1110
1111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 // changing machine groups is possible while the VM is offline
1114 rc = checkStateDependency(OfflineStateDep);
1115 if (FAILED(rc)) return rc;
1116
1117 setModified(IsModified_MachineData);
1118 mUserData.backup();
1119 mUserData->s.llGroups = llGroups;
1120
1121 return S_OK;
1122}
1123
1124STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1125{
1126 CheckComArgOutPointerValid(aOSTypeId);
1127
1128 AutoCaller autoCaller(this);
1129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1130
1131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 mUserData->s.strOsType.cloneTo(aOSTypeId);
1134
1135 return S_OK;
1136}
1137
1138STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1139{
1140 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1141
1142 AutoCaller autoCaller(this);
1143 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1144
1145 /* look up the object by Id to check it is valid */
1146 ComPtr<IGuestOSType> guestOSType;
1147 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1148 if (FAILED(rc)) return rc;
1149
1150 /* when setting, always use the "etalon" value for consistency -- lookup
1151 * by ID is case-insensitive and the input value may have different case */
1152 Bstr osTypeId;
1153 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1154 if (FAILED(rc)) return rc;
1155
1156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 rc = checkStateDependency(MutableStateDep);
1159 if (FAILED(rc)) return rc;
1160
1161 setModified(IsModified_MachineData);
1162 mUserData.backup();
1163 mUserData->s.strOsType = osTypeId;
1164
1165 return S_OK;
1166}
1167
1168
1169STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1170{
1171 CheckComArgOutPointerValid(aFirmwareType);
1172
1173 AutoCaller autoCaller(this);
1174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1175
1176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 *aFirmwareType = mHWData->mFirmwareType;
1179
1180 return S_OK;
1181}
1182
1183STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1184{
1185 AutoCaller autoCaller(this);
1186 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1188
1189 HRESULT rc = checkStateDependency(MutableStateDep);
1190 if (FAILED(rc)) return rc;
1191
1192 setModified(IsModified_MachineData);
1193 mHWData.backup();
1194 mHWData->mFirmwareType = aFirmwareType;
1195
1196 return S_OK;
1197}
1198
1199STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1200{
1201 CheckComArgOutPointerValid(aKeyboardHIDType);
1202
1203 AutoCaller autoCaller(this);
1204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1205
1206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1209
1210 return S_OK;
1211}
1212
1213STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1214{
1215 AutoCaller autoCaller(this);
1216 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1218
1219 HRESULT rc = checkStateDependency(MutableStateDep);
1220 if (FAILED(rc)) return rc;
1221
1222 setModified(IsModified_MachineData);
1223 mHWData.backup();
1224 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1225
1226 return S_OK;
1227}
1228
1229STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1230{
1231 CheckComArgOutPointerValid(aPointingHIDType);
1232
1233 AutoCaller autoCaller(this);
1234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1235
1236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1237
1238 *aPointingHIDType = mHWData->mPointingHIDType;
1239
1240 return S_OK;
1241}
1242
1243STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1244{
1245 AutoCaller autoCaller(this);
1246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 HRESULT rc = checkStateDependency(MutableStateDep);
1250 if (FAILED(rc)) return rc;
1251
1252 setModified(IsModified_MachineData);
1253 mHWData.backup();
1254 mHWData->mPointingHIDType = aPointingHIDType;
1255
1256 return S_OK;
1257}
1258
1259STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1260{
1261 CheckComArgOutPointerValid(aChipsetType);
1262
1263 AutoCaller autoCaller(this);
1264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1265
1266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 *aChipsetType = mHWData->mChipsetType;
1269
1270 return S_OK;
1271}
1272
1273STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1274{
1275 AutoCaller autoCaller(this);
1276 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1278
1279 HRESULT rc = checkStateDependency(MutableStateDep);
1280 if (FAILED(rc)) return rc;
1281
1282 if (aChipsetType != mHWData->mChipsetType)
1283 {
1284 setModified(IsModified_MachineData);
1285 mHWData.backup();
1286 mHWData->mChipsetType = aChipsetType;
1287
1288 // Resize network adapter array, to be finalized on commit/rollback.
1289 // We must not throw away entries yet, otherwise settings are lost
1290 // without a way to roll back.
1291 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1292 size_t oldCount = mNetworkAdapters.size();
1293 if (newCount > oldCount)
1294 {
1295 mNetworkAdapters.resize(newCount);
1296 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1297 {
1298 unconst(mNetworkAdapters[slot]).createObject();
1299 mNetworkAdapters[slot]->init(this, slot);
1300 }
1301 }
1302 }
1303
1304 return S_OK;
1305}
1306
1307STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1308{
1309 CheckComArgOutPointerValid(aHWVersion);
1310
1311 AutoCaller autoCaller(this);
1312 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1313
1314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1315
1316 mHWData->mHWVersion.cloneTo(aHWVersion);
1317
1318 return S_OK;
1319}
1320
1321STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1322{
1323 /* check known version */
1324 Utf8Str hwVersion = aHWVersion;
1325 if ( hwVersion.compare("1") != 0
1326 && hwVersion.compare("2") != 0)
1327 return setError(E_INVALIDARG,
1328 tr("Invalid hardware version: %ls\n"), aHWVersion);
1329
1330 AutoCaller autoCaller(this);
1331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1332
1333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 HRESULT rc = checkStateDependency(MutableStateDep);
1336 if (FAILED(rc)) return rc;
1337
1338 setModified(IsModified_MachineData);
1339 mHWData.backup();
1340 mHWData->mHWVersion = hwVersion;
1341
1342 return S_OK;
1343}
1344
1345STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1346{
1347 CheckComArgOutPointerValid(aUUID);
1348
1349 AutoCaller autoCaller(this);
1350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1351
1352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1353
1354 if (mHWData->mHardwareUUID.isValid())
1355 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1356 else
1357 mData->mUuid.toUtf16().cloneTo(aUUID);
1358
1359 return S_OK;
1360}
1361
1362STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1363{
1364 Guid hardwareUUID(aUUID);
1365 if (!hardwareUUID.isValid())
1366 return E_INVALIDARG;
1367
1368 AutoCaller autoCaller(this);
1369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1370
1371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1372
1373 HRESULT rc = checkStateDependency(MutableStateDep);
1374 if (FAILED(rc)) return rc;
1375
1376 setModified(IsModified_MachineData);
1377 mHWData.backup();
1378 if (hardwareUUID == mData->mUuid)
1379 mHWData->mHardwareUUID.clear();
1380 else
1381 mHWData->mHardwareUUID = hardwareUUID;
1382
1383 return S_OK;
1384}
1385
1386STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1387{
1388 CheckComArgOutPointerValid(memorySize);
1389
1390 AutoCaller autoCaller(this);
1391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1392
1393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1394
1395 *memorySize = mHWData->mMemorySize;
1396
1397 return S_OK;
1398}
1399
1400STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1401{
1402 /* check RAM limits */
1403 if ( memorySize < MM_RAM_MIN_IN_MB
1404 || memorySize > MM_RAM_MAX_IN_MB
1405 )
1406 return setError(E_INVALIDARG,
1407 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1408 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1409
1410 AutoCaller autoCaller(this);
1411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1412
1413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 HRESULT rc = checkStateDependency(MutableStateDep);
1416 if (FAILED(rc)) return rc;
1417
1418 setModified(IsModified_MachineData);
1419 mHWData.backup();
1420 mHWData->mMemorySize = memorySize;
1421
1422 return S_OK;
1423}
1424
1425STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1426{
1427 CheckComArgOutPointerValid(CPUCount);
1428
1429 AutoCaller autoCaller(this);
1430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1431
1432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 *CPUCount = mHWData->mCPUCount;
1435
1436 return S_OK;
1437}
1438
1439STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1440{
1441 /* check CPU limits */
1442 if ( CPUCount < SchemaDefs::MinCPUCount
1443 || CPUCount > SchemaDefs::MaxCPUCount
1444 )
1445 return setError(E_INVALIDARG,
1446 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1447 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1448
1449 AutoCaller autoCaller(this);
1450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1451
1452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1455 if (mHWData->mCPUHotPlugEnabled)
1456 {
1457 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1458 {
1459 if (mHWData->mCPUAttached[idx])
1460 return setError(E_INVALIDARG,
1461 tr("There is still a CPU attached to socket %lu."
1462 "Detach the CPU before removing the socket"),
1463 CPUCount, idx+1);
1464 }
1465 }
1466
1467 HRESULT rc = checkStateDependency(MutableStateDep);
1468 if (FAILED(rc)) return rc;
1469
1470 setModified(IsModified_MachineData);
1471 mHWData.backup();
1472 mHWData->mCPUCount = CPUCount;
1473
1474 return S_OK;
1475}
1476
1477STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1478{
1479 CheckComArgOutPointerValid(aExecutionCap);
1480
1481 AutoCaller autoCaller(this);
1482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1483
1484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1485
1486 *aExecutionCap = mHWData->mCpuExecutionCap;
1487
1488 return S_OK;
1489}
1490
1491STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1492{
1493 HRESULT rc = S_OK;
1494
1495 /* check throttle limits */
1496 if ( aExecutionCap < 1
1497 || aExecutionCap > 100
1498 )
1499 return setError(E_INVALIDARG,
1500 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1501 aExecutionCap, 1, 100);
1502
1503 AutoCaller autoCaller(this);
1504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1505
1506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1507
1508 alock.release();
1509 rc = onCPUExecutionCapChange(aExecutionCap);
1510 alock.acquire();
1511 if (FAILED(rc)) return rc;
1512
1513 setModified(IsModified_MachineData);
1514 mHWData.backup();
1515 mHWData->mCpuExecutionCap = aExecutionCap;
1516
1517 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1518 if (Global::IsOnline(mData->mMachineState))
1519 saveSettings(NULL);
1520
1521 return S_OK;
1522}
1523
1524
1525STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1526{
1527 CheckComArgOutPointerValid(aEnabled);
1528
1529 AutoCaller autoCaller(this);
1530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1531
1532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 *aEnabled = mHWData->mCPUHotPlugEnabled;
1535
1536 return S_OK;
1537}
1538
1539STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1540{
1541 HRESULT rc = S_OK;
1542
1543 AutoCaller autoCaller(this);
1544 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1545
1546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1547
1548 rc = checkStateDependency(MutableStateDep);
1549 if (FAILED(rc)) return rc;
1550
1551 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1552 {
1553 if (aEnabled)
1554 {
1555 setModified(IsModified_MachineData);
1556 mHWData.backup();
1557
1558 /* Add the amount of CPUs currently attached */
1559 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1560 {
1561 mHWData->mCPUAttached[i] = true;
1562 }
1563 }
1564 else
1565 {
1566 /*
1567 * We can disable hotplug only if the amount of maximum CPUs is equal
1568 * to the amount of attached CPUs
1569 */
1570 unsigned cCpusAttached = 0;
1571 unsigned iHighestId = 0;
1572
1573 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1574 {
1575 if (mHWData->mCPUAttached[i])
1576 {
1577 cCpusAttached++;
1578 iHighestId = i;
1579 }
1580 }
1581
1582 if ( (cCpusAttached != mHWData->mCPUCount)
1583 || (iHighestId >= mHWData->mCPUCount))
1584 return setError(E_INVALIDARG,
1585 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1586
1587 setModified(IsModified_MachineData);
1588 mHWData.backup();
1589 }
1590 }
1591
1592 mHWData->mCPUHotPlugEnabled = aEnabled;
1593
1594 return rc;
1595}
1596
1597STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1598{
1599#ifdef VBOX_WITH_USB_CARDREADER
1600 CheckComArgOutPointerValid(aEnabled);
1601
1602 AutoCaller autoCaller(this);
1603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1604
1605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1606
1607 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1608
1609 return S_OK;
1610#else
1611 NOREF(aEnabled);
1612 return E_NOTIMPL;
1613#endif
1614}
1615
1616STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1617{
1618#ifdef VBOX_WITH_USB_CARDREADER
1619 AutoCaller autoCaller(this);
1620 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1622
1623 HRESULT rc = checkStateDependency(MutableStateDep);
1624 if (FAILED(rc)) return rc;
1625
1626 setModified(IsModified_MachineData);
1627 mHWData.backup();
1628 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1629
1630 return S_OK;
1631#else
1632 NOREF(aEnabled);
1633 return E_NOTIMPL;
1634#endif
1635}
1636
1637STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1638{
1639#ifdef VBOX_WITH_USB_VIDEO
1640 CheckComArgOutPointerValid(aEnabled);
1641
1642 AutoCaller autoCaller(this);
1643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1644
1645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1646
1647 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1648
1649 return S_OK;
1650#else
1651 NOREF(aEnabled);
1652 return E_NOTIMPL;
1653#endif
1654}
1655
1656STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1657{
1658#ifdef VBOX_WITH_USB_VIDEO
1659 AutoCaller autoCaller(this);
1660 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1662
1663 HRESULT rc = checkStateDependency(MutableStateDep);
1664 if (FAILED(rc)) return rc;
1665
1666 setModified(IsModified_MachineData);
1667 mHWData.backup();
1668 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1669
1670 return S_OK;
1671#else
1672 NOREF(aEnabled);
1673 return E_NOTIMPL;
1674#endif
1675}
1676
1677STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1678{
1679 CheckComArgOutPointerValid(aEnabled);
1680
1681 AutoCaller autoCaller(this);
1682 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1684
1685 *aEnabled = mHWData->mHPETEnabled;
1686
1687 return S_OK;
1688}
1689
1690STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1691{
1692 HRESULT rc = S_OK;
1693
1694 AutoCaller autoCaller(this);
1695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1697
1698 rc = checkStateDependency(MutableStateDep);
1699 if (FAILED(rc)) return rc;
1700
1701 setModified(IsModified_MachineData);
1702 mHWData.backup();
1703
1704 mHWData->mHPETEnabled = aEnabled;
1705
1706 return rc;
1707}
1708
1709STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1710{
1711 AutoCaller autoCaller(this);
1712 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1713
1714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1715
1716 *fEnabled = mHWData->mVideoCaptureEnabled;
1717 return S_OK;
1718}
1719
1720STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1721{
1722 HRESULT rc = S_OK;
1723
1724 AutoCaller autoCaller(this);
1725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1727
1728 setModified(IsModified_MachineData);
1729 mHWData.backup();
1730 mHWData->mVideoCaptureEnabled = fEnabled;
1731
1732 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1733 if (Global::IsOnline(mData->mMachineState))
1734 saveSettings(NULL);
1735
1736 alock.release();
1737 rc = onVideoCaptureChange();
1738
1739 return rc;
1740}
1741
1742STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1743{
1744 CheckComArgOutSafeArrayPointerValid(aScreens);
1745
1746 AutoCaller autoCaller(this);
1747 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1748
1749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1750
1751 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1752 for (unsigned i = 0; i < screens.size(); i++)
1753 screens[i] = mHWData->maVideoCaptureScreens[i];
1754 screens.detachTo(ComSafeArrayOutArg(aScreens));
1755 return S_OK;
1756}
1757
1758STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1759{
1760 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1761 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1762 bool fChanged = false;
1763
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 for (unsigned i = 0; i < screens.size(); i++)
1767 {
1768 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1769 {
1770 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1771 fChanged = true;
1772 }
1773 }
1774 if (fChanged)
1775 {
1776 alock.release();
1777 HRESULT rc = onVideoCaptureChange();
1778 alock.acquire();
1779 if (FAILED(rc)) return rc;
1780 setModified(IsModified_MachineData);
1781
1782 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1783 if (Global::IsOnline(mData->mMachineState))
1784 saveSettings(NULL);
1785 }
1786
1787 return S_OK;
1788}
1789
1790STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1791{
1792 AutoCaller autoCaller(this);
1793 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1794
1795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1796 if (mHWData->mVideoCaptureFile.isEmpty())
1797 {
1798 Utf8Str defaultFile;
1799 getDefaultVideoCaptureFile(defaultFile);
1800 defaultFile.cloneTo(apFile);
1801 }
1802 else
1803 mHWData->mVideoCaptureFile.cloneTo(apFile);
1804 return S_OK;
1805}
1806
1807STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1808{
1809 Utf8Str strFile(aFile);
1810 AutoCaller autoCaller(this);
1811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1812
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 if ( Global::IsOnline(mData->mMachineState)
1816 && mHWData->mVideoCaptureEnabled)
1817 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1818
1819 if (!RTPathStartsWithRoot(strFile.c_str()))
1820 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1821
1822 if (!strFile.isEmpty())
1823 {
1824 Utf8Str defaultFile;
1825 getDefaultVideoCaptureFile(defaultFile);
1826 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1827 strFile.setNull();
1828 }
1829
1830 setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureFile = strFile;
1833
1834 return S_OK;
1835}
1836
1837STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1838{
1839 AutoCaller autoCaller(this);
1840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1841
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843 *aHorzRes = mHWData->mVideoCaptureWidth;
1844 return S_OK;
1845}
1846
1847STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1848{
1849 AutoCaller autoCaller(this);
1850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1851
1852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1853
1854 if ( Global::IsOnline(mData->mMachineState)
1855 && mHWData->mVideoCaptureEnabled)
1856 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1857
1858 setModified(IsModified_MachineData);
1859 mHWData.backup();
1860 mHWData->mVideoCaptureWidth = aHorzRes;
1861
1862 return S_OK;
1863}
1864
1865STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1866{
1867 AutoCaller autoCaller(this);
1868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1869
1870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1871 *aVertRes = mHWData->mVideoCaptureHeight;
1872 return S_OK;
1873}
1874
1875STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1876{
1877 AutoCaller autoCaller(this);
1878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1879
1880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1881
1882 if ( Global::IsOnline(mData->mMachineState)
1883 && mHWData->mVideoCaptureEnabled)
1884 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1885
1886 setModified(IsModified_MachineData);
1887 mHWData.backup();
1888 mHWData->mVideoCaptureHeight = aVertRes;
1889
1890 return S_OK;
1891}
1892
1893STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1894{
1895 AutoCaller autoCaller(this);
1896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1897
1898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1899 *aRate = mHWData->mVideoCaptureRate;
1900 return S_OK;
1901}
1902
1903STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1904{
1905 AutoCaller autoCaller(this);
1906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1907
1908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1909
1910 if ( Global::IsOnline(mData->mMachineState)
1911 && mHWData->mVideoCaptureEnabled)
1912 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1913
1914 setModified(IsModified_MachineData);
1915 mHWData.backup();
1916 mHWData->mVideoCaptureRate = aRate;
1917
1918 return S_OK;
1919}
1920
1921STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1922{
1923 AutoCaller autoCaller(this);
1924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1925
1926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1927 *aFPS = mHWData->mVideoCaptureFPS;
1928 return S_OK;
1929}
1930
1931STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1932{
1933 AutoCaller autoCaller(this);
1934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1935
1936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1937
1938 if ( Global::IsOnline(mData->mMachineState)
1939 && mHWData->mVideoCaptureEnabled)
1940 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1941
1942 setModified(IsModified_MachineData);
1943 mHWData.backup();
1944 mHWData->mVideoCaptureFPS = aFPS;
1945
1946 return S_OK;
1947}
1948
1949STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1950{
1951 CheckComArgOutPointerValid(aGraphicsControllerType);
1952
1953 AutoCaller autoCaller(this);
1954 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1955
1956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1959
1960 return S_OK;
1961}
1962
1963STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1964{
1965 switch (aGraphicsControllerType)
1966 {
1967 case GraphicsControllerType_Null:
1968 case GraphicsControllerType_VBoxVGA:
1969 break;
1970 default:
1971 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1972 }
1973
1974 AutoCaller autoCaller(this);
1975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1976
1977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1978
1979 HRESULT rc = checkStateDependency(MutableStateDep);
1980 if (FAILED(rc)) return rc;
1981
1982 setModified(IsModified_MachineData);
1983 mHWData.backup();
1984 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1985
1986 return S_OK;
1987}
1988
1989STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1990{
1991 CheckComArgOutPointerValid(memorySize);
1992
1993 AutoCaller autoCaller(this);
1994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1995
1996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1997
1998 *memorySize = mHWData->mVRAMSize;
1999
2000 return S_OK;
2001}
2002
2003STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2004{
2005 /* check VRAM limits */
2006 if (memorySize < SchemaDefs::MinGuestVRAM ||
2007 memorySize > SchemaDefs::MaxGuestVRAM)
2008 return setError(E_INVALIDARG,
2009 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2010 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2011
2012 AutoCaller autoCaller(this);
2013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2014
2015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2016
2017 HRESULT rc = checkStateDependency(MutableStateDep);
2018 if (FAILED(rc)) return rc;
2019
2020 setModified(IsModified_MachineData);
2021 mHWData.backup();
2022 mHWData->mVRAMSize = memorySize;
2023
2024 return S_OK;
2025}
2026
2027/** @todo this method should not be public */
2028STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2029{
2030 CheckComArgOutPointerValid(memoryBalloonSize);
2031
2032 AutoCaller autoCaller(this);
2033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2034
2035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2036
2037 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2038
2039 return S_OK;
2040}
2041
2042/**
2043 * Set the memory balloon size.
2044 *
2045 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2046 * we have to make sure that we never call IGuest from here.
2047 */
2048STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2049{
2050 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2051#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2052 /* check limits */
2053 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2054 return setError(E_INVALIDARG,
2055 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2056 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2057
2058 AutoCaller autoCaller(this);
2059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2060
2061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2062
2063 setModified(IsModified_MachineData);
2064 mHWData.backup();
2065 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2066
2067 return S_OK;
2068#else
2069 NOREF(memoryBalloonSize);
2070 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2071#endif
2072}
2073
2074STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2075{
2076 CheckComArgOutPointerValid(aEnabled);
2077
2078 AutoCaller autoCaller(this);
2079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2080
2081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2082
2083 *aEnabled = mHWData->mPageFusionEnabled;
2084 return S_OK;
2085}
2086
2087STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2088{
2089#ifdef VBOX_WITH_PAGE_SHARING
2090 AutoCaller autoCaller(this);
2091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2092
2093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2094
2095 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2096 setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 mHWData->mPageFusionEnabled = aEnabled;
2099 return S_OK;
2100#else
2101 NOREF(aEnabled);
2102 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2103#endif
2104}
2105
2106STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2107{
2108 CheckComArgOutPointerValid(aEnabled);
2109
2110 AutoCaller autoCaller(this);
2111 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2112
2113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 *aEnabled = mHWData->mAccelerate3DEnabled;
2116
2117 return S_OK;
2118}
2119
2120STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2121{
2122 AutoCaller autoCaller(this);
2123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2124
2125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2126
2127 HRESULT rc = checkStateDependency(MutableStateDep);
2128 if (FAILED(rc)) return rc;
2129
2130 /** @todo check validity! */
2131
2132 setModified(IsModified_MachineData);
2133 mHWData.backup();
2134 mHWData->mAccelerate3DEnabled = enable;
2135
2136 return S_OK;
2137}
2138
2139
2140STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2141{
2142 CheckComArgOutPointerValid(aEnabled);
2143
2144 AutoCaller autoCaller(this);
2145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2146
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2150
2151 return S_OK;
2152}
2153
2154STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2155{
2156 AutoCaller autoCaller(this);
2157 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2158
2159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2160
2161 HRESULT rc = checkStateDependency(MutableStateDep);
2162 if (FAILED(rc)) return rc;
2163
2164 /** @todo check validity! */
2165
2166 setModified(IsModified_MachineData);
2167 mHWData.backup();
2168 mHWData->mAccelerate2DVideoEnabled = enable;
2169
2170 return S_OK;
2171}
2172
2173STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2174{
2175 CheckComArgOutPointerValid(monitorCount);
2176
2177 AutoCaller autoCaller(this);
2178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2179
2180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2181
2182 *monitorCount = mHWData->mMonitorCount;
2183
2184 return S_OK;
2185}
2186
2187STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2188{
2189 /* make sure monitor count is a sensible number */
2190 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2191 return setError(E_INVALIDARG,
2192 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2193 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2194
2195 AutoCaller autoCaller(this);
2196 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2197
2198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2199
2200 HRESULT rc = checkStateDependency(MutableStateDep);
2201 if (FAILED(rc)) return rc;
2202
2203 setModified(IsModified_MachineData);
2204 mHWData.backup();
2205 mHWData->mMonitorCount = monitorCount;
2206
2207 return S_OK;
2208}
2209
2210STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2211{
2212 CheckComArgOutPointerValid(biosSettings);
2213
2214 AutoCaller autoCaller(this);
2215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2216
2217 /* mBIOSSettings is constant during life time, no need to lock */
2218 mBIOSSettings.queryInterfaceTo(biosSettings);
2219
2220 return S_OK;
2221}
2222
2223STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2224{
2225 CheckComArgOutPointerValid(aVal);
2226
2227 AutoCaller autoCaller(this);
2228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2229
2230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2231
2232 switch (property)
2233 {
2234 case CPUPropertyType_PAE:
2235 *aVal = mHWData->mPAEEnabled;
2236 break;
2237
2238 case CPUPropertyType_Synthetic:
2239 *aVal = mHWData->mSyntheticCpu;
2240 break;
2241
2242 case CPUPropertyType_LongMode:
2243 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2244 *aVal = TRUE;
2245 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2246 *aVal = FALSE;
2247#if HC_ARCH_BITS == 64
2248 else
2249 *aVal = TRUE;
2250#else
2251 else
2252 {
2253 *aVal = FALSE;
2254
2255 ComPtr<IGuestOSType> ptrGuestOSType;
2256 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2257 if (SUCCEEDED(hrc2))
2258 {
2259 BOOL fIs64Bit = FALSE;
2260 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2261 if (SUCCEEDED(hrc2) && fIs64Bit)
2262 {
2263 ComObjPtr<Host> ptrHost = mParent->host();
2264 alock.release();
2265
2266 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2267 if (FAILED(hrc2))
2268 *aVal = FALSE;
2269 }
2270 }
2271 }
2272#endif
2273 break;
2274
2275 default:
2276 return E_INVALIDARG;
2277 }
2278 return S_OK;
2279}
2280
2281STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2282{
2283 AutoCaller autoCaller(this);
2284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2285
2286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2287
2288 HRESULT rc = checkStateDependency(MutableStateDep);
2289 if (FAILED(rc)) return rc;
2290
2291 switch (property)
2292 {
2293 case CPUPropertyType_PAE:
2294 setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mPAEEnabled = !!aVal;
2297 break;
2298
2299 case CPUPropertyType_Synthetic:
2300 setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mSyntheticCpu = !!aVal;
2303 break;
2304
2305 case CPUPropertyType_LongMode:
2306 setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2309 break;
2310
2311 default:
2312 return E_INVALIDARG;
2313 }
2314 return S_OK;
2315}
2316
2317STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2318{
2319 CheckComArgOutPointerValid(aValEax);
2320 CheckComArgOutPointerValid(aValEbx);
2321 CheckComArgOutPointerValid(aValEcx);
2322 CheckComArgOutPointerValid(aValEdx);
2323
2324 AutoCaller autoCaller(this);
2325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2326
2327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2328
2329 switch(aId)
2330 {
2331 case 0x0:
2332 case 0x1:
2333 case 0x2:
2334 case 0x3:
2335 case 0x4:
2336 case 0x5:
2337 case 0x6:
2338 case 0x7:
2339 case 0x8:
2340 case 0x9:
2341 case 0xA:
2342 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2343 return E_INVALIDARG;
2344
2345 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2346 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2347 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2348 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2349 break;
2350
2351 case 0x80000000:
2352 case 0x80000001:
2353 case 0x80000002:
2354 case 0x80000003:
2355 case 0x80000004:
2356 case 0x80000005:
2357 case 0x80000006:
2358 case 0x80000007:
2359 case 0x80000008:
2360 case 0x80000009:
2361 case 0x8000000A:
2362 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2363 return E_INVALIDARG;
2364
2365 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2366 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2367 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2368 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2369 break;
2370
2371 default:
2372 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2373 }
2374 return S_OK;
2375}
2376
2377STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2378{
2379 AutoCaller autoCaller(this);
2380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2381
2382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 HRESULT rc = checkStateDependency(MutableStateDep);
2385 if (FAILED(rc)) return rc;
2386
2387 switch(aId)
2388 {
2389 case 0x0:
2390 case 0x1:
2391 case 0x2:
2392 case 0x3:
2393 case 0x4:
2394 case 0x5:
2395 case 0x6:
2396 case 0x7:
2397 case 0x8:
2398 case 0x9:
2399 case 0xA:
2400 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2401 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2402 setModified(IsModified_MachineData);
2403 mHWData.backup();
2404 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2405 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2406 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2407 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2408 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2409 break;
2410
2411 case 0x80000000:
2412 case 0x80000001:
2413 case 0x80000002:
2414 case 0x80000003:
2415 case 0x80000004:
2416 case 0x80000005:
2417 case 0x80000006:
2418 case 0x80000007:
2419 case 0x80000008:
2420 case 0x80000009:
2421 case 0x8000000A:
2422 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2423 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2424 setModified(IsModified_MachineData);
2425 mHWData.backup();
2426 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2427 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2428 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2429 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2430 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2431 break;
2432
2433 default:
2434 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2435 }
2436 return S_OK;
2437}
2438
2439STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2440{
2441 AutoCaller autoCaller(this);
2442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2443
2444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2445
2446 HRESULT rc = checkStateDependency(MutableStateDep);
2447 if (FAILED(rc)) return rc;
2448
2449 switch(aId)
2450 {
2451 case 0x0:
2452 case 0x1:
2453 case 0x2:
2454 case 0x3:
2455 case 0x4:
2456 case 0x5:
2457 case 0x6:
2458 case 0x7:
2459 case 0x8:
2460 case 0x9:
2461 case 0xA:
2462 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2463 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2464 setModified(IsModified_MachineData);
2465 mHWData.backup();
2466 /* Invalidate leaf. */
2467 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2468 break;
2469
2470 case 0x80000000:
2471 case 0x80000001:
2472 case 0x80000002:
2473 case 0x80000003:
2474 case 0x80000004:
2475 case 0x80000005:
2476 case 0x80000006:
2477 case 0x80000007:
2478 case 0x80000008:
2479 case 0x80000009:
2480 case 0x8000000A:
2481 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2482 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2483 setModified(IsModified_MachineData);
2484 mHWData.backup();
2485 /* Invalidate leaf. */
2486 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2487 break;
2488
2489 default:
2490 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2491 }
2492 return S_OK;
2493}
2494
2495STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2496{
2497 AutoCaller autoCaller(this);
2498 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2499
2500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2501
2502 HRESULT rc = checkStateDependency(MutableStateDep);
2503 if (FAILED(rc)) return rc;
2504
2505 setModified(IsModified_MachineData);
2506 mHWData.backup();
2507
2508 /* Invalidate all standard leafs. */
2509 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2510 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2511
2512 /* Invalidate all extended leafs. */
2513 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2514 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2515
2516 return S_OK;
2517}
2518
2519STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2520{
2521 CheckComArgOutPointerValid(aVal);
2522
2523 AutoCaller autoCaller(this);
2524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2525
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 switch(property)
2529 {
2530 case HWVirtExPropertyType_Enabled:
2531 *aVal = mHWData->mHWVirtExEnabled;
2532 break;
2533
2534 case HWVirtExPropertyType_Exclusive:
2535 *aVal = mHWData->mHWVirtExExclusive;
2536 break;
2537
2538 case HWVirtExPropertyType_VPID:
2539 *aVal = mHWData->mHWVirtExVPIDEnabled;
2540 break;
2541
2542 case HWVirtExPropertyType_NestedPaging:
2543 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2544 break;
2545
2546 case HWVirtExPropertyType_UnrestrictedExecution:
2547 *aVal = mHWData->mHWVirtExUXEnabled;
2548 break;
2549
2550 case HWVirtExPropertyType_LargePages:
2551 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2552#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2553 *aVal = FALSE;
2554#endif
2555 break;
2556
2557 case HWVirtExPropertyType_Force:
2558 *aVal = mHWData->mHWVirtExForceEnabled;
2559 break;
2560
2561 default:
2562 return E_INVALIDARG;
2563 }
2564 return S_OK;
2565}
2566
2567STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2568{
2569 AutoCaller autoCaller(this);
2570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2571
2572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 HRESULT rc = checkStateDependency(MutableStateDep);
2575 if (FAILED(rc)) return rc;
2576
2577 switch(property)
2578 {
2579 case HWVirtExPropertyType_Enabled:
2580 setModified(IsModified_MachineData);
2581 mHWData.backup();
2582 mHWData->mHWVirtExEnabled = !!aVal;
2583 break;
2584
2585 case HWVirtExPropertyType_Exclusive:
2586 setModified(IsModified_MachineData);
2587 mHWData.backup();
2588 mHWData->mHWVirtExExclusive = !!aVal;
2589 break;
2590
2591 case HWVirtExPropertyType_VPID:
2592 setModified(IsModified_MachineData);
2593 mHWData.backup();
2594 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2595 break;
2596
2597 case HWVirtExPropertyType_NestedPaging:
2598 setModified(IsModified_MachineData);
2599 mHWData.backup();
2600 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2601 break;
2602
2603 case HWVirtExPropertyType_UnrestrictedExecution:
2604 setModified(IsModified_MachineData);
2605 mHWData.backup();
2606 mHWData->mHWVirtExUXEnabled = !!aVal;
2607 break;
2608
2609 case HWVirtExPropertyType_LargePages:
2610 setModified(IsModified_MachineData);
2611 mHWData.backup();
2612 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2613 break;
2614
2615 case HWVirtExPropertyType_Force:
2616 setModified(IsModified_MachineData);
2617 mHWData.backup();
2618 mHWData->mHWVirtExForceEnabled = !!aVal;
2619 break;
2620
2621 default:
2622 return E_INVALIDARG;
2623 }
2624
2625 return S_OK;
2626}
2627
2628STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2629{
2630 CheckComArgOutPointerValid(aSnapshotFolder);
2631
2632 AutoCaller autoCaller(this);
2633 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2634
2635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2636
2637 Utf8Str strFullSnapshotFolder;
2638 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2639 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2640
2641 return S_OK;
2642}
2643
2644STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2645{
2646 /* @todo (r=dmik):
2647 * 1. Allow to change the name of the snapshot folder containing snapshots
2648 * 2. Rename the folder on disk instead of just changing the property
2649 * value (to be smart and not to leave garbage). Note that it cannot be
2650 * done here because the change may be rolled back. Thus, the right
2651 * place is #saveSettings().
2652 */
2653
2654 AutoCaller autoCaller(this);
2655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2656
2657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 HRESULT rc = checkStateDependency(MutableStateDep);
2660 if (FAILED(rc)) return rc;
2661
2662 if (!mData->mCurrentSnapshot.isNull())
2663 return setError(E_FAIL,
2664 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2665
2666 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2667
2668 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2669 if (strSnapshotFolder.isEmpty())
2670 strSnapshotFolder = "Snapshots";
2671 int vrc = calculateFullPath(strSnapshotFolder,
2672 strSnapshotFolder);
2673 if (RT_FAILURE(vrc))
2674 return setError(E_FAIL,
2675 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2676 aSnapshotFolder, vrc);
2677
2678 setModified(IsModified_MachineData);
2679 mUserData.backup();
2680
2681 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2682
2683 return S_OK;
2684}
2685
2686STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2687{
2688 CheckComArgOutSafeArrayPointerValid(aAttachments);
2689
2690 AutoCaller autoCaller(this);
2691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2692
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2696 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2697
2698 return S_OK;
2699}
2700
2701STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2702{
2703 CheckComArgOutPointerValid(vrdeServer);
2704
2705 AutoCaller autoCaller(this);
2706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2707
2708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2709
2710 Assert(!!mVRDEServer);
2711 mVRDEServer.queryInterfaceTo(vrdeServer);
2712
2713 return S_OK;
2714}
2715
2716STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2717{
2718 CheckComArgOutPointerValid(audioAdapter);
2719
2720 AutoCaller autoCaller(this);
2721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2722
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 mAudioAdapter.queryInterfaceTo(audioAdapter);
2726 return S_OK;
2727}
2728
2729STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2730{
2731#ifdef VBOX_WITH_VUSB
2732 CheckComArgOutPointerValid(aUSBController);
2733
2734 AutoCaller autoCaller(this);
2735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2736
2737 clearError();
2738 MultiResult rc(S_OK);
2739
2740# ifdef VBOX_WITH_USB
2741 rc = mParent->host()->checkUSBProxyService();
2742 if (FAILED(rc)) return rc;
2743# endif
2744
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 return rc = mUSBController.queryInterfaceTo(aUSBController);
2748#else
2749 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2750 * extended error info to indicate that USB is simply not available
2751 * (w/o treating it as a failure), for example, as in OSE */
2752 NOREF(aUSBController);
2753 ReturnComNotImplemented();
2754#endif /* VBOX_WITH_VUSB */
2755}
2756
2757STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2758{
2759 CheckComArgOutPointerValid(aFilePath);
2760
2761 AutoLimitedCaller autoCaller(this);
2762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2763
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 mData->m_strConfigFileFull.cloneTo(aFilePath);
2767 return S_OK;
2768}
2769
2770STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2771{
2772 CheckComArgOutPointerValid(aModified);
2773
2774 AutoCaller autoCaller(this);
2775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2776
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 HRESULT rc = checkStateDependency(MutableStateDep);
2780 if (FAILED(rc)) return rc;
2781
2782 if (!mData->pMachineConfigFile->fileExists())
2783 // this is a new machine, and no config file exists yet:
2784 *aModified = TRUE;
2785 else
2786 *aModified = (mData->flModifications != 0);
2787
2788 return S_OK;
2789}
2790
2791STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2792{
2793 CheckComArgOutPointerValid(aSessionState);
2794
2795 AutoCaller autoCaller(this);
2796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2797
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 *aSessionState = mData->mSession.mState;
2801
2802 return S_OK;
2803}
2804
2805STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2806{
2807 CheckComArgOutPointerValid(aSessionType);
2808
2809 AutoCaller autoCaller(this);
2810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2811
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 mData->mSession.mType.cloneTo(aSessionType);
2815
2816 return S_OK;
2817}
2818
2819STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2820{
2821 CheckComArgOutPointerValid(aSessionPID);
2822
2823 AutoCaller autoCaller(this);
2824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2825
2826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 *aSessionPID = mData->mSession.mPID;
2829
2830 return S_OK;
2831}
2832
2833STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2834{
2835 CheckComArgOutPointerValid(machineState);
2836
2837 AutoCaller autoCaller(this);
2838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2839
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *machineState = mData->mMachineState;
2843
2844 return S_OK;
2845}
2846
2847STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2848{
2849 CheckComArgOutPointerValid(aLastStateChange);
2850
2851 AutoCaller autoCaller(this);
2852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2853
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2857
2858 return S_OK;
2859}
2860
2861STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2862{
2863 CheckComArgOutPointerValid(aStateFilePath);
2864
2865 AutoCaller autoCaller(this);
2866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2867
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2871
2872 return S_OK;
2873}
2874
2875STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2876{
2877 CheckComArgOutPointerValid(aLogFolder);
2878
2879 AutoCaller autoCaller(this);
2880 AssertComRCReturnRC(autoCaller.rc());
2881
2882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 Utf8Str logFolder;
2885 getLogFolder(logFolder);
2886 logFolder.cloneTo(aLogFolder);
2887
2888 return S_OK;
2889}
2890
2891STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2892{
2893 CheckComArgOutPointerValid(aCurrentSnapshot);
2894
2895 AutoCaller autoCaller(this);
2896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2897
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2901
2902 return S_OK;
2903}
2904
2905STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2906{
2907 CheckComArgOutPointerValid(aSnapshotCount);
2908
2909 AutoCaller autoCaller(this);
2910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2911
2912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2915 ? 0
2916 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2917
2918 return S_OK;
2919}
2920
2921STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2922{
2923 CheckComArgOutPointerValid(aCurrentStateModified);
2924
2925 AutoCaller autoCaller(this);
2926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2927
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 /* Note: for machines with no snapshots, we always return FALSE
2931 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2932 * reasons :) */
2933
2934 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2935 ? FALSE
2936 : mData->mCurrentStateModified;
2937
2938 return S_OK;
2939}
2940
2941STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2942{
2943 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2944
2945 AutoCaller autoCaller(this);
2946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2947
2948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2951 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2952
2953 return S_OK;
2954}
2955
2956STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2957{
2958 CheckComArgOutPointerValid(aClipboardMode);
2959
2960 AutoCaller autoCaller(this);
2961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2962
2963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2964
2965 *aClipboardMode = mHWData->mClipboardMode;
2966
2967 return S_OK;
2968}
2969
2970STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2971{
2972 HRESULT rc = S_OK;
2973
2974 AutoCaller autoCaller(this);
2975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2976
2977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2978
2979 alock.release();
2980 rc = onClipboardModeChange(aClipboardMode);
2981 alock.acquire();
2982 if (FAILED(rc)) return rc;
2983
2984 setModified(IsModified_MachineData);
2985 mHWData.backup();
2986 mHWData->mClipboardMode = aClipboardMode;
2987
2988 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2989 if (Global::IsOnline(mData->mMachineState))
2990 saveSettings(NULL);
2991
2992 return S_OK;
2993}
2994
2995STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2996{
2997 CheckComArgOutPointerValid(aDragAndDropMode);
2998
2999 AutoCaller autoCaller(this);
3000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3001
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 *aDragAndDropMode = mHWData->mDragAndDropMode;
3005
3006 return S_OK;
3007}
3008
3009STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3010{
3011 HRESULT rc = S_OK;
3012
3013 AutoCaller autoCaller(this);
3014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3015
3016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3017
3018 alock.release();
3019 rc = onDragAndDropModeChange(aDragAndDropMode);
3020 alock.acquire();
3021 if (FAILED(rc)) return rc;
3022
3023 setModified(IsModified_MachineData);
3024 mHWData.backup();
3025 mHWData->mDragAndDropMode = aDragAndDropMode;
3026
3027 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3028 if (Global::IsOnline(mData->mMachineState))
3029 saveSettings(NULL);
3030
3031 return S_OK;
3032}
3033
3034STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3035{
3036 CheckComArgOutPointerValid(aPatterns);
3037
3038 AutoCaller autoCaller(this);
3039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3040
3041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 try
3044 {
3045 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3046 }
3047 catch (...)
3048 {
3049 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3050 }
3051
3052 return S_OK;
3053}
3054
3055STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3056{
3057 AutoCaller autoCaller(this);
3058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3059
3060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3061
3062 HRESULT rc = checkStateDependency(MutableStateDep);
3063 if (FAILED(rc)) return rc;
3064
3065 setModified(IsModified_MachineData);
3066 mHWData.backup();
3067 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3068 return rc;
3069}
3070
3071STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3072{
3073 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3074
3075 AutoCaller autoCaller(this);
3076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3077
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3081 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3082
3083 return S_OK;
3084}
3085
3086STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3087{
3088 CheckComArgOutPointerValid(aEnabled);
3089
3090 AutoCaller autoCaller(this);
3091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3092
3093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 *aEnabled = mUserData->s.fTeleporterEnabled;
3096
3097 return S_OK;
3098}
3099
3100STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3101{
3102 AutoCaller autoCaller(this);
3103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3104
3105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3106
3107 /* Only allow it to be set to true when PoweredOff or Aborted.
3108 (Clearing it is always permitted.) */
3109 if ( aEnabled
3110 && mData->mRegistered
3111 && ( !isSessionMachine()
3112 || ( mData->mMachineState != MachineState_PoweredOff
3113 && mData->mMachineState != MachineState_Teleported
3114 && mData->mMachineState != MachineState_Aborted
3115 )
3116 )
3117 )
3118 return setError(VBOX_E_INVALID_VM_STATE,
3119 tr("The machine is not powered off (state is %s)"),
3120 Global::stringifyMachineState(mData->mMachineState));
3121
3122 setModified(IsModified_MachineData);
3123 mUserData.backup();
3124 mUserData->s.fTeleporterEnabled = !!aEnabled;
3125
3126 return S_OK;
3127}
3128
3129STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3130{
3131 CheckComArgOutPointerValid(aPort);
3132
3133 AutoCaller autoCaller(this);
3134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3135
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3139
3140 return S_OK;
3141}
3142
3143STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3144{
3145 if (aPort >= _64K)
3146 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3147
3148 AutoCaller autoCaller(this);
3149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3150
3151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3152
3153 HRESULT rc = checkStateDependency(MutableStateDep);
3154 if (FAILED(rc)) return rc;
3155
3156 setModified(IsModified_MachineData);
3157 mUserData.backup();
3158 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3164{
3165 CheckComArgOutPointerValid(aAddress);
3166
3167 AutoCaller autoCaller(this);
3168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3169
3170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3171
3172 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3173
3174 return S_OK;
3175}
3176
3177STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3178{
3179 AutoCaller autoCaller(this);
3180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3181
3182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 HRESULT rc = checkStateDependency(MutableStateDep);
3185 if (FAILED(rc)) return rc;
3186
3187 setModified(IsModified_MachineData);
3188 mUserData.backup();
3189 mUserData->s.strTeleporterAddress = aAddress;
3190
3191 return S_OK;
3192}
3193
3194STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3195{
3196 CheckComArgOutPointerValid(aPassword);
3197
3198 AutoCaller autoCaller(this);
3199 HRESULT hrc = autoCaller.rc();
3200 if (SUCCEEDED(hrc))
3201 {
3202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3203 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3204 }
3205
3206 return hrc;
3207}
3208
3209STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3210{
3211 /*
3212 * Hash the password first.
3213 */
3214 Utf8Str strPassword(aPassword);
3215 if (!strPassword.isEmpty())
3216 {
3217 if (VBoxIsPasswordHashed(&strPassword))
3218 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3219 VBoxHashPassword(&strPassword);
3220 }
3221
3222 /*
3223 * Do the update.
3224 */
3225 AutoCaller autoCaller(this);
3226 HRESULT hrc = autoCaller.rc();
3227 if (SUCCEEDED(hrc))
3228 {
3229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3230 hrc = checkStateDependency(MutableStateDep);
3231 if (SUCCEEDED(hrc))
3232 {
3233 setModified(IsModified_MachineData);
3234 mUserData.backup();
3235 mUserData->s.strTeleporterPassword = strPassword;
3236 }
3237 }
3238
3239 return hrc;
3240}
3241
3242STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3243{
3244 CheckComArgOutPointerValid(aState);
3245
3246 AutoCaller autoCaller(this);
3247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3248
3249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251 *aState = mUserData->s.enmFaultToleranceState;
3252 return S_OK;
3253}
3254
3255STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3256{
3257 AutoCaller autoCaller(this);
3258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3259
3260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3261
3262 /* @todo deal with running state change. */
3263 HRESULT rc = checkStateDependency(MutableStateDep);
3264 if (FAILED(rc)) return rc;
3265
3266 setModified(IsModified_MachineData);
3267 mUserData.backup();
3268 mUserData->s.enmFaultToleranceState = aState;
3269 return S_OK;
3270}
3271
3272STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3273{
3274 CheckComArgOutPointerValid(aAddress);
3275
3276 AutoCaller autoCaller(this);
3277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3278
3279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3280
3281 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3282 return S_OK;
3283}
3284
3285STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3286{
3287 AutoCaller autoCaller(this);
3288 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3289
3290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3291
3292 /* @todo deal with running state change. */
3293 HRESULT rc = checkStateDependency(MutableStateDep);
3294 if (FAILED(rc)) return rc;
3295
3296 setModified(IsModified_MachineData);
3297 mUserData.backup();
3298 mUserData->s.strFaultToleranceAddress = aAddress;
3299 return S_OK;
3300}
3301
3302STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3303{
3304 CheckComArgOutPointerValid(aPort);
3305
3306 AutoCaller autoCaller(this);
3307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3308
3309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3310
3311 *aPort = mUserData->s.uFaultTolerancePort;
3312 return S_OK;
3313}
3314
3315STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3316{
3317 AutoCaller autoCaller(this);
3318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3319
3320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3321
3322 /* @todo deal with running state change. */
3323 HRESULT rc = checkStateDependency(MutableStateDep);
3324 if (FAILED(rc)) return rc;
3325
3326 setModified(IsModified_MachineData);
3327 mUserData.backup();
3328 mUserData->s.uFaultTolerancePort = aPort;
3329 return S_OK;
3330}
3331
3332STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3333{
3334 CheckComArgOutPointerValid(aPassword);
3335
3336 AutoCaller autoCaller(this);
3337 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3338
3339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3340
3341 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3342
3343 return S_OK;
3344}
3345
3346STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3347{
3348 AutoCaller autoCaller(this);
3349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3350
3351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3352
3353 /* @todo deal with running state change. */
3354 HRESULT rc = checkStateDependency(MutableStateDep);
3355 if (FAILED(rc)) return rc;
3356
3357 setModified(IsModified_MachineData);
3358 mUserData.backup();
3359 mUserData->s.strFaultTolerancePassword = aPassword;
3360
3361 return S_OK;
3362}
3363
3364STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3365{
3366 CheckComArgOutPointerValid(aInterval);
3367
3368 AutoCaller autoCaller(this);
3369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3370
3371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3372
3373 *aInterval = mUserData->s.uFaultToleranceInterval;
3374 return S_OK;
3375}
3376
3377STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3378{
3379 AutoCaller autoCaller(this);
3380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3381
3382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3383
3384 /* @todo deal with running state change. */
3385 HRESULT rc = checkStateDependency(MutableStateDep);
3386 if (FAILED(rc)) return rc;
3387
3388 setModified(IsModified_MachineData);
3389 mUserData.backup();
3390 mUserData->s.uFaultToleranceInterval = aInterval;
3391 return S_OK;
3392}
3393
3394STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3395{
3396 CheckComArgOutPointerValid(aEnabled);
3397
3398 AutoCaller autoCaller(this);
3399 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3400
3401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3402
3403 *aEnabled = mUserData->s.fRTCUseUTC;
3404
3405 return S_OK;
3406}
3407
3408STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3409{
3410 AutoCaller autoCaller(this);
3411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3412
3413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3414
3415 /* Only allow it to be set to true when PoweredOff or Aborted.
3416 (Clearing it is always permitted.) */
3417 if ( aEnabled
3418 && mData->mRegistered
3419 && ( !isSessionMachine()
3420 || ( mData->mMachineState != MachineState_PoweredOff
3421 && mData->mMachineState != MachineState_Teleported
3422 && mData->mMachineState != MachineState_Aborted
3423 )
3424 )
3425 )
3426 return setError(VBOX_E_INVALID_VM_STATE,
3427 tr("The machine is not powered off (state is %s)"),
3428 Global::stringifyMachineState(mData->mMachineState));
3429
3430 setModified(IsModified_MachineData);
3431 mUserData.backup();
3432 mUserData->s.fRTCUseUTC = !!aEnabled;
3433
3434 return S_OK;
3435}
3436
3437STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3438{
3439 CheckComArgOutPointerValid(aEnabled);
3440
3441 AutoCaller autoCaller(this);
3442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3443
3444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3445
3446 *aEnabled = mHWData->mIOCacheEnabled;
3447
3448 return S_OK;
3449}
3450
3451STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3452{
3453 AutoCaller autoCaller(this);
3454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3455
3456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3457
3458 HRESULT rc = checkStateDependency(MutableStateDep);
3459 if (FAILED(rc)) return rc;
3460
3461 setModified(IsModified_MachineData);
3462 mHWData.backup();
3463 mHWData->mIOCacheEnabled = aEnabled;
3464
3465 return S_OK;
3466}
3467
3468STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3469{
3470 CheckComArgOutPointerValid(aIOCacheSize);
3471
3472 AutoCaller autoCaller(this);
3473 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3474
3475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3476
3477 *aIOCacheSize = mHWData->mIOCacheSize;
3478
3479 return S_OK;
3480}
3481
3482STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3483{
3484 AutoCaller autoCaller(this);
3485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3486
3487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3488
3489 HRESULT rc = checkStateDependency(MutableStateDep);
3490 if (FAILED(rc)) return rc;
3491
3492 setModified(IsModified_MachineData);
3493 mHWData.backup();
3494 mHWData->mIOCacheSize = aIOCacheSize;
3495
3496 return S_OK;
3497}
3498
3499
3500/**
3501 * @note Locks objects!
3502 */
3503STDMETHODIMP Machine::LockMachine(ISession *aSession,
3504 LockType_T lockType)
3505{
3506 CheckComArgNotNull(aSession);
3507
3508 AutoCaller autoCaller(this);
3509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3510
3511 /* check the session state */
3512 SessionState_T state;
3513 HRESULT rc = aSession->COMGETTER(State)(&state);
3514 if (FAILED(rc)) return rc;
3515
3516 if (state != SessionState_Unlocked)
3517 return setError(VBOX_E_INVALID_OBJECT_STATE,
3518 tr("The given session is busy"));
3519
3520 // get the client's IInternalSessionControl interface
3521 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3522 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3523 E_INVALIDARG);
3524
3525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3526
3527 if (!mData->mRegistered)
3528 return setError(E_UNEXPECTED,
3529 tr("The machine '%s' is not registered"),
3530 mUserData->s.strName.c_str());
3531
3532 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3533
3534 SessionState_T oldState = mData->mSession.mState;
3535 /* Hack: in case the session is closing and there is a progress object
3536 * which allows waiting for the session to be closed, take the opportunity
3537 * and do a limited wait (max. 1 second). This helps a lot when the system
3538 * is busy and thus session closing can take a little while. */
3539 if ( mData->mSession.mState == SessionState_Unlocking
3540 && mData->mSession.mProgress)
3541 {
3542 alock.release();
3543 mData->mSession.mProgress->WaitForCompletion(1000);
3544 alock.acquire();
3545 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3546 }
3547
3548 // try again now
3549 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3550 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3551 )
3552 {
3553 // OK, share the session... we are now dealing with three processes:
3554 // 1) VBoxSVC (where this code runs);
3555 // 2) process C: the caller's client process (who wants a shared session);
3556 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3557
3558 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3559 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3560 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3561 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3562 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3563
3564 /*
3565 * Release the lock before calling the client process. It's safe here
3566 * since the only thing to do after we get the lock again is to add
3567 * the remote control to the list (which doesn't directly influence
3568 * anything).
3569 */
3570 alock.release();
3571
3572 // get the console of the session holding the write lock (this is a remote call)
3573 ComPtr<IConsole> pConsoleW;
3574 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3575 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3576 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3577 if (FAILED(rc))
3578 // the failure may occur w/o any error info (from RPC), so provide one
3579 return setError(VBOX_E_VM_ERROR,
3580 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3581
3582 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3583
3584 // share the session machine and W's console with the caller's session
3585 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3586 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3587 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3588
3589 if (FAILED(rc))
3590 // the failure may occur w/o any error info (from RPC), so provide one
3591 return setError(VBOX_E_VM_ERROR,
3592 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3593 alock.acquire();
3594
3595 // need to revalidate the state after acquiring the lock again
3596 if (mData->mSession.mState != SessionState_Locked)
3597 {
3598 pSessionControl->Uninitialize();
3599 return setError(VBOX_E_INVALID_SESSION_STATE,
3600 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3601 mUserData->s.strName.c_str());
3602 }
3603
3604 // add the caller's session to the list
3605 mData->mSession.mRemoteControls.push_back(pSessionControl);
3606 }
3607 else if ( mData->mSession.mState == SessionState_Locked
3608 || mData->mSession.mState == SessionState_Unlocking
3609 )
3610 {
3611 // sharing not permitted, or machine still unlocking:
3612 return setError(VBOX_E_INVALID_OBJECT_STATE,
3613 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3614 mUserData->s.strName.c_str());
3615 }
3616 else
3617 {
3618 // machine is not locked: then write-lock the machine (create the session machine)
3619
3620 // must not be busy
3621 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3622
3623 // get the caller's session PID
3624 RTPROCESS pid = NIL_RTPROCESS;
3625 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3626 pSessionControl->GetPID((ULONG*)&pid);
3627 Assert(pid != NIL_RTPROCESS);
3628
3629 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3630
3631 if (fLaunchingVMProcess)
3632 {
3633 // this machine is awaiting for a spawning session to be opened:
3634 // then the calling process must be the one that got started by
3635 // LaunchVMProcess()
3636
3637 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3638 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3639
3640 if (mData->mSession.mPID != pid)
3641 return setError(E_ACCESSDENIED,
3642 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3643 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3644 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3645 }
3646
3647 // create the mutable SessionMachine from the current machine
3648 ComObjPtr<SessionMachine> sessionMachine;
3649 sessionMachine.createObject();
3650 rc = sessionMachine->init(this);
3651 AssertComRC(rc);
3652
3653 /* NOTE: doing return from this function after this point but
3654 * before the end is forbidden since it may call SessionMachine::uninit()
3655 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3656 * lock while still holding the Machine lock in alock so that a deadlock
3657 * is possible due to the wrong lock order. */
3658
3659 if (SUCCEEDED(rc))
3660 {
3661 /*
3662 * Set the session state to Spawning to protect against subsequent
3663 * attempts to open a session and to unregister the machine after
3664 * we release the lock.
3665 */
3666 SessionState_T origState = mData->mSession.mState;
3667 mData->mSession.mState = SessionState_Spawning;
3668
3669 /*
3670 * Release the lock before calling the client process -- it will call
3671 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3672 * because the state is Spawning, so that LaunchVMProcess() and
3673 * LockMachine() calls will fail. This method, called before we
3674 * acquire the lock again, will fail because of the wrong PID.
3675 *
3676 * Note that mData->mSession.mRemoteControls accessed outside
3677 * the lock may not be modified when state is Spawning, so it's safe.
3678 */
3679 alock.release();
3680
3681 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3682 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3683 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3684
3685 /* The failure may occur w/o any error info (from RPC), so provide one */
3686 if (FAILED(rc))
3687 setError(VBOX_E_VM_ERROR,
3688 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3689
3690 if ( SUCCEEDED(rc)
3691 && fLaunchingVMProcess
3692 )
3693 {
3694 /* complete the remote session initialization */
3695
3696 /* get the console from the direct session */
3697 ComPtr<IConsole> console;
3698 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3699 ComAssertComRC(rc);
3700
3701 if (SUCCEEDED(rc) && !console)
3702 {
3703 ComAssert(!!console);
3704 rc = E_FAIL;
3705 }
3706
3707 /* assign machine & console to the remote session */
3708 if (SUCCEEDED(rc))
3709 {
3710 /*
3711 * after LaunchVMProcess(), the first and the only
3712 * entry in remoteControls is that remote session
3713 */
3714 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3715 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3716 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3717
3718 /* The failure may occur w/o any error info (from RPC), so provide one */
3719 if (FAILED(rc))
3720 setError(VBOX_E_VM_ERROR,
3721 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3722 }
3723
3724 if (FAILED(rc))
3725 pSessionControl->Uninitialize();
3726 }
3727
3728 /* acquire the lock again */
3729 alock.acquire();
3730
3731 /* Restore the session state */
3732 mData->mSession.mState = origState;
3733 }
3734
3735 // finalize spawning anyway (this is why we don't return on errors above)
3736 if (fLaunchingVMProcess)
3737 {
3738 /* Note that the progress object is finalized later */
3739 /** @todo Consider checking mData->mSession.mProgress for cancellation
3740 * around here. */
3741
3742 /* We don't reset mSession.mPID here because it is necessary for
3743 * SessionMachine::uninit() to reap the child process later. */
3744
3745 if (FAILED(rc))
3746 {
3747 /* Close the remote session, remove the remote control from the list
3748 * and reset session state to Closed (@note keep the code in sync
3749 * with the relevant part in openSession()). */
3750
3751 Assert(mData->mSession.mRemoteControls.size() == 1);
3752 if (mData->mSession.mRemoteControls.size() == 1)
3753 {
3754 ErrorInfoKeeper eik;
3755 mData->mSession.mRemoteControls.front()->Uninitialize();
3756 }
3757
3758 mData->mSession.mRemoteControls.clear();
3759 mData->mSession.mState = SessionState_Unlocked;
3760 }
3761 }
3762 else
3763 {
3764 /* memorize PID of the directly opened session */
3765 if (SUCCEEDED(rc))
3766 mData->mSession.mPID = pid;
3767 }
3768
3769 if (SUCCEEDED(rc))
3770 {
3771 /* memorize the direct session control and cache IUnknown for it */
3772 mData->mSession.mDirectControl = pSessionControl;
3773 mData->mSession.mState = SessionState_Locked;
3774 /* associate the SessionMachine with this Machine */
3775 mData->mSession.mMachine = sessionMachine;
3776
3777 /* request an IUnknown pointer early from the remote party for later
3778 * identity checks (it will be internally cached within mDirectControl
3779 * at least on XPCOM) */
3780 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3781 NOREF(unk);
3782 }
3783
3784 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3785 * would break the lock order */
3786 alock.release();
3787
3788 /* uninitialize the created session machine on failure */
3789 if (FAILED(rc))
3790 sessionMachine->uninit();
3791
3792 }
3793
3794 if (SUCCEEDED(rc))
3795 {
3796 /*
3797 * tell the client watcher thread to update the set of
3798 * machines that have open sessions
3799 */
3800 mParent->updateClientWatcher();
3801
3802 if (oldState != SessionState_Locked)
3803 /* fire an event */
3804 mParent->onSessionStateChange(getId(), SessionState_Locked);
3805 }
3806
3807 return rc;
3808}
3809
3810/**
3811 * @note Locks objects!
3812 */
3813STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3814 IN_BSTR aFrontend,
3815 IN_BSTR aEnvironment,
3816 IProgress **aProgress)
3817{
3818 CheckComArgStr(aFrontend);
3819 Utf8Str strFrontend(aFrontend);
3820 Utf8Str strEnvironment(aEnvironment);
3821 /* "emergencystop" doesn't need the session, so skip the checks/interface
3822 * retrieval. This code doesn't quite fit in here, but introducing a
3823 * special API method would be even more effort, and would require explicit
3824 * support by every API client. It's better to hide the feature a bit. */
3825 if (strFrontend != "emergencystop")
3826 CheckComArgNotNull(aSession);
3827 CheckComArgOutPointerValid(aProgress);
3828
3829 AutoCaller autoCaller(this);
3830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3831
3832 HRESULT rc = S_OK;
3833 if (strFrontend.isEmpty())
3834 {
3835 Bstr bstrFrontend;
3836 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3837 if (FAILED(rc))
3838 return rc;
3839 strFrontend = bstrFrontend;
3840 if (strFrontend.isEmpty())
3841 {
3842 ComPtr<ISystemProperties> systemProperties;
3843 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3844 if (FAILED(rc))
3845 return rc;
3846 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3847 if (FAILED(rc))
3848 return rc;
3849 strFrontend = bstrFrontend;
3850 }
3851 /* paranoia - emergencystop is not a valid default */
3852 if (strFrontend == "emergencystop")
3853 strFrontend = Utf8Str::Empty;
3854 }
3855 /* default frontend: Qt GUI */
3856 if (strFrontend.isEmpty())
3857 strFrontend = "GUI/Qt";
3858
3859 if (strFrontend != "emergencystop")
3860 {
3861 /* check the session state */
3862 SessionState_T state;
3863 rc = aSession->COMGETTER(State)(&state);
3864 if (FAILED(rc))
3865 return rc;
3866
3867 if (state != SessionState_Unlocked)
3868 return setError(VBOX_E_INVALID_OBJECT_STATE,
3869 tr("The given session is busy"));
3870
3871 /* get the IInternalSessionControl interface */
3872 ComPtr<IInternalSessionControl> control(aSession);
3873 ComAssertMsgRet(!control.isNull(),
3874 ("No IInternalSessionControl interface"),
3875 E_INVALIDARG);
3876
3877 /* get the teleporter enable state for the progress object init. */
3878 BOOL fTeleporterEnabled;
3879 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3880 if (FAILED(rc))
3881 return rc;
3882
3883 /* create a progress object */
3884 ComObjPtr<ProgressProxy> progress;
3885 progress.createObject();
3886 rc = progress->init(mParent,
3887 static_cast<IMachine*>(this),
3888 Bstr(tr("Starting VM")).raw(),
3889 TRUE /* aCancelable */,
3890 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3891 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3892 2 /* uFirstOperationWeight */,
3893 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3894
3895 if (SUCCEEDED(rc))
3896 {
3897 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3898 if (SUCCEEDED(rc))
3899 {
3900 progress.queryInterfaceTo(aProgress);
3901
3902 /* signal the client watcher thread */
3903 mParent->updateClientWatcher();
3904
3905 /* fire an event */
3906 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3907 }
3908 }
3909 }
3910 else
3911 {
3912 /* no progress object - either instant success or failure */
3913 *aProgress = NULL;
3914
3915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3916
3917 if (mData->mSession.mState != SessionState_Locked)
3918 return setError(VBOX_E_INVALID_OBJECT_STATE,
3919 tr("The machine '%s' is not locked by a session"),
3920 mUserData->s.strName.c_str());
3921
3922 /* must have a VM process associated - do not kill normal API clients
3923 * with an open session */
3924 if (!Global::IsOnline(mData->mMachineState))
3925 return setError(VBOX_E_INVALID_OBJECT_STATE,
3926 tr("The machine '%s' does not have a VM process"),
3927 mUserData->s.strName.c_str());
3928
3929 /* forcibly terminate the VM process */
3930 if (mData->mSession.mPID != NIL_RTPROCESS)
3931 RTProcTerminate(mData->mSession.mPID);
3932
3933 /* signal the client watcher thread, as most likely the client has
3934 * been terminated */
3935 mParent->updateClientWatcher();
3936 }
3937
3938 return rc;
3939}
3940
3941STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3942{
3943 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3944 return setError(E_INVALIDARG,
3945 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3946 aPosition, SchemaDefs::MaxBootPosition);
3947
3948 if (aDevice == DeviceType_USB)
3949 return setError(E_NOTIMPL,
3950 tr("Booting from USB device is currently not supported"));
3951
3952 AutoCaller autoCaller(this);
3953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3954
3955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3956
3957 HRESULT rc = checkStateDependency(MutableStateDep);
3958 if (FAILED(rc)) return rc;
3959
3960 setModified(IsModified_MachineData);
3961 mHWData.backup();
3962 mHWData->mBootOrder[aPosition - 1] = aDevice;
3963
3964 return S_OK;
3965}
3966
3967STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3968{
3969 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3970 return setError(E_INVALIDARG,
3971 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3972 aPosition, SchemaDefs::MaxBootPosition);
3973
3974 AutoCaller autoCaller(this);
3975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3976
3977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3978
3979 *aDevice = mHWData->mBootOrder[aPosition - 1];
3980
3981 return S_OK;
3982}
3983
3984STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3985 LONG aControllerPort,
3986 LONG aDevice,
3987 DeviceType_T aType,
3988 IMedium *aMedium)
3989{
3990 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3991 aControllerName, aControllerPort, aDevice, aType, aMedium));
3992
3993 CheckComArgStrNotEmptyOrNull(aControllerName);
3994
3995 AutoCaller autoCaller(this);
3996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3997
3998 // request the host lock first, since might be calling Host methods for getting host drives;
3999 // next, protect the media tree all the while we're in here, as well as our member variables
4000 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4001 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4002
4003 HRESULT rc = checkStateDependency(MutableStateDep);
4004 if (FAILED(rc)) return rc;
4005
4006 /// @todo NEWMEDIA implicit machine registration
4007 if (!mData->mRegistered)
4008 return setError(VBOX_E_INVALID_OBJECT_STATE,
4009 tr("Cannot attach storage devices to an unregistered machine"));
4010
4011 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4012
4013 /* Check for an existing controller. */
4014 ComObjPtr<StorageController> ctl;
4015 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4016 if (FAILED(rc)) return rc;
4017
4018 StorageControllerType_T ctrlType;
4019 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4020 if (FAILED(rc))
4021 return setError(E_FAIL,
4022 tr("Could not get type of controller '%ls'"),
4023 aControllerName);
4024
4025 bool fSilent = false;
4026 Utf8Str strReconfig;
4027
4028 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4029 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4030 if (FAILED(rc))
4031 return rc;
4032 if ( mData->mMachineState == MachineState_Paused
4033 && strReconfig == "1")
4034 fSilent = true;
4035
4036 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4037 bool fHotplug = false;
4038 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4039 fHotplug = true;
4040
4041 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4042 return setError(VBOX_E_INVALID_VM_STATE,
4043 tr("Controller '%ls' does not support hotplugging"),
4044 aControllerName);
4045
4046 // check that the port and device are not out of range
4047 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4048 if (FAILED(rc)) return rc;
4049
4050 /* check if the device slot is already busy */
4051 MediumAttachment *pAttachTemp;
4052 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4053 aControllerName,
4054 aControllerPort,
4055 aDevice)))
4056 {
4057 Medium *pMedium = pAttachTemp->getMedium();
4058 if (pMedium)
4059 {
4060 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4061 return setError(VBOX_E_OBJECT_IN_USE,
4062 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4063 pMedium->getLocationFull().c_str(),
4064 aControllerPort,
4065 aDevice,
4066 aControllerName);
4067 }
4068 else
4069 return setError(VBOX_E_OBJECT_IN_USE,
4070 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4071 aControllerPort, aDevice, aControllerName);
4072 }
4073
4074 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4075 if (aMedium && medium.isNull())
4076 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4077
4078 AutoCaller mediumCaller(medium);
4079 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4080
4081 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4082
4083 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4084 && !medium.isNull()
4085 )
4086 return setError(VBOX_E_OBJECT_IN_USE,
4087 tr("Medium '%s' is already attached to this virtual machine"),
4088 medium->getLocationFull().c_str());
4089
4090 if (!medium.isNull())
4091 {
4092 MediumType_T mtype = medium->getType();
4093 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4094 // For DVDs it's not written to the config file, so needs no global config
4095 // version bump. For floppies it's a new attribute "type", which is ignored
4096 // by older VirtualBox version, so needs no global config version bump either.
4097 // For hard disks this type is not accepted.
4098 if (mtype == MediumType_MultiAttach)
4099 {
4100 // This type is new with VirtualBox 4.0 and therefore requires settings
4101 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4102 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4103 // two reasons: The medium type is a property of the media registry tree, which
4104 // can reside in the global config file (for pre-4.0 media); we would therefore
4105 // possibly need to bump the global config version. We don't want to do that though
4106 // because that might make downgrading to pre-4.0 impossible.
4107 // As a result, we can only use these two new types if the medium is NOT in the
4108 // global registry:
4109 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4110 if ( medium->isInRegistry(uuidGlobalRegistry)
4111 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4112 )
4113 return setError(VBOX_E_INVALID_OBJECT_STATE,
4114 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4115 "to machines that were created with VirtualBox 4.0 or later"),
4116 medium->getLocationFull().c_str());
4117 }
4118 }
4119
4120 bool fIndirect = false;
4121 if (!medium.isNull())
4122 fIndirect = medium->isReadOnly();
4123 bool associate = true;
4124
4125 do
4126 {
4127 if ( aType == DeviceType_HardDisk
4128 && mMediaData.isBackedUp())
4129 {
4130 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4131
4132 /* check if the medium was attached to the VM before we started
4133 * changing attachments in which case the attachment just needs to
4134 * be restored */
4135 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4136 {
4137 AssertReturn(!fIndirect, E_FAIL);
4138
4139 /* see if it's the same bus/channel/device */
4140 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4141 {
4142 /* the simplest case: restore the whole attachment
4143 * and return, nothing else to do */
4144 mMediaData->mAttachments.push_back(pAttachTemp);
4145
4146 /* Reattach the medium to the VM. */
4147 if (fHotplug || fSilent)
4148 {
4149 mediumLock.release();
4150 treeLock.release();
4151 alock.release();
4152
4153 MediumLockList *pMediumLockList(new MediumLockList());
4154
4155 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4156 true /* fMediumLockWrite */,
4157 NULL,
4158 *pMediumLockList);
4159 alock.acquire();
4160 if (FAILED(rc))
4161 delete pMediumLockList;
4162 else
4163 {
4164 mData->mSession.mLockedMedia.Unlock();
4165 alock.release();
4166 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4167 mData->mSession.mLockedMedia.Lock();
4168 alock.acquire();
4169 }
4170 alock.release();
4171
4172 if (SUCCEEDED(rc))
4173 {
4174 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4175 /* Remove lock list in case of error. */
4176 if (FAILED(rc))
4177 {
4178 mData->mSession.mLockedMedia.Unlock();
4179 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4180 mData->mSession.mLockedMedia.Lock();
4181 }
4182 }
4183 }
4184
4185 return S_OK;
4186 }
4187
4188 /* bus/channel/device differ; we need a new attachment object,
4189 * but don't try to associate it again */
4190 associate = false;
4191 break;
4192 }
4193 }
4194
4195 /* go further only if the attachment is to be indirect */
4196 if (!fIndirect)
4197 break;
4198
4199 /* perform the so called smart attachment logic for indirect
4200 * attachments. Note that smart attachment is only applicable to base
4201 * hard disks. */
4202
4203 if (medium->getParent().isNull())
4204 {
4205 /* first, investigate the backup copy of the current hard disk
4206 * attachments to make it possible to re-attach existing diffs to
4207 * another device slot w/o losing their contents */
4208 if (mMediaData.isBackedUp())
4209 {
4210 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4211
4212 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4213 uint32_t foundLevel = 0;
4214
4215 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4216 it != oldAtts.end();
4217 ++it)
4218 {
4219 uint32_t level = 0;
4220 MediumAttachment *pAttach = *it;
4221 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4222 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4223 if (pMedium.isNull())
4224 continue;
4225
4226 if (pMedium->getBase(&level) == medium)
4227 {
4228 /* skip the hard disk if its currently attached (we
4229 * cannot attach the same hard disk twice) */
4230 if (findAttachment(mMediaData->mAttachments,
4231 pMedium))
4232 continue;
4233
4234 /* matched device, channel and bus (i.e. attached to the
4235 * same place) will win and immediately stop the search;
4236 * otherwise the attachment that has the youngest
4237 * descendant of medium will be used
4238 */
4239 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4240 {
4241 /* the simplest case: restore the whole attachment
4242 * and return, nothing else to do */
4243 mMediaData->mAttachments.push_back(*it);
4244
4245 /* Reattach the medium to the VM. */
4246 if (fHotplug || fSilent)
4247 {
4248 mediumLock.release();
4249 treeLock.release();
4250 alock.release();
4251
4252 MediumLockList *pMediumLockList(new MediumLockList());
4253
4254 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4255 true /* fMediumLockWrite */,
4256 NULL,
4257 *pMediumLockList);
4258 alock.acquire();
4259 if (FAILED(rc))
4260 delete pMediumLockList;
4261 else
4262 {
4263 mData->mSession.mLockedMedia.Unlock();
4264 alock.release();
4265 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4266 mData->mSession.mLockedMedia.Lock();
4267 alock.acquire();
4268 }
4269 alock.release();
4270
4271 if (SUCCEEDED(rc))
4272 {
4273 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4274 /* Remove lock list in case of error. */
4275 if (FAILED(rc))
4276 {
4277 mData->mSession.mLockedMedia.Unlock();
4278 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4279 mData->mSession.mLockedMedia.Lock();
4280 }
4281 }
4282 }
4283
4284 return S_OK;
4285 }
4286 else if ( foundIt == oldAtts.end()
4287 || level > foundLevel /* prefer younger */
4288 )
4289 {
4290 foundIt = it;
4291 foundLevel = level;
4292 }
4293 }
4294 }
4295
4296 if (foundIt != oldAtts.end())
4297 {
4298 /* use the previously attached hard disk */
4299 medium = (*foundIt)->getMedium();
4300 mediumCaller.attach(medium);
4301 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4302 mediumLock.attach(medium);
4303 /* not implicit, doesn't require association with this VM */
4304 fIndirect = false;
4305 associate = false;
4306 /* go right to the MediumAttachment creation */
4307 break;
4308 }
4309 }
4310
4311 /* must give up the medium lock and medium tree lock as below we
4312 * go over snapshots, which needs a lock with higher lock order. */
4313 mediumLock.release();
4314 treeLock.release();
4315
4316 /* then, search through snapshots for the best diff in the given
4317 * hard disk's chain to base the new diff on */
4318
4319 ComObjPtr<Medium> base;
4320 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4321 while (snap)
4322 {
4323 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4324
4325 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4326
4327 MediumAttachment *pAttachFound = NULL;
4328 uint32_t foundLevel = 0;
4329
4330 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4331 it != snapAtts.end();
4332 ++it)
4333 {
4334 MediumAttachment *pAttach = *it;
4335 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4336 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4337 if (pMedium.isNull())
4338 continue;
4339
4340 uint32_t level = 0;
4341 if (pMedium->getBase(&level) == medium)
4342 {
4343 /* matched device, channel and bus (i.e. attached to the
4344 * same place) will win and immediately stop the search;
4345 * otherwise the attachment that has the youngest
4346 * descendant of medium will be used
4347 */
4348 if ( pAttach->getDevice() == aDevice
4349 && pAttach->getPort() == aControllerPort
4350 && pAttach->getControllerName() == aControllerName
4351 )
4352 {
4353 pAttachFound = pAttach;
4354 break;
4355 }
4356 else if ( !pAttachFound
4357 || level > foundLevel /* prefer younger */
4358 )
4359 {
4360 pAttachFound = pAttach;
4361 foundLevel = level;
4362 }
4363 }
4364 }
4365
4366 if (pAttachFound)
4367 {
4368 base = pAttachFound->getMedium();
4369 break;
4370 }
4371
4372 snap = snap->getParent();
4373 }
4374
4375 /* re-lock medium tree and the medium, as we need it below */
4376 treeLock.acquire();
4377 mediumLock.acquire();
4378
4379 /* found a suitable diff, use it as a base */
4380 if (!base.isNull())
4381 {
4382 medium = base;
4383 mediumCaller.attach(medium);
4384 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4385 mediumLock.attach(medium);
4386 }
4387 }
4388
4389 Utf8Str strFullSnapshotFolder;
4390 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4391
4392 ComObjPtr<Medium> diff;
4393 diff.createObject();
4394 // store this diff in the same registry as the parent
4395 Guid uuidRegistryParent;
4396 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4397 {
4398 // parent image has no registry: this can happen if we're attaching a new immutable
4399 // image that has not yet been attached (medium then points to the base and we're
4400 // creating the diff image for the immutable, and the parent is not yet registered);
4401 // put the parent in the machine registry then
4402 mediumLock.release();
4403 treeLock.release();
4404 alock.release();
4405 addMediumToRegistry(medium);
4406 alock.acquire();
4407 treeLock.acquire();
4408 mediumLock.acquire();
4409 medium->getFirstRegistryMachineId(uuidRegistryParent);
4410 }
4411 rc = diff->init(mParent,
4412 medium->getPreferredDiffFormat(),
4413 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4414 uuidRegistryParent);
4415 if (FAILED(rc)) return rc;
4416
4417 /* Apply the normal locking logic to the entire chain. */
4418 MediumLockList *pMediumLockList(new MediumLockList());
4419 mediumLock.release();
4420 treeLock.release();
4421 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4422 true /* fMediumLockWrite */,
4423 medium,
4424 *pMediumLockList);
4425 treeLock.acquire();
4426 mediumLock.acquire();
4427 if (SUCCEEDED(rc))
4428 {
4429 mediumLock.release();
4430 treeLock.release();
4431 rc = pMediumLockList->Lock();
4432 treeLock.acquire();
4433 mediumLock.acquire();
4434 if (FAILED(rc))
4435 setError(rc,
4436 tr("Could not lock medium when creating diff '%s'"),
4437 diff->getLocationFull().c_str());
4438 else
4439 {
4440 /* will release the lock before the potentially lengthy
4441 * operation, so protect with the special state */
4442 MachineState_T oldState = mData->mMachineState;
4443 setMachineState(MachineState_SettingUp);
4444
4445 mediumLock.release();
4446 treeLock.release();
4447 alock.release();
4448
4449 rc = medium->createDiffStorage(diff,
4450 MediumVariant_Standard,
4451 pMediumLockList,
4452 NULL /* aProgress */,
4453 true /* aWait */);
4454
4455 alock.acquire();
4456 treeLock.acquire();
4457 mediumLock.acquire();
4458
4459 setMachineState(oldState);
4460 }
4461 }
4462
4463 /* Unlock the media and free the associated memory. */
4464 delete pMediumLockList;
4465
4466 if (FAILED(rc)) return rc;
4467
4468 /* use the created diff for the actual attachment */
4469 medium = diff;
4470 mediumCaller.attach(medium);
4471 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4472 mediumLock.attach(medium);
4473 }
4474 while (0);
4475
4476 ComObjPtr<MediumAttachment> attachment;
4477 attachment.createObject();
4478 rc = attachment->init(this,
4479 medium,
4480 aControllerName,
4481 aControllerPort,
4482 aDevice,
4483 aType,
4484 fIndirect,
4485 false /* fPassthrough */,
4486 false /* fTempEject */,
4487 false /* fNonRotational */,
4488 false /* fDiscard */,
4489 Utf8Str::Empty);
4490 if (FAILED(rc)) return rc;
4491
4492 if (associate && !medium.isNull())
4493 {
4494 // as the last step, associate the medium to the VM
4495 rc = medium->addBackReference(mData->mUuid);
4496 // here we can fail because of Deleting, or being in process of creating a Diff
4497 if (FAILED(rc)) return rc;
4498
4499 mediumLock.release();
4500 treeLock.release();
4501 alock.release();
4502 addMediumToRegistry(medium);
4503 alock.acquire();
4504 treeLock.acquire();
4505 mediumLock.acquire();
4506 }
4507
4508 /* success: finally remember the attachment */
4509 setModified(IsModified_Storage);
4510 mMediaData.backup();
4511 mMediaData->mAttachments.push_back(attachment);
4512
4513 mediumLock.release();
4514 treeLock.release();
4515 alock.release();
4516
4517 if (fHotplug || fSilent)
4518 {
4519 if (!medium.isNull())
4520 {
4521 MediumLockList *pMediumLockList(new MediumLockList());
4522
4523 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4524 true /* fMediumLockWrite */,
4525 NULL,
4526 *pMediumLockList);
4527 alock.acquire();
4528 if (FAILED(rc))
4529 delete pMediumLockList;
4530 else
4531 {
4532 mData->mSession.mLockedMedia.Unlock();
4533 alock.release();
4534 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4535 mData->mSession.mLockedMedia.Lock();
4536 alock.acquire();
4537 }
4538 alock.release();
4539 }
4540
4541 if (SUCCEEDED(rc))
4542 {
4543 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4544 /* Remove lock list in case of error. */
4545 if (FAILED(rc))
4546 {
4547 mData->mSession.mLockedMedia.Unlock();
4548 mData->mSession.mLockedMedia.Remove(attachment);
4549 mData->mSession.mLockedMedia.Lock();
4550 }
4551 }
4552 }
4553
4554 mParent->saveModifiedRegistries();
4555
4556 return rc;
4557}
4558
4559STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4560 LONG aDevice)
4561{
4562 CheckComArgStrNotEmptyOrNull(aControllerName);
4563
4564 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4565 aControllerName, aControllerPort, aDevice));
4566
4567 AutoCaller autoCaller(this);
4568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4569
4570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4571
4572 HRESULT rc = checkStateDependency(MutableStateDep);
4573 if (FAILED(rc)) return rc;
4574
4575 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4576
4577 /* Check for an existing controller. */
4578 ComObjPtr<StorageController> ctl;
4579 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4580 if (FAILED(rc)) return rc;
4581
4582 StorageControllerType_T ctrlType;
4583 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4584 if (FAILED(rc))
4585 return setError(E_FAIL,
4586 tr("Could not get type of controller '%ls'"),
4587 aControllerName);
4588
4589 bool fSilent = false;
4590 Utf8Str strReconfig;
4591
4592 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4593 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4594 if (FAILED(rc))
4595 return rc;
4596 if ( mData->mMachineState == MachineState_Paused
4597 && strReconfig == "1")
4598 fSilent = true;
4599
4600 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4601 bool fHotplug = false;
4602 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4603 fHotplug = true;
4604
4605 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4606 return setError(VBOX_E_INVALID_VM_STATE,
4607 tr("Controller '%ls' does not support hotplugging"),
4608 aControllerName);
4609
4610 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4611 aControllerName,
4612 aControllerPort,
4613 aDevice);
4614 if (!pAttach)
4615 return setError(VBOX_E_OBJECT_NOT_FOUND,
4616 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4617 aDevice, aControllerPort, aControllerName);
4618
4619 /*
4620 * The VM has to detach the device before we delete any implicit diffs.
4621 * If this fails we can roll back without loosing data.
4622 */
4623 if (fHotplug || fSilent)
4624 {
4625 alock.release();
4626 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4627 alock.acquire();
4628 }
4629 if (FAILED(rc)) return rc;
4630
4631 /* If we are here everything went well and we can delete the implicit now. */
4632 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4633
4634 alock.release();
4635
4636 mParent->saveModifiedRegistries();
4637
4638 return rc;
4639}
4640
4641STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4642 LONG aDevice, BOOL aPassthrough)
4643{
4644 CheckComArgStrNotEmptyOrNull(aControllerName);
4645
4646 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4647 aControllerName, aControllerPort, aDevice, aPassthrough));
4648
4649 AutoCaller autoCaller(this);
4650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4651
4652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4653
4654 HRESULT rc = checkStateDependency(MutableStateDep);
4655 if (FAILED(rc)) return rc;
4656
4657 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4658
4659 if (Global::IsOnlineOrTransient(mData->mMachineState))
4660 return setError(VBOX_E_INVALID_VM_STATE,
4661 tr("Invalid machine state: %s"),
4662 Global::stringifyMachineState(mData->mMachineState));
4663
4664 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4665 aControllerName,
4666 aControllerPort,
4667 aDevice);
4668 if (!pAttach)
4669 return setError(VBOX_E_OBJECT_NOT_FOUND,
4670 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4671 aDevice, aControllerPort, aControllerName);
4672
4673
4674 setModified(IsModified_Storage);
4675 mMediaData.backup();
4676
4677 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4678
4679 if (pAttach->getType() != DeviceType_DVD)
4680 return setError(E_INVALIDARG,
4681 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4682 aDevice, aControllerPort, aControllerName);
4683 pAttach->updatePassthrough(!!aPassthrough);
4684
4685 return S_OK;
4686}
4687
4688STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4689 LONG aDevice, BOOL aTemporaryEject)
4690{
4691 CheckComArgStrNotEmptyOrNull(aControllerName);
4692
4693 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4694 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4695
4696 AutoCaller autoCaller(this);
4697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4698
4699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4700
4701 HRESULT rc = checkStateDependency(MutableStateDep);
4702 if (FAILED(rc)) return rc;
4703
4704 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4705 aControllerName,
4706 aControllerPort,
4707 aDevice);
4708 if (!pAttach)
4709 return setError(VBOX_E_OBJECT_NOT_FOUND,
4710 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4711 aDevice, aControllerPort, aControllerName);
4712
4713
4714 setModified(IsModified_Storage);
4715 mMediaData.backup();
4716
4717 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4718
4719 if (pAttach->getType() != DeviceType_DVD)
4720 return setError(E_INVALIDARG,
4721 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4722 aDevice, aControllerPort, aControllerName);
4723 pAttach->updateTempEject(!!aTemporaryEject);
4724
4725 return S_OK;
4726}
4727
4728STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4729 LONG aDevice, BOOL aNonRotational)
4730{
4731 CheckComArgStrNotEmptyOrNull(aControllerName);
4732
4733 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4734 aControllerName, aControllerPort, aDevice, aNonRotational));
4735
4736 AutoCaller autoCaller(this);
4737 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4738
4739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4740
4741 HRESULT rc = checkStateDependency(MutableStateDep);
4742 if (FAILED(rc)) return rc;
4743
4744 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4745
4746 if (Global::IsOnlineOrTransient(mData->mMachineState))
4747 return setError(VBOX_E_INVALID_VM_STATE,
4748 tr("Invalid machine state: %s"),
4749 Global::stringifyMachineState(mData->mMachineState));
4750
4751 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4752 aControllerName,
4753 aControllerPort,
4754 aDevice);
4755 if (!pAttach)
4756 return setError(VBOX_E_OBJECT_NOT_FOUND,
4757 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4758 aDevice, aControllerPort, aControllerName);
4759
4760
4761 setModified(IsModified_Storage);
4762 mMediaData.backup();
4763
4764 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4765
4766 if (pAttach->getType() != DeviceType_HardDisk)
4767 return setError(E_INVALIDARG,
4768 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"),
4769 aDevice, aControllerPort, aControllerName);
4770 pAttach->updateNonRotational(!!aNonRotational);
4771
4772 return S_OK;
4773}
4774
4775STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4776 LONG aDevice, BOOL aDiscard)
4777{
4778 CheckComArgStrNotEmptyOrNull(aControllerName);
4779
4780 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4781 aControllerName, aControllerPort, aDevice, aDiscard));
4782
4783 AutoCaller autoCaller(this);
4784 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4785
4786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4787
4788 HRESULT rc = checkStateDependency(MutableStateDep);
4789 if (FAILED(rc)) return rc;
4790
4791 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4792
4793 if (Global::IsOnlineOrTransient(mData->mMachineState))
4794 return setError(VBOX_E_INVALID_VM_STATE,
4795 tr("Invalid machine state: %s"),
4796 Global::stringifyMachineState(mData->mMachineState));
4797
4798 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4799 aControllerName,
4800 aControllerPort,
4801 aDevice);
4802 if (!pAttach)
4803 return setError(VBOX_E_OBJECT_NOT_FOUND,
4804 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4805 aDevice, aControllerPort, aControllerName);
4806
4807
4808 setModified(IsModified_Storage);
4809 mMediaData.backup();
4810
4811 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4812
4813 if (pAttach->getType() != DeviceType_HardDisk)
4814 return setError(E_INVALIDARG,
4815 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"),
4816 aDevice, aControllerPort, aControllerName);
4817 pAttach->updateDiscard(!!aDiscard);
4818
4819 return S_OK;
4820}
4821
4822STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4823 LONG aDevice)
4824{
4825 int rc = S_OK;
4826 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4827 aControllerName, aControllerPort, aDevice));
4828
4829 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4830
4831 return rc;
4832}
4833
4834STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4835 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4836{
4837 CheckComArgStrNotEmptyOrNull(aControllerName);
4838
4839 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4840 aControllerName, aControllerPort, aDevice));
4841
4842 AutoCaller autoCaller(this);
4843 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4844
4845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4846
4847 HRESULT rc = checkStateDependency(MutableStateDep);
4848 if (FAILED(rc)) return rc;
4849
4850 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4851
4852 if (Global::IsOnlineOrTransient(mData->mMachineState))
4853 return setError(VBOX_E_INVALID_VM_STATE,
4854 tr("Invalid machine state: %s"),
4855 Global::stringifyMachineState(mData->mMachineState));
4856
4857 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4858 aControllerName,
4859 aControllerPort,
4860 aDevice);
4861 if (!pAttach)
4862 return setError(VBOX_E_OBJECT_NOT_FOUND,
4863 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4864 aDevice, aControllerPort, aControllerName);
4865
4866
4867 setModified(IsModified_Storage);
4868 mMediaData.backup();
4869
4870 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4871 if (aBandwidthGroup && group.isNull())
4872 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4873
4874 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4875
4876 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4877 if (strBandwidthGroupOld.isNotEmpty())
4878 {
4879 /* Get the bandwidth group object and release it - this must not fail. */
4880 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4881 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4882 Assert(SUCCEEDED(rc));
4883
4884 pBandwidthGroupOld->release();
4885 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4886 }
4887
4888 if (!group.isNull())
4889 {
4890 group->reference();
4891 pAttach->updateBandwidthGroup(group->getName());
4892 }
4893
4894 return S_OK;
4895}
4896
4897STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4898 LONG aControllerPort,
4899 LONG aDevice,
4900 DeviceType_T aType)
4901{
4902 HRESULT rc = S_OK;
4903
4904 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4905 aControllerName, aControllerPort, aDevice, aType));
4906
4907 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4908
4909 return rc;
4910}
4911
4912
4913
4914STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4915 LONG aControllerPort,
4916 LONG aDevice,
4917 BOOL aForce)
4918{
4919 int rc = S_OK;
4920 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4921 aControllerName, aControllerPort, aForce));
4922
4923 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4924
4925 return rc;
4926}
4927
4928STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4929 LONG aControllerPort,
4930 LONG aDevice,
4931 IMedium *aMedium,
4932 BOOL aForce)
4933{
4934 int rc = S_OK;
4935 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4936 aControllerName, aControllerPort, aDevice, aForce));
4937
4938 CheckComArgStrNotEmptyOrNull(aControllerName);
4939
4940 AutoCaller autoCaller(this);
4941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4942
4943 // request the host lock first, since might be calling Host methods for getting host drives;
4944 // next, protect the media tree all the while we're in here, as well as our member variables
4945 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4946 this->lockHandle(),
4947 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4948
4949 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4950 aControllerName,
4951 aControllerPort,
4952 aDevice);
4953 if (pAttach.isNull())
4954 return setError(VBOX_E_OBJECT_NOT_FOUND,
4955 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4956 aDevice, aControllerPort, aControllerName);
4957
4958 /* Remember previously mounted medium. The medium before taking the
4959 * backup is not necessarily the same thing. */
4960 ComObjPtr<Medium> oldmedium;
4961 oldmedium = pAttach->getMedium();
4962
4963 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4964 if (aMedium && pMedium.isNull())
4965 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4966
4967 AutoCaller mediumCaller(pMedium);
4968 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4969
4970 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4971 if (pMedium)
4972 {
4973 DeviceType_T mediumType = pAttach->getType();
4974 switch (mediumType)
4975 {
4976 case DeviceType_DVD:
4977 case DeviceType_Floppy:
4978 break;
4979
4980 default:
4981 return setError(VBOX_E_INVALID_OBJECT_STATE,
4982 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4983 aControllerPort,
4984 aDevice,
4985 aControllerName);
4986 }
4987 }
4988
4989 setModified(IsModified_Storage);
4990 mMediaData.backup();
4991
4992 {
4993 // The backup operation makes the pAttach reference point to the
4994 // old settings. Re-get the correct reference.
4995 pAttach = findAttachment(mMediaData->mAttachments,
4996 aControllerName,
4997 aControllerPort,
4998 aDevice);
4999 if (!oldmedium.isNull())
5000 oldmedium->removeBackReference(mData->mUuid);
5001 if (!pMedium.isNull())
5002 {
5003 pMedium->addBackReference(mData->mUuid);
5004
5005 mediumLock.release();
5006 multiLock.release();
5007 addMediumToRegistry(pMedium);
5008 multiLock.acquire();
5009 mediumLock.acquire();
5010 }
5011
5012 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5013 pAttach->updateMedium(pMedium);
5014 }
5015
5016 setModified(IsModified_Storage);
5017
5018 mediumLock.release();
5019 multiLock.release();
5020 rc = onMediumChange(pAttach, aForce);
5021 multiLock.acquire();
5022 mediumLock.acquire();
5023
5024 /* On error roll back this change only. */
5025 if (FAILED(rc))
5026 {
5027 if (!pMedium.isNull())
5028 pMedium->removeBackReference(mData->mUuid);
5029 pAttach = findAttachment(mMediaData->mAttachments,
5030 aControllerName,
5031 aControllerPort,
5032 aDevice);
5033 /* If the attachment is gone in the meantime, bail out. */
5034 if (pAttach.isNull())
5035 return rc;
5036 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5037 if (!oldmedium.isNull())
5038 oldmedium->addBackReference(mData->mUuid);
5039 pAttach->updateMedium(oldmedium);
5040 }
5041
5042 mediumLock.release();
5043 multiLock.release();
5044
5045 mParent->saveModifiedRegistries();
5046
5047 return rc;
5048}
5049
5050STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5051 LONG aControllerPort,
5052 LONG aDevice,
5053 IMedium **aMedium)
5054{
5055 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5056 aControllerName, aControllerPort, aDevice));
5057
5058 CheckComArgStrNotEmptyOrNull(aControllerName);
5059 CheckComArgOutPointerValid(aMedium);
5060
5061 AutoCaller autoCaller(this);
5062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5063
5064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5065
5066 *aMedium = NULL;
5067
5068 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5069 aControllerName,
5070 aControllerPort,
5071 aDevice);
5072 if (pAttach.isNull())
5073 return setError(VBOX_E_OBJECT_NOT_FOUND,
5074 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5075 aDevice, aControllerPort, aControllerName);
5076
5077 pAttach->getMedium().queryInterfaceTo(aMedium);
5078
5079 return S_OK;
5080}
5081
5082STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5083{
5084 CheckComArgOutPointerValid(port);
5085 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5086
5087 AutoCaller autoCaller(this);
5088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5089
5090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5091
5092 mSerialPorts[slot].queryInterfaceTo(port);
5093
5094 return S_OK;
5095}
5096
5097STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5098{
5099 CheckComArgOutPointerValid(port);
5100 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5101
5102 AutoCaller autoCaller(this);
5103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5104
5105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5106
5107 mParallelPorts[slot].queryInterfaceTo(port);
5108
5109 return S_OK;
5110}
5111
5112STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5113{
5114 CheckComArgOutPointerValid(adapter);
5115 /* Do not assert if slot is out of range, just return the advertised
5116 status. testdriver/vbox.py triggers this in logVmInfo. */
5117 if (slot >= mNetworkAdapters.size())
5118 return setError(E_INVALIDARG,
5119 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5120 slot, mNetworkAdapters.size());
5121
5122 AutoCaller autoCaller(this);
5123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5124
5125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5126
5127 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5128
5129 return S_OK;
5130}
5131
5132STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5133{
5134 CheckComArgOutSafeArrayPointerValid(aKeys);
5135
5136 AutoCaller autoCaller(this);
5137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5138
5139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5140
5141 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5142 int i = 0;
5143 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5144 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5145 ++it, ++i)
5146 {
5147 const Utf8Str &strKey = it->first;
5148 strKey.cloneTo(&saKeys[i]);
5149 }
5150 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5151
5152 return S_OK;
5153 }
5154
5155 /**
5156 * @note Locks this object for reading.
5157 */
5158STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5159 BSTR *aValue)
5160{
5161 CheckComArgStrNotEmptyOrNull(aKey);
5162 CheckComArgOutPointerValid(aValue);
5163
5164 AutoCaller autoCaller(this);
5165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5166
5167 /* start with nothing found */
5168 Bstr bstrResult("");
5169
5170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5171
5172 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5173 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5174 // found:
5175 bstrResult = it->second; // source is a Utf8Str
5176
5177 /* return the result to caller (may be empty) */
5178 bstrResult.cloneTo(aValue);
5179
5180 return S_OK;
5181}
5182
5183 /**
5184 * @note Locks mParent for writing + this object for writing.
5185 */
5186STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5187{
5188 CheckComArgStrNotEmptyOrNull(aKey);
5189
5190 AutoCaller autoCaller(this);
5191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5192
5193 Utf8Str strKey(aKey);
5194 Utf8Str strValue(aValue);
5195 Utf8Str strOldValue; // empty
5196
5197 // locking note: we only hold the read lock briefly to look up the old value,
5198 // then release it and call the onExtraCanChange callbacks. There is a small
5199 // chance of a race insofar as the callback might be called twice if two callers
5200 // change the same key at the same time, but that's a much better solution
5201 // than the deadlock we had here before. The actual changing of the extradata
5202 // is then performed under the write lock and race-free.
5203
5204 // look up the old value first; if nothing has changed then we need not do anything
5205 {
5206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5207 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5208 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5209 strOldValue = it->second;
5210 }
5211
5212 bool fChanged;
5213 if ((fChanged = (strOldValue != strValue)))
5214 {
5215 // ask for permission from all listeners outside the locks;
5216 // onExtraDataCanChange() only briefly requests the VirtualBox
5217 // lock to copy the list of callbacks to invoke
5218 Bstr error;
5219 Bstr bstrValue(aValue);
5220
5221 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5222 {
5223 const char *sep = error.isEmpty() ? "" : ": ";
5224 CBSTR err = error.raw();
5225 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5226 sep, err));
5227 return setError(E_ACCESSDENIED,
5228 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5229 aKey,
5230 bstrValue.raw(),
5231 sep,
5232 err);
5233 }
5234
5235 // data is changing and change not vetoed: then write it out under the lock
5236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5237
5238 if (isSnapshotMachine())
5239 {
5240 HRESULT rc = checkStateDependency(MutableStateDep);
5241 if (FAILED(rc)) return rc;
5242 }
5243
5244 if (strValue.isEmpty())
5245 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5246 else
5247 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5248 // creates a new key if needed
5249
5250 bool fNeedsGlobalSaveSettings = false;
5251 saveSettings(&fNeedsGlobalSaveSettings);
5252
5253 if (fNeedsGlobalSaveSettings)
5254 {
5255 // save the global settings; for that we should hold only the VirtualBox lock
5256 alock.release();
5257 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5258 mParent->saveSettings();
5259 }
5260 }
5261
5262 // fire notification outside the lock
5263 if (fChanged)
5264 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5265
5266 return S_OK;
5267}
5268
5269STDMETHODIMP Machine::SaveSettings()
5270{
5271 AutoCaller autoCaller(this);
5272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5273
5274 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5275
5276 /* when there was auto-conversion, we want to save the file even if
5277 * the VM is saved */
5278 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5279 if (FAILED(rc)) return rc;
5280
5281 /* the settings file path may never be null */
5282 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5283
5284 /* save all VM data excluding snapshots */
5285 bool fNeedsGlobalSaveSettings = false;
5286 rc = saveSettings(&fNeedsGlobalSaveSettings);
5287 mlock.release();
5288
5289 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5290 {
5291 // save the global settings; for that we should hold only the VirtualBox lock
5292 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5293 rc = mParent->saveSettings();
5294 }
5295
5296 return rc;
5297}
5298
5299STDMETHODIMP Machine::DiscardSettings()
5300{
5301 AutoCaller autoCaller(this);
5302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5303
5304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5305
5306 HRESULT rc = checkStateDependency(MutableStateDep);
5307 if (FAILED(rc)) return rc;
5308
5309 /*
5310 * during this rollback, the session will be notified if data has
5311 * been actually changed
5312 */
5313 rollback(true /* aNotify */);
5314
5315 return S_OK;
5316}
5317
5318/** @note Locks objects! */
5319STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5320 ComSafeArrayOut(IMedium*, aMedia))
5321{
5322 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5323 AutoLimitedCaller autoCaller(this);
5324 AssertComRCReturnRC(autoCaller.rc());
5325
5326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5327
5328 Guid id(getId());
5329
5330 if (mData->mSession.mState != SessionState_Unlocked)
5331 return setError(VBOX_E_INVALID_OBJECT_STATE,
5332 tr("Cannot unregister the machine '%s' while it is locked"),
5333 mUserData->s.strName.c_str());
5334
5335 // wait for state dependents to drop to zero
5336 ensureNoStateDependencies();
5337
5338 if (!mData->mAccessible)
5339 {
5340 // inaccessible maschines can only be unregistered; uninitialize ourselves
5341 // here because currently there may be no unregistered that are inaccessible
5342 // (this state combination is not supported). Note releasing the caller and
5343 // leaving the lock before calling uninit()
5344 alock.release();
5345 autoCaller.release();
5346
5347 uninit();
5348
5349 mParent->unregisterMachine(this, id);
5350 // calls VirtualBox::saveSettings()
5351
5352 return S_OK;
5353 }
5354
5355 HRESULT rc = S_OK;
5356
5357 // discard saved state
5358 if (mData->mMachineState == MachineState_Saved)
5359 {
5360 // add the saved state file to the list of files the caller should delete
5361 Assert(!mSSData->strStateFilePath.isEmpty());
5362 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5363
5364 mSSData->strStateFilePath.setNull();
5365
5366 // unconditionally set the machine state to powered off, we now
5367 // know no session has locked the machine
5368 mData->mMachineState = MachineState_PoweredOff;
5369 }
5370
5371 size_t cSnapshots = 0;
5372 if (mData->mFirstSnapshot)
5373 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5374 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5375 // fail now before we start detaching media
5376 return setError(VBOX_E_INVALID_OBJECT_STATE,
5377 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5378 mUserData->s.strName.c_str(), cSnapshots);
5379
5380 // This list collects the medium objects from all medium attachments
5381 // which we will detach from the machine and its snapshots, in a specific
5382 // order which allows for closing all media without getting "media in use"
5383 // errors, simply by going through the list from the front to the back:
5384 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5385 // and must be closed before the parent media from the snapshots, or closing the parents
5386 // will fail because they still have children);
5387 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5388 // the root ("first") snapshot of the machine.
5389 MediaList llMedia;
5390
5391 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5392 && mMediaData->mAttachments.size()
5393 )
5394 {
5395 // we have media attachments: detach them all and add the Medium objects to our list
5396 if (cleanupMode != CleanupMode_UnregisterOnly)
5397 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5398 else
5399 return setError(VBOX_E_INVALID_OBJECT_STATE,
5400 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5401 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5402 }
5403
5404 if (cSnapshots)
5405 {
5406 // autoCleanup must be true here, or we would have failed above
5407
5408 // add the media from the medium attachments of the snapshots to llMedia
5409 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5410 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5411 // into the children first
5412
5413 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5414 MachineState_T oldState = mData->mMachineState;
5415 mData->mMachineState = MachineState_DeletingSnapshot;
5416
5417 // make a copy of the first snapshot so the refcount does not drop to 0
5418 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5419 // because of the AutoCaller voodoo)
5420 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5421
5422 // GO!
5423 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5424
5425 mData->mMachineState = oldState;
5426 }
5427
5428 if (FAILED(rc))
5429 {
5430 rollbackMedia();
5431 return rc;
5432 }
5433
5434 // commit all the media changes made above
5435 commitMedia();
5436
5437 mData->mRegistered = false;
5438
5439 // machine lock no longer needed
5440 alock.release();
5441
5442 // return media to caller
5443 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5444 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5445
5446 mParent->unregisterMachine(this, id);
5447 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5448
5449 return S_OK;
5450}
5451
5452struct Machine::DeleteTask
5453{
5454 ComObjPtr<Machine> pMachine;
5455 RTCList<ComPtr<IMedium> > llMediums;
5456 StringsList llFilesToDelete;
5457 ComObjPtr<Progress> pProgress;
5458};
5459
5460STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5461{
5462 LogFlowFuncEnter();
5463
5464 AutoCaller autoCaller(this);
5465 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5466
5467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5468
5469 HRESULT rc = checkStateDependency(MutableStateDep);
5470 if (FAILED(rc)) return rc;
5471
5472 if (mData->mRegistered)
5473 return setError(VBOX_E_INVALID_VM_STATE,
5474 tr("Cannot delete settings of a registered machine"));
5475
5476 DeleteTask *pTask = new DeleteTask;
5477 pTask->pMachine = this;
5478 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5479
5480 // collect files to delete
5481 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5482
5483 for (size_t i = 0; i < sfaMedia.size(); ++i)
5484 {
5485 IMedium *pIMedium(sfaMedia[i]);
5486 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5487 if (pMedium.isNull())
5488 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5489 SafeArray<BSTR> ids;
5490 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5491 if (FAILED(rc)) return rc;
5492 /* At this point the medium should not have any back references
5493 * anymore. If it has it is attached to another VM and *must* not
5494 * deleted. */
5495 if (ids.size() < 1)
5496 pTask->llMediums.append(pMedium);
5497 }
5498 if (mData->pMachineConfigFile->fileExists())
5499 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5500
5501 pTask->pProgress.createObject();
5502 pTask->pProgress->init(getVirtualBox(),
5503 static_cast<IMachine*>(this) /* aInitiator */,
5504 Bstr(tr("Deleting files")).raw(),
5505 true /* fCancellable */,
5506 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5507 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5508
5509 int vrc = RTThreadCreate(NULL,
5510 Machine::deleteThread,
5511 (void*)pTask,
5512 0,
5513 RTTHREADTYPE_MAIN_WORKER,
5514 0,
5515 "MachineDelete");
5516
5517 pTask->pProgress.queryInterfaceTo(aProgress);
5518
5519 if (RT_FAILURE(vrc))
5520 {
5521 delete pTask;
5522 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5523 }
5524
5525 LogFlowFuncLeave();
5526
5527 return S_OK;
5528}
5529
5530/**
5531 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5532 * calls Machine::deleteTaskWorker() on the actual machine object.
5533 * @param Thread
5534 * @param pvUser
5535 * @return
5536 */
5537/*static*/
5538DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5539{
5540 LogFlowFuncEnter();
5541
5542 DeleteTask *pTask = (DeleteTask*)pvUser;
5543 Assert(pTask);
5544 Assert(pTask->pMachine);
5545 Assert(pTask->pProgress);
5546
5547 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5548 pTask->pProgress->notifyComplete(rc);
5549
5550 delete pTask;
5551
5552 LogFlowFuncLeave();
5553
5554 NOREF(Thread);
5555
5556 return VINF_SUCCESS;
5557}
5558
5559/**
5560 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5561 * @param task
5562 * @return
5563 */
5564HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5565{
5566 AutoCaller autoCaller(this);
5567 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5568
5569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5570
5571 HRESULT rc = S_OK;
5572
5573 try
5574 {
5575 ULONG uLogHistoryCount = 3;
5576 ComPtr<ISystemProperties> systemProperties;
5577 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5578 if (FAILED(rc)) throw rc;
5579
5580 if (!systemProperties.isNull())
5581 {
5582 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5583 if (FAILED(rc)) throw rc;
5584 }
5585
5586 MachineState_T oldState = mData->mMachineState;
5587 setMachineState(MachineState_SettingUp);
5588 alock.release();
5589 for (size_t i = 0; i < task.llMediums.size(); ++i)
5590 {
5591 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5592 {
5593 AutoCaller mac(pMedium);
5594 if (FAILED(mac.rc())) throw mac.rc();
5595 Utf8Str strLocation = pMedium->getLocationFull();
5596 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5597 if (FAILED(rc)) throw rc;
5598 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5599 }
5600 ComPtr<IProgress> pProgress2;
5601 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5602 if (FAILED(rc)) throw rc;
5603 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5604 if (FAILED(rc)) throw rc;
5605 /* Check the result of the asynchrony process. */
5606 LONG iRc;
5607 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5608 if (FAILED(rc)) throw rc;
5609 /* If the thread of the progress object has an error, then
5610 * retrieve the error info from there, or it'll be lost. */
5611 if (FAILED(iRc))
5612 throw setError(ProgressErrorInfo(pProgress2));
5613 }
5614 setMachineState(oldState);
5615 alock.acquire();
5616
5617 // delete the files pushed on the task list by Machine::Delete()
5618 // (this includes saved states of the machine and snapshots and
5619 // medium storage files from the IMedium list passed in, and the
5620 // machine XML file)
5621 StringsList::const_iterator it = task.llFilesToDelete.begin();
5622 while (it != task.llFilesToDelete.end())
5623 {
5624 const Utf8Str &strFile = *it;
5625 LogFunc(("Deleting file %s\n", strFile.c_str()));
5626 int vrc = RTFileDelete(strFile.c_str());
5627 if (RT_FAILURE(vrc))
5628 throw setError(VBOX_E_IPRT_ERROR,
5629 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5630
5631 ++it;
5632 if (it == task.llFilesToDelete.end())
5633 {
5634 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5635 if (FAILED(rc)) throw rc;
5636 break;
5637 }
5638
5639 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5640 if (FAILED(rc)) throw rc;
5641 }
5642
5643 /* delete the settings only when the file actually exists */
5644 if (mData->pMachineConfigFile->fileExists())
5645 {
5646 /* Delete any backup or uncommitted XML files. Ignore failures.
5647 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5648 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5649 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5650 RTFileDelete(otherXml.c_str());
5651 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5652 RTFileDelete(otherXml.c_str());
5653
5654 /* delete the Logs folder, nothing important should be left
5655 * there (we don't check for errors because the user might have
5656 * some private files there that we don't want to delete) */
5657 Utf8Str logFolder;
5658 getLogFolder(logFolder);
5659 Assert(logFolder.length());
5660 if (RTDirExists(logFolder.c_str()))
5661 {
5662 /* Delete all VBox.log[.N] files from the Logs folder
5663 * (this must be in sync with the rotation logic in
5664 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5665 * files that may have been created by the GUI. */
5666 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5667 logFolder.c_str(), RTPATH_DELIMITER);
5668 RTFileDelete(log.c_str());
5669 log = Utf8StrFmt("%s%cVBox.png",
5670 logFolder.c_str(), RTPATH_DELIMITER);
5671 RTFileDelete(log.c_str());
5672 for (int i = uLogHistoryCount; i > 0; i--)
5673 {
5674 log = Utf8StrFmt("%s%cVBox.log.%d",
5675 logFolder.c_str(), RTPATH_DELIMITER, i);
5676 RTFileDelete(log.c_str());
5677 log = Utf8StrFmt("%s%cVBox.png.%d",
5678 logFolder.c_str(), RTPATH_DELIMITER, i);
5679 RTFileDelete(log.c_str());
5680 }
5681
5682 RTDirRemove(logFolder.c_str());
5683 }
5684
5685 /* delete the Snapshots folder, nothing important should be left
5686 * there (we don't check for errors because the user might have
5687 * some private files there that we don't want to delete) */
5688 Utf8Str strFullSnapshotFolder;
5689 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5690 Assert(!strFullSnapshotFolder.isEmpty());
5691 if (RTDirExists(strFullSnapshotFolder.c_str()))
5692 RTDirRemove(strFullSnapshotFolder.c_str());
5693
5694 // delete the directory that contains the settings file, but only
5695 // if it matches the VM name
5696 Utf8Str settingsDir;
5697 if (isInOwnDir(&settingsDir))
5698 RTDirRemove(settingsDir.c_str());
5699 }
5700
5701 alock.release();
5702
5703 mParent->saveModifiedRegistries();
5704 }
5705 catch (HRESULT aRC) { rc = aRC; }
5706
5707 return rc;
5708}
5709
5710STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5711{
5712 CheckComArgOutPointerValid(aSnapshot);
5713
5714 AutoCaller autoCaller(this);
5715 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5716
5717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5718
5719 ComObjPtr<Snapshot> pSnapshot;
5720 HRESULT rc;
5721
5722 if (!aNameOrId || !*aNameOrId)
5723 // null case (caller wants root snapshot): findSnapshotById() handles this
5724 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5725 else
5726 {
5727 Guid uuid(aNameOrId);
5728 if (uuid.isValid())
5729 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5730 else
5731 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5732 }
5733 pSnapshot.queryInterfaceTo(aSnapshot);
5734
5735 return rc;
5736}
5737
5738STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5739{
5740 CheckComArgStrNotEmptyOrNull(aName);
5741 CheckComArgStrNotEmptyOrNull(aHostPath);
5742
5743 AutoCaller autoCaller(this);
5744 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5745
5746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5747
5748 HRESULT rc = checkStateDependency(MutableStateDep);
5749 if (FAILED(rc)) return rc;
5750
5751 Utf8Str strName(aName);
5752
5753 ComObjPtr<SharedFolder> sharedFolder;
5754 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5755 if (SUCCEEDED(rc))
5756 return setError(VBOX_E_OBJECT_IN_USE,
5757 tr("Shared folder named '%s' already exists"),
5758 strName.c_str());
5759
5760 sharedFolder.createObject();
5761 rc = sharedFolder->init(getMachine(),
5762 strName,
5763 aHostPath,
5764 !!aWritable,
5765 !!aAutoMount,
5766 true /* fFailOnError */);
5767 if (FAILED(rc)) return rc;
5768
5769 setModified(IsModified_SharedFolders);
5770 mHWData.backup();
5771 mHWData->mSharedFolders.push_back(sharedFolder);
5772
5773 /* inform the direct session if any */
5774 alock.release();
5775 onSharedFolderChange();
5776
5777 return S_OK;
5778}
5779
5780STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5781{
5782 CheckComArgStrNotEmptyOrNull(aName);
5783
5784 AutoCaller autoCaller(this);
5785 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5786
5787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5788
5789 HRESULT rc = checkStateDependency(MutableStateDep);
5790 if (FAILED(rc)) return rc;
5791
5792 ComObjPtr<SharedFolder> sharedFolder;
5793 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5794 if (FAILED(rc)) return rc;
5795
5796 setModified(IsModified_SharedFolders);
5797 mHWData.backup();
5798 mHWData->mSharedFolders.remove(sharedFolder);
5799
5800 /* inform the direct session if any */
5801 alock.release();
5802 onSharedFolderChange();
5803
5804 return S_OK;
5805}
5806
5807STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5808{
5809 CheckComArgOutPointerValid(aCanShow);
5810
5811 /* start with No */
5812 *aCanShow = FALSE;
5813
5814 AutoCaller autoCaller(this);
5815 AssertComRCReturnRC(autoCaller.rc());
5816
5817 ComPtr<IInternalSessionControl> directControl;
5818 {
5819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5820
5821 if (mData->mSession.mState != SessionState_Locked)
5822 return setError(VBOX_E_INVALID_VM_STATE,
5823 tr("Machine is not locked for session (session state: %s)"),
5824 Global::stringifySessionState(mData->mSession.mState));
5825
5826 directControl = mData->mSession.mDirectControl;
5827 }
5828
5829 /* ignore calls made after #OnSessionEnd() is called */
5830 if (!directControl)
5831 return S_OK;
5832
5833 LONG64 dummy;
5834 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5835}
5836
5837STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5838{
5839 CheckComArgOutPointerValid(aWinId);
5840
5841 AutoCaller autoCaller(this);
5842 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5843
5844 ComPtr<IInternalSessionControl> directControl;
5845 {
5846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5847
5848 if (mData->mSession.mState != SessionState_Locked)
5849 return setError(E_FAIL,
5850 tr("Machine is not locked for session (session state: %s)"),
5851 Global::stringifySessionState(mData->mSession.mState));
5852
5853 directControl = mData->mSession.mDirectControl;
5854 }
5855
5856 /* ignore calls made after #OnSessionEnd() is called */
5857 if (!directControl)
5858 return S_OK;
5859
5860 BOOL dummy;
5861 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5862}
5863
5864#ifdef VBOX_WITH_GUEST_PROPS
5865/**
5866 * Look up a guest property in VBoxSVC's internal structures.
5867 */
5868HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5869 BSTR *aValue,
5870 LONG64 *aTimestamp,
5871 BSTR *aFlags) const
5872{
5873 using namespace guestProp;
5874
5875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5876 Utf8Str strName(aName);
5877 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5878
5879 if (it != mHWData->mGuestProperties.end())
5880 {
5881 char szFlags[MAX_FLAGS_LEN + 1];
5882 it->second.strValue.cloneTo(aValue);
5883 *aTimestamp = it->second.mTimestamp;
5884 writeFlags(it->second.mFlags, szFlags);
5885 Bstr(szFlags).cloneTo(aFlags);
5886 }
5887
5888 return S_OK;
5889}
5890
5891/**
5892 * Query the VM that a guest property belongs to for the property.
5893 * @returns E_ACCESSDENIED if the VM process is not available or not
5894 * currently handling queries and the lookup should then be done in
5895 * VBoxSVC.
5896 */
5897HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5898 BSTR *aValue,
5899 LONG64 *aTimestamp,
5900 BSTR *aFlags) const
5901{
5902 HRESULT rc;
5903 ComPtr<IInternalSessionControl> directControl;
5904 directControl = mData->mSession.mDirectControl;
5905
5906 /* fail if we were called after #OnSessionEnd() is called. This is a
5907 * silly race condition. */
5908
5909 if (!directControl)
5910 rc = E_ACCESSDENIED;
5911 else
5912 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5913 false /* isSetter */,
5914 aValue, aTimestamp, aFlags);
5915 return rc;
5916}
5917#endif // VBOX_WITH_GUEST_PROPS
5918
5919STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5920 BSTR *aValue,
5921 LONG64 *aTimestamp,
5922 BSTR *aFlags)
5923{
5924#ifndef VBOX_WITH_GUEST_PROPS
5925 ReturnComNotImplemented();
5926#else // VBOX_WITH_GUEST_PROPS
5927 CheckComArgStrNotEmptyOrNull(aName);
5928 CheckComArgOutPointerValid(aValue);
5929 CheckComArgOutPointerValid(aTimestamp);
5930 CheckComArgOutPointerValid(aFlags);
5931
5932 AutoCaller autoCaller(this);
5933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5934
5935 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5936 if (rc == E_ACCESSDENIED)
5937 /* The VM is not running or the service is not (yet) accessible */
5938 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5939 return rc;
5940#endif // VBOX_WITH_GUEST_PROPS
5941}
5942
5943STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5944{
5945 LONG64 dummyTimestamp;
5946 Bstr dummyFlags;
5947 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5948}
5949
5950STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5951{
5952 Bstr dummyValue;
5953 Bstr dummyFlags;
5954 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5955}
5956
5957#ifdef VBOX_WITH_GUEST_PROPS
5958/**
5959 * Set a guest property in VBoxSVC's internal structures.
5960 */
5961HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5962 IN_BSTR aFlags)
5963{
5964 using namespace guestProp;
5965
5966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5967 HRESULT rc = S_OK;
5968
5969 rc = checkStateDependency(MutableStateDep);
5970 if (FAILED(rc)) return rc;
5971
5972 try
5973 {
5974 Utf8Str utf8Name(aName);
5975 Utf8Str utf8Flags(aFlags);
5976 uint32_t fFlags = NILFLAG;
5977 if ( aFlags != NULL
5978 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
5979 return setError(E_INVALIDARG,
5980 tr("Invalid guest property flag values: '%ls'"),
5981 aFlags);
5982
5983 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
5984 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
5985 if (it == mHWData->mGuestProperties.end())
5986 {
5987 if (!fDelete)
5988 {
5989 setModified(IsModified_MachineData);
5990 mHWData.backupEx();
5991
5992 RTTIMESPEC time;
5993 HWData::GuestProperty prop;
5994 prop.strValue = aValue;
5995 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5996 prop.mFlags = fFlags;
5997 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5998 }
5999 }
6000 else
6001 {
6002 if (it->second.mFlags & (RDONLYHOST))
6003 {
6004 rc = setError(E_ACCESSDENIED,
6005 tr("The property '%ls' cannot be changed by the host"),
6006 aName);
6007 }
6008 else
6009 {
6010 setModified(IsModified_MachineData);
6011 mHWData.backupEx();
6012
6013 /* The backupEx() operation invalidates our iterator,
6014 * so get a new one. */
6015 it = mHWData->mGuestProperties.find(utf8Name);
6016 Assert(it != mHWData->mGuestProperties.end());
6017
6018 if (!fDelete)
6019 {
6020 RTTIMESPEC time;
6021 it->second.strValue = aValue;
6022 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6023 it->second.mFlags = fFlags;
6024 }
6025 else
6026 mHWData->mGuestProperties.erase(it);
6027 }
6028 }
6029
6030 if ( SUCCEEDED(rc)
6031 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6032 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6033 RTSTR_MAX,
6034 utf8Name.c_str(),
6035 RTSTR_MAX,
6036 NULL)
6037 )
6038 )
6039 {
6040 alock.release();
6041
6042 mParent->onGuestPropertyChange(mData->mUuid, aName,
6043 aValue ? aValue : Bstr("").raw(),
6044 aFlags ? aFlags : Bstr("").raw());
6045 }
6046 }
6047 catch (std::bad_alloc &)
6048 {
6049 rc = E_OUTOFMEMORY;
6050 }
6051
6052 return rc;
6053}
6054
6055/**
6056 * Set a property on the VM that that property belongs to.
6057 * @returns E_ACCESSDENIED if the VM process is not available or not
6058 * currently handling queries and the setting should then be done in
6059 * VBoxSVC.
6060 */
6061HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6062 IN_BSTR aFlags)
6063{
6064 HRESULT rc;
6065
6066 try
6067 {
6068 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6069
6070 BSTR dummy = NULL; /* will not be changed (setter) */
6071 LONG64 dummy64;
6072 if (!directControl)
6073 rc = E_ACCESSDENIED;
6074 else
6075 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6076 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6077 true /* isSetter */,
6078 &dummy, &dummy64, &dummy);
6079 }
6080 catch (std::bad_alloc &)
6081 {
6082 rc = E_OUTOFMEMORY;
6083 }
6084
6085 return rc;
6086}
6087#endif // VBOX_WITH_GUEST_PROPS
6088
6089STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6090 IN_BSTR aFlags)
6091{
6092#ifndef VBOX_WITH_GUEST_PROPS
6093 ReturnComNotImplemented();
6094#else // VBOX_WITH_GUEST_PROPS
6095 CheckComArgStrNotEmptyOrNull(aName);
6096 CheckComArgMaybeNull(aFlags);
6097 CheckComArgMaybeNull(aValue);
6098
6099 AutoCaller autoCaller(this);
6100 if (FAILED(autoCaller.rc()))
6101 return autoCaller.rc();
6102
6103 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6104 if (rc == E_ACCESSDENIED)
6105 /* The VM is not running or the service is not (yet) accessible */
6106 rc = setGuestPropertyToService(aName, aValue, aFlags);
6107 return rc;
6108#endif // VBOX_WITH_GUEST_PROPS
6109}
6110
6111STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6112{
6113 return SetGuestProperty(aName, aValue, NULL);
6114}
6115
6116STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6117{
6118 return SetGuestProperty(aName, NULL, NULL);
6119}
6120
6121#ifdef VBOX_WITH_GUEST_PROPS
6122/**
6123 * Enumerate the guest properties in VBoxSVC's internal structures.
6124 */
6125HRESULT Machine::enumerateGuestPropertiesInService
6126 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6127 ComSafeArrayOut(BSTR, aValues),
6128 ComSafeArrayOut(LONG64, aTimestamps),
6129 ComSafeArrayOut(BSTR, aFlags))
6130{
6131 using namespace guestProp;
6132
6133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6134 Utf8Str strPatterns(aPatterns);
6135
6136 HWData::GuestPropertyMap propMap;
6137
6138 /*
6139 * Look for matching patterns and build up a list.
6140 */
6141 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6142 while (it != mHWData->mGuestProperties.end())
6143 {
6144 if ( strPatterns.isEmpty()
6145 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6146 RTSTR_MAX,
6147 it->first.c_str(),
6148 RTSTR_MAX,
6149 NULL)
6150 )
6151 {
6152 propMap.insert(*it);
6153 }
6154
6155 it++;
6156 }
6157
6158 alock.release();
6159
6160 /*
6161 * And build up the arrays for returning the property information.
6162 */
6163 size_t cEntries = propMap.size();
6164 SafeArray<BSTR> names(cEntries);
6165 SafeArray<BSTR> values(cEntries);
6166 SafeArray<LONG64> timestamps(cEntries);
6167 SafeArray<BSTR> flags(cEntries);
6168 size_t iProp = 0;
6169
6170 it = propMap.begin();
6171 while (it != propMap.end())
6172 {
6173 char szFlags[MAX_FLAGS_LEN + 1];
6174 it->first.cloneTo(&names[iProp]);
6175 it->second.strValue.cloneTo(&values[iProp]);
6176 timestamps[iProp] = it->second.mTimestamp;
6177 writeFlags(it->second.mFlags, szFlags);
6178 Bstr(szFlags).cloneTo(&flags[iProp++]);
6179 it++;
6180 }
6181 names.detachTo(ComSafeArrayOutArg(aNames));
6182 values.detachTo(ComSafeArrayOutArg(aValues));
6183 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6184 flags.detachTo(ComSafeArrayOutArg(aFlags));
6185 return S_OK;
6186}
6187
6188/**
6189 * Enumerate the properties managed by a VM.
6190 * @returns E_ACCESSDENIED if the VM process is not available or not
6191 * currently handling queries and the setting should then be done in
6192 * VBoxSVC.
6193 */
6194HRESULT Machine::enumerateGuestPropertiesOnVM
6195 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6196 ComSafeArrayOut(BSTR, aValues),
6197 ComSafeArrayOut(LONG64, aTimestamps),
6198 ComSafeArrayOut(BSTR, aFlags))
6199{
6200 HRESULT rc;
6201 ComPtr<IInternalSessionControl> directControl;
6202 directControl = mData->mSession.mDirectControl;
6203
6204 if (!directControl)
6205 rc = E_ACCESSDENIED;
6206 else
6207 rc = directControl->EnumerateGuestProperties
6208 (aPatterns, ComSafeArrayOutArg(aNames),
6209 ComSafeArrayOutArg(aValues),
6210 ComSafeArrayOutArg(aTimestamps),
6211 ComSafeArrayOutArg(aFlags));
6212 return rc;
6213}
6214#endif // VBOX_WITH_GUEST_PROPS
6215
6216STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6217 ComSafeArrayOut(BSTR, aNames),
6218 ComSafeArrayOut(BSTR, aValues),
6219 ComSafeArrayOut(LONG64, aTimestamps),
6220 ComSafeArrayOut(BSTR, aFlags))
6221{
6222#ifndef VBOX_WITH_GUEST_PROPS
6223 ReturnComNotImplemented();
6224#else // VBOX_WITH_GUEST_PROPS
6225 CheckComArgMaybeNull(aPatterns);
6226 CheckComArgOutSafeArrayPointerValid(aNames);
6227 CheckComArgOutSafeArrayPointerValid(aValues);
6228 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6229 CheckComArgOutSafeArrayPointerValid(aFlags);
6230
6231 AutoCaller autoCaller(this);
6232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6233
6234 HRESULT rc = enumerateGuestPropertiesOnVM
6235 (aPatterns, ComSafeArrayOutArg(aNames),
6236 ComSafeArrayOutArg(aValues),
6237 ComSafeArrayOutArg(aTimestamps),
6238 ComSafeArrayOutArg(aFlags));
6239 if (rc == E_ACCESSDENIED)
6240 /* The VM is not running or the service is not (yet) accessible */
6241 rc = enumerateGuestPropertiesInService
6242 (aPatterns, ComSafeArrayOutArg(aNames),
6243 ComSafeArrayOutArg(aValues),
6244 ComSafeArrayOutArg(aTimestamps),
6245 ComSafeArrayOutArg(aFlags));
6246 return rc;
6247#endif // VBOX_WITH_GUEST_PROPS
6248}
6249
6250STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6251 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6252{
6253 MediaData::AttachmentList atts;
6254
6255 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6256 if (FAILED(rc)) return rc;
6257
6258 SafeIfaceArray<IMediumAttachment> attachments(atts);
6259 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6260
6261 return S_OK;
6262}
6263
6264STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6265 LONG aControllerPort,
6266 LONG aDevice,
6267 IMediumAttachment **aAttachment)
6268{
6269 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6270 aControllerName, aControllerPort, aDevice));
6271
6272 CheckComArgStrNotEmptyOrNull(aControllerName);
6273 CheckComArgOutPointerValid(aAttachment);
6274
6275 AutoCaller autoCaller(this);
6276 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6277
6278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6279
6280 *aAttachment = NULL;
6281
6282 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6283 aControllerName,
6284 aControllerPort,
6285 aDevice);
6286 if (pAttach.isNull())
6287 return setError(VBOX_E_OBJECT_NOT_FOUND,
6288 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6289 aDevice, aControllerPort, aControllerName);
6290
6291 pAttach.queryInterfaceTo(aAttachment);
6292
6293 return S_OK;
6294}
6295
6296STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6297 StorageBus_T aConnectionType,
6298 IStorageController **controller)
6299{
6300 CheckComArgStrNotEmptyOrNull(aName);
6301
6302 if ( (aConnectionType <= StorageBus_Null)
6303 || (aConnectionType > StorageBus_SAS))
6304 return setError(E_INVALIDARG,
6305 tr("Invalid connection type: %d"),
6306 aConnectionType);
6307
6308 AutoCaller autoCaller(this);
6309 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6310
6311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6312
6313 HRESULT rc = checkStateDependency(MutableStateDep);
6314 if (FAILED(rc)) return rc;
6315
6316 /* try to find one with the name first. */
6317 ComObjPtr<StorageController> ctrl;
6318
6319 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6320 if (SUCCEEDED(rc))
6321 return setError(VBOX_E_OBJECT_IN_USE,
6322 tr("Storage controller named '%ls' already exists"),
6323 aName);
6324
6325 ctrl.createObject();
6326
6327 /* get a new instance number for the storage controller */
6328 ULONG ulInstance = 0;
6329 bool fBootable = true;
6330 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6331 it != mStorageControllers->end();
6332 ++it)
6333 {
6334 if ((*it)->getStorageBus() == aConnectionType)
6335 {
6336 ULONG ulCurInst = (*it)->getInstance();
6337
6338 if (ulCurInst >= ulInstance)
6339 ulInstance = ulCurInst + 1;
6340
6341 /* Only one controller of each type can be marked as bootable. */
6342 if ((*it)->getBootable())
6343 fBootable = false;
6344 }
6345 }
6346
6347 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6348 if (FAILED(rc)) return rc;
6349
6350 setModified(IsModified_Storage);
6351 mStorageControllers.backup();
6352 mStorageControllers->push_back(ctrl);
6353
6354 ctrl.queryInterfaceTo(controller);
6355
6356 /* inform the direct session if any */
6357 alock.release();
6358 onStorageControllerChange();
6359
6360 return S_OK;
6361}
6362
6363STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6364 IStorageController **aStorageController)
6365{
6366 CheckComArgStrNotEmptyOrNull(aName);
6367
6368 AutoCaller autoCaller(this);
6369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6370
6371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6372
6373 ComObjPtr<StorageController> ctrl;
6374
6375 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6376 if (SUCCEEDED(rc))
6377 ctrl.queryInterfaceTo(aStorageController);
6378
6379 return rc;
6380}
6381
6382STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6383 IStorageController **aStorageController)
6384{
6385 AutoCaller autoCaller(this);
6386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6387
6388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6389
6390 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6391 it != mStorageControllers->end();
6392 ++it)
6393 {
6394 if ((*it)->getInstance() == aInstance)
6395 {
6396 (*it).queryInterfaceTo(aStorageController);
6397 return S_OK;
6398 }
6399 }
6400
6401 return setError(VBOX_E_OBJECT_NOT_FOUND,
6402 tr("Could not find a storage controller with instance number '%lu'"),
6403 aInstance);
6404}
6405
6406STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6407{
6408 AutoCaller autoCaller(this);
6409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6410
6411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6412
6413 HRESULT rc = checkStateDependency(MutableStateDep);
6414 if (FAILED(rc)) return rc;
6415
6416 ComObjPtr<StorageController> ctrl;
6417
6418 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6419 if (SUCCEEDED(rc))
6420 {
6421 /* Ensure that only one controller of each type is marked as bootable. */
6422 if (fBootable == TRUE)
6423 {
6424 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6425 it != mStorageControllers->end();
6426 ++it)
6427 {
6428 ComObjPtr<StorageController> aCtrl = (*it);
6429
6430 if ( (aCtrl->getName() != Utf8Str(aName))
6431 && aCtrl->getBootable() == TRUE
6432 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6433 && aCtrl->getControllerType() == ctrl->getControllerType())
6434 {
6435 aCtrl->setBootable(FALSE);
6436 break;
6437 }
6438 }
6439 }
6440
6441 if (SUCCEEDED(rc))
6442 {
6443 ctrl->setBootable(fBootable);
6444 setModified(IsModified_Storage);
6445 }
6446 }
6447
6448 if (SUCCEEDED(rc))
6449 {
6450 /* inform the direct session if any */
6451 alock.release();
6452 onStorageControllerChange();
6453 }
6454
6455 return rc;
6456}
6457
6458STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6459{
6460 CheckComArgStrNotEmptyOrNull(aName);
6461
6462 AutoCaller autoCaller(this);
6463 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6464
6465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6466
6467 HRESULT rc = checkStateDependency(MutableStateDep);
6468 if (FAILED(rc)) return rc;
6469
6470 ComObjPtr<StorageController> ctrl;
6471 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6472 if (FAILED(rc)) return rc;
6473
6474 {
6475 /* find all attached devices to the appropriate storage controller and detach them all */
6476 // make a temporary list because detachDevice invalidates iterators into
6477 // mMediaData->mAttachments
6478 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6479
6480 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6481 it != llAttachments2.end();
6482 ++it)
6483 {
6484 MediumAttachment *pAttachTemp = *it;
6485
6486 AutoCaller localAutoCaller(pAttachTemp);
6487 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6488
6489 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6490
6491 if (pAttachTemp->getControllerName() == aName)
6492 {
6493 rc = detachDevice(pAttachTemp, alock, NULL);
6494 if (FAILED(rc)) return rc;
6495 }
6496 }
6497 }
6498
6499 /* We can remove it now. */
6500 setModified(IsModified_Storage);
6501 mStorageControllers.backup();
6502
6503 ctrl->unshare();
6504
6505 mStorageControllers->remove(ctrl);
6506
6507 /* inform the direct session if any */
6508 alock.release();
6509 onStorageControllerChange();
6510
6511 return S_OK;
6512}
6513
6514STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6515 ULONG *puOriginX,
6516 ULONG *puOriginY,
6517 ULONG *puWidth,
6518 ULONG *puHeight,
6519 BOOL *pfEnabled)
6520{
6521 LogFlowThisFunc(("\n"));
6522
6523 CheckComArgNotNull(puOriginX);
6524 CheckComArgNotNull(puOriginY);
6525 CheckComArgNotNull(puWidth);
6526 CheckComArgNotNull(puHeight);
6527 CheckComArgNotNull(pfEnabled);
6528
6529 uint32_t u32OriginX= 0;
6530 uint32_t u32OriginY= 0;
6531 uint32_t u32Width = 0;
6532 uint32_t u32Height = 0;
6533 uint16_t u16Flags = 0;
6534
6535 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6536 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6537 if (RT_FAILURE(vrc))
6538 {
6539#ifdef RT_OS_WINDOWS
6540 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6541 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6542 * So just assign fEnable to TRUE again.
6543 * The right fix would be to change GUI API wrappers to make sure that parameters
6544 * are changed only if API succeeds.
6545 */
6546 *pfEnabled = TRUE;
6547#endif
6548 return setError(VBOX_E_IPRT_ERROR,
6549 tr("Saved guest size is not available (%Rrc)"),
6550 vrc);
6551 }
6552
6553 *puOriginX = u32OriginX;
6554 *puOriginY = u32OriginY;
6555 *puWidth = u32Width;
6556 *puHeight = u32Height;
6557 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6558
6559 return S_OK;
6560}
6561
6562STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6563{
6564 LogFlowThisFunc(("\n"));
6565
6566 CheckComArgNotNull(aSize);
6567 CheckComArgNotNull(aWidth);
6568 CheckComArgNotNull(aHeight);
6569
6570 if (aScreenId != 0)
6571 return E_NOTIMPL;
6572
6573 AutoCaller autoCaller(this);
6574 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6575
6576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6577
6578 uint8_t *pu8Data = NULL;
6579 uint32_t cbData = 0;
6580 uint32_t u32Width = 0;
6581 uint32_t u32Height = 0;
6582
6583 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6584
6585 if (RT_FAILURE(vrc))
6586 return setError(VBOX_E_IPRT_ERROR,
6587 tr("Saved screenshot data is not available (%Rrc)"),
6588 vrc);
6589
6590 *aSize = cbData;
6591 *aWidth = u32Width;
6592 *aHeight = u32Height;
6593
6594 freeSavedDisplayScreenshot(pu8Data);
6595
6596 return S_OK;
6597}
6598
6599STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6600{
6601 LogFlowThisFunc(("\n"));
6602
6603 CheckComArgNotNull(aWidth);
6604 CheckComArgNotNull(aHeight);
6605 CheckComArgOutSafeArrayPointerValid(aData);
6606
6607 if (aScreenId != 0)
6608 return E_NOTIMPL;
6609
6610 AutoCaller autoCaller(this);
6611 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6612
6613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6614
6615 uint8_t *pu8Data = NULL;
6616 uint32_t cbData = 0;
6617 uint32_t u32Width = 0;
6618 uint32_t u32Height = 0;
6619
6620 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6621
6622 if (RT_FAILURE(vrc))
6623 return setError(VBOX_E_IPRT_ERROR,
6624 tr("Saved screenshot data is not available (%Rrc)"),
6625 vrc);
6626
6627 *aWidth = u32Width;
6628 *aHeight = u32Height;
6629
6630 com::SafeArray<BYTE> bitmap(cbData);
6631 /* Convert pixels to format expected by the API caller. */
6632 if (aBGR)
6633 {
6634 /* [0] B, [1] G, [2] R, [3] A. */
6635 for (unsigned i = 0; i < cbData; i += 4)
6636 {
6637 bitmap[i] = pu8Data[i];
6638 bitmap[i + 1] = pu8Data[i + 1];
6639 bitmap[i + 2] = pu8Data[i + 2];
6640 bitmap[i + 3] = 0xff;
6641 }
6642 }
6643 else
6644 {
6645 /* [0] R, [1] G, [2] B, [3] A. */
6646 for (unsigned i = 0; i < cbData; i += 4)
6647 {
6648 bitmap[i] = pu8Data[i + 2];
6649 bitmap[i + 1] = pu8Data[i + 1];
6650 bitmap[i + 2] = pu8Data[i];
6651 bitmap[i + 3] = 0xff;
6652 }
6653 }
6654 bitmap.detachTo(ComSafeArrayOutArg(aData));
6655
6656 freeSavedDisplayScreenshot(pu8Data);
6657
6658 return S_OK;
6659}
6660
6661
6662STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6663{
6664 LogFlowThisFunc(("\n"));
6665
6666 CheckComArgNotNull(aWidth);
6667 CheckComArgNotNull(aHeight);
6668 CheckComArgOutSafeArrayPointerValid(aData);
6669
6670 if (aScreenId != 0)
6671 return E_NOTIMPL;
6672
6673 AutoCaller autoCaller(this);
6674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6675
6676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6677
6678 uint8_t *pu8Data = NULL;
6679 uint32_t cbData = 0;
6680 uint32_t u32Width = 0;
6681 uint32_t u32Height = 0;
6682
6683 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6684
6685 if (RT_FAILURE(vrc))
6686 return setError(VBOX_E_IPRT_ERROR,
6687 tr("Saved screenshot data is not available (%Rrc)"),
6688 vrc);
6689
6690 *aWidth = u32Width;
6691 *aHeight = u32Height;
6692
6693 HRESULT rc = S_OK;
6694 uint8_t *pu8PNG = NULL;
6695 uint32_t cbPNG = 0;
6696 uint32_t cxPNG = 0;
6697 uint32_t cyPNG = 0;
6698
6699 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6700
6701 if (RT_SUCCESS(vrc))
6702 {
6703 com::SafeArray<BYTE> screenData(cbPNG);
6704 screenData.initFrom(pu8PNG, cbPNG);
6705 if (pu8PNG)
6706 RTMemFree(pu8PNG);
6707 screenData.detachTo(ComSafeArrayOutArg(aData));
6708 }
6709 else
6710 {
6711 if (pu8PNG)
6712 RTMemFree(pu8PNG);
6713 return setError(VBOX_E_IPRT_ERROR,
6714 tr("Could not convert screenshot to PNG (%Rrc)"),
6715 vrc);
6716 }
6717
6718 freeSavedDisplayScreenshot(pu8Data);
6719
6720 return rc;
6721}
6722
6723STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6724{
6725 LogFlowThisFunc(("\n"));
6726
6727 CheckComArgNotNull(aSize);
6728 CheckComArgNotNull(aWidth);
6729 CheckComArgNotNull(aHeight);
6730
6731 if (aScreenId != 0)
6732 return E_NOTIMPL;
6733
6734 AutoCaller autoCaller(this);
6735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6736
6737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6738
6739 uint8_t *pu8Data = NULL;
6740 uint32_t cbData = 0;
6741 uint32_t u32Width = 0;
6742 uint32_t u32Height = 0;
6743
6744 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6745
6746 if (RT_FAILURE(vrc))
6747 return setError(VBOX_E_IPRT_ERROR,
6748 tr("Saved screenshot data is not available (%Rrc)"),
6749 vrc);
6750
6751 *aSize = cbData;
6752 *aWidth = u32Width;
6753 *aHeight = u32Height;
6754
6755 freeSavedDisplayScreenshot(pu8Data);
6756
6757 return S_OK;
6758}
6759
6760STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6761{
6762 LogFlowThisFunc(("\n"));
6763
6764 CheckComArgNotNull(aWidth);
6765 CheckComArgNotNull(aHeight);
6766 CheckComArgOutSafeArrayPointerValid(aData);
6767
6768 if (aScreenId != 0)
6769 return E_NOTIMPL;
6770
6771 AutoCaller autoCaller(this);
6772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6773
6774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6775
6776 uint8_t *pu8Data = NULL;
6777 uint32_t cbData = 0;
6778 uint32_t u32Width = 0;
6779 uint32_t u32Height = 0;
6780
6781 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6782
6783 if (RT_FAILURE(vrc))
6784 return setError(VBOX_E_IPRT_ERROR,
6785 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6786 vrc);
6787
6788 *aWidth = u32Width;
6789 *aHeight = u32Height;
6790
6791 com::SafeArray<BYTE> png(cbData);
6792 png.initFrom(pu8Data, cbData);
6793 png.detachTo(ComSafeArrayOutArg(aData));
6794
6795 freeSavedDisplayScreenshot(pu8Data);
6796
6797 return S_OK;
6798}
6799
6800STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6801{
6802 HRESULT rc = S_OK;
6803 LogFlowThisFunc(("\n"));
6804
6805 AutoCaller autoCaller(this);
6806 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6807
6808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6809
6810 if (!mHWData->mCPUHotPlugEnabled)
6811 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6812
6813 if (aCpu >= mHWData->mCPUCount)
6814 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6815
6816 if (mHWData->mCPUAttached[aCpu])
6817 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6818
6819 alock.release();
6820 rc = onCPUChange(aCpu, false);
6821 alock.acquire();
6822 if (FAILED(rc)) return rc;
6823
6824 setModified(IsModified_MachineData);
6825 mHWData.backup();
6826 mHWData->mCPUAttached[aCpu] = true;
6827
6828 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6829 if (Global::IsOnline(mData->mMachineState))
6830 saveSettings(NULL);
6831
6832 return S_OK;
6833}
6834
6835STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6836{
6837 HRESULT rc = S_OK;
6838 LogFlowThisFunc(("\n"));
6839
6840 AutoCaller autoCaller(this);
6841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6842
6843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6844
6845 if (!mHWData->mCPUHotPlugEnabled)
6846 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6847
6848 if (aCpu >= SchemaDefs::MaxCPUCount)
6849 return setError(E_INVALIDARG,
6850 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6851 SchemaDefs::MaxCPUCount);
6852
6853 if (!mHWData->mCPUAttached[aCpu])
6854 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6855
6856 /* CPU 0 can't be detached */
6857 if (aCpu == 0)
6858 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6859
6860 alock.release();
6861 rc = onCPUChange(aCpu, true);
6862 alock.acquire();
6863 if (FAILED(rc)) return rc;
6864
6865 setModified(IsModified_MachineData);
6866 mHWData.backup();
6867 mHWData->mCPUAttached[aCpu] = false;
6868
6869 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6870 if (Global::IsOnline(mData->mMachineState))
6871 saveSettings(NULL);
6872
6873 return S_OK;
6874}
6875
6876STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6877{
6878 LogFlowThisFunc(("\n"));
6879
6880 CheckComArgNotNull(aCpuAttached);
6881
6882 *aCpuAttached = false;
6883
6884 AutoCaller autoCaller(this);
6885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6886
6887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6888
6889 /* If hotplug is enabled the CPU is always enabled. */
6890 if (!mHWData->mCPUHotPlugEnabled)
6891 {
6892 if (aCpu < mHWData->mCPUCount)
6893 *aCpuAttached = true;
6894 }
6895 else
6896 {
6897 if (aCpu < SchemaDefs::MaxCPUCount)
6898 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6899 }
6900
6901 return S_OK;
6902}
6903
6904STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6905{
6906 CheckComArgOutPointerValid(aName);
6907
6908 AutoCaller autoCaller(this);
6909 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6910
6911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6912
6913 Utf8Str log = queryLogFilename(aIdx);
6914 if (!RTFileExists(log.c_str()))
6915 log.setNull();
6916 log.cloneTo(aName);
6917
6918 return S_OK;
6919}
6920
6921STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6922{
6923 LogFlowThisFunc(("\n"));
6924 CheckComArgOutSafeArrayPointerValid(aData);
6925 if (aSize < 0)
6926 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6927
6928 AutoCaller autoCaller(this);
6929 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6930
6931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6932
6933 HRESULT rc = S_OK;
6934 Utf8Str log = queryLogFilename(aIdx);
6935
6936 /* do not unnecessarily hold the lock while doing something which does
6937 * not need the lock and potentially takes a long time. */
6938 alock.release();
6939
6940 /* Limit the chunk size to 32K for now, as that gives better performance
6941 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6942 * One byte expands to approx. 25 bytes of breathtaking XML. */
6943 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6944 com::SafeArray<BYTE> logData(cbData);
6945
6946 RTFILE LogFile;
6947 int vrc = RTFileOpen(&LogFile, log.c_str(),
6948 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6949 if (RT_SUCCESS(vrc))
6950 {
6951 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6952 if (RT_SUCCESS(vrc))
6953 logData.resize(cbData);
6954 else
6955 rc = setError(VBOX_E_IPRT_ERROR,
6956 tr("Could not read log file '%s' (%Rrc)"),
6957 log.c_str(), vrc);
6958 RTFileClose(LogFile);
6959 }
6960 else
6961 rc = setError(VBOX_E_IPRT_ERROR,
6962 tr("Could not open log file '%s' (%Rrc)"),
6963 log.c_str(), vrc);
6964
6965 if (FAILED(rc))
6966 logData.resize(0);
6967 logData.detachTo(ComSafeArrayOutArg(aData));
6968
6969 return rc;
6970}
6971
6972
6973/**
6974 * Currently this method doesn't attach device to the running VM,
6975 * just makes sure it's plugged on next VM start.
6976 */
6977STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6978{
6979 AutoCaller autoCaller(this);
6980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6981
6982 // lock scope
6983 {
6984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6985
6986 HRESULT rc = checkStateDependency(MutableStateDep);
6987 if (FAILED(rc)) return rc;
6988
6989 ChipsetType_T aChipset = ChipsetType_PIIX3;
6990 COMGETTER(ChipsetType)(&aChipset);
6991
6992 if (aChipset != ChipsetType_ICH9)
6993 {
6994 return setError(E_INVALIDARG,
6995 tr("Host PCI attachment only supported with ICH9 chipset"));
6996 }
6997
6998 // check if device with this host PCI address already attached
6999 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7000 it != mHWData->mPCIDeviceAssignments.end();
7001 ++it)
7002 {
7003 LONG iHostAddress = -1;
7004 ComPtr<PCIDeviceAttachment> pAttach;
7005 pAttach = *it;
7006 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7007 if (iHostAddress == hostAddress)
7008 return setError(E_INVALIDARG,
7009 tr("Device with host PCI address already attached to this VM"));
7010 }
7011
7012 ComObjPtr<PCIDeviceAttachment> pda;
7013 char name[32];
7014
7015 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7016 Bstr bname(name);
7017 pda.createObject();
7018 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7019 setModified(IsModified_MachineData);
7020 mHWData.backup();
7021 mHWData->mPCIDeviceAssignments.push_back(pda);
7022 }
7023
7024 return S_OK;
7025}
7026
7027/**
7028 * Currently this method doesn't detach device from the running VM,
7029 * just makes sure it's not plugged on next VM start.
7030 */
7031STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7032{
7033 AutoCaller autoCaller(this);
7034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7035
7036 ComObjPtr<PCIDeviceAttachment> pAttach;
7037 bool fRemoved = false;
7038 HRESULT rc;
7039
7040 // lock scope
7041 {
7042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7043
7044 rc = checkStateDependency(MutableStateDep);
7045 if (FAILED(rc)) return rc;
7046
7047 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7048 it != mHWData->mPCIDeviceAssignments.end();
7049 ++it)
7050 {
7051 LONG iHostAddress = -1;
7052 pAttach = *it;
7053 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7054 if (iHostAddress != -1 && iHostAddress == hostAddress)
7055 {
7056 setModified(IsModified_MachineData);
7057 mHWData.backup();
7058 mHWData->mPCIDeviceAssignments.remove(pAttach);
7059 fRemoved = true;
7060 break;
7061 }
7062 }
7063 }
7064
7065
7066 /* Fire event outside of the lock */
7067 if (fRemoved)
7068 {
7069 Assert(!pAttach.isNull());
7070 ComPtr<IEventSource> es;
7071 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7072 Assert(SUCCEEDED(rc));
7073 Bstr mid;
7074 rc = this->COMGETTER(Id)(mid.asOutParam());
7075 Assert(SUCCEEDED(rc));
7076 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7077 }
7078
7079 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7080 tr("No host PCI device %08x attached"),
7081 hostAddress
7082 );
7083}
7084
7085STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7086{
7087 CheckComArgOutSafeArrayPointerValid(aAssignments);
7088
7089 AutoCaller autoCaller(this);
7090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7091
7092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7093
7094 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7095 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7096
7097 return S_OK;
7098}
7099
7100STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7101{
7102 CheckComArgOutPointerValid(aBandwidthControl);
7103
7104 AutoCaller autoCaller(this);
7105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7106
7107 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7108
7109 return S_OK;
7110}
7111
7112STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7113{
7114 CheckComArgOutPointerValid(pfEnabled);
7115 AutoCaller autoCaller(this);
7116 HRESULT hrc = autoCaller.rc();
7117 if (SUCCEEDED(hrc))
7118 {
7119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7120 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7121 }
7122 return hrc;
7123}
7124
7125STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7126{
7127 AutoCaller autoCaller(this);
7128 HRESULT hrc = autoCaller.rc();
7129 if (SUCCEEDED(hrc))
7130 {
7131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7132 hrc = checkStateDependency(MutableStateDep);
7133 if (SUCCEEDED(hrc))
7134 {
7135 hrc = mHWData.backupEx();
7136 if (SUCCEEDED(hrc))
7137 {
7138 setModified(IsModified_MachineData);
7139 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7140 }
7141 }
7142 }
7143 return hrc;
7144}
7145
7146STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7147{
7148 CheckComArgOutPointerValid(pbstrConfig);
7149 AutoCaller autoCaller(this);
7150 HRESULT hrc = autoCaller.rc();
7151 if (SUCCEEDED(hrc))
7152 {
7153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7154 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7155 }
7156 return hrc;
7157}
7158
7159STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7160{
7161 CheckComArgStr(bstrConfig);
7162 AutoCaller autoCaller(this);
7163 HRESULT hrc = autoCaller.rc();
7164 if (SUCCEEDED(hrc))
7165 {
7166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7167 hrc = checkStateDependency(MutableStateDep);
7168 if (SUCCEEDED(hrc))
7169 {
7170 hrc = mHWData.backupEx();
7171 if (SUCCEEDED(hrc))
7172 {
7173 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7174 if (SUCCEEDED(hrc))
7175 setModified(IsModified_MachineData);
7176 }
7177 }
7178 }
7179 return hrc;
7180
7181}
7182
7183STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7184{
7185 CheckComArgOutPointerValid(pfAllow);
7186 AutoCaller autoCaller(this);
7187 HRESULT hrc = autoCaller.rc();
7188 if (SUCCEEDED(hrc))
7189 {
7190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7191 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7192 }
7193 return hrc;
7194}
7195
7196STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7197{
7198 AutoCaller autoCaller(this);
7199 HRESULT hrc = autoCaller.rc();
7200 if (SUCCEEDED(hrc))
7201 {
7202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7203 hrc = checkStateDependency(MutableStateDep);
7204 if (SUCCEEDED(hrc))
7205 {
7206 hrc = mHWData.backupEx();
7207 if (SUCCEEDED(hrc))
7208 {
7209 setModified(IsModified_MachineData);
7210 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7211 }
7212 }
7213 }
7214 return hrc;
7215}
7216
7217STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7218{
7219 CheckComArgOutPointerValid(pfEnabled);
7220 AutoCaller autoCaller(this);
7221 HRESULT hrc = autoCaller.rc();
7222 if (SUCCEEDED(hrc))
7223 {
7224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7225 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7226 }
7227 return hrc;
7228}
7229
7230STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7231{
7232 AutoCaller autoCaller(this);
7233 HRESULT hrc = autoCaller.rc();
7234 if (SUCCEEDED(hrc))
7235 {
7236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7237 hrc = checkStateDependency(MutableStateDep);
7238 if ( SUCCEEDED(hrc)
7239 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7240 {
7241 AutostartDb *autostartDb = mParent->getAutostartDb();
7242 int vrc;
7243
7244 if (fEnabled)
7245 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7246 else
7247 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7248
7249 if (RT_SUCCESS(vrc))
7250 {
7251 hrc = mHWData.backupEx();
7252 if (SUCCEEDED(hrc))
7253 {
7254 setModified(IsModified_MachineData);
7255 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7256 }
7257 }
7258 else if (vrc == VERR_NOT_SUPPORTED)
7259 hrc = setError(VBOX_E_NOT_SUPPORTED,
7260 tr("The VM autostart feature is not supported on this platform"));
7261 else if (vrc == VERR_PATH_NOT_FOUND)
7262 hrc = setError(E_FAIL,
7263 tr("The path to the autostart database is not set"));
7264 else
7265 hrc = setError(E_UNEXPECTED,
7266 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7267 fEnabled ? "Adding" : "Removing",
7268 mUserData->s.strName.c_str(), vrc);
7269 }
7270 }
7271 return hrc;
7272}
7273
7274STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7275{
7276 CheckComArgOutPointerValid(puDelay);
7277 AutoCaller autoCaller(this);
7278 HRESULT hrc = autoCaller.rc();
7279 if (SUCCEEDED(hrc))
7280 {
7281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7282 *puDelay = mHWData->mAutostart.uAutostartDelay;
7283 }
7284 return hrc;
7285}
7286
7287STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7288{
7289 AutoCaller autoCaller(this);
7290 HRESULT hrc = autoCaller.rc();
7291 if (SUCCEEDED(hrc))
7292 {
7293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7294 hrc = checkStateDependency(MutableStateDep);
7295 if (SUCCEEDED(hrc))
7296 {
7297 hrc = mHWData.backupEx();
7298 if (SUCCEEDED(hrc))
7299 {
7300 setModified(IsModified_MachineData);
7301 mHWData->mAutostart.uAutostartDelay = uDelay;
7302 }
7303 }
7304 }
7305 return hrc;
7306}
7307
7308STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7309{
7310 CheckComArgOutPointerValid(penmAutostopType);
7311 AutoCaller autoCaller(this);
7312 HRESULT hrc = autoCaller.rc();
7313 if (SUCCEEDED(hrc))
7314 {
7315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7316 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7317 }
7318 return hrc;
7319}
7320
7321STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7322{
7323 AutoCaller autoCaller(this);
7324 HRESULT hrc = autoCaller.rc();
7325 if (SUCCEEDED(hrc))
7326 {
7327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7328 hrc = checkStateDependency(MutableStateDep);
7329 if ( SUCCEEDED(hrc)
7330 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7331 {
7332 AutostartDb *autostartDb = mParent->getAutostartDb();
7333 int vrc;
7334
7335 if (enmAutostopType != AutostopType_Disabled)
7336 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7337 else
7338 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7339
7340 if (RT_SUCCESS(vrc))
7341 {
7342 hrc = mHWData.backupEx();
7343 if (SUCCEEDED(hrc))
7344 {
7345 setModified(IsModified_MachineData);
7346 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7347 }
7348 }
7349 else if (vrc == VERR_NOT_SUPPORTED)
7350 hrc = setError(VBOX_E_NOT_SUPPORTED,
7351 tr("The VM autostop feature is not supported on this platform"));
7352 else if (vrc == VERR_PATH_NOT_FOUND)
7353 hrc = setError(E_FAIL,
7354 tr("The path to the autostart database is not set"));
7355 else
7356 hrc = setError(E_UNEXPECTED,
7357 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7358 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7359 mUserData->s.strName.c_str(), vrc);
7360 }
7361 }
7362 return hrc;
7363}
7364
7365STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7366{
7367 CheckComArgOutPointerValid(aDefaultFrontend);
7368 AutoCaller autoCaller(this);
7369 HRESULT hrc = autoCaller.rc();
7370 if (SUCCEEDED(hrc))
7371 {
7372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7373 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7374 }
7375 return hrc;
7376}
7377
7378STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7379{
7380 CheckComArgStr(aDefaultFrontend);
7381 AutoCaller autoCaller(this);
7382 HRESULT hrc = autoCaller.rc();
7383 if (SUCCEEDED(hrc))
7384 {
7385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7386 hrc = checkStateDependency(MutableOrSavedStateDep);
7387 if (SUCCEEDED(hrc))
7388 {
7389 hrc = mHWData.backupEx();
7390 if (SUCCEEDED(hrc))
7391 {
7392 setModified(IsModified_MachineData);
7393 mHWData->mDefaultFrontend = aDefaultFrontend;
7394 }
7395 }
7396 }
7397 return hrc;
7398}
7399
7400STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7401{
7402 CheckComArgSafeArrayNotNull(aIcon);
7403 CheckComArgOutSafeArrayPointerValid(aIcon);
7404 AutoCaller autoCaller(this);
7405 HRESULT hrc = autoCaller.rc();
7406 if (SUCCEEDED(hrc))
7407 {
7408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7409 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7410 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7411 icon.detachTo(ComSafeArrayOutArg(aIcon));
7412 }
7413 return hrc;
7414}
7415
7416STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7417{
7418 CheckComArgSafeArrayNotNull(aIcon);
7419 AutoCaller autoCaller(this);
7420 HRESULT hrc = autoCaller.rc();
7421 if (SUCCEEDED(hrc))
7422 {
7423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7424 hrc = checkStateDependency(MutableOrSavedStateDep);
7425 if (SUCCEEDED(hrc))
7426 {
7427 setModified(IsModified_MachineData);
7428 mUserData.backup();
7429 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7430 mUserData->mIcon.clear();
7431 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7432 }
7433 }
7434 return hrc;
7435}
7436
7437STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7438{
7439 LogFlowFuncEnter();
7440
7441 CheckComArgNotNull(pTarget);
7442 CheckComArgOutPointerValid(pProgress);
7443
7444 /* Convert the options. */
7445 RTCList<CloneOptions_T> optList;
7446 if (options != NULL)
7447 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7448
7449 if (optList.contains(CloneOptions_Link))
7450 {
7451 if (!isSnapshotMachine())
7452 return setError(E_INVALIDARG,
7453 tr("Linked clone can only be created from a snapshot"));
7454 if (mode != CloneMode_MachineState)
7455 return setError(E_INVALIDARG,
7456 tr("Linked clone can only be created for a single machine state"));
7457 }
7458 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7459
7460 AutoCaller autoCaller(this);
7461 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7462
7463
7464 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7465
7466 HRESULT rc = pWorker->start(pProgress);
7467
7468 LogFlowFuncLeave();
7469
7470 return rc;
7471}
7472
7473// public methods for internal purposes
7474/////////////////////////////////////////////////////////////////////////////
7475
7476/**
7477 * Adds the given IsModified_* flag to the dirty flags of the machine.
7478 * This must be called either during loadSettings or under the machine write lock.
7479 * @param fl
7480 */
7481void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7482{
7483 mData->flModifications |= fl;
7484 if (fAllowStateModification && isStateModificationAllowed())
7485 mData->mCurrentStateModified = true;
7486}
7487
7488/**
7489 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7490 * care of the write locking.
7491 *
7492 * @param fModifications The flag to add.
7493 */
7494void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7495{
7496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7497 setModified(fModification, fAllowStateModification);
7498}
7499
7500/**
7501 * Saves the registry entry of this machine to the given configuration node.
7502 *
7503 * @param aEntryNode Node to save the registry entry to.
7504 *
7505 * @note locks this object for reading.
7506 */
7507HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7508{
7509 AutoLimitedCaller autoCaller(this);
7510 AssertComRCReturnRC(autoCaller.rc());
7511
7512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7513
7514 data.uuid = mData->mUuid;
7515 data.strSettingsFile = mData->m_strConfigFile;
7516
7517 return S_OK;
7518}
7519
7520/**
7521 * Calculates the absolute path of the given path taking the directory of the
7522 * machine settings file as the current directory.
7523 *
7524 * @param aPath Path to calculate the absolute path for.
7525 * @param aResult Where to put the result (used only on success, can be the
7526 * same Utf8Str instance as passed in @a aPath).
7527 * @return IPRT result.
7528 *
7529 * @note Locks this object for reading.
7530 */
7531int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7532{
7533 AutoCaller autoCaller(this);
7534 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7535
7536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7537
7538 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7539
7540 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7541
7542 strSettingsDir.stripFilename();
7543 char folder[RTPATH_MAX];
7544 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7545 if (RT_SUCCESS(vrc))
7546 aResult = folder;
7547
7548 return vrc;
7549}
7550
7551/**
7552 * Copies strSource to strTarget, making it relative to the machine folder
7553 * if it is a subdirectory thereof, or simply copying it otherwise.
7554 *
7555 * @param strSource Path to evaluate and copy.
7556 * @param strTarget Buffer to receive target path.
7557 *
7558 * @note Locks this object for reading.
7559 */
7560void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7561 Utf8Str &strTarget)
7562{
7563 AutoCaller autoCaller(this);
7564 AssertComRCReturn(autoCaller.rc(), (void)0);
7565
7566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7567
7568 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7569 // use strTarget as a temporary buffer to hold the machine settings dir
7570 strTarget = mData->m_strConfigFileFull;
7571 strTarget.stripFilename();
7572 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7573 {
7574 // is relative: then append what's left
7575 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7576 // for empty paths (only possible for subdirs) use "." to avoid
7577 // triggering default settings for not present config attributes.
7578 if (strTarget.isEmpty())
7579 strTarget = ".";
7580 }
7581 else
7582 // is not relative: then overwrite
7583 strTarget = strSource;
7584}
7585
7586/**
7587 * Returns the full path to the machine's log folder in the
7588 * \a aLogFolder argument.
7589 */
7590void Machine::getLogFolder(Utf8Str &aLogFolder)
7591{
7592 AutoCaller autoCaller(this);
7593 AssertComRCReturnVoid(autoCaller.rc());
7594
7595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7596
7597 char szTmp[RTPATH_MAX];
7598 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7599 if (RT_SUCCESS(vrc))
7600 {
7601 if (szTmp[0] && !mUserData.isNull())
7602 {
7603 char szTmp2[RTPATH_MAX];
7604 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7605 if (RT_SUCCESS(vrc))
7606 aLogFolder = BstrFmt("%s%c%s",
7607 szTmp2,
7608 RTPATH_DELIMITER,
7609 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7610 }
7611 else
7612 vrc = VERR_PATH_IS_RELATIVE;
7613 }
7614
7615 if (RT_FAILURE(vrc))
7616 {
7617 // fallback if VBOX_USER_LOGHOME is not set or invalid
7618 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7619 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7620 aLogFolder.append(RTPATH_DELIMITER);
7621 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7622 }
7623}
7624
7625/**
7626 * Returns the full path to the machine's log file for an given index.
7627 */
7628Utf8Str Machine::queryLogFilename(ULONG idx)
7629{
7630 Utf8Str logFolder;
7631 getLogFolder(logFolder);
7632 Assert(logFolder.length());
7633 Utf8Str log;
7634 if (idx == 0)
7635 log = Utf8StrFmt("%s%cVBox.log",
7636 logFolder.c_str(), RTPATH_DELIMITER);
7637 else
7638 log = Utf8StrFmt("%s%cVBox.log.%d",
7639 logFolder.c_str(), RTPATH_DELIMITER, idx);
7640 return log;
7641}
7642
7643/**
7644 * Composes a unique saved state filename based on the current system time. The filename is
7645 * granular to the second so this will work so long as no more than one snapshot is taken on
7646 * a machine per second.
7647 *
7648 * Before version 4.1, we used this formula for saved state files:
7649 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7650 * which no longer works because saved state files can now be shared between the saved state of the
7651 * "saved" machine and an online snapshot, and the following would cause problems:
7652 * 1) save machine
7653 * 2) create online snapshot from that machine state --> reusing saved state file
7654 * 3) save machine again --> filename would be reused, breaking the online snapshot
7655 *
7656 * So instead we now use a timestamp.
7657 *
7658 * @param str
7659 */
7660void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7661{
7662 AutoCaller autoCaller(this);
7663 AssertComRCReturnVoid(autoCaller.rc());
7664
7665 {
7666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7667 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7668 }
7669
7670 RTTIMESPEC ts;
7671 RTTimeNow(&ts);
7672 RTTIME time;
7673 RTTimeExplode(&time, &ts);
7674
7675 strStateFilePath += RTPATH_DELIMITER;
7676 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7677 time.i32Year, time.u8Month, time.u8MonthDay,
7678 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7679}
7680
7681/**
7682 * Returns the full path to the default video capture file.
7683 */
7684void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7685{
7686 AutoCaller autoCaller(this);
7687 AssertComRCReturnVoid(autoCaller.rc());
7688
7689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7690
7691 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7692 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7693 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7694}
7695
7696/**
7697 * @note Locks this object for writing, calls the client process
7698 * (inside the lock).
7699 */
7700HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7701 const Utf8Str &strFrontend,
7702 const Utf8Str &strEnvironment,
7703 ProgressProxy *aProgress)
7704{
7705 LogFlowThisFuncEnter();
7706
7707 AssertReturn(aControl, E_FAIL);
7708 AssertReturn(aProgress, E_FAIL);
7709 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7710
7711 AutoCaller autoCaller(this);
7712 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7713
7714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7715
7716 if (!mData->mRegistered)
7717 return setError(E_UNEXPECTED,
7718 tr("The machine '%s' is not registered"),
7719 mUserData->s.strName.c_str());
7720
7721 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7722
7723 if ( mData->mSession.mState == SessionState_Locked
7724 || mData->mSession.mState == SessionState_Spawning
7725 || mData->mSession.mState == SessionState_Unlocking)
7726 return setError(VBOX_E_INVALID_OBJECT_STATE,
7727 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7728 mUserData->s.strName.c_str());
7729
7730 /* may not be busy */
7731 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7732
7733 /* get the path to the executable */
7734 char szPath[RTPATH_MAX];
7735 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7736 size_t sz = strlen(szPath);
7737 szPath[sz++] = RTPATH_DELIMITER;
7738 szPath[sz] = 0;
7739 char *cmd = szPath + sz;
7740 sz = RTPATH_MAX - sz;
7741
7742 int vrc = VINF_SUCCESS;
7743 RTPROCESS pid = NIL_RTPROCESS;
7744
7745 RTENV env = RTENV_DEFAULT;
7746
7747 if (!strEnvironment.isEmpty())
7748 {
7749 char *newEnvStr = NULL;
7750
7751 do
7752 {
7753 /* clone the current environment */
7754 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7755 AssertRCBreakStmt(vrc2, vrc = vrc2);
7756
7757 newEnvStr = RTStrDup(strEnvironment.c_str());
7758 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7759
7760 /* put new variables to the environment
7761 * (ignore empty variable names here since RTEnv API
7762 * intentionally doesn't do that) */
7763 char *var = newEnvStr;
7764 for (char *p = newEnvStr; *p; ++p)
7765 {
7766 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7767 {
7768 *p = '\0';
7769 if (*var)
7770 {
7771 char *val = strchr(var, '=');
7772 if (val)
7773 {
7774 *val++ = '\0';
7775 vrc2 = RTEnvSetEx(env, var, val);
7776 }
7777 else
7778 vrc2 = RTEnvUnsetEx(env, var);
7779 if (RT_FAILURE(vrc2))
7780 break;
7781 }
7782 var = p + 1;
7783 }
7784 }
7785 if (RT_SUCCESS(vrc2) && *var)
7786 vrc2 = RTEnvPutEx(env, var);
7787
7788 AssertRCBreakStmt(vrc2, vrc = vrc2);
7789 }
7790 while (0);
7791
7792 if (newEnvStr != NULL)
7793 RTStrFree(newEnvStr);
7794 }
7795
7796#ifdef VBOX_WITH_QTGUI
7797 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7798 {
7799# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7800 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7801# else
7802 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7803# endif
7804 Assert(sz >= sizeof(VirtualBox_exe));
7805 strcpy(cmd, VirtualBox_exe);
7806
7807 Utf8Str idStr = mData->mUuid.toString();
7808 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7809 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7810 }
7811#else /* !VBOX_WITH_QTGUI */
7812 if (0)
7813 ;
7814#endif /* VBOX_WITH_QTGUI */
7815
7816 else
7817
7818#ifdef VBOX_WITH_VBOXSDL
7819 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7820 {
7821 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7822 Assert(sz >= sizeof(VBoxSDL_exe));
7823 strcpy(cmd, VBoxSDL_exe);
7824
7825 Utf8Str idStr = mData->mUuid.toString();
7826 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7827 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7828 }
7829#else /* !VBOX_WITH_VBOXSDL */
7830 if (0)
7831 ;
7832#endif /* !VBOX_WITH_VBOXSDL */
7833
7834 else
7835
7836#ifdef VBOX_WITH_HEADLESS
7837 if ( strFrontend == "headless"
7838 || strFrontend == "capture"
7839 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7840 )
7841 {
7842 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7843 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7844 * and a VM works even if the server has not been installed.
7845 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7846 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7847 * differently in 4.0 and 3.x.
7848 */
7849 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7850 Assert(sz >= sizeof(VBoxHeadless_exe));
7851 strcpy(cmd, VBoxHeadless_exe);
7852
7853 Utf8Str idStr = mData->mUuid.toString();
7854 /* Leave space for "--capture" arg. */
7855 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7856 "--startvm", idStr.c_str(),
7857 "--vrde", "config",
7858 0, /* For "--capture". */
7859 0 };
7860 if (strFrontend == "capture")
7861 {
7862 unsigned pos = RT_ELEMENTS(args) - 2;
7863 args[pos] = "--capture";
7864 }
7865 vrc = RTProcCreate(szPath, args, env,
7866#ifdef RT_OS_WINDOWS
7867 RTPROC_FLAGS_NO_WINDOW
7868#else
7869 0
7870#endif
7871 , &pid);
7872 }
7873#else /* !VBOX_WITH_HEADLESS */
7874 if (0)
7875 ;
7876#endif /* !VBOX_WITH_HEADLESS */
7877 else
7878 {
7879 RTEnvDestroy(env);
7880 return setError(E_INVALIDARG,
7881 tr("Invalid frontend name: '%s'"),
7882 strFrontend.c_str());
7883 }
7884
7885 RTEnvDestroy(env);
7886
7887 if (RT_FAILURE(vrc))
7888 return setError(VBOX_E_IPRT_ERROR,
7889 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7890 mUserData->s.strName.c_str(), vrc);
7891
7892 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7893
7894 /*
7895 * Note that we don't release the lock here before calling the client,
7896 * because it doesn't need to call us back if called with a NULL argument.
7897 * Releasing the lock here is dangerous because we didn't prepare the
7898 * launch data yet, but the client we've just started may happen to be
7899 * too fast and call openSession() that will fail (because of PID, etc.),
7900 * so that the Machine will never get out of the Spawning session state.
7901 */
7902
7903 /* inform the session that it will be a remote one */
7904 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7905 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7906 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7907
7908 if (FAILED(rc))
7909 {
7910 /* restore the session state */
7911 mData->mSession.mState = SessionState_Unlocked;
7912 /* The failure may occur w/o any error info (from RPC), so provide one */
7913 return setError(VBOX_E_VM_ERROR,
7914 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7915 }
7916
7917 /* attach launch data to the machine */
7918 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7919 mData->mSession.mRemoteControls.push_back(aControl);
7920 mData->mSession.mProgress = aProgress;
7921 mData->mSession.mPID = pid;
7922 mData->mSession.mState = SessionState_Spawning;
7923 mData->mSession.mType = strFrontend;
7924
7925 LogFlowThisFuncLeave();
7926 return S_OK;
7927}
7928
7929/**
7930 * Returns @c true if the given machine has an open direct session and returns
7931 * the session machine instance and additional session data (on some platforms)
7932 * if so.
7933 *
7934 * Note that when the method returns @c false, the arguments remain unchanged.
7935 *
7936 * @param aMachine Session machine object.
7937 * @param aControl Direct session control object (optional).
7938 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7939 *
7940 * @note locks this object for reading.
7941 */
7942#if defined(RT_OS_WINDOWS)
7943bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7944 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7945 HANDLE *aIPCSem /*= NULL*/,
7946 bool aAllowClosing /*= false*/)
7947#elif defined(RT_OS_OS2)
7948bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7949 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7950 HMTX *aIPCSem /*= NULL*/,
7951 bool aAllowClosing /*= false*/)
7952#else
7953bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7954 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7955 bool aAllowClosing /*= false*/)
7956#endif
7957{
7958 AutoLimitedCaller autoCaller(this);
7959 AssertComRCReturn(autoCaller.rc(), false);
7960
7961 /* just return false for inaccessible machines */
7962 if (autoCaller.state() != Ready)
7963 return false;
7964
7965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7966
7967 if ( mData->mSession.mState == SessionState_Locked
7968 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7969 )
7970 {
7971 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7972
7973 aMachine = mData->mSession.mMachine;
7974
7975 if (aControl != NULL)
7976 *aControl = mData->mSession.mDirectControl;
7977
7978#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7979 /* Additional session data */
7980 if (aIPCSem != NULL)
7981 *aIPCSem = aMachine->mIPCSem;
7982#endif
7983 return true;
7984 }
7985
7986 return false;
7987}
7988
7989/**
7990 * Returns @c true if the given machine has an spawning direct session and
7991 * returns and additional session data (on some platforms) if so.
7992 *
7993 * Note that when the method returns @c false, the arguments remain unchanged.
7994 *
7995 * @param aPID PID of the spawned direct session process.
7996 *
7997 * @note locks this object for reading.
7998 */
7999#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8000bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
8001#else
8002bool Machine::isSessionSpawning()
8003#endif
8004{
8005 AutoLimitedCaller autoCaller(this);
8006 AssertComRCReturn(autoCaller.rc(), false);
8007
8008 /* just return false for inaccessible machines */
8009 if (autoCaller.state() != Ready)
8010 return false;
8011
8012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8013
8014 if (mData->mSession.mState == SessionState_Spawning)
8015 {
8016#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8017 /* Additional session data */
8018 if (aPID != NULL)
8019 {
8020 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
8021 *aPID = mData->mSession.mPID;
8022 }
8023#endif
8024 return true;
8025 }
8026
8027 return false;
8028}
8029
8030/**
8031 * Called from the client watcher thread to check for unexpected client process
8032 * death during Session_Spawning state (e.g. before it successfully opened a
8033 * direct session).
8034 *
8035 * On Win32 and on OS/2, this method is called only when we've got the
8036 * direct client's process termination notification, so it always returns @c
8037 * true.
8038 *
8039 * On other platforms, this method returns @c true if the client process is
8040 * terminated and @c false if it's still alive.
8041 *
8042 * @note Locks this object for writing.
8043 */
8044bool Machine::checkForSpawnFailure()
8045{
8046 AutoCaller autoCaller(this);
8047 if (!autoCaller.isOk())
8048 {
8049 /* nothing to do */
8050 LogFlowThisFunc(("Already uninitialized!\n"));
8051 return true;
8052 }
8053
8054 /* VirtualBox::addProcessToReap() needs a write lock */
8055 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
8056
8057 if (mData->mSession.mState != SessionState_Spawning)
8058 {
8059 /* nothing to do */
8060 LogFlowThisFunc(("Not spawning any more!\n"));
8061 return true;
8062 }
8063
8064 HRESULT rc = S_OK;
8065
8066#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8067
8068 /* the process was already unexpectedly terminated, we just need to set an
8069 * error and finalize session spawning */
8070 rc = setError(E_FAIL,
8071 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
8072 getName().c_str());
8073#else
8074
8075 /* PID not yet initialized, skip check. */
8076 if (mData->mSession.mPID == NIL_RTPROCESS)
8077 return false;
8078
8079 RTPROCSTATUS status;
8080 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
8081 &status);
8082
8083 if (vrc != VERR_PROCESS_RUNNING)
8084 {
8085 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8086 rc = setError(E_FAIL,
8087 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8088 getName().c_str(), status.iStatus);
8089 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8090 rc = setError(E_FAIL,
8091 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8092 getName().c_str(), status.iStatus);
8093 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8094 rc = setError(E_FAIL,
8095 tr("The virtual machine '%s' has terminated abnormally"),
8096 getName().c_str(), status.iStatus);
8097 else
8098 rc = setError(E_FAIL,
8099 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8100 getName().c_str(), rc);
8101 }
8102
8103#endif
8104
8105 if (FAILED(rc))
8106 {
8107 /* Close the remote session, remove the remote control from the list
8108 * and reset session state to Closed (@note keep the code in sync with
8109 * the relevant part in checkForSpawnFailure()). */
8110
8111 Assert(mData->mSession.mRemoteControls.size() == 1);
8112 if (mData->mSession.mRemoteControls.size() == 1)
8113 {
8114 ErrorInfoKeeper eik;
8115 mData->mSession.mRemoteControls.front()->Uninitialize();
8116 }
8117
8118 mData->mSession.mRemoteControls.clear();
8119 mData->mSession.mState = SessionState_Unlocked;
8120
8121 /* finalize the progress after setting the state */
8122 if (!mData->mSession.mProgress.isNull())
8123 {
8124 mData->mSession.mProgress->notifyComplete(rc);
8125 mData->mSession.mProgress.setNull();
8126 }
8127
8128 mParent->addProcessToReap(mData->mSession.mPID);
8129 mData->mSession.mPID = NIL_RTPROCESS;
8130
8131 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8132 return true;
8133 }
8134
8135 return false;
8136}
8137
8138/**
8139 * Checks whether the machine can be registered. If so, commits and saves
8140 * all settings.
8141 *
8142 * @note Must be called from mParent's write lock. Locks this object and
8143 * children for writing.
8144 */
8145HRESULT Machine::prepareRegister()
8146{
8147 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8148
8149 AutoLimitedCaller autoCaller(this);
8150 AssertComRCReturnRC(autoCaller.rc());
8151
8152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8153
8154 /* wait for state dependents to drop to zero */
8155 ensureNoStateDependencies();
8156
8157 if (!mData->mAccessible)
8158 return setError(VBOX_E_INVALID_OBJECT_STATE,
8159 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8160 mUserData->s.strName.c_str(),
8161 mData->mUuid.toString().c_str());
8162
8163 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8164
8165 if (mData->mRegistered)
8166 return setError(VBOX_E_INVALID_OBJECT_STATE,
8167 tr("The machine '%s' with UUID {%s} is already registered"),
8168 mUserData->s.strName.c_str(),
8169 mData->mUuid.toString().c_str());
8170
8171 HRESULT rc = S_OK;
8172
8173 // Ensure the settings are saved. If we are going to be registered and
8174 // no config file exists yet, create it by calling saveSettings() too.
8175 if ( (mData->flModifications)
8176 || (!mData->pMachineConfigFile->fileExists())
8177 )
8178 {
8179 rc = saveSettings(NULL);
8180 // no need to check whether VirtualBox.xml needs saving too since
8181 // we can't have a machine XML file rename pending
8182 if (FAILED(rc)) return rc;
8183 }
8184
8185 /* more config checking goes here */
8186
8187 if (SUCCEEDED(rc))
8188 {
8189 /* we may have had implicit modifications we want to fix on success */
8190 commit();
8191
8192 mData->mRegistered = true;
8193 }
8194 else
8195 {
8196 /* we may have had implicit modifications we want to cancel on failure*/
8197 rollback(false /* aNotify */);
8198 }
8199
8200 return rc;
8201}
8202
8203/**
8204 * Increases the number of objects dependent on the machine state or on the
8205 * registered state. Guarantees that these two states will not change at least
8206 * until #releaseStateDependency() is called.
8207 *
8208 * Depending on the @a aDepType value, additional state checks may be made.
8209 * These checks will set extended error info on failure. See
8210 * #checkStateDependency() for more info.
8211 *
8212 * If this method returns a failure, the dependency is not added and the caller
8213 * is not allowed to rely on any particular machine state or registration state
8214 * value and may return the failed result code to the upper level.
8215 *
8216 * @param aDepType Dependency type to add.
8217 * @param aState Current machine state (NULL if not interested).
8218 * @param aRegistered Current registered state (NULL if not interested).
8219 *
8220 * @note Locks this object for writing.
8221 */
8222HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8223 MachineState_T *aState /* = NULL */,
8224 BOOL *aRegistered /* = NULL */)
8225{
8226 AutoCaller autoCaller(this);
8227 AssertComRCReturnRC(autoCaller.rc());
8228
8229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8230
8231 HRESULT rc = checkStateDependency(aDepType);
8232 if (FAILED(rc)) return rc;
8233
8234 {
8235 if (mData->mMachineStateChangePending != 0)
8236 {
8237 /* ensureNoStateDependencies() is waiting for state dependencies to
8238 * drop to zero so don't add more. It may make sense to wait a bit
8239 * and retry before reporting an error (since the pending state
8240 * transition should be really quick) but let's just assert for
8241 * now to see if it ever happens on practice. */
8242
8243 AssertFailed();
8244
8245 return setError(E_ACCESSDENIED,
8246 tr("Machine state change is in progress. Please retry the operation later."));
8247 }
8248
8249 ++mData->mMachineStateDeps;
8250 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8251 }
8252
8253 if (aState)
8254 *aState = mData->mMachineState;
8255 if (aRegistered)
8256 *aRegistered = mData->mRegistered;
8257
8258 return S_OK;
8259}
8260
8261/**
8262 * Decreases the number of objects dependent on the machine state.
8263 * Must always complete the #addStateDependency() call after the state
8264 * dependency is no more necessary.
8265 */
8266void Machine::releaseStateDependency()
8267{
8268 AutoCaller autoCaller(this);
8269 AssertComRCReturnVoid(autoCaller.rc());
8270
8271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8272
8273 /* releaseStateDependency() w/o addStateDependency()? */
8274 AssertReturnVoid(mData->mMachineStateDeps != 0);
8275 -- mData->mMachineStateDeps;
8276
8277 if (mData->mMachineStateDeps == 0)
8278 {
8279 /* inform ensureNoStateDependencies() that there are no more deps */
8280 if (mData->mMachineStateChangePending != 0)
8281 {
8282 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8283 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8284 }
8285 }
8286}
8287
8288Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8289{
8290 /* start with nothing found */
8291 Utf8Str strResult("");
8292
8293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8294
8295 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8296 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8297 // found:
8298 strResult = it->second; // source is a Utf8Str
8299
8300 return strResult;
8301}
8302
8303// protected methods
8304/////////////////////////////////////////////////////////////////////////////
8305
8306/**
8307 * Performs machine state checks based on the @a aDepType value. If a check
8308 * fails, this method will set extended error info, otherwise it will return
8309 * S_OK. It is supposed, that on failure, the caller will immediately return
8310 * the return value of this method to the upper level.
8311 *
8312 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8313 *
8314 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8315 * current state of this machine object allows to change settings of the
8316 * machine (i.e. the machine is not registered, or registered but not running
8317 * and not saved). It is useful to call this method from Machine setters
8318 * before performing any change.
8319 *
8320 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8321 * as for MutableStateDep except that if the machine is saved, S_OK is also
8322 * returned. This is useful in setters which allow changing machine
8323 * properties when it is in the saved state.
8324 *
8325 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8326 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8327 * Aborted).
8328 *
8329 * @param aDepType Dependency type to check.
8330 *
8331 * @note Non Machine based classes should use #addStateDependency() and
8332 * #releaseStateDependency() methods or the smart AutoStateDependency
8333 * template.
8334 *
8335 * @note This method must be called from under this object's read or write
8336 * lock.
8337 */
8338HRESULT Machine::checkStateDependency(StateDependency aDepType)
8339{
8340 switch (aDepType)
8341 {
8342 case AnyStateDep:
8343 {
8344 break;
8345 }
8346 case MutableStateDep:
8347 {
8348 if ( mData->mRegistered
8349 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8350 || ( mData->mMachineState != MachineState_Paused
8351 && mData->mMachineState != MachineState_Running
8352 && mData->mMachineState != MachineState_Aborted
8353 && mData->mMachineState != MachineState_Teleported
8354 && mData->mMachineState != MachineState_PoweredOff
8355 )
8356 )
8357 )
8358 return setError(VBOX_E_INVALID_VM_STATE,
8359 tr("The machine is not mutable (state is %s)"),
8360 Global::stringifyMachineState(mData->mMachineState));
8361 break;
8362 }
8363 case MutableOrSavedStateDep:
8364 {
8365 if ( mData->mRegistered
8366 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8367 || ( mData->mMachineState != MachineState_Paused
8368 && mData->mMachineState != MachineState_Running
8369 && mData->mMachineState != MachineState_Aborted
8370 && mData->mMachineState != MachineState_Teleported
8371 && mData->mMachineState != MachineState_Saved
8372 && mData->mMachineState != MachineState_PoweredOff
8373 )
8374 )
8375 )
8376 return setError(VBOX_E_INVALID_VM_STATE,
8377 tr("The machine is not mutable (state is %s)"),
8378 Global::stringifyMachineState(mData->mMachineState));
8379 break;
8380 }
8381 case OfflineStateDep:
8382 {
8383 if ( mData->mRegistered
8384 && ( !isSessionMachine()
8385 || ( mData->mMachineState != MachineState_PoweredOff
8386 && mData->mMachineState != MachineState_Saved
8387 && mData->mMachineState != MachineState_Aborted
8388 && mData->mMachineState != MachineState_Teleported
8389 )
8390 )
8391 )
8392 return setError(VBOX_E_INVALID_VM_STATE,
8393 tr("The machine is not offline (state is %s)"),
8394 Global::stringifyMachineState(mData->mMachineState));
8395 break;
8396 }
8397 }
8398
8399 return S_OK;
8400}
8401
8402/**
8403 * Helper to initialize all associated child objects and allocate data
8404 * structures.
8405 *
8406 * This method must be called as a part of the object's initialization procedure
8407 * (usually done in the #init() method).
8408 *
8409 * @note Must be called only from #init() or from #registeredInit().
8410 */
8411HRESULT Machine::initDataAndChildObjects()
8412{
8413 AutoCaller autoCaller(this);
8414 AssertComRCReturnRC(autoCaller.rc());
8415 AssertComRCReturn(autoCaller.state() == InInit ||
8416 autoCaller.state() == Limited, E_FAIL);
8417
8418 AssertReturn(!mData->mAccessible, E_FAIL);
8419
8420 /* allocate data structures */
8421 mSSData.allocate();
8422 mUserData.allocate();
8423 mHWData.allocate();
8424 mMediaData.allocate();
8425 mStorageControllers.allocate();
8426
8427 /* initialize mOSTypeId */
8428 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8429
8430 /* create associated BIOS settings object */
8431 unconst(mBIOSSettings).createObject();
8432 mBIOSSettings->init(this);
8433
8434 /* create an associated VRDE object (default is disabled) */
8435 unconst(mVRDEServer).createObject();
8436 mVRDEServer->init(this);
8437
8438 /* create associated serial port objects */
8439 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8440 {
8441 unconst(mSerialPorts[slot]).createObject();
8442 mSerialPorts[slot]->init(this, slot);
8443 }
8444
8445 /* create associated parallel port objects */
8446 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8447 {
8448 unconst(mParallelPorts[slot]).createObject();
8449 mParallelPorts[slot]->init(this, slot);
8450 }
8451
8452 /* create the audio adapter object (always present, default is disabled) */
8453 unconst(mAudioAdapter).createObject();
8454 mAudioAdapter->init(this);
8455
8456 /* create the USB controller object (always present, default is disabled) */
8457 unconst(mUSBController).createObject();
8458 mUSBController->init(this);
8459
8460 /* create associated network adapter objects */
8461 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8462 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8463 {
8464 unconst(mNetworkAdapters[slot]).createObject();
8465 mNetworkAdapters[slot]->init(this, slot);
8466 }
8467
8468 /* create the bandwidth control */
8469 unconst(mBandwidthControl).createObject();
8470 mBandwidthControl->init(this);
8471
8472 return S_OK;
8473}
8474
8475/**
8476 * Helper to uninitialize all associated child objects and to free all data
8477 * structures.
8478 *
8479 * This method must be called as a part of the object's uninitialization
8480 * procedure (usually done in the #uninit() method).
8481 *
8482 * @note Must be called only from #uninit() or from #registeredInit().
8483 */
8484void Machine::uninitDataAndChildObjects()
8485{
8486 AutoCaller autoCaller(this);
8487 AssertComRCReturnVoid(autoCaller.rc());
8488 AssertComRCReturnVoid( autoCaller.state() == InUninit
8489 || autoCaller.state() == Limited);
8490
8491 /* tell all our other child objects we've been uninitialized */
8492 if (mBandwidthControl)
8493 {
8494 mBandwidthControl->uninit();
8495 unconst(mBandwidthControl).setNull();
8496 }
8497
8498 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8499 {
8500 if (mNetworkAdapters[slot])
8501 {
8502 mNetworkAdapters[slot]->uninit();
8503 unconst(mNetworkAdapters[slot]).setNull();
8504 }
8505 }
8506
8507 if (mUSBController)
8508 {
8509 mUSBController->uninit();
8510 unconst(mUSBController).setNull();
8511 }
8512
8513 if (mAudioAdapter)
8514 {
8515 mAudioAdapter->uninit();
8516 unconst(mAudioAdapter).setNull();
8517 }
8518
8519 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8520 {
8521 if (mParallelPorts[slot])
8522 {
8523 mParallelPorts[slot]->uninit();
8524 unconst(mParallelPorts[slot]).setNull();
8525 }
8526 }
8527
8528 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8529 {
8530 if (mSerialPorts[slot])
8531 {
8532 mSerialPorts[slot]->uninit();
8533 unconst(mSerialPorts[slot]).setNull();
8534 }
8535 }
8536
8537 if (mVRDEServer)
8538 {
8539 mVRDEServer->uninit();
8540 unconst(mVRDEServer).setNull();
8541 }
8542
8543 if (mBIOSSettings)
8544 {
8545 mBIOSSettings->uninit();
8546 unconst(mBIOSSettings).setNull();
8547 }
8548
8549 /* Deassociate media (only when a real Machine or a SnapshotMachine
8550 * instance is uninitialized; SessionMachine instances refer to real
8551 * Machine media). This is necessary for a clean re-initialization of
8552 * the VM after successfully re-checking the accessibility state. Note
8553 * that in case of normal Machine or SnapshotMachine uninitialization (as
8554 * a result of unregistering or deleting the snapshot), outdated media
8555 * attachments will already be uninitialized and deleted, so this
8556 * code will not affect them. */
8557 if ( !!mMediaData
8558 && (!isSessionMachine())
8559 )
8560 {
8561 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8562 it != mMediaData->mAttachments.end();
8563 ++it)
8564 {
8565 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8566 if (pMedium.isNull())
8567 continue;
8568 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8569 AssertComRC(rc);
8570 }
8571 }
8572
8573 if (!isSessionMachine() && !isSnapshotMachine())
8574 {
8575 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8576 if (mData->mFirstSnapshot)
8577 {
8578 // snapshots tree is protected by machine write lock; strictly
8579 // this isn't necessary here since we're deleting the entire
8580 // machine, but otherwise we assert in Snapshot::uninit()
8581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8582 mData->mFirstSnapshot->uninit();
8583 mData->mFirstSnapshot.setNull();
8584 }
8585
8586 mData->mCurrentSnapshot.setNull();
8587 }
8588
8589 /* free data structures (the essential mData structure is not freed here
8590 * since it may be still in use) */
8591 mMediaData.free();
8592 mStorageControllers.free();
8593 mHWData.free();
8594 mUserData.free();
8595 mSSData.free();
8596}
8597
8598/**
8599 * Returns a pointer to the Machine object for this machine that acts like a
8600 * parent for complex machine data objects such as shared folders, etc.
8601 *
8602 * For primary Machine objects and for SnapshotMachine objects, returns this
8603 * object's pointer itself. For SessionMachine objects, returns the peer
8604 * (primary) machine pointer.
8605 */
8606Machine* Machine::getMachine()
8607{
8608 if (isSessionMachine())
8609 return (Machine*)mPeer;
8610 return this;
8611}
8612
8613/**
8614 * Makes sure that there are no machine state dependents. If necessary, waits
8615 * for the number of dependents to drop to zero.
8616 *
8617 * Make sure this method is called from under this object's write lock to
8618 * guarantee that no new dependents may be added when this method returns
8619 * control to the caller.
8620 *
8621 * @note Locks this object for writing. The lock will be released while waiting
8622 * (if necessary).
8623 *
8624 * @warning To be used only in methods that change the machine state!
8625 */
8626void Machine::ensureNoStateDependencies()
8627{
8628 AssertReturnVoid(isWriteLockOnCurrentThread());
8629
8630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8631
8632 /* Wait for all state dependents if necessary */
8633 if (mData->mMachineStateDeps != 0)
8634 {
8635 /* lazy semaphore creation */
8636 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8637 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8638
8639 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8640 mData->mMachineStateDeps));
8641
8642 ++mData->mMachineStateChangePending;
8643
8644 /* reset the semaphore before waiting, the last dependent will signal
8645 * it */
8646 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8647
8648 alock.release();
8649
8650 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8651
8652 alock.acquire();
8653
8654 -- mData->mMachineStateChangePending;
8655 }
8656}
8657
8658/**
8659 * Changes the machine state and informs callbacks.
8660 *
8661 * This method is not intended to fail so it either returns S_OK or asserts (and
8662 * returns a failure).
8663 *
8664 * @note Locks this object for writing.
8665 */
8666HRESULT Machine::setMachineState(MachineState_T aMachineState)
8667{
8668 LogFlowThisFuncEnter();
8669 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8670
8671 AutoCaller autoCaller(this);
8672 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8673
8674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8675
8676 /* wait for state dependents to drop to zero */
8677 ensureNoStateDependencies();
8678
8679 if (mData->mMachineState != aMachineState)
8680 {
8681 mData->mMachineState = aMachineState;
8682
8683 RTTimeNow(&mData->mLastStateChange);
8684
8685 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8686 }
8687
8688 LogFlowThisFuncLeave();
8689 return S_OK;
8690}
8691
8692/**
8693 * Searches for a shared folder with the given logical name
8694 * in the collection of shared folders.
8695 *
8696 * @param aName logical name of the shared folder
8697 * @param aSharedFolder where to return the found object
8698 * @param aSetError whether to set the error info if the folder is
8699 * not found
8700 * @return
8701 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8702 *
8703 * @note
8704 * must be called from under the object's lock!
8705 */
8706HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8707 ComObjPtr<SharedFolder> &aSharedFolder,
8708 bool aSetError /* = false */)
8709{
8710 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8711 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8712 it != mHWData->mSharedFolders.end();
8713 ++it)
8714 {
8715 SharedFolder *pSF = *it;
8716 AutoCaller autoCaller(pSF);
8717 if (pSF->getName() == aName)
8718 {
8719 aSharedFolder = pSF;
8720 rc = S_OK;
8721 break;
8722 }
8723 }
8724
8725 if (aSetError && FAILED(rc))
8726 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8727
8728 return rc;
8729}
8730
8731/**
8732 * Initializes all machine instance data from the given settings structures
8733 * from XML. The exception is the machine UUID which needs special handling
8734 * depending on the caller's use case, so the caller needs to set that herself.
8735 *
8736 * This gets called in several contexts during machine initialization:
8737 *
8738 * -- When machine XML exists on disk already and needs to be loaded into memory,
8739 * for example, from registeredInit() to load all registered machines on
8740 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8741 * attached to the machine should be part of some media registry already.
8742 *
8743 * -- During OVF import, when a machine config has been constructed from an
8744 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8745 * ensure that the media listed as attachments in the config (which have
8746 * been imported from the OVF) receive the correct registry ID.
8747 *
8748 * -- During VM cloning.
8749 *
8750 * @param config Machine settings from XML.
8751 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8752 * @return
8753 */
8754HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8755 const Guid *puuidRegistry)
8756{
8757 // copy name, description, OS type, teleporter, UTC etc.
8758 #define DECODE_STR_MAX _1M
8759 mUserData->s = config.machineUserData;
8760
8761 // Decode the Icon overide data from config userdata and set onto Machine.
8762 const char* pszStr = config.machineUserData.ovIcon.c_str();
8763 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8764 if (cbOut > DECODE_STR_MAX)
8765 return setError(E_FAIL,
8766 tr("Icon Data too long.'%d' > '%d'"),
8767 cbOut,
8768 DECODE_STR_MAX);
8769 com::SafeArray<BYTE> iconByte(cbOut);
8770 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8771 if (FAILED(rc))
8772 return setError(E_FAIL,
8773 tr("Failure to Decode Icon Data. '%s' (%d)"),
8774 pszStr,
8775 rc);
8776 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8777
8778 // look up the object by Id to check it is valid
8779 ComPtr<IGuestOSType> guestOSType;
8780 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8781 guestOSType.asOutParam());
8782 if (FAILED(rc)) return rc;
8783
8784 // stateFile (optional)
8785 if (config.strStateFile.isEmpty())
8786 mSSData->strStateFilePath.setNull();
8787 else
8788 {
8789 Utf8Str stateFilePathFull(config.strStateFile);
8790 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8791 if (RT_FAILURE(vrc))
8792 return setError(E_FAIL,
8793 tr("Invalid saved state file path '%s' (%Rrc)"),
8794 config.strStateFile.c_str(),
8795 vrc);
8796 mSSData->strStateFilePath = stateFilePathFull;
8797 }
8798
8799 // snapshot folder needs special processing so set it again
8800 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8801 if (FAILED(rc)) return rc;
8802
8803 /* Copy the extra data items (Not in any case config is already the same as
8804 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8805 * make sure the extra data map is copied). */
8806 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8807
8808 /* currentStateModified (optional, default is true) */
8809 mData->mCurrentStateModified = config.fCurrentStateModified;
8810
8811 mData->mLastStateChange = config.timeLastStateChange;
8812
8813 /*
8814 * note: all mUserData members must be assigned prior this point because
8815 * we need to commit changes in order to let mUserData be shared by all
8816 * snapshot machine instances.
8817 */
8818 mUserData.commitCopy();
8819
8820 // machine registry, if present (must be loaded before snapshots)
8821 if (config.canHaveOwnMediaRegistry())
8822 {
8823 // determine machine folder
8824 Utf8Str strMachineFolder = getSettingsFileFull();
8825 strMachineFolder.stripFilename();
8826 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8827 config.mediaRegistry,
8828 strMachineFolder);
8829 if (FAILED(rc)) return rc;
8830 }
8831
8832 /* Snapshot node (optional) */
8833 size_t cRootSnapshots;
8834 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8835 {
8836 // there must be only one root snapshot
8837 Assert(cRootSnapshots == 1);
8838
8839 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8840
8841 rc = loadSnapshot(snap,
8842 config.uuidCurrentSnapshot,
8843 NULL); // no parent == first snapshot
8844 if (FAILED(rc)) return rc;
8845 }
8846
8847 // hardware data
8848 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8849 if (FAILED(rc)) return rc;
8850
8851 // load storage controllers
8852 rc = loadStorageControllers(config.storageMachine,
8853 puuidRegistry,
8854 NULL /* puuidSnapshot */);
8855 if (FAILED(rc)) return rc;
8856
8857 /*
8858 * NOTE: the assignment below must be the last thing to do,
8859 * otherwise it will be not possible to change the settings
8860 * somewhere in the code above because all setters will be
8861 * blocked by checkStateDependency(MutableStateDep).
8862 */
8863
8864 /* set the machine state to Aborted or Saved when appropriate */
8865 if (config.fAborted)
8866 {
8867 mSSData->strStateFilePath.setNull();
8868
8869 /* no need to use setMachineState() during init() */
8870 mData->mMachineState = MachineState_Aborted;
8871 }
8872 else if (!mSSData->strStateFilePath.isEmpty())
8873 {
8874 /* no need to use setMachineState() during init() */
8875 mData->mMachineState = MachineState_Saved;
8876 }
8877
8878 // after loading settings, we are no longer different from the XML on disk
8879 mData->flModifications = 0;
8880
8881 return S_OK;
8882}
8883
8884/**
8885 * Recursively loads all snapshots starting from the given.
8886 *
8887 * @param aNode <Snapshot> node.
8888 * @param aCurSnapshotId Current snapshot ID from the settings file.
8889 * @param aParentSnapshot Parent snapshot.
8890 */
8891HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8892 const Guid &aCurSnapshotId,
8893 Snapshot *aParentSnapshot)
8894{
8895 AssertReturn(!isSnapshotMachine(), E_FAIL);
8896 AssertReturn(!isSessionMachine(), E_FAIL);
8897
8898 HRESULT rc = S_OK;
8899
8900 Utf8Str strStateFile;
8901 if (!data.strStateFile.isEmpty())
8902 {
8903 /* optional */
8904 strStateFile = data.strStateFile;
8905 int vrc = calculateFullPath(strStateFile, strStateFile);
8906 if (RT_FAILURE(vrc))
8907 return setError(E_FAIL,
8908 tr("Invalid saved state file path '%s' (%Rrc)"),
8909 strStateFile.c_str(),
8910 vrc);
8911 }
8912
8913 /* create a snapshot machine object */
8914 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8915 pSnapshotMachine.createObject();
8916 rc = pSnapshotMachine->initFromSettings(this,
8917 data.hardware,
8918 &data.debugging,
8919 &data.autostart,
8920 data.storage,
8921 data.uuid.ref(),
8922 strStateFile);
8923 if (FAILED(rc)) return rc;
8924
8925 /* create a snapshot object */
8926 ComObjPtr<Snapshot> pSnapshot;
8927 pSnapshot.createObject();
8928 /* initialize the snapshot */
8929 rc = pSnapshot->init(mParent, // VirtualBox object
8930 data.uuid,
8931 data.strName,
8932 data.strDescription,
8933 data.timestamp,
8934 pSnapshotMachine,
8935 aParentSnapshot);
8936 if (FAILED(rc)) return rc;
8937
8938 /* memorize the first snapshot if necessary */
8939 if (!mData->mFirstSnapshot)
8940 mData->mFirstSnapshot = pSnapshot;
8941
8942 /* memorize the current snapshot when appropriate */
8943 if ( !mData->mCurrentSnapshot
8944 && pSnapshot->getId() == aCurSnapshotId
8945 )
8946 mData->mCurrentSnapshot = pSnapshot;
8947
8948 // now create the children
8949 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8950 it != data.llChildSnapshots.end();
8951 ++it)
8952 {
8953 const settings::Snapshot &childData = *it;
8954 // recurse
8955 rc = loadSnapshot(childData,
8956 aCurSnapshotId,
8957 pSnapshot); // parent = the one we created above
8958 if (FAILED(rc)) return rc;
8959 }
8960
8961 return rc;
8962}
8963
8964/**
8965 * Loads settings into mHWData.
8966 *
8967 * @param data Reference to the hardware settings.
8968 * @param pDbg Pointer to the debugging settings.
8969 * @param pAutostart Pointer to the autostart settings.
8970 */
8971HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8972 const settings::Autostart *pAutostart)
8973{
8974 AssertReturn(!isSessionMachine(), E_FAIL);
8975
8976 HRESULT rc = S_OK;
8977
8978 try
8979 {
8980 /* The hardware version attribute (optional). */
8981 mHWData->mHWVersion = data.strVersion;
8982 mHWData->mHardwareUUID = data.uuid;
8983
8984 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8985 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8986 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8987 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8988 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8989 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8990 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8991 mHWData->mPAEEnabled = data.fPAE;
8992 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8993 mHWData->mLongMode = data.enmLongMode;
8994 mHWData->mCPUCount = data.cCPUs;
8995 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8996 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8997
8998 // cpu
8999 if (mHWData->mCPUHotPlugEnabled)
9000 {
9001 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9002 it != data.llCpus.end();
9003 ++it)
9004 {
9005 const settings::Cpu &cpu = *it;
9006
9007 mHWData->mCPUAttached[cpu.ulId] = true;
9008 }
9009 }
9010
9011 // cpuid leafs
9012 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9013 it != data.llCpuIdLeafs.end();
9014 ++it)
9015 {
9016 const settings::CpuIdLeaf &leaf = *it;
9017
9018 switch (leaf.ulId)
9019 {
9020 case 0x0:
9021 case 0x1:
9022 case 0x2:
9023 case 0x3:
9024 case 0x4:
9025 case 0x5:
9026 case 0x6:
9027 case 0x7:
9028 case 0x8:
9029 case 0x9:
9030 case 0xA:
9031 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9032 break;
9033
9034 case 0x80000000:
9035 case 0x80000001:
9036 case 0x80000002:
9037 case 0x80000003:
9038 case 0x80000004:
9039 case 0x80000005:
9040 case 0x80000006:
9041 case 0x80000007:
9042 case 0x80000008:
9043 case 0x80000009:
9044 case 0x8000000A:
9045 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9046 break;
9047
9048 default:
9049 /* just ignore */
9050 break;
9051 }
9052 }
9053
9054 mHWData->mMemorySize = data.ulMemorySizeMB;
9055 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9056
9057 // boot order
9058 for (size_t i = 0;
9059 i < RT_ELEMENTS(mHWData->mBootOrder);
9060 i++)
9061 {
9062 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9063 if (it == data.mapBootOrder.end())
9064 mHWData->mBootOrder[i] = DeviceType_Null;
9065 else
9066 mHWData->mBootOrder[i] = it->second;
9067 }
9068
9069 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9070 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9071 mHWData->mMonitorCount = data.cMonitors;
9072 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9073 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9074 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9075 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9076 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9077 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9078 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9079 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9080 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9081 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9082 if (!data.strVideoCaptureFile.isEmpty())
9083 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9084 else
9085 mHWData->mVideoCaptureFile.setNull();
9086 mHWData->mFirmwareType = data.firmwareType;
9087 mHWData->mPointingHIDType = data.pointingHIDType;
9088 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9089 mHWData->mChipsetType = data.chipsetType;
9090 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9091 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9092 mHWData->mHPETEnabled = data.fHPETEnabled;
9093
9094 /* VRDEServer */
9095 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9096 if (FAILED(rc)) return rc;
9097
9098 /* BIOS */
9099 rc = mBIOSSettings->loadSettings(data.biosSettings);
9100 if (FAILED(rc)) return rc;
9101
9102 // Bandwidth control (must come before network adapters)
9103 rc = mBandwidthControl->loadSettings(data.ioSettings);
9104 if (FAILED(rc)) return rc;
9105
9106 /* USB Controller */
9107 rc = mUSBController->loadSettings(data.usbController);
9108 if (FAILED(rc)) return rc;
9109
9110 // network adapters
9111 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9112 uint32_t oldCount = mNetworkAdapters.size();
9113 if (newCount > oldCount)
9114 {
9115 mNetworkAdapters.resize(newCount);
9116 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9117 {
9118 unconst(mNetworkAdapters[slot]).createObject();
9119 mNetworkAdapters[slot]->init(this, slot);
9120 }
9121 }
9122 else if (newCount < oldCount)
9123 mNetworkAdapters.resize(newCount);
9124 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9125 it != data.llNetworkAdapters.end();
9126 ++it)
9127 {
9128 const settings::NetworkAdapter &nic = *it;
9129
9130 /* slot unicity is guaranteed by XML Schema */
9131 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9132 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9133 if (FAILED(rc)) return rc;
9134 }
9135
9136 // serial ports
9137 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9138 it != data.llSerialPorts.end();
9139 ++it)
9140 {
9141 const settings::SerialPort &s = *it;
9142
9143 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9144 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9145 if (FAILED(rc)) return rc;
9146 }
9147
9148 // parallel ports (optional)
9149 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9150 it != data.llParallelPorts.end();
9151 ++it)
9152 {
9153 const settings::ParallelPort &p = *it;
9154
9155 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9156 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9157 if (FAILED(rc)) return rc;
9158 }
9159
9160 /* AudioAdapter */
9161 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9162 if (FAILED(rc)) return rc;
9163
9164 /* Shared folders */
9165 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9166 it != data.llSharedFolders.end();
9167 ++it)
9168 {
9169 const settings::SharedFolder &sf = *it;
9170
9171 ComObjPtr<SharedFolder> sharedFolder;
9172 /* Check for double entries. Not allowed! */
9173 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9174 if (SUCCEEDED(rc))
9175 return setError(VBOX_E_OBJECT_IN_USE,
9176 tr("Shared folder named '%s' already exists"),
9177 sf.strName.c_str());
9178
9179 /* Create the new shared folder. Don't break on error. This will be
9180 * reported when the machine starts. */
9181 sharedFolder.createObject();
9182 rc = sharedFolder->init(getMachine(),
9183 sf.strName,
9184 sf.strHostPath,
9185 RT_BOOL(sf.fWritable),
9186 RT_BOOL(sf.fAutoMount),
9187 false /* fFailOnError */);
9188 if (FAILED(rc)) return rc;
9189 mHWData->mSharedFolders.push_back(sharedFolder);
9190 }
9191
9192 // Clipboard
9193 mHWData->mClipboardMode = data.clipboardMode;
9194
9195 // drag'n'drop
9196 mHWData->mDragAndDropMode = data.dragAndDropMode;
9197
9198 // guest settings
9199 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9200
9201 // IO settings
9202 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9203 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9204
9205 // Host PCI devices
9206 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9207 it != data.pciAttachments.end();
9208 ++it)
9209 {
9210 const settings::HostPCIDeviceAttachment &hpda = *it;
9211 ComObjPtr<PCIDeviceAttachment> pda;
9212
9213 pda.createObject();
9214 pda->loadSettings(this, hpda);
9215 mHWData->mPCIDeviceAssignments.push_back(pda);
9216 }
9217
9218 /*
9219 * (The following isn't really real hardware, but it lives in HWData
9220 * for reasons of convenience.)
9221 */
9222
9223#ifdef VBOX_WITH_GUEST_PROPS
9224 /* Guest properties (optional) */
9225 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9226 it != data.llGuestProperties.end();
9227 ++it)
9228 {
9229 const settings::GuestProperty &prop = *it;
9230 uint32_t fFlags = guestProp::NILFLAG;
9231 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9232 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9233 mHWData->mGuestProperties[prop.strName] = property;
9234 }
9235
9236 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9237#endif /* VBOX_WITH_GUEST_PROPS defined */
9238
9239 rc = loadDebugging(pDbg);
9240 if (FAILED(rc))
9241 return rc;
9242
9243 mHWData->mAutostart = *pAutostart;
9244
9245 /* default frontend */
9246 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9247 }
9248 catch(std::bad_alloc &)
9249 {
9250 return E_OUTOFMEMORY;
9251 }
9252
9253 AssertComRC(rc);
9254 return rc;
9255}
9256
9257/**
9258 * Called from Machine::loadHardware() to load the debugging settings of the
9259 * machine.
9260 *
9261 * @param pDbg Pointer to the settings.
9262 */
9263HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9264{
9265 mHWData->mDebugging = *pDbg;
9266 /* no more processing currently required, this will probably change. */
9267 return S_OK;
9268}
9269
9270/**
9271 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9272 *
9273 * @param data
9274 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9275 * @param puuidSnapshot
9276 * @return
9277 */
9278HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9279 const Guid *puuidRegistry,
9280 const Guid *puuidSnapshot)
9281{
9282 AssertReturn(!isSessionMachine(), E_FAIL);
9283
9284 HRESULT rc = S_OK;
9285
9286 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9287 it != data.llStorageControllers.end();
9288 ++it)
9289 {
9290 const settings::StorageController &ctlData = *it;
9291
9292 ComObjPtr<StorageController> pCtl;
9293 /* Try to find one with the name first. */
9294 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9295 if (SUCCEEDED(rc))
9296 return setError(VBOX_E_OBJECT_IN_USE,
9297 tr("Storage controller named '%s' already exists"),
9298 ctlData.strName.c_str());
9299
9300 pCtl.createObject();
9301 rc = pCtl->init(this,
9302 ctlData.strName,
9303 ctlData.storageBus,
9304 ctlData.ulInstance,
9305 ctlData.fBootable);
9306 if (FAILED(rc)) return rc;
9307
9308 mStorageControllers->push_back(pCtl);
9309
9310 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9311 if (FAILED(rc)) return rc;
9312
9313 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9314 if (FAILED(rc)) return rc;
9315
9316 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9317 if (FAILED(rc)) return rc;
9318
9319 /* Set IDE emulation settings (only for AHCI controller). */
9320 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9321 {
9322 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9323 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9324 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9325 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9326 )
9327 return rc;
9328 }
9329
9330 /* Load the attached devices now. */
9331 rc = loadStorageDevices(pCtl,
9332 ctlData,
9333 puuidRegistry,
9334 puuidSnapshot);
9335 if (FAILED(rc)) return rc;
9336 }
9337
9338 return S_OK;
9339}
9340
9341/**
9342 * Called from loadStorageControllers for a controller's devices.
9343 *
9344 * @param aStorageController
9345 * @param data
9346 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9347 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9348 * @return
9349 */
9350HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9351 const settings::StorageController &data,
9352 const Guid *puuidRegistry,
9353 const Guid *puuidSnapshot)
9354{
9355 HRESULT rc = S_OK;
9356
9357 /* paranoia: detect duplicate attachments */
9358 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9359 it != data.llAttachedDevices.end();
9360 ++it)
9361 {
9362 const settings::AttachedDevice &ad = *it;
9363
9364 for (settings::AttachedDevicesList::const_iterator it2 = it;
9365 it2 != data.llAttachedDevices.end();
9366 ++it2)
9367 {
9368 if (it == it2)
9369 continue;
9370
9371 const settings::AttachedDevice &ad2 = *it2;
9372
9373 if ( ad.lPort == ad2.lPort
9374 && ad.lDevice == ad2.lDevice)
9375 {
9376 return setError(E_FAIL,
9377 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9378 aStorageController->getName().c_str(),
9379 ad.lPort,
9380 ad.lDevice,
9381 mUserData->s.strName.c_str());
9382 }
9383 }
9384 }
9385
9386 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9387 it != data.llAttachedDevices.end();
9388 ++it)
9389 {
9390 const settings::AttachedDevice &dev = *it;
9391 ComObjPtr<Medium> medium;
9392
9393 switch (dev.deviceType)
9394 {
9395 case DeviceType_Floppy:
9396 case DeviceType_DVD:
9397 if (dev.strHostDriveSrc.isNotEmpty())
9398 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9399 else
9400 rc = mParent->findRemoveableMedium(dev.deviceType,
9401 dev.uuid,
9402 false /* fRefresh */,
9403 false /* aSetError */,
9404 medium);
9405 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9406 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9407 rc = S_OK;
9408 break;
9409
9410 case DeviceType_HardDisk:
9411 {
9412 /* find a hard disk by UUID */
9413 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9414 if (FAILED(rc))
9415 {
9416 if (isSnapshotMachine())
9417 {
9418 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9419 // so the user knows that the bad disk is in a snapshot somewhere
9420 com::ErrorInfo info;
9421 return setError(E_FAIL,
9422 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9423 puuidSnapshot->raw(),
9424 info.getText().raw());
9425 }
9426 else
9427 return rc;
9428 }
9429
9430 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9431
9432 if (medium->getType() == MediumType_Immutable)
9433 {
9434 if (isSnapshotMachine())
9435 return setError(E_FAIL,
9436 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9437 "of the virtual machine '%s' ('%s')"),
9438 medium->getLocationFull().c_str(),
9439 dev.uuid.raw(),
9440 puuidSnapshot->raw(),
9441 mUserData->s.strName.c_str(),
9442 mData->m_strConfigFileFull.c_str());
9443
9444 return setError(E_FAIL,
9445 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9446 medium->getLocationFull().c_str(),
9447 dev.uuid.raw(),
9448 mUserData->s.strName.c_str(),
9449 mData->m_strConfigFileFull.c_str());
9450 }
9451
9452 if (medium->getType() == MediumType_MultiAttach)
9453 {
9454 if (isSnapshotMachine())
9455 return setError(E_FAIL,
9456 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9457 "of the virtual machine '%s' ('%s')"),
9458 medium->getLocationFull().c_str(),
9459 dev.uuid.raw(),
9460 puuidSnapshot->raw(),
9461 mUserData->s.strName.c_str(),
9462 mData->m_strConfigFileFull.c_str());
9463
9464 return setError(E_FAIL,
9465 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9466 medium->getLocationFull().c_str(),
9467 dev.uuid.raw(),
9468 mUserData->s.strName.c_str(),
9469 mData->m_strConfigFileFull.c_str());
9470 }
9471
9472 if ( !isSnapshotMachine()
9473 && medium->getChildren().size() != 0
9474 )
9475 return setError(E_FAIL,
9476 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9477 "because it has %d differencing child hard disks"),
9478 medium->getLocationFull().c_str(),
9479 dev.uuid.raw(),
9480 mUserData->s.strName.c_str(),
9481 mData->m_strConfigFileFull.c_str(),
9482 medium->getChildren().size());
9483
9484 if (findAttachment(mMediaData->mAttachments,
9485 medium))
9486 return setError(E_FAIL,
9487 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9488 medium->getLocationFull().c_str(),
9489 dev.uuid.raw(),
9490 mUserData->s.strName.c_str(),
9491 mData->m_strConfigFileFull.c_str());
9492
9493 break;
9494 }
9495
9496 default:
9497 return setError(E_FAIL,
9498 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9499 medium->getLocationFull().c_str(),
9500 mUserData->s.strName.c_str(),
9501 mData->m_strConfigFileFull.c_str());
9502 }
9503
9504 if (FAILED(rc))
9505 break;
9506
9507 /* Bandwidth groups are loaded at this point. */
9508 ComObjPtr<BandwidthGroup> pBwGroup;
9509
9510 if (!dev.strBwGroup.isEmpty())
9511 {
9512 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9513 if (FAILED(rc))
9514 return setError(E_FAIL,
9515 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9516 medium->getLocationFull().c_str(),
9517 dev.strBwGroup.c_str(),
9518 mUserData->s.strName.c_str(),
9519 mData->m_strConfigFileFull.c_str());
9520 pBwGroup->reference();
9521 }
9522
9523 const Bstr controllerName = aStorageController->getName();
9524 ComObjPtr<MediumAttachment> pAttachment;
9525 pAttachment.createObject();
9526 rc = pAttachment->init(this,
9527 medium,
9528 controllerName,
9529 dev.lPort,
9530 dev.lDevice,
9531 dev.deviceType,
9532 false,
9533 dev.fPassThrough,
9534 dev.fTempEject,
9535 dev.fNonRotational,
9536 dev.fDiscard,
9537 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9538 if (FAILED(rc)) break;
9539
9540 /* associate the medium with this machine and snapshot */
9541 if (!medium.isNull())
9542 {
9543 AutoCaller medCaller(medium);
9544 if (FAILED(medCaller.rc())) return medCaller.rc();
9545 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9546
9547 if (isSnapshotMachine())
9548 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9549 else
9550 rc = medium->addBackReference(mData->mUuid);
9551 /* If the medium->addBackReference fails it sets an appropriate
9552 * error message, so no need to do any guesswork here. */
9553
9554 if (puuidRegistry)
9555 // caller wants registry ID to be set on all attached media (OVF import case)
9556 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9557 }
9558
9559 if (FAILED(rc))
9560 break;
9561
9562 /* back up mMediaData to let registeredInit() properly rollback on failure
9563 * (= limited accessibility) */
9564 setModified(IsModified_Storage);
9565 mMediaData.backup();
9566 mMediaData->mAttachments.push_back(pAttachment);
9567 }
9568
9569 return rc;
9570}
9571
9572/**
9573 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9574 *
9575 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9576 * @param aSnapshot where to return the found snapshot
9577 * @param aSetError true to set extended error info on failure
9578 */
9579HRESULT Machine::findSnapshotById(const Guid &aId,
9580 ComObjPtr<Snapshot> &aSnapshot,
9581 bool aSetError /* = false */)
9582{
9583 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9584
9585 if (!mData->mFirstSnapshot)
9586 {
9587 if (aSetError)
9588 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9589 return E_FAIL;
9590 }
9591
9592 if (aId.isZero())
9593 aSnapshot = mData->mFirstSnapshot;
9594 else
9595 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9596
9597 if (!aSnapshot)
9598 {
9599 if (aSetError)
9600 return setError(E_FAIL,
9601 tr("Could not find a snapshot with UUID {%s}"),
9602 aId.toString().c_str());
9603 return E_FAIL;
9604 }
9605
9606 return S_OK;
9607}
9608
9609/**
9610 * Returns the snapshot with the given name or fails of no such snapshot.
9611 *
9612 * @param aName snapshot name to find
9613 * @param aSnapshot where to return the found snapshot
9614 * @param aSetError true to set extended error info on failure
9615 */
9616HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9617 ComObjPtr<Snapshot> &aSnapshot,
9618 bool aSetError /* = false */)
9619{
9620 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9621
9622 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9623
9624 if (!mData->mFirstSnapshot)
9625 {
9626 if (aSetError)
9627 return setError(VBOX_E_OBJECT_NOT_FOUND,
9628 tr("This machine does not have any snapshots"));
9629 return VBOX_E_OBJECT_NOT_FOUND;
9630 }
9631
9632 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9633
9634 if (!aSnapshot)
9635 {
9636 if (aSetError)
9637 return setError(VBOX_E_OBJECT_NOT_FOUND,
9638 tr("Could not find a snapshot named '%s'"), strName.c_str());
9639 return VBOX_E_OBJECT_NOT_FOUND;
9640 }
9641
9642 return S_OK;
9643}
9644
9645/**
9646 * Returns a storage controller object with the given name.
9647 *
9648 * @param aName storage controller name to find
9649 * @param aStorageController where to return the found storage controller
9650 * @param aSetError true to set extended error info on failure
9651 */
9652HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9653 ComObjPtr<StorageController> &aStorageController,
9654 bool aSetError /* = false */)
9655{
9656 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9657
9658 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9659 it != mStorageControllers->end();
9660 ++it)
9661 {
9662 if ((*it)->getName() == aName)
9663 {
9664 aStorageController = (*it);
9665 return S_OK;
9666 }
9667 }
9668
9669 if (aSetError)
9670 return setError(VBOX_E_OBJECT_NOT_FOUND,
9671 tr("Could not find a storage controller named '%s'"),
9672 aName.c_str());
9673 return VBOX_E_OBJECT_NOT_FOUND;
9674}
9675
9676HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9677 MediaData::AttachmentList &atts)
9678{
9679 AutoCaller autoCaller(this);
9680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9681
9682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9683
9684 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9685 it != mMediaData->mAttachments.end();
9686 ++it)
9687 {
9688 const ComObjPtr<MediumAttachment> &pAtt = *it;
9689
9690 // should never happen, but deal with NULL pointers in the list.
9691 AssertStmt(!pAtt.isNull(), continue);
9692
9693 // getControllerName() needs caller+read lock
9694 AutoCaller autoAttCaller(pAtt);
9695 if (FAILED(autoAttCaller.rc()))
9696 {
9697 atts.clear();
9698 return autoAttCaller.rc();
9699 }
9700 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9701
9702 if (pAtt->getControllerName() == aName)
9703 atts.push_back(pAtt);
9704 }
9705
9706 return S_OK;
9707}
9708
9709/**
9710 * Helper for #saveSettings. Cares about renaming the settings directory and
9711 * file if the machine name was changed and about creating a new settings file
9712 * if this is a new machine.
9713 *
9714 * @note Must be never called directly but only from #saveSettings().
9715 */
9716HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9717{
9718 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9719
9720 HRESULT rc = S_OK;
9721
9722 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9723
9724 /// @todo need to handle primary group change, too
9725
9726 /* attempt to rename the settings file if machine name is changed */
9727 if ( mUserData->s.fNameSync
9728 && mUserData.isBackedUp()
9729 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9730 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9731 )
9732 {
9733 bool dirRenamed = false;
9734 bool fileRenamed = false;
9735
9736 Utf8Str configFile, newConfigFile;
9737 Utf8Str configFilePrev, newConfigFilePrev;
9738 Utf8Str configDir, newConfigDir;
9739
9740 do
9741 {
9742 int vrc = VINF_SUCCESS;
9743
9744 Utf8Str name = mUserData.backedUpData()->s.strName;
9745 Utf8Str newName = mUserData->s.strName;
9746 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9747 if (group == "/")
9748 group.setNull();
9749 Utf8Str newGroup = mUserData->s.llGroups.front();
9750 if (newGroup == "/")
9751 newGroup.setNull();
9752
9753 configFile = mData->m_strConfigFileFull;
9754
9755 /* first, rename the directory if it matches the group and machine name */
9756 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9757 group.c_str(), RTPATH_DELIMITER, name.c_str());
9758 /** @todo hack, make somehow use of ComposeMachineFilename */
9759 if (mUserData->s.fDirectoryIncludesUUID)
9760 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9761 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9762 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9763 /** @todo hack, make somehow use of ComposeMachineFilename */
9764 if (mUserData->s.fDirectoryIncludesUUID)
9765 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9766 configDir = configFile;
9767 configDir.stripFilename();
9768 newConfigDir = configDir;
9769 if ( configDir.length() >= groupPlusName.length()
9770 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9771 {
9772 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9773 Utf8Str newConfigBaseDir(newConfigDir);
9774 newConfigDir.append(newGroupPlusName);
9775 /* consistency: use \ if appropriate on the platform */
9776 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9777 /* new dir and old dir cannot be equal here because of 'if'
9778 * above and because name != newName */
9779 Assert(configDir != newConfigDir);
9780 if (!fSettingsFileIsNew)
9781 {
9782 /* perform real rename only if the machine is not new */
9783 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9784 if ( vrc == VERR_FILE_NOT_FOUND
9785 || vrc == VERR_PATH_NOT_FOUND)
9786 {
9787 /* create the parent directory, then retry renaming */
9788 Utf8Str parent(newConfigDir);
9789 parent.stripFilename();
9790 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9791 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9792 }
9793 if (RT_FAILURE(vrc))
9794 {
9795 rc = setError(E_FAIL,
9796 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9797 configDir.c_str(),
9798 newConfigDir.c_str(),
9799 vrc);
9800 break;
9801 }
9802 /* delete subdirectories which are no longer needed */
9803 Utf8Str dir(configDir);
9804 dir.stripFilename();
9805 while (dir != newConfigBaseDir && dir != ".")
9806 {
9807 vrc = RTDirRemove(dir.c_str());
9808 if (RT_FAILURE(vrc))
9809 break;
9810 dir.stripFilename();
9811 }
9812 dirRenamed = true;
9813 }
9814 }
9815
9816 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9817 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9818
9819 /* then try to rename the settings file itself */
9820 if (newConfigFile != configFile)
9821 {
9822 /* get the path to old settings file in renamed directory */
9823 configFile = Utf8StrFmt("%s%c%s",
9824 newConfigDir.c_str(),
9825 RTPATH_DELIMITER,
9826 RTPathFilename(configFile.c_str()));
9827 if (!fSettingsFileIsNew)
9828 {
9829 /* perform real rename only if the machine is not new */
9830 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9831 if (RT_FAILURE(vrc))
9832 {
9833 rc = setError(E_FAIL,
9834 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9835 configFile.c_str(),
9836 newConfigFile.c_str(),
9837 vrc);
9838 break;
9839 }
9840 fileRenamed = true;
9841 configFilePrev = configFile;
9842 configFilePrev += "-prev";
9843 newConfigFilePrev = newConfigFile;
9844 newConfigFilePrev += "-prev";
9845 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9846 }
9847 }
9848
9849 // update m_strConfigFileFull amd mConfigFile
9850 mData->m_strConfigFileFull = newConfigFile;
9851 // compute the relative path too
9852 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9853
9854 // store the old and new so that VirtualBox::saveSettings() can update
9855 // the media registry
9856 if ( mData->mRegistered
9857 && configDir != newConfigDir)
9858 {
9859 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9860
9861 if (pfNeedsGlobalSaveSettings)
9862 *pfNeedsGlobalSaveSettings = true;
9863 }
9864
9865 // in the saved state file path, replace the old directory with the new directory
9866 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9867 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9868
9869 // and do the same thing for the saved state file paths of all the online snapshots
9870 if (mData->mFirstSnapshot)
9871 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9872 newConfigDir.c_str());
9873 }
9874 while (0);
9875
9876 if (FAILED(rc))
9877 {
9878 /* silently try to rename everything back */
9879 if (fileRenamed)
9880 {
9881 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9882 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9883 }
9884 if (dirRenamed)
9885 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9886 }
9887
9888 if (FAILED(rc)) return rc;
9889 }
9890
9891 if (fSettingsFileIsNew)
9892 {
9893 /* create a virgin config file */
9894 int vrc = VINF_SUCCESS;
9895
9896 /* ensure the settings directory exists */
9897 Utf8Str path(mData->m_strConfigFileFull);
9898 path.stripFilename();
9899 if (!RTDirExists(path.c_str()))
9900 {
9901 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9902 if (RT_FAILURE(vrc))
9903 {
9904 return setError(E_FAIL,
9905 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9906 path.c_str(),
9907 vrc);
9908 }
9909 }
9910
9911 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9912 path = Utf8Str(mData->m_strConfigFileFull);
9913 RTFILE f = NIL_RTFILE;
9914 vrc = RTFileOpen(&f, path.c_str(),
9915 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9916 if (RT_FAILURE(vrc))
9917 return setError(E_FAIL,
9918 tr("Could not create the settings file '%s' (%Rrc)"),
9919 path.c_str(),
9920 vrc);
9921 RTFileClose(f);
9922 }
9923
9924 return rc;
9925}
9926
9927/**
9928 * Saves and commits machine data, user data and hardware data.
9929 *
9930 * Note that on failure, the data remains uncommitted.
9931 *
9932 * @a aFlags may combine the following flags:
9933 *
9934 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9935 * Used when saving settings after an operation that makes them 100%
9936 * correspond to the settings from the current snapshot.
9937 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9938 * #isReallyModified() returns false. This is necessary for cases when we
9939 * change machine data directly, not through the backup()/commit() mechanism.
9940 * - SaveS_Force: settings will be saved without doing a deep compare of the
9941 * settings structures. This is used when this is called because snapshots
9942 * have changed to avoid the overhead of the deep compare.
9943 *
9944 * @note Must be called from under this object's write lock. Locks children for
9945 * writing.
9946 *
9947 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9948 * initialized to false and that will be set to true by this function if
9949 * the caller must invoke VirtualBox::saveSettings() because the global
9950 * settings have changed. This will happen if a machine rename has been
9951 * saved and the global machine and media registries will therefore need
9952 * updating.
9953 */
9954HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9955 int aFlags /*= 0*/)
9956{
9957 LogFlowThisFuncEnter();
9958
9959 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9960
9961 /* make sure child objects are unable to modify the settings while we are
9962 * saving them */
9963 ensureNoStateDependencies();
9964
9965 AssertReturn(!isSnapshotMachine(),
9966 E_FAIL);
9967
9968 HRESULT rc = S_OK;
9969 bool fNeedsWrite = false;
9970
9971 /* First, prepare to save settings. It will care about renaming the
9972 * settings directory and file if the machine name was changed and about
9973 * creating a new settings file if this is a new machine. */
9974 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9975 if (FAILED(rc)) return rc;
9976
9977 // keep a pointer to the current settings structures
9978 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9979 settings::MachineConfigFile *pNewConfig = NULL;
9980
9981 try
9982 {
9983 // make a fresh one to have everyone write stuff into
9984 pNewConfig = new settings::MachineConfigFile(NULL);
9985 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9986
9987 // now go and copy all the settings data from COM to the settings structures
9988 // (this calles saveSettings() on all the COM objects in the machine)
9989 copyMachineDataToSettings(*pNewConfig);
9990
9991 if (aFlags & SaveS_ResetCurStateModified)
9992 {
9993 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9994 mData->mCurrentStateModified = FALSE;
9995 fNeedsWrite = true; // always, no need to compare
9996 }
9997 else if (aFlags & SaveS_Force)
9998 {
9999 fNeedsWrite = true; // always, no need to compare
10000 }
10001 else
10002 {
10003 if (!mData->mCurrentStateModified)
10004 {
10005 // do a deep compare of the settings that we just saved with the settings
10006 // previously stored in the config file; this invokes MachineConfigFile::operator==
10007 // which does a deep compare of all the settings, which is expensive but less expensive
10008 // than writing out XML in vain
10009 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10010
10011 // could still be modified if any settings changed
10012 mData->mCurrentStateModified = fAnySettingsChanged;
10013
10014 fNeedsWrite = fAnySettingsChanged;
10015 }
10016 else
10017 fNeedsWrite = true;
10018 }
10019
10020 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10021
10022 if (fNeedsWrite)
10023 // now spit it all out!
10024 pNewConfig->write(mData->m_strConfigFileFull);
10025
10026 mData->pMachineConfigFile = pNewConfig;
10027 delete pOldConfig;
10028 commit();
10029
10030 // after saving settings, we are no longer different from the XML on disk
10031 mData->flModifications = 0;
10032 }
10033 catch (HRESULT err)
10034 {
10035 // we assume that error info is set by the thrower
10036 rc = err;
10037
10038 // restore old config
10039 delete pNewConfig;
10040 mData->pMachineConfigFile = pOldConfig;
10041 }
10042 catch (...)
10043 {
10044 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10045 }
10046
10047 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10048 {
10049 /* Fire the data change event, even on failure (since we've already
10050 * committed all data). This is done only for SessionMachines because
10051 * mutable Machine instances are always not registered (i.e. private
10052 * to the client process that creates them) and thus don't need to
10053 * inform callbacks. */
10054 if (isSessionMachine())
10055 mParent->onMachineDataChange(mData->mUuid);
10056 }
10057
10058 LogFlowThisFunc(("rc=%08X\n", rc));
10059 LogFlowThisFuncLeave();
10060 return rc;
10061}
10062
10063/**
10064 * Implementation for saving the machine settings into the given
10065 * settings::MachineConfigFile instance. This copies machine extradata
10066 * from the previous machine config file in the instance data, if any.
10067 *
10068 * This gets called from two locations:
10069 *
10070 * -- Machine::saveSettings(), during the regular XML writing;
10071 *
10072 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10073 * exported to OVF and we write the VirtualBox proprietary XML
10074 * into a <vbox:Machine> tag.
10075 *
10076 * This routine fills all the fields in there, including snapshots, *except*
10077 * for the following:
10078 *
10079 * -- fCurrentStateModified. There is some special logic associated with that.
10080 *
10081 * The caller can then call MachineConfigFile::write() or do something else
10082 * with it.
10083 *
10084 * Caller must hold the machine lock!
10085 *
10086 * This throws XML errors and HRESULT, so the caller must have a catch block!
10087 */
10088void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10089{
10090 // deep copy extradata
10091 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10092
10093 config.uuid = mData->mUuid;
10094
10095 // copy name, description, OS type, teleport, UTC etc.
10096 config.machineUserData = mUserData->s;
10097
10098 // Encode the Icon Override data from Machine and store on config userdata.
10099 com::SafeArray<BYTE> iconByte;
10100 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10101 ssize_t cbData = iconByte.size();
10102 if (cbData > 0)
10103 {
10104 ssize_t cchOut = RTBase64EncodedLength(cbData);
10105 Utf8Str strIconData;
10106 strIconData.reserve(cchOut+1);
10107 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10108 strIconData.mutableRaw(), strIconData.capacity(),
10109 NULL);
10110 if (RT_FAILURE(vrc))
10111 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10112 strIconData.jolt();
10113 config.machineUserData.ovIcon = strIconData;
10114 }
10115 else
10116 config.machineUserData.ovIcon.setNull();
10117
10118 if ( mData->mMachineState == MachineState_Saved
10119 || mData->mMachineState == MachineState_Restoring
10120 // when deleting a snapshot we may or may not have a saved state in the current state,
10121 // so let's not assert here please
10122 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10123 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10124 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10125 && (!mSSData->strStateFilePath.isEmpty())
10126 )
10127 )
10128 {
10129 Assert(!mSSData->strStateFilePath.isEmpty());
10130 /* try to make the file name relative to the settings file dir */
10131 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10132 }
10133 else
10134 {
10135 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10136 config.strStateFile.setNull();
10137 }
10138
10139 if (mData->mCurrentSnapshot)
10140 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10141 else
10142 config.uuidCurrentSnapshot.clear();
10143
10144 config.timeLastStateChange = mData->mLastStateChange;
10145 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10146 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10147
10148 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10149 if (FAILED(rc)) throw rc;
10150
10151 rc = saveStorageControllers(config.storageMachine);
10152 if (FAILED(rc)) throw rc;
10153
10154 // save machine's media registry if this is VirtualBox 4.0 or later
10155 if (config.canHaveOwnMediaRegistry())
10156 {
10157 // determine machine folder
10158 Utf8Str strMachineFolder = getSettingsFileFull();
10159 strMachineFolder.stripFilename();
10160 mParent->saveMediaRegistry(config.mediaRegistry,
10161 getId(), // only media with registry ID == machine UUID
10162 strMachineFolder);
10163 // this throws HRESULT
10164 }
10165
10166 // save snapshots
10167 rc = saveAllSnapshots(config);
10168 if (FAILED(rc)) throw rc;
10169}
10170
10171/**
10172 * Saves all snapshots of the machine into the given machine config file. Called
10173 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10174 * @param config
10175 * @return
10176 */
10177HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10178{
10179 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10180
10181 HRESULT rc = S_OK;
10182
10183 try
10184 {
10185 config.llFirstSnapshot.clear();
10186
10187 if (mData->mFirstSnapshot)
10188 {
10189 settings::Snapshot snapNew;
10190 config.llFirstSnapshot.push_back(snapNew);
10191
10192 // get reference to the fresh copy of the snapshot on the list and
10193 // work on that copy directly to avoid excessive copying later
10194 settings::Snapshot &snap = config.llFirstSnapshot.front();
10195
10196 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10197 if (FAILED(rc)) throw rc;
10198 }
10199
10200// if (mType == IsSessionMachine)
10201// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10202
10203 }
10204 catch (HRESULT err)
10205 {
10206 /* we assume that error info is set by the thrower */
10207 rc = err;
10208 }
10209 catch (...)
10210 {
10211 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10212 }
10213
10214 return rc;
10215}
10216
10217/**
10218 * Saves the VM hardware configuration. It is assumed that the
10219 * given node is empty.
10220 *
10221 * @param data Reference to the settings object for the hardware config.
10222 * @param pDbg Pointer to the settings object for the debugging config
10223 * which happens to live in mHWData.
10224 * @param pAutostart Pointer to the settings object for the autostart config
10225 * which happens to live in mHWData.
10226 */
10227HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10228 settings::Autostart *pAutostart)
10229{
10230 HRESULT rc = S_OK;
10231
10232 try
10233 {
10234 /* The hardware version attribute (optional).
10235 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10236 if ( mHWData->mHWVersion == "1"
10237 && mSSData->strStateFilePath.isEmpty()
10238 )
10239 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. */
10240
10241 data.strVersion = mHWData->mHWVersion;
10242 data.uuid = mHWData->mHardwareUUID;
10243
10244 // CPU
10245 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10246 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10247 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10248 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10249 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10250 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10251 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10252 data.fPAE = !!mHWData->mPAEEnabled;
10253 data.enmLongMode = mHWData->mLongMode;
10254 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10255
10256 /* Standard and Extended CPUID leafs. */
10257 data.llCpuIdLeafs.clear();
10258 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10259 {
10260 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10261 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10262 }
10263 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10264 {
10265 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10266 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10267 }
10268
10269 data.cCPUs = mHWData->mCPUCount;
10270 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10271 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10272
10273 data.llCpus.clear();
10274 if (data.fCpuHotPlug)
10275 {
10276 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10277 {
10278 if (mHWData->mCPUAttached[idx])
10279 {
10280 settings::Cpu cpu;
10281 cpu.ulId = idx;
10282 data.llCpus.push_back(cpu);
10283 }
10284 }
10285 }
10286
10287 // memory
10288 data.ulMemorySizeMB = mHWData->mMemorySize;
10289 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10290
10291 // firmware
10292 data.firmwareType = mHWData->mFirmwareType;
10293
10294 // HID
10295 data.pointingHIDType = mHWData->mPointingHIDType;
10296 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10297
10298 // chipset
10299 data.chipsetType = mHWData->mChipsetType;
10300
10301 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10302 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10303
10304 // HPET
10305 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10306
10307 // boot order
10308 data.mapBootOrder.clear();
10309 for (size_t i = 0;
10310 i < RT_ELEMENTS(mHWData->mBootOrder);
10311 ++i)
10312 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10313
10314 // display
10315 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10316 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10317 data.cMonitors = mHWData->mMonitorCount;
10318 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10319 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10320 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10321 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10322 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10323 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10324 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10325 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10326 {
10327 if (mHWData->maVideoCaptureScreens[i])
10328 ASMBitSet(&data.u64VideoCaptureScreens, i);
10329 else
10330 ASMBitClear(&data.u64VideoCaptureScreens, i);
10331 }
10332 /* store relative video capture file if possible */
10333 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10334
10335 /* VRDEServer settings (optional) */
10336 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10337 if (FAILED(rc)) throw rc;
10338
10339 /* BIOS (required) */
10340 rc = mBIOSSettings->saveSettings(data.biosSettings);
10341 if (FAILED(rc)) throw rc;
10342
10343 /* USB Controller (required) */
10344 rc = mUSBController->saveSettings(data.usbController);
10345 if (FAILED(rc)) throw rc;
10346
10347 /* Network adapters (required) */
10348 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10349 data.llNetworkAdapters.clear();
10350 /* Write out only the nominal number of network adapters for this
10351 * chipset type. Since Machine::commit() hasn't been called there
10352 * may be extra NIC settings in the vector. */
10353 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10354 {
10355 settings::NetworkAdapter nic;
10356 nic.ulSlot = slot;
10357 /* paranoia check... must not be NULL, but must not crash either. */
10358 if (mNetworkAdapters[slot])
10359 {
10360 rc = mNetworkAdapters[slot]->saveSettings(nic);
10361 if (FAILED(rc)) throw rc;
10362
10363 data.llNetworkAdapters.push_back(nic);
10364 }
10365 }
10366
10367 /* Serial ports */
10368 data.llSerialPorts.clear();
10369 for (ULONG slot = 0;
10370 slot < RT_ELEMENTS(mSerialPorts);
10371 ++slot)
10372 {
10373 settings::SerialPort s;
10374 s.ulSlot = slot;
10375 rc = mSerialPorts[slot]->saveSettings(s);
10376 if (FAILED(rc)) return rc;
10377
10378 data.llSerialPorts.push_back(s);
10379 }
10380
10381 /* Parallel ports */
10382 data.llParallelPorts.clear();
10383 for (ULONG slot = 0;
10384 slot < RT_ELEMENTS(mParallelPorts);
10385 ++slot)
10386 {
10387 settings::ParallelPort p;
10388 p.ulSlot = slot;
10389 rc = mParallelPorts[slot]->saveSettings(p);
10390 if (FAILED(rc)) return rc;
10391
10392 data.llParallelPorts.push_back(p);
10393 }
10394
10395 /* Audio adapter */
10396 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10397 if (FAILED(rc)) return rc;
10398
10399 /* Shared folders */
10400 data.llSharedFolders.clear();
10401 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10402 it != mHWData->mSharedFolders.end();
10403 ++it)
10404 {
10405 SharedFolder *pSF = *it;
10406 AutoCaller sfCaller(pSF);
10407 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10408 settings::SharedFolder sf;
10409 sf.strName = pSF->getName();
10410 sf.strHostPath = pSF->getHostPath();
10411 sf.fWritable = !!pSF->isWritable();
10412 sf.fAutoMount = !!pSF->isAutoMounted();
10413
10414 data.llSharedFolders.push_back(sf);
10415 }
10416
10417 // clipboard
10418 data.clipboardMode = mHWData->mClipboardMode;
10419
10420 // drag'n'drop
10421 data.dragAndDropMode = mHWData->mDragAndDropMode;
10422
10423 /* Guest */
10424 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10425
10426 // IO settings
10427 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10428 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10429
10430 /* BandwidthControl (required) */
10431 rc = mBandwidthControl->saveSettings(data.ioSettings);
10432 if (FAILED(rc)) throw rc;
10433
10434 /* Host PCI devices */
10435 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10436 it != mHWData->mPCIDeviceAssignments.end();
10437 ++it)
10438 {
10439 ComObjPtr<PCIDeviceAttachment> pda = *it;
10440 settings::HostPCIDeviceAttachment hpda;
10441
10442 rc = pda->saveSettings(hpda);
10443 if (FAILED(rc)) throw rc;
10444
10445 data.pciAttachments.push_back(hpda);
10446 }
10447
10448
10449 // guest properties
10450 data.llGuestProperties.clear();
10451#ifdef VBOX_WITH_GUEST_PROPS
10452 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10453 it != mHWData->mGuestProperties.end();
10454 ++it)
10455 {
10456 HWData::GuestProperty property = it->second;
10457
10458 /* Remove transient guest properties at shutdown unless we
10459 * are saving state */
10460 if ( ( mData->mMachineState == MachineState_PoweredOff
10461 || mData->mMachineState == MachineState_Aborted
10462 || mData->mMachineState == MachineState_Teleported)
10463 && ( property.mFlags & guestProp::TRANSIENT
10464 || property.mFlags & guestProp::TRANSRESET))
10465 continue;
10466 settings::GuestProperty prop;
10467 prop.strName = it->first;
10468 prop.strValue = property.strValue;
10469 prop.timestamp = property.mTimestamp;
10470 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10471 guestProp::writeFlags(property.mFlags, szFlags);
10472 prop.strFlags = szFlags;
10473
10474 data.llGuestProperties.push_back(prop);
10475 }
10476
10477 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10478 /* I presume this doesn't require a backup(). */
10479 mData->mGuestPropertiesModified = FALSE;
10480#endif /* VBOX_WITH_GUEST_PROPS defined */
10481
10482 *pDbg = mHWData->mDebugging;
10483 *pAutostart = mHWData->mAutostart;
10484
10485 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10486 }
10487 catch(std::bad_alloc &)
10488 {
10489 return E_OUTOFMEMORY;
10490 }
10491
10492 AssertComRC(rc);
10493 return rc;
10494}
10495
10496/**
10497 * Saves the storage controller configuration.
10498 *
10499 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10500 */
10501HRESULT Machine::saveStorageControllers(settings::Storage &data)
10502{
10503 data.llStorageControllers.clear();
10504
10505 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10506 it != mStorageControllers->end();
10507 ++it)
10508 {
10509 HRESULT rc;
10510 ComObjPtr<StorageController> pCtl = *it;
10511
10512 settings::StorageController ctl;
10513 ctl.strName = pCtl->getName();
10514 ctl.controllerType = pCtl->getControllerType();
10515 ctl.storageBus = pCtl->getStorageBus();
10516 ctl.ulInstance = pCtl->getInstance();
10517 ctl.fBootable = pCtl->getBootable();
10518
10519 /* Save the port count. */
10520 ULONG portCount;
10521 rc = pCtl->COMGETTER(PortCount)(&portCount);
10522 ComAssertComRCRet(rc, rc);
10523 ctl.ulPortCount = portCount;
10524
10525 /* Save fUseHostIOCache */
10526 BOOL fUseHostIOCache;
10527 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10528 ComAssertComRCRet(rc, rc);
10529 ctl.fUseHostIOCache = !!fUseHostIOCache;
10530
10531 /* Save IDE emulation settings. */
10532 if (ctl.controllerType == StorageControllerType_IntelAhci)
10533 {
10534 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10535 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10536 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10537 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10538 )
10539 ComAssertComRCRet(rc, rc);
10540 }
10541
10542 /* save the devices now. */
10543 rc = saveStorageDevices(pCtl, ctl);
10544 ComAssertComRCRet(rc, rc);
10545
10546 data.llStorageControllers.push_back(ctl);
10547 }
10548
10549 return S_OK;
10550}
10551
10552/**
10553 * Saves the hard disk configuration.
10554 */
10555HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10556 settings::StorageController &data)
10557{
10558 MediaData::AttachmentList atts;
10559
10560 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10561 if (FAILED(rc)) return rc;
10562
10563 data.llAttachedDevices.clear();
10564 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10565 it != atts.end();
10566 ++it)
10567 {
10568 settings::AttachedDevice dev;
10569
10570 MediumAttachment *pAttach = *it;
10571 Medium *pMedium = pAttach->getMedium();
10572
10573 dev.deviceType = pAttach->getType();
10574 dev.lPort = pAttach->getPort();
10575 dev.lDevice = pAttach->getDevice();
10576 if (pMedium)
10577 {
10578 if (pMedium->isHostDrive())
10579 dev.strHostDriveSrc = pMedium->getLocationFull();
10580 else
10581 dev.uuid = pMedium->getId();
10582 dev.fPassThrough = pAttach->getPassthrough();
10583 dev.fTempEject = pAttach->getTempEject();
10584 dev.fNonRotational = pAttach->getNonRotational();
10585 dev.fDiscard = pAttach->getDiscard();
10586 }
10587
10588 dev.strBwGroup = pAttach->getBandwidthGroup();
10589
10590 data.llAttachedDevices.push_back(dev);
10591 }
10592
10593 return S_OK;
10594}
10595
10596/**
10597 * Saves machine state settings as defined by aFlags
10598 * (SaveSTS_* values).
10599 *
10600 * @param aFlags Combination of SaveSTS_* flags.
10601 *
10602 * @note Locks objects for writing.
10603 */
10604HRESULT Machine::saveStateSettings(int aFlags)
10605{
10606 if (aFlags == 0)
10607 return S_OK;
10608
10609 AutoCaller autoCaller(this);
10610 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10611
10612 /* This object's write lock is also necessary to serialize file access
10613 * (prevent concurrent reads and writes) */
10614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10615
10616 HRESULT rc = S_OK;
10617
10618 Assert(mData->pMachineConfigFile);
10619
10620 try
10621 {
10622 if (aFlags & SaveSTS_CurStateModified)
10623 mData->pMachineConfigFile->fCurrentStateModified = true;
10624
10625 if (aFlags & SaveSTS_StateFilePath)
10626 {
10627 if (!mSSData->strStateFilePath.isEmpty())
10628 /* try to make the file name relative to the settings file dir */
10629 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10630 else
10631 mData->pMachineConfigFile->strStateFile.setNull();
10632 }
10633
10634 if (aFlags & SaveSTS_StateTimeStamp)
10635 {
10636 Assert( mData->mMachineState != MachineState_Aborted
10637 || mSSData->strStateFilePath.isEmpty());
10638
10639 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10640
10641 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10642//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10643 }
10644
10645 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10646 }
10647 catch (...)
10648 {
10649 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10650 }
10651
10652 return rc;
10653}
10654
10655/**
10656 * Ensures that the given medium is added to a media registry. If this machine
10657 * was created with 4.0 or later, then the machine registry is used. Otherwise
10658 * the global VirtualBox media registry is used.
10659 *
10660 * Caller must NOT hold machine lock, media tree or any medium locks!
10661 *
10662 * @param pMedium
10663 */
10664void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10665{
10666 /* Paranoia checks: do not hold machine or media tree locks. */
10667 AssertReturnVoid(!isWriteLockOnCurrentThread());
10668 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10669
10670 ComObjPtr<Medium> pBase;
10671 {
10672 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10673 pBase = pMedium->getBase();
10674 }
10675
10676 /* Paranoia checks: do not hold medium locks. */
10677 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10678 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10679
10680 // decide which medium registry to use now that the medium is attached:
10681 Guid uuid;
10682 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10683 // machine XML is VirtualBox 4.0 or higher:
10684 uuid = getId(); // machine UUID
10685 else
10686 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10687
10688 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10689 mParent->markRegistryModified(uuid);
10690
10691 /* For more complex hard disk structures it can happen that the base
10692 * medium isn't yet associated with any medium registry. Do that now. */
10693 if (pMedium != pBase)
10694 {
10695 if (pBase->addRegistry(uuid, true /* fRecurse */))
10696 mParent->markRegistryModified(uuid);
10697 }
10698}
10699
10700/**
10701 * Creates differencing hard disks for all normal hard disks attached to this
10702 * machine and a new set of attachments to refer to created disks.
10703 *
10704 * Used when taking a snapshot or when deleting the current state. Gets called
10705 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10706 *
10707 * This method assumes that mMediaData contains the original hard disk attachments
10708 * it needs to create diffs for. On success, these attachments will be replaced
10709 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10710 * called to delete created diffs which will also rollback mMediaData and restore
10711 * whatever was backed up before calling this method.
10712 *
10713 * Attachments with non-normal hard disks are left as is.
10714 *
10715 * If @a aOnline is @c false then the original hard disks that require implicit
10716 * diffs will be locked for reading. Otherwise it is assumed that they are
10717 * already locked for writing (when the VM was started). Note that in the latter
10718 * case it is responsibility of the caller to lock the newly created diffs for
10719 * writing if this method succeeds.
10720 *
10721 * @param aProgress Progress object to run (must contain at least as
10722 * many operations left as the number of hard disks
10723 * attached).
10724 * @param aOnline Whether the VM was online prior to this operation.
10725 *
10726 * @note The progress object is not marked as completed, neither on success nor
10727 * on failure. This is a responsibility of the caller.
10728 *
10729 * @note Locks this object and the media tree for writing.
10730 */
10731HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10732 ULONG aWeight,
10733 bool aOnline)
10734{
10735 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10736
10737 AutoCaller autoCaller(this);
10738 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10739
10740 AutoMultiWriteLock2 alock(this->lockHandle(),
10741 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10742
10743 /* must be in a protective state because we release the lock below */
10744 AssertReturn( mData->mMachineState == MachineState_Saving
10745 || mData->mMachineState == MachineState_LiveSnapshotting
10746 || mData->mMachineState == MachineState_RestoringSnapshot
10747 || mData->mMachineState == MachineState_DeletingSnapshot
10748 , E_FAIL);
10749
10750 HRESULT rc = S_OK;
10751
10752 // use appropriate locked media map (online or offline)
10753 MediumLockListMap lockedMediaOffline;
10754 MediumLockListMap *lockedMediaMap;
10755 if (aOnline)
10756 lockedMediaMap = &mData->mSession.mLockedMedia;
10757 else
10758 lockedMediaMap = &lockedMediaOffline;
10759
10760 try
10761 {
10762 if (!aOnline)
10763 {
10764 /* lock all attached hard disks early to detect "in use"
10765 * situations before creating actual diffs */
10766 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10767 it != mMediaData->mAttachments.end();
10768 ++it)
10769 {
10770 MediumAttachment* pAtt = *it;
10771 if (pAtt->getType() == DeviceType_HardDisk)
10772 {
10773 Medium* pMedium = pAtt->getMedium();
10774 Assert(pMedium);
10775
10776 MediumLockList *pMediumLockList(new MediumLockList());
10777 alock.release();
10778 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10779 false /* fMediumLockWrite */,
10780 NULL,
10781 *pMediumLockList);
10782 alock.acquire();
10783 if (FAILED(rc))
10784 {
10785 delete pMediumLockList;
10786 throw rc;
10787 }
10788 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10789 if (FAILED(rc))
10790 {
10791 throw setError(rc,
10792 tr("Collecting locking information for all attached media failed"));
10793 }
10794 }
10795 }
10796
10797 /* Now lock all media. If this fails, nothing is locked. */
10798 alock.release();
10799 rc = lockedMediaMap->Lock();
10800 alock.acquire();
10801 if (FAILED(rc))
10802 {
10803 throw setError(rc,
10804 tr("Locking of attached media failed"));
10805 }
10806 }
10807
10808 /* remember the current list (note that we don't use backup() since
10809 * mMediaData may be already backed up) */
10810 MediaData::AttachmentList atts = mMediaData->mAttachments;
10811
10812 /* start from scratch */
10813 mMediaData->mAttachments.clear();
10814
10815 /* go through remembered attachments and create diffs for normal hard
10816 * disks and attach them */
10817 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10818 it != atts.end();
10819 ++it)
10820 {
10821 MediumAttachment* pAtt = *it;
10822
10823 DeviceType_T devType = pAtt->getType();
10824 Medium* pMedium = pAtt->getMedium();
10825
10826 if ( devType != DeviceType_HardDisk
10827 || pMedium == NULL
10828 || pMedium->getType() != MediumType_Normal)
10829 {
10830 /* copy the attachment as is */
10831
10832 /** @todo the progress object created in Console::TakeSnaphot
10833 * only expects operations for hard disks. Later other
10834 * device types need to show up in the progress as well. */
10835 if (devType == DeviceType_HardDisk)
10836 {
10837 if (pMedium == NULL)
10838 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10839 aWeight); // weight
10840 else
10841 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10842 pMedium->getBase()->getName().c_str()).raw(),
10843 aWeight); // weight
10844 }
10845
10846 mMediaData->mAttachments.push_back(pAtt);
10847 continue;
10848 }
10849
10850 /* need a diff */
10851 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10852 pMedium->getBase()->getName().c_str()).raw(),
10853 aWeight); // weight
10854
10855 Utf8Str strFullSnapshotFolder;
10856 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10857
10858 ComObjPtr<Medium> diff;
10859 diff.createObject();
10860 // store the diff in the same registry as the parent
10861 // (this cannot fail here because we can't create implicit diffs for
10862 // unregistered images)
10863 Guid uuidRegistryParent;
10864 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10865 Assert(fInRegistry); NOREF(fInRegistry);
10866 rc = diff->init(mParent,
10867 pMedium->getPreferredDiffFormat(),
10868 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10869 uuidRegistryParent);
10870 if (FAILED(rc)) throw rc;
10871
10872 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10873 * the push_back? Looks like we're going to release medium with the
10874 * wrong kind of lock (general issue with if we fail anywhere at all)
10875 * and an orphaned VDI in the snapshots folder. */
10876
10877 /* update the appropriate lock list */
10878 MediumLockList *pMediumLockList;
10879 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10880 AssertComRCThrowRC(rc);
10881 if (aOnline)
10882 {
10883 alock.release();
10884 /* The currently attached medium will be read-only, change
10885 * the lock type to read. */
10886 rc = pMediumLockList->Update(pMedium, false);
10887 alock.acquire();
10888 AssertComRCThrowRC(rc);
10889 }
10890
10891 /* release the locks before the potentially lengthy operation */
10892 alock.release();
10893 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10894 pMediumLockList,
10895 NULL /* aProgress */,
10896 true /* aWait */);
10897 alock.acquire();
10898 if (FAILED(rc)) throw rc;
10899
10900 /* actual lock list update is done in Medium::commitMedia */
10901
10902 rc = diff->addBackReference(mData->mUuid);
10903 AssertComRCThrowRC(rc);
10904
10905 /* add a new attachment */
10906 ComObjPtr<MediumAttachment> attachment;
10907 attachment.createObject();
10908 rc = attachment->init(this,
10909 diff,
10910 pAtt->getControllerName(),
10911 pAtt->getPort(),
10912 pAtt->getDevice(),
10913 DeviceType_HardDisk,
10914 true /* aImplicit */,
10915 false /* aPassthrough */,
10916 false /* aTempEject */,
10917 pAtt->getNonRotational(),
10918 pAtt->getDiscard(),
10919 pAtt->getBandwidthGroup());
10920 if (FAILED(rc)) throw rc;
10921
10922 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10923 AssertComRCThrowRC(rc);
10924 mMediaData->mAttachments.push_back(attachment);
10925 }
10926 }
10927 catch (HRESULT aRC) { rc = aRC; }
10928
10929 /* unlock all hard disks we locked when there is no VM */
10930 if (!aOnline)
10931 {
10932 ErrorInfoKeeper eik;
10933
10934 HRESULT rc1 = lockedMediaMap->Clear();
10935 AssertComRC(rc1);
10936 }
10937
10938 return rc;
10939}
10940
10941/**
10942 * Deletes implicit differencing hard disks created either by
10943 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10944 *
10945 * Note that to delete hard disks created by #AttachDevice() this method is
10946 * called from #fixupMedia() when the changes are rolled back.
10947 *
10948 * @note Locks this object and the media tree for writing.
10949 */
10950HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10951{
10952 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10953
10954 AutoCaller autoCaller(this);
10955 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10956
10957 AutoMultiWriteLock2 alock(this->lockHandle(),
10958 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10959
10960 /* We absolutely must have backed up state. */
10961 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10962
10963 /* Check if there are any implicitly created diff images. */
10964 bool fImplicitDiffs = false;
10965 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10966 it != mMediaData->mAttachments.end();
10967 ++it)
10968 {
10969 const ComObjPtr<MediumAttachment> &pAtt = *it;
10970 if (pAtt->isImplicit())
10971 {
10972 fImplicitDiffs = true;
10973 break;
10974 }
10975 }
10976 /* If there is nothing to do, leave early. This saves lots of image locking
10977 * effort. It also avoids a MachineStateChanged event without real reason.
10978 * This is important e.g. when loading a VM config, because there should be
10979 * no events. Otherwise API clients can become thoroughly confused for
10980 * inaccessible VMs (the code for loading VM configs uses this method for
10981 * cleanup if the config makes no sense), as they take such events as an
10982 * indication that the VM is alive, and they would force the VM config to
10983 * be reread, leading to an endless loop. */
10984 if (!fImplicitDiffs)
10985 return S_OK;
10986
10987 HRESULT rc = S_OK;
10988 MachineState_T oldState = mData->mMachineState;
10989
10990 /* will release the lock before the potentially lengthy operation,
10991 * so protect with the special state (unless already protected) */
10992 if ( oldState != MachineState_Saving
10993 && oldState != MachineState_LiveSnapshotting
10994 && oldState != MachineState_RestoringSnapshot
10995 && oldState != MachineState_DeletingSnapshot
10996 && oldState != MachineState_DeletingSnapshotOnline
10997 && oldState != MachineState_DeletingSnapshotPaused
10998 )
10999 setMachineState(MachineState_SettingUp);
11000
11001 // use appropriate locked media map (online or offline)
11002 MediumLockListMap lockedMediaOffline;
11003 MediumLockListMap *lockedMediaMap;
11004 if (aOnline)
11005 lockedMediaMap = &mData->mSession.mLockedMedia;
11006 else
11007 lockedMediaMap = &lockedMediaOffline;
11008
11009 try
11010 {
11011 if (!aOnline)
11012 {
11013 /* lock all attached hard disks early to detect "in use"
11014 * situations before deleting actual diffs */
11015 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11016 it != mMediaData->mAttachments.end();
11017 ++it)
11018 {
11019 MediumAttachment* pAtt = *it;
11020 if (pAtt->getType() == DeviceType_HardDisk)
11021 {
11022 Medium* pMedium = pAtt->getMedium();
11023 Assert(pMedium);
11024
11025 MediumLockList *pMediumLockList(new MediumLockList());
11026 alock.release();
11027 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11028 false /* fMediumLockWrite */,
11029 NULL,
11030 *pMediumLockList);
11031 alock.acquire();
11032
11033 if (FAILED(rc))
11034 {
11035 delete pMediumLockList;
11036 throw rc;
11037 }
11038
11039 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11040 if (FAILED(rc))
11041 throw rc;
11042 }
11043 }
11044
11045 if (FAILED(rc))
11046 throw rc;
11047 } // end of offline
11048
11049 /* Lock lists are now up to date and include implicitly created media */
11050
11051 /* Go through remembered attachments and delete all implicitly created
11052 * diffs and fix up the attachment information */
11053 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11054 MediaData::AttachmentList implicitAtts;
11055 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11056 it != mMediaData->mAttachments.end();
11057 ++it)
11058 {
11059 ComObjPtr<MediumAttachment> pAtt = *it;
11060 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11061 if (pMedium.isNull())
11062 continue;
11063
11064 // Implicit attachments go on the list for deletion and back references are removed.
11065 if (pAtt->isImplicit())
11066 {
11067 /* Deassociate and mark for deletion */
11068 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11069 rc = pMedium->removeBackReference(mData->mUuid);
11070 if (FAILED(rc))
11071 throw rc;
11072 implicitAtts.push_back(pAtt);
11073 continue;
11074 }
11075
11076 /* Was this medium attached before? */
11077 if (!findAttachment(oldAtts, pMedium))
11078 {
11079 /* no: de-associate */
11080 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11081 rc = pMedium->removeBackReference(mData->mUuid);
11082 if (FAILED(rc))
11083 throw rc;
11084 continue;
11085 }
11086 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11087 }
11088
11089 /* If there are implicit attachments to delete, throw away the lock
11090 * map contents (which will unlock all media) since the medium
11091 * attachments will be rolled back. Below we need to completely
11092 * recreate the lock map anyway since it is infinitely complex to
11093 * do this incrementally (would need reconstructing each attachment
11094 * change, which would be extremely hairy). */
11095 if (implicitAtts.size() != 0)
11096 {
11097 ErrorInfoKeeper eik;
11098
11099 HRESULT rc1 = lockedMediaMap->Clear();
11100 AssertComRC(rc1);
11101 }
11102
11103 /* rollback hard disk changes */
11104 mMediaData.rollback();
11105
11106 MultiResult mrc(S_OK);
11107
11108 // Delete unused implicit diffs.
11109 if (implicitAtts.size() != 0)
11110 {
11111 alock.release();
11112
11113 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11114 it != implicitAtts.end();
11115 ++it)
11116 {
11117 // Remove medium associated with this attachment.
11118 ComObjPtr<MediumAttachment> pAtt = *it;
11119 Assert(pAtt);
11120 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11121 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11122 Assert(pMedium);
11123
11124 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11125 // continue on delete failure, just collect error messages
11126 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11127 mrc = rc;
11128 }
11129
11130 alock.acquire();
11131
11132 /* if there is a VM recreate media lock map as mentioned above,
11133 * otherwise it is a waste of time and we leave things unlocked */
11134 if (aOnline)
11135 {
11136 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11137 /* must never be NULL, but better safe than sorry */
11138 if (!pMachine.isNull())
11139 {
11140 alock.release();
11141 rc = mData->mSession.mMachine->lockMedia();
11142 alock.acquire();
11143 if (FAILED(rc))
11144 throw rc;
11145 }
11146 }
11147 }
11148 }
11149 catch (HRESULT aRC) {rc = aRC;}
11150
11151 if (mData->mMachineState == MachineState_SettingUp)
11152 setMachineState(oldState);
11153
11154 /* unlock all hard disks we locked when there is no VM */
11155 if (!aOnline)
11156 {
11157 ErrorInfoKeeper eik;
11158
11159 HRESULT rc1 = lockedMediaMap->Clear();
11160 AssertComRC(rc1);
11161 }
11162
11163 return rc;
11164}
11165
11166
11167/**
11168 * Looks through the given list of media attachments for one with the given parameters
11169 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11170 * can be searched as well if needed.
11171 *
11172 * @param list
11173 * @param aControllerName
11174 * @param aControllerPort
11175 * @param aDevice
11176 * @return
11177 */
11178MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11179 IN_BSTR aControllerName,
11180 LONG aControllerPort,
11181 LONG aDevice)
11182{
11183 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11184 it != ll.end();
11185 ++it)
11186 {
11187 MediumAttachment *pAttach = *it;
11188 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11189 return pAttach;
11190 }
11191
11192 return NULL;
11193}
11194
11195/**
11196 * Looks through the given list of media attachments for one with the given parameters
11197 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11198 * can be searched as well if needed.
11199 *
11200 * @param list
11201 * @param aControllerName
11202 * @param aControllerPort
11203 * @param aDevice
11204 * @return
11205 */
11206MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11207 ComObjPtr<Medium> pMedium)
11208{
11209 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11210 it != ll.end();
11211 ++it)
11212 {
11213 MediumAttachment *pAttach = *it;
11214 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11215 if (pMediumThis == pMedium)
11216 return pAttach;
11217 }
11218
11219 return NULL;
11220}
11221
11222/**
11223 * Looks through the given list of media attachments for one with the given parameters
11224 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11225 * can be searched as well if needed.
11226 *
11227 * @param list
11228 * @param aControllerName
11229 * @param aControllerPort
11230 * @param aDevice
11231 * @return
11232 */
11233MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11234 Guid &id)
11235{
11236 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11237 it != ll.end();
11238 ++it)
11239 {
11240 MediumAttachment *pAttach = *it;
11241 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11242 if (pMediumThis->getId() == id)
11243 return pAttach;
11244 }
11245
11246 return NULL;
11247}
11248
11249/**
11250 * Main implementation for Machine::DetachDevice. This also gets called
11251 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11252 *
11253 * @param pAttach Medium attachment to detach.
11254 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11255 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11256 * @return
11257 */
11258HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11259 AutoWriteLock &writeLock,
11260 Snapshot *pSnapshot)
11261{
11262 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11263 DeviceType_T mediumType = pAttach->getType();
11264
11265 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11266
11267 if (pAttach->isImplicit())
11268 {
11269 /* attempt to implicitly delete the implicitly created diff */
11270
11271 /// @todo move the implicit flag from MediumAttachment to Medium
11272 /// and forbid any hard disk operation when it is implicit. Or maybe
11273 /// a special media state for it to make it even more simple.
11274
11275 Assert(mMediaData.isBackedUp());
11276
11277 /* will release the lock before the potentially lengthy operation, so
11278 * protect with the special state */
11279 MachineState_T oldState = mData->mMachineState;
11280 setMachineState(MachineState_SettingUp);
11281
11282 writeLock.release();
11283
11284 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11285 true /*aWait*/);
11286
11287 writeLock.acquire();
11288
11289 setMachineState(oldState);
11290
11291 if (FAILED(rc)) return rc;
11292 }
11293
11294 setModified(IsModified_Storage);
11295 mMediaData.backup();
11296 mMediaData->mAttachments.remove(pAttach);
11297
11298 if (!oldmedium.isNull())
11299 {
11300 // if this is from a snapshot, do not defer detachment to commitMedia()
11301 if (pSnapshot)
11302 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11303 // else if non-hard disk media, do not defer detachment to commitMedia() either
11304 else if (mediumType != DeviceType_HardDisk)
11305 oldmedium->removeBackReference(mData->mUuid);
11306 }
11307
11308 return S_OK;
11309}
11310
11311/**
11312 * Goes thru all media of the given list and
11313 *
11314 * 1) calls detachDevice() on each of them for this machine and
11315 * 2) adds all Medium objects found in the process to the given list,
11316 * depending on cleanupMode.
11317 *
11318 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11319 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11320 * media to the list.
11321 *
11322 * This gets called from Machine::Unregister, both for the actual Machine and
11323 * the SnapshotMachine objects that might be found in the snapshots.
11324 *
11325 * Requires caller and locking. The machine lock must be passed in because it
11326 * will be passed on to detachDevice which needs it for temporary unlocking.
11327 *
11328 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11329 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11330 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11331 * otherwise no media get added.
11332 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11333 * @return
11334 */
11335HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11336 Snapshot *pSnapshot,
11337 CleanupMode_T cleanupMode,
11338 MediaList &llMedia)
11339{
11340 Assert(isWriteLockOnCurrentThread());
11341
11342 HRESULT rc;
11343
11344 // make a temporary list because detachDevice invalidates iterators into
11345 // mMediaData->mAttachments
11346 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11347
11348 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11349 it != llAttachments2.end();
11350 ++it)
11351 {
11352 ComObjPtr<MediumAttachment> &pAttach = *it;
11353 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11354
11355 if (!pMedium.isNull())
11356 {
11357 AutoCaller mac(pMedium);
11358 if (FAILED(mac.rc())) return mac.rc();
11359 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11360 DeviceType_T devType = pMedium->getDeviceType();
11361 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11362 && devType == DeviceType_HardDisk)
11363 || (cleanupMode == CleanupMode_Full)
11364 )
11365 {
11366 llMedia.push_back(pMedium);
11367 ComObjPtr<Medium> pParent = pMedium->getParent();
11368 /*
11369 * Search for medias which are not attached to any machine, but
11370 * in the chain to an attached disk. Mediums are only consided
11371 * if they are:
11372 * - have only one child
11373 * - no references to any machines
11374 * - are of normal medium type
11375 */
11376 while (!pParent.isNull())
11377 {
11378 AutoCaller mac1(pParent);
11379 if (FAILED(mac1.rc())) return mac1.rc();
11380 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11381 if (pParent->getChildren().size() == 1)
11382 {
11383 if ( pParent->getMachineBackRefCount() == 0
11384 && pParent->getType() == MediumType_Normal
11385 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11386 llMedia.push_back(pParent);
11387 }
11388 else
11389 break;
11390 pParent = pParent->getParent();
11391 }
11392 }
11393 }
11394
11395 // real machine: then we need to use the proper method
11396 rc = detachDevice(pAttach, writeLock, pSnapshot);
11397
11398 if (FAILED(rc))
11399 return rc;
11400 }
11401
11402 return S_OK;
11403}
11404
11405/**
11406 * Perform deferred hard disk detachments.
11407 *
11408 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11409 * backed up).
11410 *
11411 * If @a aOnline is @c true then this method will also unlock the old hard disks
11412 * for which the new implicit diffs were created and will lock these new diffs for
11413 * writing.
11414 *
11415 * @param aOnline Whether the VM was online prior to this operation.
11416 *
11417 * @note Locks this object for writing!
11418 */
11419void Machine::commitMedia(bool aOnline /*= false*/)
11420{
11421 AutoCaller autoCaller(this);
11422 AssertComRCReturnVoid(autoCaller.rc());
11423
11424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11425
11426 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11427
11428 HRESULT rc = S_OK;
11429
11430 /* no attach/detach operations -- nothing to do */
11431 if (!mMediaData.isBackedUp())
11432 return;
11433
11434 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11435 bool fMediaNeedsLocking = false;
11436
11437 /* enumerate new attachments */
11438 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11439 it != mMediaData->mAttachments.end();
11440 ++it)
11441 {
11442 MediumAttachment *pAttach = *it;
11443
11444 pAttach->commit();
11445
11446 Medium* pMedium = pAttach->getMedium();
11447 bool fImplicit = pAttach->isImplicit();
11448
11449 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11450 (pMedium) ? pMedium->getName().c_str() : "NULL",
11451 fImplicit));
11452
11453 /** @todo convert all this Machine-based voodoo to MediumAttachment
11454 * based commit logic. */
11455 if (fImplicit)
11456 {
11457 /* convert implicit attachment to normal */
11458 pAttach->setImplicit(false);
11459
11460 if ( aOnline
11461 && pMedium
11462 && pAttach->getType() == DeviceType_HardDisk
11463 )
11464 {
11465 ComObjPtr<Medium> parent = pMedium->getParent();
11466 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11467
11468 /* update the appropriate lock list */
11469 MediumLockList *pMediumLockList;
11470 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11471 AssertComRC(rc);
11472 if (pMediumLockList)
11473 {
11474 /* unlock if there's a need to change the locking */
11475 if (!fMediaNeedsLocking)
11476 {
11477 rc = mData->mSession.mLockedMedia.Unlock();
11478 AssertComRC(rc);
11479 fMediaNeedsLocking = true;
11480 }
11481 rc = pMediumLockList->Update(parent, false);
11482 AssertComRC(rc);
11483 rc = pMediumLockList->Append(pMedium, true);
11484 AssertComRC(rc);
11485 }
11486 }
11487
11488 continue;
11489 }
11490
11491 if (pMedium)
11492 {
11493 /* was this medium attached before? */
11494 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11495 oldIt != oldAtts.end();
11496 ++oldIt)
11497 {
11498 MediumAttachment *pOldAttach = *oldIt;
11499 if (pOldAttach->getMedium() == pMedium)
11500 {
11501 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11502
11503 /* yes: remove from old to avoid de-association */
11504 oldAtts.erase(oldIt);
11505 break;
11506 }
11507 }
11508 }
11509 }
11510
11511 /* enumerate remaining old attachments and de-associate from the
11512 * current machine state */
11513 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11514 it != oldAtts.end();
11515 ++it)
11516 {
11517 MediumAttachment *pAttach = *it;
11518 Medium* pMedium = pAttach->getMedium();
11519
11520 /* Detach only hard disks, since DVD/floppy media is detached
11521 * instantly in MountMedium. */
11522 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11523 {
11524 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11525
11526 /* now de-associate from the current machine state */
11527 rc = pMedium->removeBackReference(mData->mUuid);
11528 AssertComRC(rc);
11529
11530 if (aOnline)
11531 {
11532 /* unlock since medium is not used anymore */
11533 MediumLockList *pMediumLockList;
11534 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11535 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11536 {
11537 /* this happens for online snapshots, there the attachment
11538 * is changing, but only to a diff image created under
11539 * the old one, so there is no separate lock list */
11540 Assert(!pMediumLockList);
11541 }
11542 else
11543 {
11544 AssertComRC(rc);
11545 if (pMediumLockList)
11546 {
11547 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11548 AssertComRC(rc);
11549 }
11550 }
11551 }
11552 }
11553 }
11554
11555 /* take media locks again so that the locking state is consistent */
11556 if (fMediaNeedsLocking)
11557 {
11558 Assert(aOnline);
11559 rc = mData->mSession.mLockedMedia.Lock();
11560 AssertComRC(rc);
11561 }
11562
11563 /* commit the hard disk changes */
11564 mMediaData.commit();
11565
11566 if (isSessionMachine())
11567 {
11568 /*
11569 * Update the parent machine to point to the new owner.
11570 * This is necessary because the stored parent will point to the
11571 * session machine otherwise and cause crashes or errors later
11572 * when the session machine gets invalid.
11573 */
11574 /** @todo Change the MediumAttachment class to behave like any other
11575 * class in this regard by creating peer MediumAttachment
11576 * objects for session machines and share the data with the peer
11577 * machine.
11578 */
11579 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11580 it != mMediaData->mAttachments.end();
11581 ++it)
11582 {
11583 (*it)->updateParentMachine(mPeer);
11584 }
11585
11586 /* attach new data to the primary machine and reshare it */
11587 mPeer->mMediaData.attach(mMediaData);
11588 }
11589
11590 return;
11591}
11592
11593/**
11594 * Perform deferred deletion of implicitly created diffs.
11595 *
11596 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11597 * backed up).
11598 *
11599 * @note Locks this object for writing!
11600 */
11601void Machine::rollbackMedia()
11602{
11603 AutoCaller autoCaller(this);
11604 AssertComRCReturnVoid(autoCaller.rc());
11605
11606 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11607 LogFlowThisFunc(("Entering rollbackMedia\n"));
11608
11609 HRESULT rc = S_OK;
11610
11611 /* no attach/detach operations -- nothing to do */
11612 if (!mMediaData.isBackedUp())
11613 return;
11614
11615 /* enumerate new attachments */
11616 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11617 it != mMediaData->mAttachments.end();
11618 ++it)
11619 {
11620 MediumAttachment *pAttach = *it;
11621 /* Fix up the backrefs for DVD/floppy media. */
11622 if (pAttach->getType() != DeviceType_HardDisk)
11623 {
11624 Medium* pMedium = pAttach->getMedium();
11625 if (pMedium)
11626 {
11627 rc = pMedium->removeBackReference(mData->mUuid);
11628 AssertComRC(rc);
11629 }
11630 }
11631
11632 (*it)->rollback();
11633
11634 pAttach = *it;
11635 /* Fix up the backrefs for DVD/floppy media. */
11636 if (pAttach->getType() != DeviceType_HardDisk)
11637 {
11638 Medium* pMedium = pAttach->getMedium();
11639 if (pMedium)
11640 {
11641 rc = pMedium->addBackReference(mData->mUuid);
11642 AssertComRC(rc);
11643 }
11644 }
11645 }
11646
11647 /** @todo convert all this Machine-based voodoo to MediumAttachment
11648 * based rollback logic. */
11649 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11650
11651 return;
11652}
11653
11654/**
11655 * Returns true if the settings file is located in the directory named exactly
11656 * as the machine; this means, among other things, that the machine directory
11657 * should be auto-renamed.
11658 *
11659 * @param aSettingsDir if not NULL, the full machine settings file directory
11660 * name will be assigned there.
11661 *
11662 * @note Doesn't lock anything.
11663 * @note Not thread safe (must be called from this object's lock).
11664 */
11665bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11666{
11667 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11668 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11669 if (aSettingsDir)
11670 *aSettingsDir = strMachineDirName;
11671 strMachineDirName.stripPath(); // vmname
11672 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11673 strConfigFileOnly.stripPath() // vmname.vbox
11674 .stripExt(); // vmname
11675 /** @todo hack, make somehow use of ComposeMachineFilename */
11676 if (mUserData->s.fDirectoryIncludesUUID)
11677 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11678
11679 AssertReturn(!strMachineDirName.isEmpty(), false);
11680 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11681
11682 return strMachineDirName == strConfigFileOnly;
11683}
11684
11685/**
11686 * Discards all changes to machine settings.
11687 *
11688 * @param aNotify Whether to notify the direct session about changes or not.
11689 *
11690 * @note Locks objects for writing!
11691 */
11692void Machine::rollback(bool aNotify)
11693{
11694 AutoCaller autoCaller(this);
11695 AssertComRCReturn(autoCaller.rc(), (void)0);
11696
11697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11698
11699 if (!mStorageControllers.isNull())
11700 {
11701 if (mStorageControllers.isBackedUp())
11702 {
11703 /* unitialize all new devices (absent in the backed up list). */
11704 StorageControllerList::const_iterator it = mStorageControllers->begin();
11705 StorageControllerList *backedList = mStorageControllers.backedUpData();
11706 while (it != mStorageControllers->end())
11707 {
11708 if ( std::find(backedList->begin(), backedList->end(), *it)
11709 == backedList->end()
11710 )
11711 {
11712 (*it)->uninit();
11713 }
11714 ++it;
11715 }
11716
11717 /* restore the list */
11718 mStorageControllers.rollback();
11719 }
11720
11721 /* rollback any changes to devices after restoring the list */
11722 if (mData->flModifications & IsModified_Storage)
11723 {
11724 StorageControllerList::const_iterator it = mStorageControllers->begin();
11725 while (it != mStorageControllers->end())
11726 {
11727 (*it)->rollback();
11728 ++it;
11729 }
11730 }
11731 }
11732
11733 mUserData.rollback();
11734
11735 mHWData.rollback();
11736
11737 if (mData->flModifications & IsModified_Storage)
11738 rollbackMedia();
11739
11740 if (mBIOSSettings)
11741 mBIOSSettings->rollback();
11742
11743 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11744 mVRDEServer->rollback();
11745
11746 if (mAudioAdapter)
11747 mAudioAdapter->rollback();
11748
11749 if (mUSBController && (mData->flModifications & IsModified_USB))
11750 mUSBController->rollback();
11751
11752 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11753 mBandwidthControl->rollback();
11754
11755 if (!mHWData.isNull())
11756 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11757 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11758 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11759 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11760
11761 if (mData->flModifications & IsModified_NetworkAdapters)
11762 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11763 if ( mNetworkAdapters[slot]
11764 && mNetworkAdapters[slot]->isModified())
11765 {
11766 mNetworkAdapters[slot]->rollback();
11767 networkAdapters[slot] = mNetworkAdapters[slot];
11768 }
11769
11770 if (mData->flModifications & IsModified_SerialPorts)
11771 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11772 if ( mSerialPorts[slot]
11773 && mSerialPorts[slot]->isModified())
11774 {
11775 mSerialPorts[slot]->rollback();
11776 serialPorts[slot] = mSerialPorts[slot];
11777 }
11778
11779 if (mData->flModifications & IsModified_ParallelPorts)
11780 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11781 if ( mParallelPorts[slot]
11782 && mParallelPorts[slot]->isModified())
11783 {
11784 mParallelPorts[slot]->rollback();
11785 parallelPorts[slot] = mParallelPorts[slot];
11786 }
11787
11788 if (aNotify)
11789 {
11790 /* inform the direct session about changes */
11791
11792 ComObjPtr<Machine> that = this;
11793 uint32_t flModifications = mData->flModifications;
11794 alock.release();
11795
11796 if (flModifications & IsModified_SharedFolders)
11797 that->onSharedFolderChange();
11798
11799 if (flModifications & IsModified_VRDEServer)
11800 that->onVRDEServerChange(/* aRestart */ TRUE);
11801 if (flModifications & IsModified_USB)
11802 that->onUSBControllerChange();
11803
11804 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11805 if (networkAdapters[slot])
11806 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11807 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11808 if (serialPorts[slot])
11809 that->onSerialPortChange(serialPorts[slot]);
11810 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11811 if (parallelPorts[slot])
11812 that->onParallelPortChange(parallelPorts[slot]);
11813
11814 if (flModifications & IsModified_Storage)
11815 that->onStorageControllerChange();
11816
11817#if 0
11818 if (flModifications & IsModified_BandwidthControl)
11819 that->onBandwidthControlChange();
11820#endif
11821 }
11822}
11823
11824/**
11825 * Commits all the changes to machine settings.
11826 *
11827 * Note that this operation is supposed to never fail.
11828 *
11829 * @note Locks this object and children for writing.
11830 */
11831void Machine::commit()
11832{
11833 AutoCaller autoCaller(this);
11834 AssertComRCReturnVoid(autoCaller.rc());
11835
11836 AutoCaller peerCaller(mPeer);
11837 AssertComRCReturnVoid(peerCaller.rc());
11838
11839 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11840
11841 /*
11842 * use safe commit to ensure Snapshot machines (that share mUserData)
11843 * will still refer to a valid memory location
11844 */
11845 mUserData.commitCopy();
11846
11847 mHWData.commit();
11848
11849 if (mMediaData.isBackedUp())
11850 commitMedia(Global::IsOnline(mData->mMachineState));
11851
11852 mBIOSSettings->commit();
11853 mVRDEServer->commit();
11854 mAudioAdapter->commit();
11855 mUSBController->commit();
11856 mBandwidthControl->commit();
11857
11858 /* Since mNetworkAdapters is a list which might have been changed (resized)
11859 * without using the Backupable<> template we need to handle the copying
11860 * of the list entries manually, including the creation of peers for the
11861 * new objects. */
11862 bool commitNetworkAdapters = false;
11863 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11864 if (mPeer)
11865 {
11866 /* commit everything, even the ones which will go away */
11867 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11868 mNetworkAdapters[slot]->commit();
11869 /* copy over the new entries, creating a peer and uninit the original */
11870 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11871 for (size_t slot = 0; slot < newSize; slot++)
11872 {
11873 /* look if this adapter has a peer device */
11874 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11875 if (!peer)
11876 {
11877 /* no peer means the adapter is a newly created one;
11878 * create a peer owning data this data share it with */
11879 peer.createObject();
11880 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11881 }
11882 mPeer->mNetworkAdapters[slot] = peer;
11883 }
11884 /* uninit any no longer needed network adapters */
11885 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11886 mNetworkAdapters[slot]->uninit();
11887 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11888 {
11889 if (mPeer->mNetworkAdapters[slot])
11890 mPeer->mNetworkAdapters[slot]->uninit();
11891 }
11892 /* Keep the original network adapter count until this point, so that
11893 * discarding a chipset type change will not lose settings. */
11894 mNetworkAdapters.resize(newSize);
11895 mPeer->mNetworkAdapters.resize(newSize);
11896 }
11897 else
11898 {
11899 /* we have no peer (our parent is the newly created machine);
11900 * just commit changes to the network adapters */
11901 commitNetworkAdapters = true;
11902 }
11903 if (commitNetworkAdapters)
11904 {
11905 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11906 mNetworkAdapters[slot]->commit();
11907 }
11908
11909 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11910 mSerialPorts[slot]->commit();
11911 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11912 mParallelPorts[slot]->commit();
11913
11914 bool commitStorageControllers = false;
11915
11916 if (mStorageControllers.isBackedUp())
11917 {
11918 mStorageControllers.commit();
11919
11920 if (mPeer)
11921 {
11922 /* Commit all changes to new controllers (this will reshare data with
11923 * peers for those who have peers) */
11924 StorageControllerList *newList = new StorageControllerList();
11925 StorageControllerList::const_iterator it = mStorageControllers->begin();
11926 while (it != mStorageControllers->end())
11927 {
11928 (*it)->commit();
11929
11930 /* look if this controller has a peer device */
11931 ComObjPtr<StorageController> peer = (*it)->getPeer();
11932 if (!peer)
11933 {
11934 /* no peer means the device is a newly created one;
11935 * create a peer owning data this device share it with */
11936 peer.createObject();
11937 peer->init(mPeer, *it, true /* aReshare */);
11938 }
11939 else
11940 {
11941 /* remove peer from the old list */
11942 mPeer->mStorageControllers->remove(peer);
11943 }
11944 /* and add it to the new list */
11945 newList->push_back(peer);
11946
11947 ++it;
11948 }
11949
11950 /* uninit old peer's controllers that are left */
11951 it = mPeer->mStorageControllers->begin();
11952 while (it != mPeer->mStorageControllers->end())
11953 {
11954 (*it)->uninit();
11955 ++it;
11956 }
11957
11958 /* attach new list of controllers to our peer */
11959 mPeer->mStorageControllers.attach(newList);
11960 }
11961 else
11962 {
11963 /* we have no peer (our parent is the newly created machine);
11964 * just commit changes to devices */
11965 commitStorageControllers = true;
11966 }
11967 }
11968 else
11969 {
11970 /* the list of controllers itself is not changed,
11971 * just commit changes to controllers themselves */
11972 commitStorageControllers = true;
11973 }
11974
11975 if (commitStorageControllers)
11976 {
11977 StorageControllerList::const_iterator it = mStorageControllers->begin();
11978 while (it != mStorageControllers->end())
11979 {
11980 (*it)->commit();
11981 ++it;
11982 }
11983 }
11984
11985 if (isSessionMachine())
11986 {
11987 /* attach new data to the primary machine and reshare it */
11988 mPeer->mUserData.attach(mUserData);
11989 mPeer->mHWData.attach(mHWData);
11990 /* mMediaData is reshared by fixupMedia */
11991 // mPeer->mMediaData.attach(mMediaData);
11992 Assert(mPeer->mMediaData.data() == mMediaData.data());
11993 }
11994}
11995
11996/**
11997 * Copies all the hardware data from the given machine.
11998 *
11999 * Currently, only called when the VM is being restored from a snapshot. In
12000 * particular, this implies that the VM is not running during this method's
12001 * call.
12002 *
12003 * @note This method must be called from under this object's lock.
12004 *
12005 * @note This method doesn't call #commit(), so all data remains backed up and
12006 * unsaved.
12007 */
12008void Machine::copyFrom(Machine *aThat)
12009{
12010 AssertReturnVoid(!isSnapshotMachine());
12011 AssertReturnVoid(aThat->isSnapshotMachine());
12012
12013 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12014
12015 mHWData.assignCopy(aThat->mHWData);
12016
12017 // create copies of all shared folders (mHWData after attaching a copy
12018 // contains just references to original objects)
12019 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12020 it != mHWData->mSharedFolders.end();
12021 ++it)
12022 {
12023 ComObjPtr<SharedFolder> folder;
12024 folder.createObject();
12025 HRESULT rc = folder->initCopy(getMachine(), *it);
12026 AssertComRC(rc);
12027 *it = folder;
12028 }
12029
12030 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12031 mVRDEServer->copyFrom(aThat->mVRDEServer);
12032 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12033 mUSBController->copyFrom(aThat->mUSBController);
12034 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12035
12036 /* create private copies of all controllers */
12037 mStorageControllers.backup();
12038 mStorageControllers->clear();
12039 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12040 it != aThat->mStorageControllers->end();
12041 ++it)
12042 {
12043 ComObjPtr<StorageController> ctrl;
12044 ctrl.createObject();
12045 ctrl->initCopy(this, *it);
12046 mStorageControllers->push_back(ctrl);
12047 }
12048
12049 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12050 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12051 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12052 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12053 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12054 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12055 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12056}
12057
12058/**
12059 * Returns whether the given storage controller is hotplug capable.
12060 *
12061 * @returns true if the controller supports hotplugging
12062 * false otherwise.
12063 * @param enmCtrlType The controller type to check for.
12064 */
12065bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12066{
12067 switch (enmCtrlType)
12068 {
12069 case StorageControllerType_IntelAhci:
12070 return true;
12071 case StorageControllerType_LsiLogic:
12072 case StorageControllerType_LsiLogicSas:
12073 case StorageControllerType_BusLogic:
12074 case StorageControllerType_PIIX3:
12075 case StorageControllerType_PIIX4:
12076 case StorageControllerType_ICH6:
12077 case StorageControllerType_I82078:
12078 default:
12079 return false;
12080 }
12081}
12082
12083#ifdef VBOX_WITH_RESOURCE_USAGE_API
12084
12085void Machine::getDiskList(MediaList &list)
12086{
12087 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12088 it != mMediaData->mAttachments.end();
12089 ++it)
12090 {
12091 MediumAttachment* pAttach = *it;
12092 /* just in case */
12093 AssertStmt(pAttach, continue);
12094
12095 AutoCaller localAutoCallerA(pAttach);
12096 if (FAILED(localAutoCallerA.rc())) continue;
12097
12098 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12099
12100 if (pAttach->getType() == DeviceType_HardDisk)
12101 list.push_back(pAttach->getMedium());
12102 }
12103}
12104
12105void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12106{
12107 AssertReturnVoid(isWriteLockOnCurrentThread());
12108 AssertPtrReturnVoid(aCollector);
12109
12110 pm::CollectorHAL *hal = aCollector->getHAL();
12111 /* Create sub metrics */
12112 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12113 "Percentage of processor time spent in user mode by the VM process.");
12114 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12115 "Percentage of processor time spent in kernel mode by the VM process.");
12116 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12117 "Size of resident portion of VM process in memory.");
12118 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12119 "Actual size of all VM disks combined.");
12120 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12121 "Network receive rate.");
12122 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12123 "Network transmit rate.");
12124 /* Create and register base metrics */
12125 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12126 cpuLoadUser, cpuLoadKernel);
12127 aCollector->registerBaseMetric(cpuLoad);
12128 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12129 ramUsageUsed);
12130 aCollector->registerBaseMetric(ramUsage);
12131 MediaList disks;
12132 getDiskList(disks);
12133 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12134 diskUsageUsed);
12135 aCollector->registerBaseMetric(diskUsage);
12136
12137 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12138 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12139 new pm::AggregateAvg()));
12140 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12141 new pm::AggregateMin()));
12142 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12143 new pm::AggregateMax()));
12144 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12145 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12146 new pm::AggregateAvg()));
12147 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12148 new pm::AggregateMin()));
12149 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12150 new pm::AggregateMax()));
12151
12152 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12153 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12154 new pm::AggregateAvg()));
12155 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12156 new pm::AggregateMin()));
12157 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12158 new pm::AggregateMax()));
12159
12160 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12161 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12162 new pm::AggregateAvg()));
12163 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12164 new pm::AggregateMin()));
12165 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12166 new pm::AggregateMax()));
12167
12168
12169 /* Guest metrics collector */
12170 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12171 aCollector->registerGuest(mCollectorGuest);
12172 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12173 this, __PRETTY_FUNCTION__, mCollectorGuest));
12174
12175 /* Create sub metrics */
12176 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12177 "Percentage of processor time spent in user mode as seen by the guest.");
12178 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12179 "Percentage of processor time spent in kernel mode as seen by the guest.");
12180 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12181 "Percentage of processor time spent idling as seen by the guest.");
12182
12183 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12184 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12185 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12186 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12187 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12188 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12189
12190 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12191
12192 /* Create and register base metrics */
12193 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12194 machineNetRx, machineNetTx);
12195 aCollector->registerBaseMetric(machineNetRate);
12196
12197 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12198 guestLoadUser, guestLoadKernel, guestLoadIdle);
12199 aCollector->registerBaseMetric(guestCpuLoad);
12200
12201 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12202 guestMemTotal, guestMemFree,
12203 guestMemBalloon, guestMemShared,
12204 guestMemCache, guestPagedTotal);
12205 aCollector->registerBaseMetric(guestCpuMem);
12206
12207 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12208 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12209 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12210 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12211
12212 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12213 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12214 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12215 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12216
12217 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12218 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12219 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12220 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12221
12222 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12223 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12225 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12226
12227 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12228 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12230 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12231
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12233 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12236
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12241
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12246
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12251
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12256
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12261}
12262
12263void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12264{
12265 AssertReturnVoid(isWriteLockOnCurrentThread());
12266
12267 if (aCollector)
12268 {
12269 aCollector->unregisterMetricsFor(aMachine);
12270 aCollector->unregisterBaseMetricsFor(aMachine);
12271 }
12272}
12273
12274#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12275
12276
12277////////////////////////////////////////////////////////////////////////////////
12278
12279DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12280
12281HRESULT SessionMachine::FinalConstruct()
12282{
12283 LogFlowThisFunc(("\n"));
12284
12285#if defined(RT_OS_WINDOWS)
12286 mIPCSem = NULL;
12287#elif defined(RT_OS_OS2)
12288 mIPCSem = NULLHANDLE;
12289#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12290 mIPCSem = -1;
12291#else
12292# error "Port me!"
12293#endif
12294
12295 return BaseFinalConstruct();
12296}
12297
12298void SessionMachine::FinalRelease()
12299{
12300 LogFlowThisFunc(("\n"));
12301
12302 uninit(Uninit::Unexpected);
12303
12304 BaseFinalRelease();
12305}
12306
12307/**
12308 * @note Must be called only by Machine::openSession() from its own write lock.
12309 */
12310HRESULT SessionMachine::init(Machine *aMachine)
12311{
12312 LogFlowThisFuncEnter();
12313 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12314
12315 AssertReturn(aMachine, E_INVALIDARG);
12316
12317 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12318
12319 /* Enclose the state transition NotReady->InInit->Ready */
12320 AutoInitSpan autoInitSpan(this);
12321 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12322
12323 /* create the interprocess semaphore */
12324#if defined(RT_OS_WINDOWS)
12325 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12326 for (size_t i = 0; i < mIPCSemName.length(); i++)
12327 if (mIPCSemName.raw()[i] == '\\')
12328 mIPCSemName.raw()[i] = '/';
12329 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12330 ComAssertMsgRet(mIPCSem,
12331 ("Cannot create IPC mutex '%ls', err=%d",
12332 mIPCSemName.raw(), ::GetLastError()),
12333 E_FAIL);
12334#elif defined(RT_OS_OS2)
12335 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12336 aMachine->mData->mUuid.raw());
12337 mIPCSemName = ipcSem;
12338 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12339 ComAssertMsgRet(arc == NO_ERROR,
12340 ("Cannot create IPC mutex '%s', arc=%ld",
12341 ipcSem.c_str(), arc),
12342 E_FAIL);
12343#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12344# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12345# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12346 /** @todo Check that this still works correctly. */
12347 AssertCompileSize(key_t, 8);
12348# else
12349 AssertCompileSize(key_t, 4);
12350# endif
12351 key_t key;
12352 mIPCSem = -1;
12353 mIPCKey = "0";
12354 for (uint32_t i = 0; i < 1 << 24; i++)
12355 {
12356 key = ((uint32_t)'V' << 24) | i;
12357 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12358 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12359 {
12360 mIPCSem = sem;
12361 if (sem >= 0)
12362 mIPCKey = BstrFmt("%u", key);
12363 break;
12364 }
12365 }
12366# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12367 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12368 char *pszSemName = NULL;
12369 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12370 key_t key = ::ftok(pszSemName, 'V');
12371 RTStrFree(pszSemName);
12372
12373 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12374# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12375
12376 int errnoSave = errno;
12377 if (mIPCSem < 0 && errnoSave == ENOSYS)
12378 {
12379 setError(E_FAIL,
12380 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12381 "support for SysV IPC. Check the host kernel configuration for "
12382 "CONFIG_SYSVIPC=y"));
12383 return E_FAIL;
12384 }
12385 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12386 * the IPC semaphores */
12387 if (mIPCSem < 0 && errnoSave == ENOSPC)
12388 {
12389#ifdef RT_OS_LINUX
12390 setError(E_FAIL,
12391 tr("Cannot create IPC semaphore because the system limit for the "
12392 "maximum number of semaphore sets (SEMMNI), or the system wide "
12393 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12394 "current set of SysV IPC semaphores can be determined from "
12395 "the file /proc/sysvipc/sem"));
12396#else
12397 setError(E_FAIL,
12398 tr("Cannot create IPC semaphore because the system-imposed limit "
12399 "on the maximum number of allowed semaphores or semaphore "
12400 "identifiers system-wide would be exceeded"));
12401#endif
12402 return E_FAIL;
12403 }
12404 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12405 E_FAIL);
12406 /* set the initial value to 1 */
12407 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12408 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12409 E_FAIL);
12410#else
12411# error "Port me!"
12412#endif
12413
12414 /* memorize the peer Machine */
12415 unconst(mPeer) = aMachine;
12416 /* share the parent pointer */
12417 unconst(mParent) = aMachine->mParent;
12418
12419 /* take the pointers to data to share */
12420 mData.share(aMachine->mData);
12421 mSSData.share(aMachine->mSSData);
12422
12423 mUserData.share(aMachine->mUserData);
12424 mHWData.share(aMachine->mHWData);
12425 mMediaData.share(aMachine->mMediaData);
12426
12427 mStorageControllers.allocate();
12428 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12429 it != aMachine->mStorageControllers->end();
12430 ++it)
12431 {
12432 ComObjPtr<StorageController> ctl;
12433 ctl.createObject();
12434 ctl->init(this, *it);
12435 mStorageControllers->push_back(ctl);
12436 }
12437
12438 unconst(mBIOSSettings).createObject();
12439 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12440 /* create another VRDEServer object that will be mutable */
12441 unconst(mVRDEServer).createObject();
12442 mVRDEServer->init(this, aMachine->mVRDEServer);
12443 /* create another audio adapter object that will be mutable */
12444 unconst(mAudioAdapter).createObject();
12445 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12446 /* create a list of serial ports that will be mutable */
12447 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12448 {
12449 unconst(mSerialPorts[slot]).createObject();
12450 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12451 }
12452 /* create a list of parallel ports that will be mutable */
12453 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12454 {
12455 unconst(mParallelPorts[slot]).createObject();
12456 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12457 }
12458 /* create another USB controller object that will be mutable */
12459 unconst(mUSBController).createObject();
12460 mUSBController->init(this, aMachine->mUSBController);
12461
12462 /* create a list of network adapters that will be mutable */
12463 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12464 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12465 {
12466 unconst(mNetworkAdapters[slot]).createObject();
12467 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12468 }
12469
12470 /* create another bandwidth control object that will be mutable */
12471 unconst(mBandwidthControl).createObject();
12472 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12473
12474 /* default is to delete saved state on Saved -> PoweredOff transition */
12475 mRemoveSavedState = true;
12476
12477 /* Confirm a successful initialization when it's the case */
12478 autoInitSpan.setSucceeded();
12479
12480 LogFlowThisFuncLeave();
12481 return S_OK;
12482}
12483
12484/**
12485 * Uninitializes this session object. If the reason is other than
12486 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12487 *
12488 * @param aReason uninitialization reason
12489 *
12490 * @note Locks mParent + this object for writing.
12491 */
12492void SessionMachine::uninit(Uninit::Reason aReason)
12493{
12494 LogFlowThisFuncEnter();
12495 LogFlowThisFunc(("reason=%d\n", aReason));
12496
12497 /*
12498 * Strongly reference ourselves to prevent this object deletion after
12499 * mData->mSession.mMachine.setNull() below (which can release the last
12500 * reference and call the destructor). Important: this must be done before
12501 * accessing any members (and before AutoUninitSpan that does it as well).
12502 * This self reference will be released as the very last step on return.
12503 */
12504 ComObjPtr<SessionMachine> selfRef = this;
12505
12506 /* Enclose the state transition Ready->InUninit->NotReady */
12507 AutoUninitSpan autoUninitSpan(this);
12508 if (autoUninitSpan.uninitDone())
12509 {
12510 LogFlowThisFunc(("Already uninitialized\n"));
12511 LogFlowThisFuncLeave();
12512 return;
12513 }
12514
12515 if (autoUninitSpan.initFailed())
12516 {
12517 /* We've been called by init() because it's failed. It's not really
12518 * necessary (nor it's safe) to perform the regular uninit sequence
12519 * below, the following is enough.
12520 */
12521 LogFlowThisFunc(("Initialization failed.\n"));
12522#if defined(RT_OS_WINDOWS)
12523 if (mIPCSem)
12524 ::CloseHandle(mIPCSem);
12525 mIPCSem = NULL;
12526#elif defined(RT_OS_OS2)
12527 if (mIPCSem != NULLHANDLE)
12528 ::DosCloseMutexSem(mIPCSem);
12529 mIPCSem = NULLHANDLE;
12530#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12531 if (mIPCSem >= 0)
12532 ::semctl(mIPCSem, 0, IPC_RMID);
12533 mIPCSem = -1;
12534# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12535 mIPCKey = "0";
12536# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12537#else
12538# error "Port me!"
12539#endif
12540 uninitDataAndChildObjects();
12541 mData.free();
12542 unconst(mParent) = NULL;
12543 unconst(mPeer) = NULL;
12544 LogFlowThisFuncLeave();
12545 return;
12546 }
12547
12548 MachineState_T lastState;
12549 {
12550 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12551 lastState = mData->mMachineState;
12552 }
12553 NOREF(lastState);
12554
12555#ifdef VBOX_WITH_USB
12556 // release all captured USB devices, but do this before requesting the locks below
12557 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12558 {
12559 /* Console::captureUSBDevices() is called in the VM process only after
12560 * setting the machine state to Starting or Restoring.
12561 * Console::detachAllUSBDevices() will be called upon successful
12562 * termination. So, we need to release USB devices only if there was
12563 * an abnormal termination of a running VM.
12564 *
12565 * This is identical to SessionMachine::DetachAllUSBDevices except
12566 * for the aAbnormal argument. */
12567 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12568 AssertComRC(rc);
12569 NOREF(rc);
12570
12571 USBProxyService *service = mParent->host()->usbProxyService();
12572 if (service)
12573 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12574 }
12575#endif /* VBOX_WITH_USB */
12576
12577 // we need to lock this object in uninit() because the lock is shared
12578 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12579 // and others need mParent lock, and USB needs host lock.
12580 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12581
12582#if 0
12583 // Trigger async cleanup tasks, avoid doing things here which are not
12584 // vital to be done immediately and maybe need more locks. This calls
12585 // Machine::unregisterMetrics().
12586 mParent->onMachineUninit(mPeer);
12587#else
12588 /*
12589 * It is safe to call Machine::unregisterMetrics() here because
12590 * PerformanceCollector::samplerCallback no longer accesses guest methods
12591 * holding the lock.
12592 */
12593 unregisterMetrics(mParent->performanceCollector(), mPeer);
12594#endif
12595 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12596 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12597 this, __PRETTY_FUNCTION__, mCollectorGuest));
12598 if (mCollectorGuest)
12599 {
12600 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12601 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12602 mCollectorGuest = NULL;
12603 }
12604
12605 if (aReason == Uninit::Abnormal)
12606 {
12607 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12608 Global::IsOnlineOrTransient(lastState)));
12609
12610 /* reset the state to Aborted */
12611 if (mData->mMachineState != MachineState_Aborted)
12612 setMachineState(MachineState_Aborted);
12613 }
12614
12615 // any machine settings modified?
12616 if (mData->flModifications)
12617 {
12618 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12619 rollback(false /* aNotify */);
12620 }
12621
12622 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12623 || !mConsoleTaskData.mSnapshot);
12624 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12625 {
12626 LogWarningThisFunc(("canceling failed save state request!\n"));
12627 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12628 }
12629 else if (!mConsoleTaskData.mSnapshot.isNull())
12630 {
12631 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12632
12633 /* delete all differencing hard disks created (this will also attach
12634 * their parents back by rolling back mMediaData) */
12635 rollbackMedia();
12636
12637 // delete the saved state file (it might have been already created)
12638 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12639 // think it's still in use
12640 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12641 mConsoleTaskData.mSnapshot->uninit();
12642 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12643 }
12644
12645 if (!mData->mSession.mType.isEmpty())
12646 {
12647 /* mType is not null when this machine's process has been started by
12648 * Machine::LaunchVMProcess(), therefore it is our child. We
12649 * need to queue the PID to reap the process (and avoid zombies on
12650 * Linux). */
12651 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12652 mParent->addProcessToReap(mData->mSession.mPID);
12653 }
12654
12655 mData->mSession.mPID = NIL_RTPROCESS;
12656
12657 if (aReason == Uninit::Unexpected)
12658 {
12659 /* Uninitialization didn't come from #checkForDeath(), so tell the
12660 * client watcher thread to update the set of machines that have open
12661 * sessions. */
12662 mParent->updateClientWatcher();
12663 }
12664
12665 /* uninitialize all remote controls */
12666 if (mData->mSession.mRemoteControls.size())
12667 {
12668 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12669 mData->mSession.mRemoteControls.size()));
12670
12671 Data::Session::RemoteControlList::iterator it =
12672 mData->mSession.mRemoteControls.begin();
12673 while (it != mData->mSession.mRemoteControls.end())
12674 {
12675 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12676 HRESULT rc = (*it)->Uninitialize();
12677 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12678 if (FAILED(rc))
12679 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12680 ++it;
12681 }
12682 mData->mSession.mRemoteControls.clear();
12683 }
12684
12685 /*
12686 * An expected uninitialization can come only from #checkForDeath().
12687 * Otherwise it means that something's gone really wrong (for example,
12688 * the Session implementation has released the VirtualBox reference
12689 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12690 * etc). However, it's also possible, that the client releases the IPC
12691 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12692 * but the VirtualBox release event comes first to the server process.
12693 * This case is practically possible, so we should not assert on an
12694 * unexpected uninit, just log a warning.
12695 */
12696
12697 if ((aReason == Uninit::Unexpected))
12698 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12699
12700 if (aReason != Uninit::Normal)
12701 {
12702 mData->mSession.mDirectControl.setNull();
12703 }
12704 else
12705 {
12706 /* this must be null here (see #OnSessionEnd()) */
12707 Assert(mData->mSession.mDirectControl.isNull());
12708 Assert(mData->mSession.mState == SessionState_Unlocking);
12709 Assert(!mData->mSession.mProgress.isNull());
12710 }
12711 if (mData->mSession.mProgress)
12712 {
12713 if (aReason == Uninit::Normal)
12714 mData->mSession.mProgress->notifyComplete(S_OK);
12715 else
12716 mData->mSession.mProgress->notifyComplete(E_FAIL,
12717 COM_IIDOF(ISession),
12718 getComponentName(),
12719 tr("The VM session was aborted"));
12720 mData->mSession.mProgress.setNull();
12721 }
12722
12723 /* remove the association between the peer machine and this session machine */
12724 Assert( (SessionMachine*)mData->mSession.mMachine == this
12725 || aReason == Uninit::Unexpected);
12726
12727 /* reset the rest of session data */
12728 mData->mSession.mMachine.setNull();
12729 mData->mSession.mState = SessionState_Unlocked;
12730 mData->mSession.mType.setNull();
12731
12732 /* close the interprocess semaphore before leaving the exclusive lock */
12733#if defined(RT_OS_WINDOWS)
12734 if (mIPCSem)
12735 ::CloseHandle(mIPCSem);
12736 mIPCSem = NULL;
12737#elif defined(RT_OS_OS2)
12738 if (mIPCSem != NULLHANDLE)
12739 ::DosCloseMutexSem(mIPCSem);
12740 mIPCSem = NULLHANDLE;
12741#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12742 if (mIPCSem >= 0)
12743 ::semctl(mIPCSem, 0, IPC_RMID);
12744 mIPCSem = -1;
12745# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12746 mIPCKey = "0";
12747# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12748#else
12749# error "Port me!"
12750#endif
12751
12752 /* fire an event */
12753 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12754
12755 uninitDataAndChildObjects();
12756
12757 /* free the essential data structure last */
12758 mData.free();
12759
12760 /* release the exclusive lock before setting the below two to NULL */
12761 multilock.release();
12762
12763 unconst(mParent) = NULL;
12764 unconst(mPeer) = NULL;
12765
12766 LogFlowThisFuncLeave();
12767}
12768
12769// util::Lockable interface
12770////////////////////////////////////////////////////////////////////////////////
12771
12772/**
12773 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12774 * with the primary Machine instance (mPeer).
12775 */
12776RWLockHandle *SessionMachine::lockHandle() const
12777{
12778 AssertReturn(mPeer != NULL, NULL);
12779 return mPeer->lockHandle();
12780}
12781
12782// IInternalMachineControl methods
12783////////////////////////////////////////////////////////////////////////////////
12784
12785/**
12786 * Passes collected guest statistics to performance collector object
12787 */
12788STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12789 ULONG aCpuKernel, ULONG aCpuIdle,
12790 ULONG aMemTotal, ULONG aMemFree,
12791 ULONG aMemBalloon, ULONG aMemShared,
12792 ULONG aMemCache, ULONG aPageTotal,
12793 ULONG aAllocVMM, ULONG aFreeVMM,
12794 ULONG aBalloonedVMM, ULONG aSharedVMM,
12795 ULONG aVmNetRx, ULONG aVmNetTx)
12796{
12797 if (mCollectorGuest)
12798 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12799 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12800 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12801 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12802
12803 return S_OK;
12804}
12805
12806/**
12807 * @note Locks this object for writing.
12808 */
12809STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12810{
12811 AutoCaller autoCaller(this);
12812 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12813
12814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12815
12816 mRemoveSavedState = aRemove;
12817
12818 return S_OK;
12819}
12820
12821/**
12822 * @note Locks the same as #setMachineState() does.
12823 */
12824STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12825{
12826 return setMachineState(aMachineState);
12827}
12828
12829/**
12830 * @note Locks this object for reading.
12831 */
12832STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12833{
12834 AutoCaller autoCaller(this);
12835 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12836
12837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12838
12839#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12840 mIPCSemName.cloneTo(aId);
12841 return S_OK;
12842#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12843# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12844 mIPCKey.cloneTo(aId);
12845# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12846 mData->m_strConfigFileFull.cloneTo(aId);
12847# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12848 return S_OK;
12849#else
12850# error "Port me!"
12851#endif
12852}
12853
12854/**
12855 * @note Locks this object for writing.
12856 */
12857STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12858{
12859 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12860 AutoCaller autoCaller(this);
12861 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12862
12863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12864
12865 if (mData->mSession.mState != SessionState_Locked)
12866 return VBOX_E_INVALID_OBJECT_STATE;
12867
12868 if (!mData->mSession.mProgress.isNull())
12869 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12870
12871 LogFlowThisFunc(("returns S_OK.\n"));
12872 return S_OK;
12873}
12874
12875/**
12876 * @note Locks this object for writing.
12877 */
12878STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12879{
12880 AutoCaller autoCaller(this);
12881 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12882
12883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12884
12885 if (mData->mSession.mState != SessionState_Locked)
12886 return VBOX_E_INVALID_OBJECT_STATE;
12887
12888 /* Finalize the LaunchVMProcess progress object. */
12889 if (mData->mSession.mProgress)
12890 {
12891 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12892 mData->mSession.mProgress.setNull();
12893 }
12894
12895 if (SUCCEEDED((HRESULT)iResult))
12896 {
12897#ifdef VBOX_WITH_RESOURCE_USAGE_API
12898 /* The VM has been powered up successfully, so it makes sense
12899 * now to offer the performance metrics for a running machine
12900 * object. Doing it earlier wouldn't be safe. */
12901 registerMetrics(mParent->performanceCollector(), mPeer,
12902 mData->mSession.mPID);
12903#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12904 }
12905
12906 return S_OK;
12907}
12908
12909/**
12910 * @note Locks this object for writing.
12911 */
12912STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12913{
12914 LogFlowThisFuncEnter();
12915
12916 CheckComArgOutPointerValid(aProgress);
12917
12918 AutoCaller autoCaller(this);
12919 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12920
12921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12922
12923 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12924 E_FAIL);
12925
12926 /* create a progress object to track operation completion */
12927 ComObjPtr<Progress> pProgress;
12928 pProgress.createObject();
12929 pProgress->init(getVirtualBox(),
12930 static_cast<IMachine *>(this) /* aInitiator */,
12931 Bstr(tr("Stopping the virtual machine")).raw(),
12932 FALSE /* aCancelable */);
12933
12934 /* fill in the console task data */
12935 mConsoleTaskData.mLastState = mData->mMachineState;
12936 mConsoleTaskData.mProgress = pProgress;
12937
12938 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12939 setMachineState(MachineState_Stopping);
12940
12941 pProgress.queryInterfaceTo(aProgress);
12942
12943 return S_OK;
12944}
12945
12946/**
12947 * @note Locks this object for writing.
12948 */
12949STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12950{
12951 LogFlowThisFuncEnter();
12952
12953 AutoCaller autoCaller(this);
12954 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12955
12956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12957
12958 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12959 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12960 && mConsoleTaskData.mLastState != MachineState_Null,
12961 E_FAIL);
12962
12963 /*
12964 * On failure, set the state to the state we had when BeginPoweringDown()
12965 * was called (this is expected by Console::PowerDown() and the associated
12966 * task). On success the VM process already changed the state to
12967 * MachineState_PoweredOff, so no need to do anything.
12968 */
12969 if (FAILED(iResult))
12970 setMachineState(mConsoleTaskData.mLastState);
12971
12972 /* notify the progress object about operation completion */
12973 Assert(mConsoleTaskData.mProgress);
12974 if (SUCCEEDED(iResult))
12975 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12976 else
12977 {
12978 Utf8Str strErrMsg(aErrMsg);
12979 if (strErrMsg.length())
12980 mConsoleTaskData.mProgress->notifyComplete(iResult,
12981 COM_IIDOF(ISession),
12982 getComponentName(),
12983 strErrMsg.c_str());
12984 else
12985 mConsoleTaskData.mProgress->notifyComplete(iResult);
12986 }
12987
12988 /* clear out the temporary saved state data */
12989 mConsoleTaskData.mLastState = MachineState_Null;
12990 mConsoleTaskData.mProgress.setNull();
12991
12992 LogFlowThisFuncLeave();
12993 return S_OK;
12994}
12995
12996
12997/**
12998 * Goes through the USB filters of the given machine to see if the given
12999 * device matches any filter or not.
13000 *
13001 * @note Locks the same as USBController::hasMatchingFilter() does.
13002 */
13003STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13004 BOOL *aMatched,
13005 ULONG *aMaskedIfs)
13006{
13007 LogFlowThisFunc(("\n"));
13008
13009 CheckComArgNotNull(aUSBDevice);
13010 CheckComArgOutPointerValid(aMatched);
13011
13012 AutoCaller autoCaller(this);
13013 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13014
13015#ifdef VBOX_WITH_USB
13016 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13017#else
13018 NOREF(aUSBDevice);
13019 NOREF(aMaskedIfs);
13020 *aMatched = FALSE;
13021#endif
13022
13023 return S_OK;
13024}
13025
13026/**
13027 * @note Locks the same as Host::captureUSBDevice() does.
13028 */
13029STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13030{
13031 LogFlowThisFunc(("\n"));
13032
13033 AutoCaller autoCaller(this);
13034 AssertComRCReturnRC(autoCaller.rc());
13035
13036#ifdef VBOX_WITH_USB
13037 /* if captureDeviceForVM() fails, it must have set extended error info */
13038 clearError();
13039 MultiResult rc = mParent->host()->checkUSBProxyService();
13040 if (FAILED(rc)) return rc;
13041
13042 USBProxyService *service = mParent->host()->usbProxyService();
13043 AssertReturn(service, E_FAIL);
13044 return service->captureDeviceForVM(this, Guid(aId).ref());
13045#else
13046 NOREF(aId);
13047 return E_NOTIMPL;
13048#endif
13049}
13050
13051/**
13052 * @note Locks the same as Host::detachUSBDevice() does.
13053 */
13054STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13055{
13056 LogFlowThisFunc(("\n"));
13057
13058 AutoCaller autoCaller(this);
13059 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13060
13061#ifdef VBOX_WITH_USB
13062 USBProxyService *service = mParent->host()->usbProxyService();
13063 AssertReturn(service, E_FAIL);
13064 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13065#else
13066 NOREF(aId);
13067 NOREF(aDone);
13068 return E_NOTIMPL;
13069#endif
13070}
13071
13072/**
13073 * Inserts all machine filters to the USB proxy service and then calls
13074 * Host::autoCaptureUSBDevices().
13075 *
13076 * Called by Console from the VM process upon VM startup.
13077 *
13078 * @note Locks what called methods lock.
13079 */
13080STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13081{
13082 LogFlowThisFunc(("\n"));
13083
13084 AutoCaller autoCaller(this);
13085 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13086
13087#ifdef VBOX_WITH_USB
13088 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
13089 AssertComRC(rc);
13090 NOREF(rc);
13091
13092 USBProxyService *service = mParent->host()->usbProxyService();
13093 AssertReturn(service, E_FAIL);
13094 return service->autoCaptureDevicesForVM(this);
13095#else
13096 return S_OK;
13097#endif
13098}
13099
13100/**
13101 * Removes all machine filters from the USB proxy service and then calls
13102 * Host::detachAllUSBDevices().
13103 *
13104 * Called by Console from the VM process upon normal VM termination or by
13105 * SessionMachine::uninit() upon abnormal VM termination (from under the
13106 * Machine/SessionMachine lock).
13107 *
13108 * @note Locks what called methods lock.
13109 */
13110STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13111{
13112 LogFlowThisFunc(("\n"));
13113
13114 AutoCaller autoCaller(this);
13115 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13116
13117#ifdef VBOX_WITH_USB
13118 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
13119 AssertComRC(rc);
13120 NOREF(rc);
13121
13122 USBProxyService *service = mParent->host()->usbProxyService();
13123 AssertReturn(service, E_FAIL);
13124 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13125#else
13126 NOREF(aDone);
13127 return S_OK;
13128#endif
13129}
13130
13131/**
13132 * @note Locks this object for writing.
13133 */
13134STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13135 IProgress **aProgress)
13136{
13137 LogFlowThisFuncEnter();
13138
13139 AssertReturn(aSession, E_INVALIDARG);
13140 AssertReturn(aProgress, E_INVALIDARG);
13141
13142 AutoCaller autoCaller(this);
13143
13144 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13145 /*
13146 * We don't assert below because it might happen that a non-direct session
13147 * informs us it is closed right after we've been uninitialized -- it's ok.
13148 */
13149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13150
13151 /* get IInternalSessionControl interface */
13152 ComPtr<IInternalSessionControl> control(aSession);
13153
13154 ComAssertRet(!control.isNull(), E_INVALIDARG);
13155
13156 /* Creating a Progress object requires the VirtualBox lock, and
13157 * thus locking it here is required by the lock order rules. */
13158 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13159
13160 if (control == mData->mSession.mDirectControl)
13161 {
13162 ComAssertRet(aProgress, E_POINTER);
13163
13164 /* The direct session is being normally closed by the client process
13165 * ----------------------------------------------------------------- */
13166
13167 /* go to the closing state (essential for all open*Session() calls and
13168 * for #checkForDeath()) */
13169 Assert(mData->mSession.mState == SessionState_Locked);
13170 mData->mSession.mState = SessionState_Unlocking;
13171
13172 /* set direct control to NULL to release the remote instance */
13173 mData->mSession.mDirectControl.setNull();
13174 LogFlowThisFunc(("Direct control is set to NULL\n"));
13175
13176 if (mData->mSession.mProgress)
13177 {
13178 /* finalize the progress, someone might wait if a frontend
13179 * closes the session before powering on the VM. */
13180 mData->mSession.mProgress->notifyComplete(E_FAIL,
13181 COM_IIDOF(ISession),
13182 getComponentName(),
13183 tr("The VM session was closed before any attempt to power it on"));
13184 mData->mSession.mProgress.setNull();
13185 }
13186
13187 /* Create the progress object the client will use to wait until
13188 * #checkForDeath() is called to uninitialize this session object after
13189 * it releases the IPC semaphore.
13190 * Note! Because we're "reusing" mProgress here, this must be a proxy
13191 * object just like for LaunchVMProcess. */
13192 Assert(mData->mSession.mProgress.isNull());
13193 ComObjPtr<ProgressProxy> progress;
13194 progress.createObject();
13195 ComPtr<IUnknown> pPeer(mPeer);
13196 progress->init(mParent, pPeer,
13197 Bstr(tr("Closing session")).raw(),
13198 FALSE /* aCancelable */);
13199 progress.queryInterfaceTo(aProgress);
13200 mData->mSession.mProgress = progress;
13201 }
13202 else
13203 {
13204 /* the remote session is being normally closed */
13205 Data::Session::RemoteControlList::iterator it =
13206 mData->mSession.mRemoteControls.begin();
13207 while (it != mData->mSession.mRemoteControls.end())
13208 {
13209 if (control == *it)
13210 break;
13211 ++it;
13212 }
13213 BOOL found = it != mData->mSession.mRemoteControls.end();
13214 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13215 E_INVALIDARG);
13216 // This MUST be erase(it), not remove(*it) as the latter triggers a
13217 // very nasty use after free due to the place where the value "lives".
13218 mData->mSession.mRemoteControls.erase(it);
13219 }
13220
13221 /* signal the client watcher thread, because the client is going away */
13222 mParent->updateClientWatcher();
13223
13224 LogFlowThisFuncLeave();
13225 return S_OK;
13226}
13227
13228/**
13229 * @note Locks this object for writing.
13230 */
13231STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13232{
13233 LogFlowThisFuncEnter();
13234
13235 CheckComArgOutPointerValid(aProgress);
13236 CheckComArgOutPointerValid(aStateFilePath);
13237
13238 AutoCaller autoCaller(this);
13239 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13240
13241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13242
13243 AssertReturn( mData->mMachineState == MachineState_Paused
13244 && mConsoleTaskData.mLastState == MachineState_Null
13245 && mConsoleTaskData.strStateFilePath.isEmpty(),
13246 E_FAIL);
13247
13248 /* create a progress object to track operation completion */
13249 ComObjPtr<Progress> pProgress;
13250 pProgress.createObject();
13251 pProgress->init(getVirtualBox(),
13252 static_cast<IMachine *>(this) /* aInitiator */,
13253 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13254 FALSE /* aCancelable */);
13255
13256 Utf8Str strStateFilePath;
13257 /* stateFilePath is null when the machine is not running */
13258 if (mData->mMachineState == MachineState_Paused)
13259 composeSavedStateFilename(strStateFilePath);
13260
13261 /* fill in the console task data */
13262 mConsoleTaskData.mLastState = mData->mMachineState;
13263 mConsoleTaskData.strStateFilePath = strStateFilePath;
13264 mConsoleTaskData.mProgress = pProgress;
13265
13266 /* set the state to Saving (this is expected by Console::SaveState()) */
13267 setMachineState(MachineState_Saving);
13268
13269 strStateFilePath.cloneTo(aStateFilePath);
13270 pProgress.queryInterfaceTo(aProgress);
13271
13272 return S_OK;
13273}
13274
13275/**
13276 * @note Locks mParent + this object for writing.
13277 */
13278STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13279{
13280 LogFlowThisFunc(("\n"));
13281
13282 AutoCaller autoCaller(this);
13283 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13284
13285 /* endSavingState() need mParent lock */
13286 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13287
13288 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13289 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13290 && mConsoleTaskData.mLastState != MachineState_Null
13291 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13292 E_FAIL);
13293
13294 /*
13295 * On failure, set the state to the state we had when BeginSavingState()
13296 * was called (this is expected by Console::SaveState() and the associated
13297 * task). On success the VM process already changed the state to
13298 * MachineState_Saved, so no need to do anything.
13299 */
13300 if (FAILED(iResult))
13301 setMachineState(mConsoleTaskData.mLastState);
13302
13303 return endSavingState(iResult, aErrMsg);
13304}
13305
13306/**
13307 * @note Locks this object for writing.
13308 */
13309STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13310{
13311 LogFlowThisFunc(("\n"));
13312
13313 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13314
13315 AutoCaller autoCaller(this);
13316 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13317
13318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13319
13320 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13321 || mData->mMachineState == MachineState_Teleported
13322 || mData->mMachineState == MachineState_Aborted
13323 , E_FAIL); /** @todo setError. */
13324
13325 Utf8Str stateFilePathFull = aSavedStateFile;
13326 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13327 if (RT_FAILURE(vrc))
13328 return setError(VBOX_E_FILE_ERROR,
13329 tr("Invalid saved state file path '%ls' (%Rrc)"),
13330 aSavedStateFile,
13331 vrc);
13332
13333 mSSData->strStateFilePath = stateFilePathFull;
13334
13335 /* The below setMachineState() will detect the state transition and will
13336 * update the settings file */
13337
13338 return setMachineState(MachineState_Saved);
13339}
13340
13341STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13342 ComSafeArrayOut(BSTR, aValues),
13343 ComSafeArrayOut(LONG64, aTimestamps),
13344 ComSafeArrayOut(BSTR, aFlags))
13345{
13346 LogFlowThisFunc(("\n"));
13347
13348#ifdef VBOX_WITH_GUEST_PROPS
13349 using namespace guestProp;
13350
13351 AutoCaller autoCaller(this);
13352 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13353
13354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13355
13356 CheckComArgOutSafeArrayPointerValid(aNames);
13357 CheckComArgOutSafeArrayPointerValid(aValues);
13358 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13359 CheckComArgOutSafeArrayPointerValid(aFlags);
13360
13361 size_t cEntries = mHWData->mGuestProperties.size();
13362 com::SafeArray<BSTR> names(cEntries);
13363 com::SafeArray<BSTR> values(cEntries);
13364 com::SafeArray<LONG64> timestamps(cEntries);
13365 com::SafeArray<BSTR> flags(cEntries);
13366 unsigned i = 0;
13367 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13368 it != mHWData->mGuestProperties.end();
13369 ++it)
13370 {
13371 char szFlags[MAX_FLAGS_LEN + 1];
13372 it->first.cloneTo(&names[i]);
13373 it->second.strValue.cloneTo(&values[i]);
13374 timestamps[i] = it->second.mTimestamp;
13375 /* If it is NULL, keep it NULL. */
13376 if (it->second.mFlags)
13377 {
13378 writeFlags(it->second.mFlags, szFlags);
13379 Bstr(szFlags).cloneTo(&flags[i]);
13380 }
13381 else
13382 flags[i] = NULL;
13383 ++i;
13384 }
13385 names.detachTo(ComSafeArrayOutArg(aNames));
13386 values.detachTo(ComSafeArrayOutArg(aValues));
13387 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13388 flags.detachTo(ComSafeArrayOutArg(aFlags));
13389 return S_OK;
13390#else
13391 ReturnComNotImplemented();
13392#endif
13393}
13394
13395STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13396 IN_BSTR aValue,
13397 LONG64 aTimestamp,
13398 IN_BSTR aFlags)
13399{
13400 LogFlowThisFunc(("\n"));
13401
13402#ifdef VBOX_WITH_GUEST_PROPS
13403 using namespace guestProp;
13404
13405 CheckComArgStrNotEmptyOrNull(aName);
13406 CheckComArgNotNull(aValue);
13407 CheckComArgNotNull(aFlags);
13408
13409 try
13410 {
13411 /*
13412 * Convert input up front.
13413 */
13414 Utf8Str utf8Name(aName);
13415 uint32_t fFlags = NILFLAG;
13416 if (aFlags)
13417 {
13418 Utf8Str utf8Flags(aFlags);
13419 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13420 AssertRCReturn(vrc, E_INVALIDARG);
13421 }
13422
13423 /*
13424 * Now grab the object lock, validate the state and do the update.
13425 */
13426 AutoCaller autoCaller(this);
13427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13428
13429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13430
13431 switch (mData->mMachineState)
13432 {
13433 case MachineState_Paused:
13434 case MachineState_Running:
13435 case MachineState_Teleporting:
13436 case MachineState_TeleportingPausedVM:
13437 case MachineState_LiveSnapshotting:
13438 case MachineState_DeletingSnapshotOnline:
13439 case MachineState_DeletingSnapshotPaused:
13440 case MachineState_Saving:
13441 case MachineState_Stopping:
13442 break;
13443
13444 default:
13445 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13446 VBOX_E_INVALID_VM_STATE);
13447 }
13448
13449 setModified(IsModified_MachineData);
13450 mHWData.backup();
13451
13452 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13453 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13454 if (it != mHWData->mGuestProperties.end())
13455 {
13456 if (!fDelete)
13457 {
13458 it->second.strValue = aValue;
13459 it->second.mTimestamp = aTimestamp;
13460 it->second.mFlags = fFlags;
13461 }
13462 else
13463 mHWData->mGuestProperties.erase(it);
13464
13465 mData->mGuestPropertiesModified = TRUE;
13466 }
13467 else if (!fDelete)
13468 {
13469 HWData::GuestProperty prop;
13470 prop.strValue = aValue;
13471 prop.mTimestamp = aTimestamp;
13472 prop.mFlags = fFlags;
13473
13474 mHWData->mGuestProperties[utf8Name] = prop;
13475 mData->mGuestPropertiesModified = TRUE;
13476 }
13477
13478 /*
13479 * Send a callback notification if appropriate
13480 */
13481 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13482 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13483 RTSTR_MAX,
13484 utf8Name.c_str(),
13485 RTSTR_MAX, NULL)
13486 )
13487 {
13488 alock.release();
13489
13490 mParent->onGuestPropertyChange(mData->mUuid,
13491 aName,
13492 aValue,
13493 aFlags);
13494 }
13495 }
13496 catch (...)
13497 {
13498 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13499 }
13500 return S_OK;
13501#else
13502 ReturnComNotImplemented();
13503#endif
13504}
13505
13506STDMETHODIMP SessionMachine::LockMedia()
13507{
13508 AutoCaller autoCaller(this);
13509 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13510
13511 AutoMultiWriteLock2 alock(this->lockHandle(),
13512 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13513
13514 AssertReturn( mData->mMachineState == MachineState_Starting
13515 || mData->mMachineState == MachineState_Restoring
13516 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13517
13518 clearError();
13519 alock.release();
13520 return lockMedia();
13521}
13522
13523STDMETHODIMP SessionMachine::UnlockMedia()
13524{
13525 unlockMedia();
13526 return S_OK;
13527}
13528
13529STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13530 IMediumAttachment **aNewAttachment)
13531{
13532 CheckComArgNotNull(aAttachment);
13533 CheckComArgOutPointerValid(aNewAttachment);
13534
13535 AutoCaller autoCaller(this);
13536 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13537
13538 // request the host lock first, since might be calling Host methods for getting host drives;
13539 // next, protect the media tree all the while we're in here, as well as our member variables
13540 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13541 this->lockHandle(),
13542 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13543
13544 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13545
13546 Bstr ctrlName;
13547 LONG lPort;
13548 LONG lDevice;
13549 bool fTempEject;
13550 {
13551 AutoCaller autoAttachCaller(this);
13552 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13553
13554 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13555
13556 /* Need to query the details first, as the IMediumAttachment reference
13557 * might be to the original settings, which we are going to change. */
13558 ctrlName = pAttach->getControllerName();
13559 lPort = pAttach->getPort();
13560 lDevice = pAttach->getDevice();
13561 fTempEject = pAttach->getTempEject();
13562 }
13563
13564 if (!fTempEject)
13565 {
13566 /* Remember previously mounted medium. The medium before taking the
13567 * backup is not necessarily the same thing. */
13568 ComObjPtr<Medium> oldmedium;
13569 oldmedium = pAttach->getMedium();
13570
13571 setModified(IsModified_Storage);
13572 mMediaData.backup();
13573
13574 // The backup operation makes the pAttach reference point to the
13575 // old settings. Re-get the correct reference.
13576 pAttach = findAttachment(mMediaData->mAttachments,
13577 ctrlName.raw(),
13578 lPort,
13579 lDevice);
13580
13581 {
13582 AutoCaller autoAttachCaller(this);
13583 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13584
13585 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13586 if (!oldmedium.isNull())
13587 oldmedium->removeBackReference(mData->mUuid);
13588
13589 pAttach->updateMedium(NULL);
13590 pAttach->updateEjected();
13591 }
13592
13593 setModified(IsModified_Storage);
13594 }
13595 else
13596 {
13597 {
13598 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13599 pAttach->updateEjected();
13600 }
13601 }
13602
13603 pAttach.queryInterfaceTo(aNewAttachment);
13604
13605 return S_OK;
13606}
13607
13608// public methods only for internal purposes
13609/////////////////////////////////////////////////////////////////////////////
13610
13611/**
13612 * Called from the client watcher thread to check for expected or unexpected
13613 * death of the client process that has a direct session to this machine.
13614 *
13615 * On Win32 and on OS/2, this method is called only when we've got the
13616 * mutex (i.e. the client has either died or terminated normally) so it always
13617 * returns @c true (the client is terminated, the session machine is
13618 * uninitialized).
13619 *
13620 * On other platforms, the method returns @c true if the client process has
13621 * terminated normally or abnormally and the session machine was uninitialized,
13622 * and @c false if the client process is still alive.
13623 *
13624 * @note Locks this object for writing.
13625 */
13626bool SessionMachine::checkForDeath()
13627{
13628 Uninit::Reason reason;
13629 bool terminated = false;
13630
13631 /* Enclose autoCaller with a block because calling uninit() from under it
13632 * will deadlock. */
13633 {
13634 AutoCaller autoCaller(this);
13635 if (!autoCaller.isOk())
13636 {
13637 /* return true if not ready, to cause the client watcher to exclude
13638 * the corresponding session from watching */
13639 LogFlowThisFunc(("Already uninitialized!\n"));
13640 return true;
13641 }
13642
13643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13644
13645 /* Determine the reason of death: if the session state is Closing here,
13646 * everything is fine. Otherwise it means that the client did not call
13647 * OnSessionEnd() before it released the IPC semaphore. This may happen
13648 * either because the client process has abnormally terminated, or
13649 * because it simply forgot to call ISession::Close() before exiting. We
13650 * threat the latter also as an abnormal termination (see
13651 * Session::uninit() for details). */
13652 reason = mData->mSession.mState == SessionState_Unlocking ?
13653 Uninit::Normal :
13654 Uninit::Abnormal;
13655
13656#if defined(RT_OS_WINDOWS)
13657
13658 AssertMsg(mIPCSem, ("semaphore must be created"));
13659
13660 /* release the IPC mutex */
13661 ::ReleaseMutex(mIPCSem);
13662
13663 terminated = true;
13664
13665#elif defined(RT_OS_OS2)
13666
13667 AssertMsg(mIPCSem, ("semaphore must be created"));
13668
13669 /* release the IPC mutex */
13670 ::DosReleaseMutexSem(mIPCSem);
13671
13672 terminated = true;
13673
13674#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13675
13676 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13677
13678 int val = ::semctl(mIPCSem, 0, GETVAL);
13679 if (val > 0)
13680 {
13681 /* the semaphore is signaled, meaning the session is terminated */
13682 terminated = true;
13683 }
13684
13685#else
13686# error "Port me!"
13687#endif
13688
13689 } /* AutoCaller block */
13690
13691 if (terminated)
13692 uninit(reason);
13693
13694 return terminated;
13695}
13696
13697/**
13698 * @note Locks this object for reading.
13699 */
13700HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13701{
13702 LogFlowThisFunc(("\n"));
13703
13704 AutoCaller autoCaller(this);
13705 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13706
13707 ComPtr<IInternalSessionControl> directControl;
13708 {
13709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13710 directControl = mData->mSession.mDirectControl;
13711 }
13712
13713 /* ignore notifications sent after #OnSessionEnd() is called */
13714 if (!directControl)
13715 return S_OK;
13716
13717 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13718}
13719
13720/**
13721 * @note Locks this object for reading.
13722 */
13723HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13724 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13725{
13726 LogFlowThisFunc(("\n"));
13727
13728 AutoCaller autoCaller(this);
13729 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13730
13731 ComPtr<IInternalSessionControl> directControl;
13732 {
13733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13734 directControl = mData->mSession.mDirectControl;
13735 }
13736
13737 /* ignore notifications sent after #OnSessionEnd() is called */
13738 if (!directControl)
13739 return S_OK;
13740 /*
13741 * instead acting like callback we ask IVirtualBox deliver corresponding event
13742 */
13743
13744 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13745 return S_OK;
13746}
13747
13748/**
13749 * @note Locks this object for reading.
13750 */
13751HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13752{
13753 LogFlowThisFunc(("\n"));
13754
13755 AutoCaller autoCaller(this);
13756 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13757
13758 ComPtr<IInternalSessionControl> directControl;
13759 {
13760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13761 directControl = mData->mSession.mDirectControl;
13762 }
13763
13764 /* ignore notifications sent after #OnSessionEnd() is called */
13765 if (!directControl)
13766 return S_OK;
13767
13768 return directControl->OnSerialPortChange(serialPort);
13769}
13770
13771/**
13772 * @note Locks this object for reading.
13773 */
13774HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13775{
13776 LogFlowThisFunc(("\n"));
13777
13778 AutoCaller autoCaller(this);
13779 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13780
13781 ComPtr<IInternalSessionControl> directControl;
13782 {
13783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13784 directControl = mData->mSession.mDirectControl;
13785 }
13786
13787 /* ignore notifications sent after #OnSessionEnd() is called */
13788 if (!directControl)
13789 return S_OK;
13790
13791 return directControl->OnParallelPortChange(parallelPort);
13792}
13793
13794/**
13795 * @note Locks this object for reading.
13796 */
13797HRESULT SessionMachine::onStorageControllerChange()
13798{
13799 LogFlowThisFunc(("\n"));
13800
13801 AutoCaller autoCaller(this);
13802 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13803
13804 ComPtr<IInternalSessionControl> directControl;
13805 {
13806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13807 directControl = mData->mSession.mDirectControl;
13808 }
13809
13810 /* ignore notifications sent after #OnSessionEnd() is called */
13811 if (!directControl)
13812 return S_OK;
13813
13814 return directControl->OnStorageControllerChange();
13815}
13816
13817/**
13818 * @note Locks this object for reading.
13819 */
13820HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13821{
13822 LogFlowThisFunc(("\n"));
13823
13824 AutoCaller autoCaller(this);
13825 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13826
13827 ComPtr<IInternalSessionControl> directControl;
13828 {
13829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13830 directControl = mData->mSession.mDirectControl;
13831 }
13832
13833 /* ignore notifications sent after #OnSessionEnd() is called */
13834 if (!directControl)
13835 return S_OK;
13836
13837 return directControl->OnMediumChange(aAttachment, aForce);
13838}
13839
13840/**
13841 * @note Locks this object for reading.
13842 */
13843HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13844{
13845 LogFlowThisFunc(("\n"));
13846
13847 AutoCaller autoCaller(this);
13848 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13849
13850 ComPtr<IInternalSessionControl> directControl;
13851 {
13852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13853 directControl = mData->mSession.mDirectControl;
13854 }
13855
13856 /* ignore notifications sent after #OnSessionEnd() is called */
13857 if (!directControl)
13858 return S_OK;
13859
13860 return directControl->OnCPUChange(aCPU, aRemove);
13861}
13862
13863HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13864{
13865 LogFlowThisFunc(("\n"));
13866
13867 AutoCaller autoCaller(this);
13868 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13869
13870 ComPtr<IInternalSessionControl> directControl;
13871 {
13872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13873 directControl = mData->mSession.mDirectControl;
13874 }
13875
13876 /* ignore notifications sent after #OnSessionEnd() is called */
13877 if (!directControl)
13878 return S_OK;
13879
13880 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13881}
13882
13883/**
13884 * @note Locks this object for reading.
13885 */
13886HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13887{
13888 LogFlowThisFunc(("\n"));
13889
13890 AutoCaller autoCaller(this);
13891 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13892
13893 ComPtr<IInternalSessionControl> directControl;
13894 {
13895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13896 directControl = mData->mSession.mDirectControl;
13897 }
13898
13899 /* ignore notifications sent after #OnSessionEnd() is called */
13900 if (!directControl)
13901 return S_OK;
13902
13903 return directControl->OnVRDEServerChange(aRestart);
13904}
13905
13906/**
13907 * @note Locks this object for reading.
13908 */
13909HRESULT SessionMachine::onVideoCaptureChange()
13910{
13911 LogFlowThisFunc(("\n"));
13912
13913 AutoCaller autoCaller(this);
13914 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13915
13916 ComPtr<IInternalSessionControl> directControl;
13917 {
13918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13919 directControl = mData->mSession.mDirectControl;
13920 }
13921
13922 /* ignore notifications sent after #OnSessionEnd() is called */
13923 if (!directControl)
13924 return S_OK;
13925
13926 return directControl->OnVideoCaptureChange();
13927}
13928
13929/**
13930 * @note Locks this object for reading.
13931 */
13932HRESULT SessionMachine::onUSBControllerChange()
13933{
13934 LogFlowThisFunc(("\n"));
13935
13936 AutoCaller autoCaller(this);
13937 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13938
13939 ComPtr<IInternalSessionControl> directControl;
13940 {
13941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13942 directControl = mData->mSession.mDirectControl;
13943 }
13944
13945 /* ignore notifications sent after #OnSessionEnd() is called */
13946 if (!directControl)
13947 return S_OK;
13948
13949 return directControl->OnUSBControllerChange();
13950}
13951
13952/**
13953 * @note Locks this object for reading.
13954 */
13955HRESULT SessionMachine::onSharedFolderChange()
13956{
13957 LogFlowThisFunc(("\n"));
13958
13959 AutoCaller autoCaller(this);
13960 AssertComRCReturnRC(autoCaller.rc());
13961
13962 ComPtr<IInternalSessionControl> directControl;
13963 {
13964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13965 directControl = mData->mSession.mDirectControl;
13966 }
13967
13968 /* ignore notifications sent after #OnSessionEnd() is called */
13969 if (!directControl)
13970 return S_OK;
13971
13972 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13973}
13974
13975/**
13976 * @note Locks this object for reading.
13977 */
13978HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13979{
13980 LogFlowThisFunc(("\n"));
13981
13982 AutoCaller autoCaller(this);
13983 AssertComRCReturnRC(autoCaller.rc());
13984
13985 ComPtr<IInternalSessionControl> directControl;
13986 {
13987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13988 directControl = mData->mSession.mDirectControl;
13989 }
13990
13991 /* ignore notifications sent after #OnSessionEnd() is called */
13992 if (!directControl)
13993 return S_OK;
13994
13995 return directControl->OnClipboardModeChange(aClipboardMode);
13996}
13997
13998/**
13999 * @note Locks this object for reading.
14000 */
14001HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14002{
14003 LogFlowThisFunc(("\n"));
14004
14005 AutoCaller autoCaller(this);
14006 AssertComRCReturnRC(autoCaller.rc());
14007
14008 ComPtr<IInternalSessionControl> directControl;
14009 {
14010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14011 directControl = mData->mSession.mDirectControl;
14012 }
14013
14014 /* ignore notifications sent after #OnSessionEnd() is called */
14015 if (!directControl)
14016 return S_OK;
14017
14018 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14019}
14020
14021/**
14022 * @note Locks this object for reading.
14023 */
14024HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14025{
14026 LogFlowThisFunc(("\n"));
14027
14028 AutoCaller autoCaller(this);
14029 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14030
14031 ComPtr<IInternalSessionControl> directControl;
14032 {
14033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14034 directControl = mData->mSession.mDirectControl;
14035 }
14036
14037 /* ignore notifications sent after #OnSessionEnd() is called */
14038 if (!directControl)
14039 return S_OK;
14040
14041 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14042}
14043
14044/**
14045 * @note Locks this object for reading.
14046 */
14047HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14048{
14049 LogFlowThisFunc(("\n"));
14050
14051 AutoCaller autoCaller(this);
14052 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14053
14054 ComPtr<IInternalSessionControl> directControl;
14055 {
14056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14057 directControl = mData->mSession.mDirectControl;
14058 }
14059
14060 /* ignore notifications sent after #OnSessionEnd() is called */
14061 if (!directControl)
14062 return S_OK;
14063
14064 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14065}
14066
14067/**
14068 * Returns @c true if this machine's USB controller reports it has a matching
14069 * filter for the given USB device and @c false otherwise.
14070 *
14071 * @note locks this object for reading.
14072 */
14073bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14074{
14075 AutoCaller autoCaller(this);
14076 /* silently return if not ready -- this method may be called after the
14077 * direct machine session has been called */
14078 if (!autoCaller.isOk())
14079 return false;
14080
14081#ifdef VBOX_WITH_USB
14082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14083
14084 switch (mData->mMachineState)
14085 {
14086 case MachineState_Starting:
14087 case MachineState_Restoring:
14088 case MachineState_TeleportingIn:
14089 case MachineState_Paused:
14090 case MachineState_Running:
14091 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14092 * elsewhere... */
14093 alock.release();
14094 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
14095 default: break;
14096 }
14097#else
14098 NOREF(aDevice);
14099 NOREF(aMaskedIfs);
14100#endif
14101 return false;
14102}
14103
14104/**
14105 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14106 */
14107HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14108 IVirtualBoxErrorInfo *aError,
14109 ULONG aMaskedIfs)
14110{
14111 LogFlowThisFunc(("\n"));
14112
14113 AutoCaller autoCaller(this);
14114
14115 /* This notification may happen after the machine object has been
14116 * uninitialized (the session was closed), so don't assert. */
14117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14118
14119 ComPtr<IInternalSessionControl> directControl;
14120 {
14121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14122 directControl = mData->mSession.mDirectControl;
14123 }
14124
14125 /* fail on notifications sent after #OnSessionEnd() is called, it is
14126 * expected by the caller */
14127 if (!directControl)
14128 return E_FAIL;
14129
14130 /* No locks should be held at this point. */
14131 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14132 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14133
14134 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14135}
14136
14137/**
14138 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14139 */
14140HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14141 IVirtualBoxErrorInfo *aError)
14142{
14143 LogFlowThisFunc(("\n"));
14144
14145 AutoCaller autoCaller(this);
14146
14147 /* This notification may happen after the machine object has been
14148 * uninitialized (the session was closed), so don't assert. */
14149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14150
14151 ComPtr<IInternalSessionControl> directControl;
14152 {
14153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14154 directControl = mData->mSession.mDirectControl;
14155 }
14156
14157 /* fail on notifications sent after #OnSessionEnd() is called, it is
14158 * expected by the caller */
14159 if (!directControl)
14160 return E_FAIL;
14161
14162 /* No locks should be held at this point. */
14163 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14164 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14165
14166 return directControl->OnUSBDeviceDetach(aId, aError);
14167}
14168
14169// protected methods
14170/////////////////////////////////////////////////////////////////////////////
14171
14172/**
14173 * Helper method to finalize saving the state.
14174 *
14175 * @note Must be called from under this object's lock.
14176 *
14177 * @param aRc S_OK if the snapshot has been taken successfully
14178 * @param aErrMsg human readable error message for failure
14179 *
14180 * @note Locks mParent + this objects for writing.
14181 */
14182HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14183{
14184 LogFlowThisFuncEnter();
14185
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14188
14189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14190
14191 HRESULT rc = S_OK;
14192
14193 if (SUCCEEDED(aRc))
14194 {
14195 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14196
14197 /* save all VM settings */
14198 rc = saveSettings(NULL);
14199 // no need to check whether VirtualBox.xml needs saving also since
14200 // we can't have a name change pending at this point
14201 }
14202 else
14203 {
14204 // delete the saved state file (it might have been already created);
14205 // we need not check whether this is shared with a snapshot here because
14206 // we certainly created this saved state file here anew
14207 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14208 }
14209
14210 /* notify the progress object about operation completion */
14211 Assert(mConsoleTaskData.mProgress);
14212 if (SUCCEEDED(aRc))
14213 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14214 else
14215 {
14216 if (aErrMsg.length())
14217 mConsoleTaskData.mProgress->notifyComplete(aRc,
14218 COM_IIDOF(ISession),
14219 getComponentName(),
14220 aErrMsg.c_str());
14221 else
14222 mConsoleTaskData.mProgress->notifyComplete(aRc);
14223 }
14224
14225 /* clear out the temporary saved state data */
14226 mConsoleTaskData.mLastState = MachineState_Null;
14227 mConsoleTaskData.strStateFilePath.setNull();
14228 mConsoleTaskData.mProgress.setNull();
14229
14230 LogFlowThisFuncLeave();
14231 return rc;
14232}
14233
14234/**
14235 * Deletes the given file if it is no longer in use by either the current machine state
14236 * (if the machine is "saved") or any of the machine's snapshots.
14237 *
14238 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14239 * but is different for each SnapshotMachine. When calling this, the order of calling this
14240 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14241 * is therefore critical. I know, it's all rather messy.
14242 *
14243 * @param strStateFile
14244 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14245 */
14246void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14247 Snapshot *pSnapshotToIgnore)
14248{
14249 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14250 if ( (strStateFile.isNotEmpty())
14251 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14252 )
14253 // ... and it must also not be shared with other snapshots
14254 if ( !mData->mFirstSnapshot
14255 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14256 // this checks the SnapshotMachine's state file paths
14257 )
14258 RTFileDelete(strStateFile.c_str());
14259}
14260
14261/**
14262 * Locks the attached media.
14263 *
14264 * All attached hard disks are locked for writing and DVD/floppy are locked for
14265 * reading. Parents of attached hard disks (if any) are locked for reading.
14266 *
14267 * This method also performs accessibility check of all media it locks: if some
14268 * media is inaccessible, the method will return a failure and a bunch of
14269 * extended error info objects per each inaccessible medium.
14270 *
14271 * Note that this method is atomic: if it returns a success, all media are
14272 * locked as described above; on failure no media is locked at all (all
14273 * succeeded individual locks will be undone).
14274 *
14275 * The caller is responsible for doing the necessary state sanity checks.
14276 *
14277 * The locks made by this method must be undone by calling #unlockMedia() when
14278 * no more needed.
14279 */
14280HRESULT SessionMachine::lockMedia()
14281{
14282 AutoCaller autoCaller(this);
14283 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14284
14285 AutoMultiWriteLock2 alock(this->lockHandle(),
14286 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14287
14288 /* bail out if trying to lock things with already set up locking */
14289 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14290
14291 MultiResult mrc(S_OK);
14292
14293 /* Collect locking information for all medium objects attached to the VM. */
14294 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14295 it != mMediaData->mAttachments.end();
14296 ++it)
14297 {
14298 MediumAttachment* pAtt = *it;
14299 DeviceType_T devType = pAtt->getType();
14300 Medium *pMedium = pAtt->getMedium();
14301
14302 MediumLockList *pMediumLockList(new MediumLockList());
14303 // There can be attachments without a medium (floppy/dvd), and thus
14304 // it's impossible to create a medium lock list. It still makes sense
14305 // to have the empty medium lock list in the map in case a medium is
14306 // attached later.
14307 if (pMedium != NULL)
14308 {
14309 MediumType_T mediumType = pMedium->getType();
14310 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14311 || mediumType == MediumType_Shareable;
14312 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14313
14314 alock.release();
14315 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14316 !fIsReadOnlyLock /* fMediumLockWrite */,
14317 NULL,
14318 *pMediumLockList);
14319 alock.acquire();
14320 if (FAILED(mrc))
14321 {
14322 delete pMediumLockList;
14323 mData->mSession.mLockedMedia.Clear();
14324 break;
14325 }
14326 }
14327
14328 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14329 if (FAILED(rc))
14330 {
14331 mData->mSession.mLockedMedia.Clear();
14332 mrc = setError(rc,
14333 tr("Collecting locking information for all attached media failed"));
14334 break;
14335 }
14336 }
14337
14338 if (SUCCEEDED(mrc))
14339 {
14340 /* Now lock all media. If this fails, nothing is locked. */
14341 alock.release();
14342 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14343 alock.acquire();
14344 if (FAILED(rc))
14345 {
14346 mrc = setError(rc,
14347 tr("Locking of attached media failed"));
14348 }
14349 }
14350
14351 return mrc;
14352}
14353
14354/**
14355 * Undoes the locks made by by #lockMedia().
14356 */
14357void SessionMachine::unlockMedia()
14358{
14359 AutoCaller autoCaller(this);
14360 AssertComRCReturnVoid(autoCaller.rc());
14361
14362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14363
14364 /* we may be holding important error info on the current thread;
14365 * preserve it */
14366 ErrorInfoKeeper eik;
14367
14368 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14369 AssertComRC(rc);
14370}
14371
14372/**
14373 * Helper to change the machine state (reimplementation).
14374 *
14375 * @note Locks this object for writing.
14376 * @note This method must not call saveSettings or SaveSettings, otherwise
14377 * it can cause crashes in random places due to unexpectedly committing
14378 * the current settings. The caller is responsible for that. The call
14379 * to saveStateSettings is fine, because this method does not commit.
14380 */
14381HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14382{
14383 LogFlowThisFuncEnter();
14384 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14385
14386 AutoCaller autoCaller(this);
14387 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14388
14389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14390
14391 MachineState_T oldMachineState = mData->mMachineState;
14392
14393 AssertMsgReturn(oldMachineState != aMachineState,
14394 ("oldMachineState=%s, aMachineState=%s\n",
14395 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14396 E_FAIL);
14397
14398 HRESULT rc = S_OK;
14399
14400 int stsFlags = 0;
14401 bool deleteSavedState = false;
14402
14403 /* detect some state transitions */
14404
14405 if ( ( oldMachineState == MachineState_Saved
14406 && aMachineState == MachineState_Restoring)
14407 || ( ( oldMachineState == MachineState_PoweredOff
14408 || oldMachineState == MachineState_Teleported
14409 || oldMachineState == MachineState_Aborted
14410 )
14411 && ( aMachineState == MachineState_TeleportingIn
14412 || aMachineState == MachineState_Starting
14413 )
14414 )
14415 )
14416 {
14417 /* The EMT thread is about to start */
14418
14419 /* Nothing to do here for now... */
14420
14421 /// @todo NEWMEDIA don't let mDVDDrive and other children
14422 /// change anything when in the Starting/Restoring state
14423 }
14424 else if ( ( oldMachineState == MachineState_Running
14425 || oldMachineState == MachineState_Paused
14426 || oldMachineState == MachineState_Teleporting
14427 || oldMachineState == MachineState_LiveSnapshotting
14428 || oldMachineState == MachineState_Stuck
14429 || oldMachineState == MachineState_Starting
14430 || oldMachineState == MachineState_Stopping
14431 || oldMachineState == MachineState_Saving
14432 || oldMachineState == MachineState_Restoring
14433 || oldMachineState == MachineState_TeleportingPausedVM
14434 || oldMachineState == MachineState_TeleportingIn
14435 )
14436 && ( aMachineState == MachineState_PoweredOff
14437 || aMachineState == MachineState_Saved
14438 || aMachineState == MachineState_Teleported
14439 || aMachineState == MachineState_Aborted
14440 )
14441 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14442 * snapshot */
14443 && ( mConsoleTaskData.mSnapshot.isNull()
14444 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14445 )
14446 )
14447 {
14448 /* The EMT thread has just stopped, unlock attached media. Note that as
14449 * opposed to locking that is done from Console, we do unlocking here
14450 * because the VM process may have aborted before having a chance to
14451 * properly unlock all media it locked. */
14452
14453 unlockMedia();
14454 }
14455
14456 if (oldMachineState == MachineState_Restoring)
14457 {
14458 if (aMachineState != MachineState_Saved)
14459 {
14460 /*
14461 * delete the saved state file once the machine has finished
14462 * restoring from it (note that Console sets the state from
14463 * Restoring to Saved if the VM couldn't restore successfully,
14464 * to give the user an ability to fix an error and retry --
14465 * we keep the saved state file in this case)
14466 */
14467 deleteSavedState = true;
14468 }
14469 }
14470 else if ( oldMachineState == MachineState_Saved
14471 && ( aMachineState == MachineState_PoweredOff
14472 || aMachineState == MachineState_Aborted
14473 || aMachineState == MachineState_Teleported
14474 )
14475 )
14476 {
14477 /*
14478 * delete the saved state after Console::ForgetSavedState() is called
14479 * or if the VM process (owning a direct VM session) crashed while the
14480 * VM was Saved
14481 */
14482
14483 /// @todo (dmik)
14484 // Not sure that deleting the saved state file just because of the
14485 // client death before it attempted to restore the VM is a good
14486 // thing. But when it crashes we need to go to the Aborted state
14487 // which cannot have the saved state file associated... The only
14488 // way to fix this is to make the Aborted condition not a VM state
14489 // but a bool flag: i.e., when a crash occurs, set it to true and
14490 // change the state to PoweredOff or Saved depending on the
14491 // saved state presence.
14492
14493 deleteSavedState = true;
14494 mData->mCurrentStateModified = TRUE;
14495 stsFlags |= SaveSTS_CurStateModified;
14496 }
14497
14498 if ( aMachineState == MachineState_Starting
14499 || aMachineState == MachineState_Restoring
14500 || aMachineState == MachineState_TeleportingIn
14501 )
14502 {
14503 /* set the current state modified flag to indicate that the current
14504 * state is no more identical to the state in the
14505 * current snapshot */
14506 if (!mData->mCurrentSnapshot.isNull())
14507 {
14508 mData->mCurrentStateModified = TRUE;
14509 stsFlags |= SaveSTS_CurStateModified;
14510 }
14511 }
14512
14513 if (deleteSavedState)
14514 {
14515 if (mRemoveSavedState)
14516 {
14517 Assert(!mSSData->strStateFilePath.isEmpty());
14518
14519 // it is safe to delete the saved state file if ...
14520 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14521 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14522 // ... none of the snapshots share the saved state file
14523 )
14524 RTFileDelete(mSSData->strStateFilePath.c_str());
14525 }
14526
14527 mSSData->strStateFilePath.setNull();
14528 stsFlags |= SaveSTS_StateFilePath;
14529 }
14530
14531 /* redirect to the underlying peer machine */
14532 mPeer->setMachineState(aMachineState);
14533
14534 if ( aMachineState == MachineState_PoweredOff
14535 || aMachineState == MachineState_Teleported
14536 || aMachineState == MachineState_Aborted
14537 || aMachineState == MachineState_Saved)
14538 {
14539 /* the machine has stopped execution
14540 * (or the saved state file was adopted) */
14541 stsFlags |= SaveSTS_StateTimeStamp;
14542 }
14543
14544 if ( ( oldMachineState == MachineState_PoweredOff
14545 || oldMachineState == MachineState_Aborted
14546 || oldMachineState == MachineState_Teleported
14547 )
14548 && aMachineState == MachineState_Saved)
14549 {
14550 /* the saved state file was adopted */
14551 Assert(!mSSData->strStateFilePath.isEmpty());
14552 stsFlags |= SaveSTS_StateFilePath;
14553 }
14554
14555#ifdef VBOX_WITH_GUEST_PROPS
14556 if ( aMachineState == MachineState_PoweredOff
14557 || aMachineState == MachineState_Aborted
14558 || aMachineState == MachineState_Teleported)
14559 {
14560 /* Make sure any transient guest properties get removed from the
14561 * property store on shutdown. */
14562
14563 HWData::GuestPropertyMap::const_iterator it;
14564 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14565 if (!fNeedsSaving)
14566 for (it = mHWData->mGuestProperties.begin();
14567 it != mHWData->mGuestProperties.end(); ++it)
14568 if ( (it->second.mFlags & guestProp::TRANSIENT)
14569 || (it->second.mFlags & guestProp::TRANSRESET))
14570 {
14571 fNeedsSaving = true;
14572 break;
14573 }
14574 if (fNeedsSaving)
14575 {
14576 mData->mCurrentStateModified = TRUE;
14577 stsFlags |= SaveSTS_CurStateModified;
14578 }
14579 }
14580#endif
14581
14582 rc = saveStateSettings(stsFlags);
14583
14584 if ( ( oldMachineState != MachineState_PoweredOff
14585 && oldMachineState != MachineState_Aborted
14586 && oldMachineState != MachineState_Teleported
14587 )
14588 && ( aMachineState == MachineState_PoweredOff
14589 || aMachineState == MachineState_Aborted
14590 || aMachineState == MachineState_Teleported
14591 )
14592 )
14593 {
14594 /* we've been shut down for any reason */
14595 /* no special action so far */
14596 }
14597
14598 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14599 LogFlowThisFuncLeave();
14600 return rc;
14601}
14602
14603/**
14604 * Sends the current machine state value to the VM process.
14605 *
14606 * @note Locks this object for reading, then calls a client process.
14607 */
14608HRESULT SessionMachine::updateMachineStateOnClient()
14609{
14610 AutoCaller autoCaller(this);
14611 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14612
14613 ComPtr<IInternalSessionControl> directControl;
14614 {
14615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14616 AssertReturn(!!mData, E_FAIL);
14617 directControl = mData->mSession.mDirectControl;
14618
14619 /* directControl may be already set to NULL here in #OnSessionEnd()
14620 * called too early by the direct session process while there is still
14621 * some operation (like deleting the snapshot) in progress. The client
14622 * process in this case is waiting inside Session::close() for the
14623 * "end session" process object to complete, while #uninit() called by
14624 * #checkForDeath() on the Watcher thread is waiting for the pending
14625 * operation to complete. For now, we accept this inconsistent behavior
14626 * and simply do nothing here. */
14627
14628 if (mData->mSession.mState == SessionState_Unlocking)
14629 return S_OK;
14630
14631 AssertReturn(!directControl.isNull(), E_FAIL);
14632 }
14633
14634 return directControl->UpdateMachineState(mData->mMachineState);
14635}
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