VirtualBox

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

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

Main/VPX: only save the settings if the change was successful

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

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