VirtualBox

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

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

Main: fix for r85814

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