VirtualBox

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

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

Main/Machine: always update the property flags in Machine::setGuestPropertyToService()

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 485.7 KB
Line 
1/* $Id: MachineImpl.cpp 46303 2013-05-29 08:24:40Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
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 = mHWData->mGuestProperties.find(strName);
5786
5787 if (it != mHWData->mGuestProperties.end())
5788 {
5789 char szFlags[MAX_FLAGS_LEN + 1];
5790 it->second.strValue.cloneTo(aValue);
5791 *aTimestamp = it->second.mTimestamp;
5792 writeFlags(it->second.mFlags, szFlags);
5793 Bstr(szFlags).cloneTo(aFlags);
5794 }
5795
5796 return S_OK;
5797}
5798
5799/**
5800 * Query the VM that a guest property belongs to for the property.
5801 * @returns E_ACCESSDENIED if the VM process is not available or not
5802 * currently handling queries and the lookup should then be done in
5803 * VBoxSVC.
5804 */
5805HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5806 BSTR *aValue,
5807 LONG64 *aTimestamp,
5808 BSTR *aFlags) const
5809{
5810 HRESULT rc;
5811 ComPtr<IInternalSessionControl> directControl;
5812 directControl = mData->mSession.mDirectControl;
5813
5814 /* fail if we were called after #OnSessionEnd() is called. This is a
5815 * silly race condition. */
5816
5817 if (!directControl)
5818 rc = E_ACCESSDENIED;
5819 else
5820 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5821 false /* isSetter */,
5822 aValue, aTimestamp, aFlags);
5823 return rc;
5824}
5825#endif // VBOX_WITH_GUEST_PROPS
5826
5827STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5828 BSTR *aValue,
5829 LONG64 *aTimestamp,
5830 BSTR *aFlags)
5831{
5832#ifndef VBOX_WITH_GUEST_PROPS
5833 ReturnComNotImplemented();
5834#else // VBOX_WITH_GUEST_PROPS
5835 CheckComArgStrNotEmptyOrNull(aName);
5836 CheckComArgOutPointerValid(aValue);
5837 CheckComArgOutPointerValid(aTimestamp);
5838 CheckComArgOutPointerValid(aFlags);
5839
5840 AutoCaller autoCaller(this);
5841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5842
5843 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5844 if (rc == E_ACCESSDENIED)
5845 /* The VM is not running or the service is not (yet) accessible */
5846 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5847 return rc;
5848#endif // VBOX_WITH_GUEST_PROPS
5849}
5850
5851STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5852{
5853 LONG64 dummyTimestamp;
5854 Bstr dummyFlags;
5855 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5856}
5857
5858STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5859{
5860 Bstr dummyValue;
5861 Bstr dummyFlags;
5862 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5863}
5864
5865#ifdef VBOX_WITH_GUEST_PROPS
5866/**
5867 * Set a guest property in VBoxSVC's internal structures.
5868 */
5869HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5870 IN_BSTR aFlags)
5871{
5872 using namespace guestProp;
5873
5874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5875 HRESULT rc = S_OK;
5876
5877 rc = checkStateDependency(MutableStateDep);
5878 if (FAILED(rc)) return rc;
5879
5880 try
5881 {
5882 Utf8Str utf8Name(aName);
5883 Utf8Str utf8Flags(aFlags);
5884 uint32_t fFlags = NILFLAG;
5885 if ( aFlags != NULL
5886 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
5887 return setError(E_INVALIDARG,
5888 tr("Invalid guest property flag values: '%ls'"),
5889 aFlags);
5890
5891 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
5892 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
5893 if (it == mHWData->mGuestProperties.end())
5894 {
5895 if (!fDelete)
5896 {
5897 setModified(IsModified_MachineData);
5898 mHWData.backupEx();
5899
5900 RTTIMESPEC time;
5901 HWData::GuestProperty prop;
5902 prop.strValue = aValue;
5903 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5904 prop.mFlags = fFlags;
5905 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5906 }
5907 }
5908 else
5909 {
5910 if (it->second.mFlags & (RDONLYHOST))
5911 {
5912 rc = setError(E_ACCESSDENIED,
5913 tr("The property '%ls' cannot be changed by the host"),
5914 aName);
5915 }
5916 else
5917 {
5918 setModified(IsModified_MachineData);
5919 mHWData.backupEx();
5920
5921 /* The backupEx() operation invalidates our iterator,
5922 * so get a new one. */
5923 it = mHWData->mGuestProperties.find(utf8Name);
5924 Assert(it != mHWData->mGuestProperties.end());
5925
5926 if (!fDelete)
5927 {
5928 RTTIMESPEC time;
5929 it->second.strValue = aValue;
5930 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5931 it->second.mFlags = fFlags;
5932 }
5933 else
5934 mHWData->mGuestProperties.erase(it);
5935 }
5936 }
5937
5938 if ( SUCCEEDED(rc)
5939 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5940 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5941 RTSTR_MAX,
5942 utf8Name.c_str(),
5943 RTSTR_MAX,
5944 NULL)
5945 )
5946 )
5947 {
5948 alock.release();
5949
5950 mParent->onGuestPropertyChange(mData->mUuid, aName,
5951 aValue ? aValue : Bstr("").raw(),
5952 aFlags ? aFlags : Bstr("").raw());
5953 }
5954 }
5955 catch (std::bad_alloc &)
5956 {
5957 rc = E_OUTOFMEMORY;
5958 }
5959
5960 return rc;
5961}
5962
5963/**
5964 * Set a property on the VM that that property belongs to.
5965 * @returns E_ACCESSDENIED if the VM process is not available or not
5966 * currently handling queries and the setting should then be done in
5967 * VBoxSVC.
5968 */
5969HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5970 IN_BSTR aFlags)
5971{
5972 HRESULT rc;
5973
5974 try
5975 {
5976 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5977
5978 BSTR dummy = NULL; /* will not be changed (setter) */
5979 LONG64 dummy64;
5980 if (!directControl)
5981 rc = E_ACCESSDENIED;
5982 else
5983 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5984 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5985 true /* isSetter */,
5986 &dummy, &dummy64, &dummy);
5987 }
5988 catch (std::bad_alloc &)
5989 {
5990 rc = E_OUTOFMEMORY;
5991 }
5992
5993 return rc;
5994}
5995#endif // VBOX_WITH_GUEST_PROPS
5996
5997STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5998 IN_BSTR aFlags)
5999{
6000#ifndef VBOX_WITH_GUEST_PROPS
6001 ReturnComNotImplemented();
6002#else // VBOX_WITH_GUEST_PROPS
6003 CheckComArgStrNotEmptyOrNull(aName);
6004 CheckComArgMaybeNull(aFlags);
6005 CheckComArgMaybeNull(aValue);
6006
6007 AutoCaller autoCaller(this);
6008 if (FAILED(autoCaller.rc()))
6009 return autoCaller.rc();
6010
6011 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6012 if (rc == E_ACCESSDENIED)
6013 /* The VM is not running or the service is not (yet) accessible */
6014 rc = setGuestPropertyToService(aName, aValue, aFlags);
6015 return rc;
6016#endif // VBOX_WITH_GUEST_PROPS
6017}
6018
6019STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6020{
6021 return SetGuestProperty(aName, aValue, NULL);
6022}
6023
6024STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6025{
6026 return SetGuestProperty(aName, NULL, NULL);
6027}
6028
6029#ifdef VBOX_WITH_GUEST_PROPS
6030/**
6031 * Enumerate the guest properties in VBoxSVC's internal structures.
6032 */
6033HRESULT Machine::enumerateGuestPropertiesInService
6034 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6035 ComSafeArrayOut(BSTR, aValues),
6036 ComSafeArrayOut(LONG64, aTimestamps),
6037 ComSafeArrayOut(BSTR, aFlags))
6038{
6039 using namespace guestProp;
6040
6041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6042 Utf8Str strPatterns(aPatterns);
6043
6044 HWData::GuestPropertyMap propMap;
6045
6046 /*
6047 * Look for matching patterns and build up a list.
6048 */
6049 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6050 while (it != mHWData->mGuestProperties.end())
6051 {
6052 if ( strPatterns.isEmpty()
6053 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6054 RTSTR_MAX,
6055 it->first.c_str(),
6056 RTSTR_MAX,
6057 NULL)
6058 )
6059 {
6060 propMap.insert(*it);
6061 }
6062
6063 it++;
6064 }
6065
6066 alock.release();
6067
6068 /*
6069 * And build up the arrays for returning the property information.
6070 */
6071 size_t cEntries = propMap.size();
6072 SafeArray<BSTR> names(cEntries);
6073 SafeArray<BSTR> values(cEntries);
6074 SafeArray<LONG64> timestamps(cEntries);
6075 SafeArray<BSTR> flags(cEntries);
6076 size_t iProp = 0;
6077
6078 it = propMap.begin();
6079 while (it != propMap.end())
6080 {
6081 char szFlags[MAX_FLAGS_LEN + 1];
6082 it->first.cloneTo(&names[iProp]);
6083 it->second.strValue.cloneTo(&values[iProp]);
6084 timestamps[iProp] = it->second.mTimestamp;
6085 writeFlags(it->second.mFlags, szFlags);
6086 Bstr(szFlags).cloneTo(&flags[iProp++]);
6087 it++;
6088 }
6089 names.detachTo(ComSafeArrayOutArg(aNames));
6090 values.detachTo(ComSafeArrayOutArg(aValues));
6091 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6092 flags.detachTo(ComSafeArrayOutArg(aFlags));
6093 return S_OK;
6094}
6095
6096/**
6097 * Enumerate the properties managed by a VM.
6098 * @returns E_ACCESSDENIED if the VM process is not available or not
6099 * currently handling queries and the setting should then be done in
6100 * VBoxSVC.
6101 */
6102HRESULT Machine::enumerateGuestPropertiesOnVM
6103 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6104 ComSafeArrayOut(BSTR, aValues),
6105 ComSafeArrayOut(LONG64, aTimestamps),
6106 ComSafeArrayOut(BSTR, aFlags))
6107{
6108 HRESULT rc;
6109 ComPtr<IInternalSessionControl> directControl;
6110 directControl = mData->mSession.mDirectControl;
6111
6112 if (!directControl)
6113 rc = E_ACCESSDENIED;
6114 else
6115 rc = directControl->EnumerateGuestProperties
6116 (aPatterns, ComSafeArrayOutArg(aNames),
6117 ComSafeArrayOutArg(aValues),
6118 ComSafeArrayOutArg(aTimestamps),
6119 ComSafeArrayOutArg(aFlags));
6120 return rc;
6121}
6122#endif // VBOX_WITH_GUEST_PROPS
6123
6124STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6125 ComSafeArrayOut(BSTR, aNames),
6126 ComSafeArrayOut(BSTR, aValues),
6127 ComSafeArrayOut(LONG64, aTimestamps),
6128 ComSafeArrayOut(BSTR, aFlags))
6129{
6130#ifndef VBOX_WITH_GUEST_PROPS
6131 ReturnComNotImplemented();
6132#else // VBOX_WITH_GUEST_PROPS
6133 CheckComArgMaybeNull(aPatterns);
6134 CheckComArgOutSafeArrayPointerValid(aNames);
6135 CheckComArgOutSafeArrayPointerValid(aValues);
6136 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6137 CheckComArgOutSafeArrayPointerValid(aFlags);
6138
6139 AutoCaller autoCaller(this);
6140 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6141
6142 HRESULT rc = enumerateGuestPropertiesOnVM
6143 (aPatterns, ComSafeArrayOutArg(aNames),
6144 ComSafeArrayOutArg(aValues),
6145 ComSafeArrayOutArg(aTimestamps),
6146 ComSafeArrayOutArg(aFlags));
6147 if (rc == E_ACCESSDENIED)
6148 /* The VM is not running or the service is not (yet) accessible */
6149 rc = enumerateGuestPropertiesInService
6150 (aPatterns, ComSafeArrayOutArg(aNames),
6151 ComSafeArrayOutArg(aValues),
6152 ComSafeArrayOutArg(aTimestamps),
6153 ComSafeArrayOutArg(aFlags));
6154 return rc;
6155#endif // VBOX_WITH_GUEST_PROPS
6156}
6157
6158STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6159 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6160{
6161 MediaData::AttachmentList atts;
6162
6163 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6164 if (FAILED(rc)) return rc;
6165
6166 SafeIfaceArray<IMediumAttachment> attachments(atts);
6167 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6168
6169 return S_OK;
6170}
6171
6172STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6173 LONG aControllerPort,
6174 LONG aDevice,
6175 IMediumAttachment **aAttachment)
6176{
6177 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6178 aControllerName, aControllerPort, aDevice));
6179
6180 CheckComArgStrNotEmptyOrNull(aControllerName);
6181 CheckComArgOutPointerValid(aAttachment);
6182
6183 AutoCaller autoCaller(this);
6184 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6185
6186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6187
6188 *aAttachment = NULL;
6189
6190 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6191 aControllerName,
6192 aControllerPort,
6193 aDevice);
6194 if (pAttach.isNull())
6195 return setError(VBOX_E_OBJECT_NOT_FOUND,
6196 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6197 aDevice, aControllerPort, aControllerName);
6198
6199 pAttach.queryInterfaceTo(aAttachment);
6200
6201 return S_OK;
6202}
6203
6204STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6205 StorageBus_T aConnectionType,
6206 IStorageController **controller)
6207{
6208 CheckComArgStrNotEmptyOrNull(aName);
6209
6210 if ( (aConnectionType <= StorageBus_Null)
6211 || (aConnectionType > StorageBus_SAS))
6212 return setError(E_INVALIDARG,
6213 tr("Invalid connection type: %d"),
6214 aConnectionType);
6215
6216 AutoCaller autoCaller(this);
6217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6218
6219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6220
6221 HRESULT rc = checkStateDependency(MutableStateDep);
6222 if (FAILED(rc)) return rc;
6223
6224 /* try to find one with the name first. */
6225 ComObjPtr<StorageController> ctrl;
6226
6227 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6228 if (SUCCEEDED(rc))
6229 return setError(VBOX_E_OBJECT_IN_USE,
6230 tr("Storage controller named '%ls' already exists"),
6231 aName);
6232
6233 ctrl.createObject();
6234
6235 /* get a new instance number for the storage controller */
6236 ULONG ulInstance = 0;
6237 bool fBootable = true;
6238 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6239 it != mStorageControllers->end();
6240 ++it)
6241 {
6242 if ((*it)->getStorageBus() == aConnectionType)
6243 {
6244 ULONG ulCurInst = (*it)->getInstance();
6245
6246 if (ulCurInst >= ulInstance)
6247 ulInstance = ulCurInst + 1;
6248
6249 /* Only one controller of each type can be marked as bootable. */
6250 if ((*it)->getBootable())
6251 fBootable = false;
6252 }
6253 }
6254
6255 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6256 if (FAILED(rc)) return rc;
6257
6258 setModified(IsModified_Storage);
6259 mStorageControllers.backup();
6260 mStorageControllers->push_back(ctrl);
6261
6262 ctrl.queryInterfaceTo(controller);
6263
6264 /* inform the direct session if any */
6265 alock.release();
6266 onStorageControllerChange();
6267
6268 return S_OK;
6269}
6270
6271STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6272 IStorageController **aStorageController)
6273{
6274 CheckComArgStrNotEmptyOrNull(aName);
6275
6276 AutoCaller autoCaller(this);
6277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6278
6279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6280
6281 ComObjPtr<StorageController> ctrl;
6282
6283 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6284 if (SUCCEEDED(rc))
6285 ctrl.queryInterfaceTo(aStorageController);
6286
6287 return rc;
6288}
6289
6290STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6291 IStorageController **aStorageController)
6292{
6293 AutoCaller autoCaller(this);
6294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6295
6296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6297
6298 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6299 it != mStorageControllers->end();
6300 ++it)
6301 {
6302 if ((*it)->getInstance() == aInstance)
6303 {
6304 (*it).queryInterfaceTo(aStorageController);
6305 return S_OK;
6306 }
6307 }
6308
6309 return setError(VBOX_E_OBJECT_NOT_FOUND,
6310 tr("Could not find a storage controller with instance number '%lu'"),
6311 aInstance);
6312}
6313
6314STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6315{
6316 AutoCaller autoCaller(this);
6317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6318
6319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6320
6321 HRESULT rc = checkStateDependency(MutableStateDep);
6322 if (FAILED(rc)) return rc;
6323
6324 ComObjPtr<StorageController> ctrl;
6325
6326 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6327 if (SUCCEEDED(rc))
6328 {
6329 /* Ensure that only one controller of each type is marked as bootable. */
6330 if (fBootable == TRUE)
6331 {
6332 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6333 it != mStorageControllers->end();
6334 ++it)
6335 {
6336 ComObjPtr<StorageController> aCtrl = (*it);
6337
6338 if ( (aCtrl->getName() != Utf8Str(aName))
6339 && aCtrl->getBootable() == TRUE
6340 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6341 && aCtrl->getControllerType() == ctrl->getControllerType())
6342 {
6343 aCtrl->setBootable(FALSE);
6344 break;
6345 }
6346 }
6347 }
6348
6349 if (SUCCEEDED(rc))
6350 {
6351 ctrl->setBootable(fBootable);
6352 setModified(IsModified_Storage);
6353 }
6354 }
6355
6356 if (SUCCEEDED(rc))
6357 {
6358 /* inform the direct session if any */
6359 alock.release();
6360 onStorageControllerChange();
6361 }
6362
6363 return rc;
6364}
6365
6366STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6367{
6368 CheckComArgStrNotEmptyOrNull(aName);
6369
6370 AutoCaller autoCaller(this);
6371 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6372
6373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6374
6375 HRESULT rc = checkStateDependency(MutableStateDep);
6376 if (FAILED(rc)) return rc;
6377
6378 ComObjPtr<StorageController> ctrl;
6379 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6380 if (FAILED(rc)) return rc;
6381
6382 {
6383 /* find all attached devices to the appropriate storage controller and detach them all */
6384 // make a temporary list because detachDevice invalidates iterators into
6385 // mMediaData->mAttachments
6386 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6387
6388 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6389 it != llAttachments2.end();
6390 ++it)
6391 {
6392 MediumAttachment *pAttachTemp = *it;
6393
6394 AutoCaller localAutoCaller(pAttachTemp);
6395 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6396
6397 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6398
6399 if (pAttachTemp->getControllerName() == aName)
6400 {
6401 rc = detachDevice(pAttachTemp, alock, NULL);
6402 if (FAILED(rc)) return rc;
6403 }
6404 }
6405 }
6406
6407 /* We can remove it now. */
6408 setModified(IsModified_Storage);
6409 mStorageControllers.backup();
6410
6411 ctrl->unshare();
6412
6413 mStorageControllers->remove(ctrl);
6414
6415 /* inform the direct session if any */
6416 alock.release();
6417 onStorageControllerChange();
6418
6419 return S_OK;
6420}
6421
6422STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6423 ULONG *puOriginX,
6424 ULONG *puOriginY,
6425 ULONG *puWidth,
6426 ULONG *puHeight,
6427 BOOL *pfEnabled)
6428{
6429 LogFlowThisFunc(("\n"));
6430
6431 CheckComArgNotNull(puOriginX);
6432 CheckComArgNotNull(puOriginY);
6433 CheckComArgNotNull(puWidth);
6434 CheckComArgNotNull(puHeight);
6435 CheckComArgNotNull(pfEnabled);
6436
6437 uint32_t u32OriginX= 0;
6438 uint32_t u32OriginY= 0;
6439 uint32_t u32Width = 0;
6440 uint32_t u32Height = 0;
6441 uint16_t u16Flags = 0;
6442
6443 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6444 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6445 if (RT_FAILURE(vrc))
6446 {
6447#ifdef RT_OS_WINDOWS
6448 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6449 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6450 * So just assign fEnable to TRUE again.
6451 * The right fix would be to change GUI API wrappers to make sure that parameters
6452 * are changed only if API succeeds.
6453 */
6454 *pfEnabled = TRUE;
6455#endif
6456 return setError(VBOX_E_IPRT_ERROR,
6457 tr("Saved guest size is not available (%Rrc)"),
6458 vrc);
6459 }
6460
6461 *puOriginX = u32OriginX;
6462 *puOriginY = u32OriginY;
6463 *puWidth = u32Width;
6464 *puHeight = u32Height;
6465 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6466
6467 return S_OK;
6468}
6469
6470STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6471{
6472 LogFlowThisFunc(("\n"));
6473
6474 CheckComArgNotNull(aSize);
6475 CheckComArgNotNull(aWidth);
6476 CheckComArgNotNull(aHeight);
6477
6478 if (aScreenId != 0)
6479 return E_NOTIMPL;
6480
6481 AutoCaller autoCaller(this);
6482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6483
6484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6485
6486 uint8_t *pu8Data = NULL;
6487 uint32_t cbData = 0;
6488 uint32_t u32Width = 0;
6489 uint32_t u32Height = 0;
6490
6491 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6492
6493 if (RT_FAILURE(vrc))
6494 return setError(VBOX_E_IPRT_ERROR,
6495 tr("Saved screenshot data is not available (%Rrc)"),
6496 vrc);
6497
6498 *aSize = cbData;
6499 *aWidth = u32Width;
6500 *aHeight = u32Height;
6501
6502 freeSavedDisplayScreenshot(pu8Data);
6503
6504 return S_OK;
6505}
6506
6507STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6508{
6509 LogFlowThisFunc(("\n"));
6510
6511 CheckComArgNotNull(aWidth);
6512 CheckComArgNotNull(aHeight);
6513 CheckComArgOutSafeArrayPointerValid(aData);
6514
6515 if (aScreenId != 0)
6516 return E_NOTIMPL;
6517
6518 AutoCaller autoCaller(this);
6519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6520
6521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6522
6523 uint8_t *pu8Data = NULL;
6524 uint32_t cbData = 0;
6525 uint32_t u32Width = 0;
6526 uint32_t u32Height = 0;
6527
6528 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6529
6530 if (RT_FAILURE(vrc))
6531 return setError(VBOX_E_IPRT_ERROR,
6532 tr("Saved screenshot data is not available (%Rrc)"),
6533 vrc);
6534
6535 *aWidth = u32Width;
6536 *aHeight = u32Height;
6537
6538 com::SafeArray<BYTE> bitmap(cbData);
6539 /* Convert pixels to format expected by the API caller. */
6540 if (aBGR)
6541 {
6542 /* [0] B, [1] G, [2] R, [3] A. */
6543 for (unsigned i = 0; i < cbData; i += 4)
6544 {
6545 bitmap[i] = pu8Data[i];
6546 bitmap[i + 1] = pu8Data[i + 1];
6547 bitmap[i + 2] = pu8Data[i + 2];
6548 bitmap[i + 3] = 0xff;
6549 }
6550 }
6551 else
6552 {
6553 /* [0] R, [1] G, [2] B, [3] A. */
6554 for (unsigned i = 0; i < cbData; i += 4)
6555 {
6556 bitmap[i] = pu8Data[i + 2];
6557 bitmap[i + 1] = pu8Data[i + 1];
6558 bitmap[i + 2] = pu8Data[i];
6559 bitmap[i + 3] = 0xff;
6560 }
6561 }
6562 bitmap.detachTo(ComSafeArrayOutArg(aData));
6563
6564 freeSavedDisplayScreenshot(pu8Data);
6565
6566 return S_OK;
6567}
6568
6569
6570STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6571{
6572 LogFlowThisFunc(("\n"));
6573
6574 CheckComArgNotNull(aWidth);
6575 CheckComArgNotNull(aHeight);
6576 CheckComArgOutSafeArrayPointerValid(aData);
6577
6578 if (aScreenId != 0)
6579 return E_NOTIMPL;
6580
6581 AutoCaller autoCaller(this);
6582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6583
6584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6585
6586 uint8_t *pu8Data = NULL;
6587 uint32_t cbData = 0;
6588 uint32_t u32Width = 0;
6589 uint32_t u32Height = 0;
6590
6591 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6592
6593 if (RT_FAILURE(vrc))
6594 return setError(VBOX_E_IPRT_ERROR,
6595 tr("Saved screenshot data is not available (%Rrc)"),
6596 vrc);
6597
6598 *aWidth = u32Width;
6599 *aHeight = u32Height;
6600
6601 HRESULT rc = S_OK;
6602 uint8_t *pu8PNG = NULL;
6603 uint32_t cbPNG = 0;
6604 uint32_t cxPNG = 0;
6605 uint32_t cyPNG = 0;
6606
6607 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6608
6609 if (RT_SUCCESS(vrc))
6610 {
6611 com::SafeArray<BYTE> screenData(cbPNG);
6612 screenData.initFrom(pu8PNG, cbPNG);
6613 if (pu8PNG)
6614 RTMemFree(pu8PNG);
6615 screenData.detachTo(ComSafeArrayOutArg(aData));
6616 }
6617 else
6618 {
6619 if (pu8PNG)
6620 RTMemFree(pu8PNG);
6621 return setError(VBOX_E_IPRT_ERROR,
6622 tr("Could not convert screenshot to PNG (%Rrc)"),
6623 vrc);
6624 }
6625
6626 freeSavedDisplayScreenshot(pu8Data);
6627
6628 return rc;
6629}
6630
6631STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6632{
6633 LogFlowThisFunc(("\n"));
6634
6635 CheckComArgNotNull(aSize);
6636 CheckComArgNotNull(aWidth);
6637 CheckComArgNotNull(aHeight);
6638
6639 if (aScreenId != 0)
6640 return E_NOTIMPL;
6641
6642 AutoCaller autoCaller(this);
6643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6644
6645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6646
6647 uint8_t *pu8Data = NULL;
6648 uint32_t cbData = 0;
6649 uint32_t u32Width = 0;
6650 uint32_t u32Height = 0;
6651
6652 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6653
6654 if (RT_FAILURE(vrc))
6655 return setError(VBOX_E_IPRT_ERROR,
6656 tr("Saved screenshot data is not available (%Rrc)"),
6657 vrc);
6658
6659 *aSize = cbData;
6660 *aWidth = u32Width;
6661 *aHeight = u32Height;
6662
6663 freeSavedDisplayScreenshot(pu8Data);
6664
6665 return S_OK;
6666}
6667
6668STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6669{
6670 LogFlowThisFunc(("\n"));
6671
6672 CheckComArgNotNull(aWidth);
6673 CheckComArgNotNull(aHeight);
6674 CheckComArgOutSafeArrayPointerValid(aData);
6675
6676 if (aScreenId != 0)
6677 return E_NOTIMPL;
6678
6679 AutoCaller autoCaller(this);
6680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6681
6682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 uint8_t *pu8Data = NULL;
6685 uint32_t cbData = 0;
6686 uint32_t u32Width = 0;
6687 uint32_t u32Height = 0;
6688
6689 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6690
6691 if (RT_FAILURE(vrc))
6692 return setError(VBOX_E_IPRT_ERROR,
6693 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6694 vrc);
6695
6696 *aWidth = u32Width;
6697 *aHeight = u32Height;
6698
6699 com::SafeArray<BYTE> png(cbData);
6700 png.initFrom(pu8Data, cbData);
6701 png.detachTo(ComSafeArrayOutArg(aData));
6702
6703 freeSavedDisplayScreenshot(pu8Data);
6704
6705 return S_OK;
6706}
6707
6708STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6709{
6710 HRESULT rc = S_OK;
6711 LogFlowThisFunc(("\n"));
6712
6713 AutoCaller autoCaller(this);
6714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6715
6716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6717
6718 if (!mHWData->mCPUHotPlugEnabled)
6719 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6720
6721 if (aCpu >= mHWData->mCPUCount)
6722 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6723
6724 if (mHWData->mCPUAttached[aCpu])
6725 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6726
6727 alock.release();
6728 rc = onCPUChange(aCpu, false);
6729 alock.acquire();
6730 if (FAILED(rc)) return rc;
6731
6732 setModified(IsModified_MachineData);
6733 mHWData.backup();
6734 mHWData->mCPUAttached[aCpu] = true;
6735
6736 /* Save settings if online */
6737 if (Global::IsOnline(mData->mMachineState))
6738 saveSettings(NULL);
6739
6740 return S_OK;
6741}
6742
6743STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6744{
6745 HRESULT rc = S_OK;
6746 LogFlowThisFunc(("\n"));
6747
6748 AutoCaller autoCaller(this);
6749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6750
6751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6752
6753 if (!mHWData->mCPUHotPlugEnabled)
6754 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6755
6756 if (aCpu >= SchemaDefs::MaxCPUCount)
6757 return setError(E_INVALIDARG,
6758 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6759 SchemaDefs::MaxCPUCount);
6760
6761 if (!mHWData->mCPUAttached[aCpu])
6762 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6763
6764 /* CPU 0 can't be detached */
6765 if (aCpu == 0)
6766 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6767
6768 alock.release();
6769 rc = onCPUChange(aCpu, true);
6770 alock.acquire();
6771 if (FAILED(rc)) return rc;
6772
6773 setModified(IsModified_MachineData);
6774 mHWData.backup();
6775 mHWData->mCPUAttached[aCpu] = false;
6776
6777 /* Save settings if online */
6778 if (Global::IsOnline(mData->mMachineState))
6779 saveSettings(NULL);
6780
6781 return S_OK;
6782}
6783
6784STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6785{
6786 LogFlowThisFunc(("\n"));
6787
6788 CheckComArgNotNull(aCpuAttached);
6789
6790 *aCpuAttached = false;
6791
6792 AutoCaller autoCaller(this);
6793 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6794
6795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6796
6797 /* If hotplug is enabled the CPU is always enabled. */
6798 if (!mHWData->mCPUHotPlugEnabled)
6799 {
6800 if (aCpu < mHWData->mCPUCount)
6801 *aCpuAttached = true;
6802 }
6803 else
6804 {
6805 if (aCpu < SchemaDefs::MaxCPUCount)
6806 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6807 }
6808
6809 return S_OK;
6810}
6811
6812STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6813{
6814 CheckComArgOutPointerValid(aName);
6815
6816 AutoCaller autoCaller(this);
6817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6818
6819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6820
6821 Utf8Str log = queryLogFilename(aIdx);
6822 if (!RTFileExists(log.c_str()))
6823 log.setNull();
6824 log.cloneTo(aName);
6825
6826 return S_OK;
6827}
6828
6829STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6830{
6831 LogFlowThisFunc(("\n"));
6832 CheckComArgOutSafeArrayPointerValid(aData);
6833 if (aSize < 0)
6834 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6835
6836 AutoCaller autoCaller(this);
6837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6838
6839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6840
6841 HRESULT rc = S_OK;
6842 Utf8Str log = queryLogFilename(aIdx);
6843
6844 /* do not unnecessarily hold the lock while doing something which does
6845 * not need the lock and potentially takes a long time. */
6846 alock.release();
6847
6848 /* Limit the chunk size to 32K for now, as that gives better performance
6849 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6850 * One byte expands to approx. 25 bytes of breathtaking XML. */
6851 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6852 com::SafeArray<BYTE> logData(cbData);
6853
6854 RTFILE LogFile;
6855 int vrc = RTFileOpen(&LogFile, log.c_str(),
6856 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6857 if (RT_SUCCESS(vrc))
6858 {
6859 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6860 if (RT_SUCCESS(vrc))
6861 logData.resize(cbData);
6862 else
6863 rc = setError(VBOX_E_IPRT_ERROR,
6864 tr("Could not read log file '%s' (%Rrc)"),
6865 log.c_str(), vrc);
6866 RTFileClose(LogFile);
6867 }
6868 else
6869 rc = setError(VBOX_E_IPRT_ERROR,
6870 tr("Could not open log file '%s' (%Rrc)"),
6871 log.c_str(), vrc);
6872
6873 if (FAILED(rc))
6874 logData.resize(0);
6875 logData.detachTo(ComSafeArrayOutArg(aData));
6876
6877 return rc;
6878}
6879
6880
6881/**
6882 * Currently this method doesn't attach device to the running VM,
6883 * just makes sure it's plugged on next VM start.
6884 */
6885STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6886{
6887 AutoCaller autoCaller(this);
6888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6889
6890 // lock scope
6891 {
6892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6893
6894 HRESULT rc = checkStateDependency(MutableStateDep);
6895 if (FAILED(rc)) return rc;
6896
6897 ChipsetType_T aChipset = ChipsetType_PIIX3;
6898 COMGETTER(ChipsetType)(&aChipset);
6899
6900 if (aChipset != ChipsetType_ICH9)
6901 {
6902 return setError(E_INVALIDARG,
6903 tr("Host PCI attachment only supported with ICH9 chipset"));
6904 }
6905
6906 // check if device with this host PCI address already attached
6907 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6908 it != mHWData->mPCIDeviceAssignments.end();
6909 ++it)
6910 {
6911 LONG iHostAddress = -1;
6912 ComPtr<PCIDeviceAttachment> pAttach;
6913 pAttach = *it;
6914 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6915 if (iHostAddress == hostAddress)
6916 return setError(E_INVALIDARG,
6917 tr("Device with host PCI address already attached to this VM"));
6918 }
6919
6920 ComObjPtr<PCIDeviceAttachment> pda;
6921 char name[32];
6922
6923 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6924 Bstr bname(name);
6925 pda.createObject();
6926 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6927 setModified(IsModified_MachineData);
6928 mHWData.backup();
6929 mHWData->mPCIDeviceAssignments.push_back(pda);
6930 }
6931
6932 return S_OK;
6933}
6934
6935/**
6936 * Currently this method doesn't detach device from the running VM,
6937 * just makes sure it's not plugged on next VM start.
6938 */
6939STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6940{
6941 AutoCaller autoCaller(this);
6942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6943
6944 ComObjPtr<PCIDeviceAttachment> pAttach;
6945 bool fRemoved = false;
6946 HRESULT rc;
6947
6948 // lock scope
6949 {
6950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6951
6952 rc = checkStateDependency(MutableStateDep);
6953 if (FAILED(rc)) return rc;
6954
6955 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6956 it != mHWData->mPCIDeviceAssignments.end();
6957 ++it)
6958 {
6959 LONG iHostAddress = -1;
6960 pAttach = *it;
6961 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6962 if (iHostAddress != -1 && iHostAddress == hostAddress)
6963 {
6964 setModified(IsModified_MachineData);
6965 mHWData.backup();
6966 mHWData->mPCIDeviceAssignments.remove(pAttach);
6967 fRemoved = true;
6968 break;
6969 }
6970 }
6971 }
6972
6973
6974 /* Fire event outside of the lock */
6975 if (fRemoved)
6976 {
6977 Assert(!pAttach.isNull());
6978 ComPtr<IEventSource> es;
6979 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6980 Assert(SUCCEEDED(rc));
6981 Bstr mid;
6982 rc = this->COMGETTER(Id)(mid.asOutParam());
6983 Assert(SUCCEEDED(rc));
6984 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6985 }
6986
6987 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6988 tr("No host PCI device %08x attached"),
6989 hostAddress
6990 );
6991}
6992
6993STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6994{
6995 CheckComArgOutSafeArrayPointerValid(aAssignments);
6996
6997 AutoCaller autoCaller(this);
6998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6999
7000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7001
7002 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7003 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7004
7005 return S_OK;
7006}
7007
7008STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7009{
7010 CheckComArgOutPointerValid(aBandwidthControl);
7011
7012 AutoCaller autoCaller(this);
7013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7014
7015 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7016
7017 return S_OK;
7018}
7019
7020STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7021{
7022 CheckComArgOutPointerValid(pfEnabled);
7023 AutoCaller autoCaller(this);
7024 HRESULT hrc = autoCaller.rc();
7025 if (SUCCEEDED(hrc))
7026 {
7027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7028 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7029 }
7030 return hrc;
7031}
7032
7033STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7034{
7035 AutoCaller autoCaller(this);
7036 HRESULT hrc = autoCaller.rc();
7037 if (SUCCEEDED(hrc))
7038 {
7039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7040 hrc = checkStateDependency(MutableStateDep);
7041 if (SUCCEEDED(hrc))
7042 {
7043 hrc = mHWData.backupEx();
7044 if (SUCCEEDED(hrc))
7045 {
7046 setModified(IsModified_MachineData);
7047 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7048 }
7049 }
7050 }
7051 return hrc;
7052}
7053
7054STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7055{
7056 CheckComArgOutPointerValid(pbstrConfig);
7057 AutoCaller autoCaller(this);
7058 HRESULT hrc = autoCaller.rc();
7059 if (SUCCEEDED(hrc))
7060 {
7061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7062 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7063 }
7064 return hrc;
7065}
7066
7067STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7068{
7069 CheckComArgStr(bstrConfig);
7070 AutoCaller autoCaller(this);
7071 HRESULT hrc = autoCaller.rc();
7072 if (SUCCEEDED(hrc))
7073 {
7074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7075 hrc = checkStateDependency(MutableStateDep);
7076 if (SUCCEEDED(hrc))
7077 {
7078 hrc = mHWData.backupEx();
7079 if (SUCCEEDED(hrc))
7080 {
7081 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7082 if (SUCCEEDED(hrc))
7083 setModified(IsModified_MachineData);
7084 }
7085 }
7086 }
7087 return hrc;
7088
7089}
7090
7091STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7092{
7093 CheckComArgOutPointerValid(pfAllow);
7094 AutoCaller autoCaller(this);
7095 HRESULT hrc = autoCaller.rc();
7096 if (SUCCEEDED(hrc))
7097 {
7098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7099 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7100 }
7101 return hrc;
7102}
7103
7104STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7105{
7106 AutoCaller autoCaller(this);
7107 HRESULT hrc = autoCaller.rc();
7108 if (SUCCEEDED(hrc))
7109 {
7110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7111 hrc = checkStateDependency(MutableStateDep);
7112 if (SUCCEEDED(hrc))
7113 {
7114 hrc = mHWData.backupEx();
7115 if (SUCCEEDED(hrc))
7116 {
7117 setModified(IsModified_MachineData);
7118 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7119 }
7120 }
7121 }
7122 return hrc;
7123}
7124
7125STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7126{
7127 CheckComArgOutPointerValid(pfEnabled);
7128 AutoCaller autoCaller(this);
7129 HRESULT hrc = autoCaller.rc();
7130 if (SUCCEEDED(hrc))
7131 {
7132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7133 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7134 }
7135 return hrc;
7136}
7137
7138STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7139{
7140 AutoCaller autoCaller(this);
7141 HRESULT hrc = autoCaller.rc();
7142 if (SUCCEEDED(hrc))
7143 {
7144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7145 hrc = checkStateDependency(MutableStateDep);
7146 if ( SUCCEEDED(hrc)
7147 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7148 {
7149 AutostartDb *autostartDb = mParent->getAutostartDb();
7150 int vrc;
7151
7152 if (fEnabled)
7153 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7154 else
7155 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7156
7157 if (RT_SUCCESS(vrc))
7158 {
7159 hrc = mHWData.backupEx();
7160 if (SUCCEEDED(hrc))
7161 {
7162 setModified(IsModified_MachineData);
7163 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7164 }
7165 }
7166 else if (vrc == VERR_NOT_SUPPORTED)
7167 hrc = setError(VBOX_E_NOT_SUPPORTED,
7168 tr("The VM autostart feature is not supported on this platform"));
7169 else if (vrc == VERR_PATH_NOT_FOUND)
7170 hrc = setError(E_FAIL,
7171 tr("The path to the autostart database is not set"));
7172 else
7173 hrc = setError(E_UNEXPECTED,
7174 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7175 fEnabled ? "Adding" : "Removing",
7176 mUserData->s.strName.c_str(), vrc);
7177 }
7178 }
7179 return hrc;
7180}
7181
7182STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7183{
7184 CheckComArgOutPointerValid(puDelay);
7185 AutoCaller autoCaller(this);
7186 HRESULT hrc = autoCaller.rc();
7187 if (SUCCEEDED(hrc))
7188 {
7189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7190 *puDelay = mHWData->mAutostart.uAutostartDelay;
7191 }
7192 return hrc;
7193}
7194
7195STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7196{
7197 AutoCaller autoCaller(this);
7198 HRESULT hrc = autoCaller.rc();
7199 if (SUCCEEDED(hrc))
7200 {
7201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7202 hrc = checkStateDependency(MutableStateDep);
7203 if (SUCCEEDED(hrc))
7204 {
7205 hrc = mHWData.backupEx();
7206 if (SUCCEEDED(hrc))
7207 {
7208 setModified(IsModified_MachineData);
7209 mHWData->mAutostart.uAutostartDelay = uDelay;
7210 }
7211 }
7212 }
7213 return hrc;
7214}
7215
7216STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7217{
7218 CheckComArgOutPointerValid(penmAutostopType);
7219 AutoCaller autoCaller(this);
7220 HRESULT hrc = autoCaller.rc();
7221 if (SUCCEEDED(hrc))
7222 {
7223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7224 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7225 }
7226 return hrc;
7227}
7228
7229STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7230{
7231 AutoCaller autoCaller(this);
7232 HRESULT hrc = autoCaller.rc();
7233 if (SUCCEEDED(hrc))
7234 {
7235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7236 hrc = checkStateDependency(MutableStateDep);
7237 if ( SUCCEEDED(hrc)
7238 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7239 {
7240 AutostartDb *autostartDb = mParent->getAutostartDb();
7241 int vrc;
7242
7243 if (enmAutostopType != AutostopType_Disabled)
7244 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7245 else
7246 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7247
7248 if (RT_SUCCESS(vrc))
7249 {
7250 hrc = mHWData.backupEx();
7251 if (SUCCEEDED(hrc))
7252 {
7253 setModified(IsModified_MachineData);
7254 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7255 }
7256 }
7257 else if (vrc == VERR_NOT_SUPPORTED)
7258 hrc = setError(VBOX_E_NOT_SUPPORTED,
7259 tr("The VM autostop feature is not supported on this platform"));
7260 else if (vrc == VERR_PATH_NOT_FOUND)
7261 hrc = setError(E_FAIL,
7262 tr("The path to the autostart database is not set"));
7263 else
7264 hrc = setError(E_UNEXPECTED,
7265 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7266 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7267 mUserData->s.strName.c_str(), vrc);
7268 }
7269 }
7270 return hrc;
7271}
7272
7273STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7274{
7275 CheckComArgOutPointerValid(aDefaultFrontend);
7276 AutoCaller autoCaller(this);
7277 HRESULT hrc = autoCaller.rc();
7278 if (SUCCEEDED(hrc))
7279 {
7280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7281 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7282 }
7283 return hrc;
7284}
7285
7286STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7287{
7288 CheckComArgStr(aDefaultFrontend);
7289 AutoCaller autoCaller(this);
7290 HRESULT hrc = autoCaller.rc();
7291 if (SUCCEEDED(hrc))
7292 {
7293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7294 hrc = checkStateDependency(MutableOrSavedStateDep);
7295 if (SUCCEEDED(hrc))
7296 {
7297 hrc = mHWData.backupEx();
7298 if (SUCCEEDED(hrc))
7299 {
7300 setModified(IsModified_MachineData);
7301 mHWData->mDefaultFrontend = aDefaultFrontend;
7302 }
7303 }
7304 }
7305 return hrc;
7306}
7307
7308
7309STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7310{
7311 LogFlowFuncEnter();
7312
7313 CheckComArgNotNull(pTarget);
7314 CheckComArgOutPointerValid(pProgress);
7315
7316 /* Convert the options. */
7317 RTCList<CloneOptions_T> optList;
7318 if (options != NULL)
7319 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7320
7321 if (optList.contains(CloneOptions_Link))
7322 {
7323 if (!isSnapshotMachine())
7324 return setError(E_INVALIDARG,
7325 tr("Linked clone can only be created from a snapshot"));
7326 if (mode != CloneMode_MachineState)
7327 return setError(E_INVALIDARG,
7328 tr("Linked clone can only be created for a single machine state"));
7329 }
7330 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7331
7332 AutoCaller autoCaller(this);
7333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7334
7335
7336 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7337
7338 HRESULT rc = pWorker->start(pProgress);
7339
7340 LogFlowFuncLeave();
7341
7342 return rc;
7343}
7344
7345// public methods for internal purposes
7346/////////////////////////////////////////////////////////////////////////////
7347
7348/**
7349 * Adds the given IsModified_* flag to the dirty flags of the machine.
7350 * This must be called either during loadSettings or under the machine write lock.
7351 * @param fl
7352 */
7353void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7354{
7355 mData->flModifications |= fl;
7356 if (fAllowStateModification && isStateModificationAllowed())
7357 mData->mCurrentStateModified = true;
7358}
7359
7360/**
7361 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7362 * care of the write locking.
7363 *
7364 * @param fModifications The flag to add.
7365 */
7366void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7367{
7368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7369 setModified(fModification, fAllowStateModification);
7370}
7371
7372/**
7373 * Saves the registry entry of this machine to the given configuration node.
7374 *
7375 * @param aEntryNode Node to save the registry entry to.
7376 *
7377 * @note locks this object for reading.
7378 */
7379HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7380{
7381 AutoLimitedCaller autoCaller(this);
7382 AssertComRCReturnRC(autoCaller.rc());
7383
7384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7385
7386 data.uuid = mData->mUuid;
7387 data.strSettingsFile = mData->m_strConfigFile;
7388
7389 return S_OK;
7390}
7391
7392/**
7393 * Calculates the absolute path of the given path taking the directory of the
7394 * machine settings file as the current directory.
7395 *
7396 * @param aPath Path to calculate the absolute path for.
7397 * @param aResult Where to put the result (used only on success, can be the
7398 * same Utf8Str instance as passed in @a aPath).
7399 * @return IPRT result.
7400 *
7401 * @note Locks this object for reading.
7402 */
7403int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7404{
7405 AutoCaller autoCaller(this);
7406 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7407
7408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7409
7410 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7411
7412 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7413
7414 strSettingsDir.stripFilename();
7415 char folder[RTPATH_MAX];
7416 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7417 if (RT_SUCCESS(vrc))
7418 aResult = folder;
7419
7420 return vrc;
7421}
7422
7423/**
7424 * Copies strSource to strTarget, making it relative to the machine folder
7425 * if it is a subdirectory thereof, or simply copying it otherwise.
7426 *
7427 * @param strSource Path to evaluate and copy.
7428 * @param strTarget Buffer to receive target path.
7429 *
7430 * @note Locks this object for reading.
7431 */
7432void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7433 Utf8Str &strTarget)
7434{
7435 AutoCaller autoCaller(this);
7436 AssertComRCReturn(autoCaller.rc(), (void)0);
7437
7438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7439
7440 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7441 // use strTarget as a temporary buffer to hold the machine settings dir
7442 strTarget = mData->m_strConfigFileFull;
7443 strTarget.stripFilename();
7444 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7445 {
7446 // is relative: then append what's left
7447 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7448 // for empty paths (only possible for subdirs) use "." to avoid
7449 // triggering default settings for not present config attributes.
7450 if (strTarget.isEmpty())
7451 strTarget = ".";
7452 }
7453 else
7454 // is not relative: then overwrite
7455 strTarget = strSource;
7456}
7457
7458/**
7459 * Returns the full path to the machine's log folder in the
7460 * \a aLogFolder argument.
7461 */
7462void Machine::getLogFolder(Utf8Str &aLogFolder)
7463{
7464 AutoCaller autoCaller(this);
7465 AssertComRCReturnVoid(autoCaller.rc());
7466
7467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7468
7469 char szTmp[RTPATH_MAX];
7470 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7471 if (RT_SUCCESS(vrc))
7472 {
7473 if (szTmp[0] && !mUserData.isNull())
7474 {
7475 char szTmp2[RTPATH_MAX];
7476 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7477 if (RT_SUCCESS(vrc))
7478 aLogFolder = BstrFmt("%s%c%s",
7479 szTmp2,
7480 RTPATH_DELIMITER,
7481 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7482 }
7483 else
7484 vrc = VERR_PATH_IS_RELATIVE;
7485 }
7486
7487 if (RT_FAILURE(vrc))
7488 {
7489 // fallback if VBOX_USER_LOGHOME is not set or invalid
7490 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7491 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7492 aLogFolder.append(RTPATH_DELIMITER);
7493 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7494 }
7495}
7496
7497/**
7498 * Returns the full path to the machine's log file for an given index.
7499 */
7500Utf8Str Machine::queryLogFilename(ULONG idx)
7501{
7502 Utf8Str logFolder;
7503 getLogFolder(logFolder);
7504 Assert(logFolder.length());
7505 Utf8Str log;
7506 if (idx == 0)
7507 log = Utf8StrFmt("%s%cVBox.log",
7508 logFolder.c_str(), RTPATH_DELIMITER);
7509 else
7510 log = Utf8StrFmt("%s%cVBox.log.%d",
7511 logFolder.c_str(), RTPATH_DELIMITER, idx);
7512 return log;
7513}
7514
7515/**
7516 * Composes a unique saved state filename based on the current system time. The filename is
7517 * granular to the second so this will work so long as no more than one snapshot is taken on
7518 * a machine per second.
7519 *
7520 * Before version 4.1, we used this formula for saved state files:
7521 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7522 * which no longer works because saved state files can now be shared between the saved state of the
7523 * "saved" machine and an online snapshot, and the following would cause problems:
7524 * 1) save machine
7525 * 2) create online snapshot from that machine state --> reusing saved state file
7526 * 3) save machine again --> filename would be reused, breaking the online snapshot
7527 *
7528 * So instead we now use a timestamp.
7529 *
7530 * @param str
7531 */
7532void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7533{
7534 AutoCaller autoCaller(this);
7535 AssertComRCReturnVoid(autoCaller.rc());
7536
7537 {
7538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7539 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7540 }
7541
7542 RTTIMESPEC ts;
7543 RTTimeNow(&ts);
7544 RTTIME time;
7545 RTTimeExplode(&time, &ts);
7546
7547 strStateFilePath += RTPATH_DELIMITER;
7548 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7549 time.i32Year, time.u8Month, time.u8MonthDay,
7550 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7551}
7552
7553/**
7554 * @note Locks this object for writing, calls the client process
7555 * (inside the lock).
7556 */
7557HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7558 const Utf8Str &strFrontend,
7559 const Utf8Str &strEnvironment,
7560 ProgressProxy *aProgress)
7561{
7562 LogFlowThisFuncEnter();
7563
7564 AssertReturn(aControl, E_FAIL);
7565 AssertReturn(aProgress, E_FAIL);
7566
7567 AutoCaller autoCaller(this);
7568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7569
7570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7571
7572 if (!mData->mRegistered)
7573 return setError(E_UNEXPECTED,
7574 tr("The machine '%s' is not registered"),
7575 mUserData->s.strName.c_str());
7576
7577 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7578
7579 if ( mData->mSession.mState == SessionState_Locked
7580 || mData->mSession.mState == SessionState_Spawning
7581 || mData->mSession.mState == SessionState_Unlocking)
7582 return setError(VBOX_E_INVALID_OBJECT_STATE,
7583 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7584 mUserData->s.strName.c_str());
7585
7586 /* may not be busy */
7587 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7588
7589 /* get the path to the executable */
7590 char szPath[RTPATH_MAX];
7591 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7592 size_t sz = strlen(szPath);
7593 szPath[sz++] = RTPATH_DELIMITER;
7594 szPath[sz] = 0;
7595 char *cmd = szPath + sz;
7596 sz = RTPATH_MAX - sz;
7597
7598 int vrc = VINF_SUCCESS;
7599 RTPROCESS pid = NIL_RTPROCESS;
7600
7601 RTENV env = RTENV_DEFAULT;
7602
7603 if (!strEnvironment.isEmpty())
7604 {
7605 char *newEnvStr = NULL;
7606
7607 do
7608 {
7609 /* clone the current environment */
7610 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7611 AssertRCBreakStmt(vrc2, vrc = vrc2);
7612
7613 newEnvStr = RTStrDup(strEnvironment.c_str());
7614 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7615
7616 /* put new variables to the environment
7617 * (ignore empty variable names here since RTEnv API
7618 * intentionally doesn't do that) */
7619 char *var = newEnvStr;
7620 for (char *p = newEnvStr; *p; ++p)
7621 {
7622 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7623 {
7624 *p = '\0';
7625 if (*var)
7626 {
7627 char *val = strchr(var, '=');
7628 if (val)
7629 {
7630 *val++ = '\0';
7631 vrc2 = RTEnvSetEx(env, var, val);
7632 }
7633 else
7634 vrc2 = RTEnvUnsetEx(env, var);
7635 if (RT_FAILURE(vrc2))
7636 break;
7637 }
7638 var = p + 1;
7639 }
7640 }
7641 if (RT_SUCCESS(vrc2) && *var)
7642 vrc2 = RTEnvPutEx(env, var);
7643
7644 AssertRCBreakStmt(vrc2, vrc = vrc2);
7645 }
7646 while (0);
7647
7648 if (newEnvStr != NULL)
7649 RTStrFree(newEnvStr);
7650 }
7651
7652 /* Qt is default */
7653#ifdef VBOX_WITH_QTGUI
7654 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7655 {
7656# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7657 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7658# else
7659 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7660# endif
7661 Assert(sz >= sizeof(VirtualBox_exe));
7662 strcpy(cmd, VirtualBox_exe);
7663
7664 Utf8Str idStr = mData->mUuid.toString();
7665 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7666 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7667 }
7668#else /* !VBOX_WITH_QTGUI */
7669 if (0)
7670 ;
7671#endif /* VBOX_WITH_QTGUI */
7672
7673 else
7674
7675#ifdef VBOX_WITH_VBOXSDL
7676 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7677 {
7678 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7679 Assert(sz >= sizeof(VBoxSDL_exe));
7680 strcpy(cmd, VBoxSDL_exe);
7681
7682 Utf8Str idStr = mData->mUuid.toString();
7683 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7684 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7685 }
7686#else /* !VBOX_WITH_VBOXSDL */
7687 if (0)
7688 ;
7689#endif /* !VBOX_WITH_VBOXSDL */
7690
7691 else
7692
7693#ifdef VBOX_WITH_HEADLESS
7694 if ( strFrontend == "headless"
7695 || strFrontend == "capture"
7696 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7697 )
7698 {
7699 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7700 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7701 * and a VM works even if the server has not been installed.
7702 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7703 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7704 * differently in 4.0 and 3.x.
7705 */
7706 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7707 Assert(sz >= sizeof(VBoxHeadless_exe));
7708 strcpy(cmd, VBoxHeadless_exe);
7709
7710 Utf8Str idStr = mData->mUuid.toString();
7711 /* Leave space for "--capture" arg. */
7712 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7713 "--startvm", idStr.c_str(),
7714 "--vrde", "config",
7715 0, /* For "--capture". */
7716 0 };
7717 if (strFrontend == "capture")
7718 {
7719 unsigned pos = RT_ELEMENTS(args) - 2;
7720 args[pos] = "--capture";
7721 }
7722 vrc = RTProcCreate(szPath, args, env,
7723#ifdef RT_OS_WINDOWS
7724 RTPROC_FLAGS_NO_WINDOW
7725#else
7726 0
7727#endif
7728 , &pid);
7729 }
7730#else /* !VBOX_WITH_HEADLESS */
7731 if (0)
7732 ;
7733#endif /* !VBOX_WITH_HEADLESS */
7734 else
7735 {
7736 RTEnvDestroy(env);
7737 return setError(E_INVALIDARG,
7738 tr("Invalid frontend name: '%s'"),
7739 strFrontend.c_str());
7740 }
7741
7742 RTEnvDestroy(env);
7743
7744 if (RT_FAILURE(vrc))
7745 return setError(VBOX_E_IPRT_ERROR,
7746 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7747 mUserData->s.strName.c_str(), vrc);
7748
7749 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7750
7751 /*
7752 * Note that we don't release the lock here before calling the client,
7753 * because it doesn't need to call us back if called with a NULL argument.
7754 * Releasing the lock here is dangerous because we didn't prepare the
7755 * launch data yet, but the client we've just started may happen to be
7756 * too fast and call openSession() that will fail (because of PID, etc.),
7757 * so that the Machine will never get out of the Spawning session state.
7758 */
7759
7760 /* inform the session that it will be a remote one */
7761 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7762 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7763 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7764
7765 if (FAILED(rc))
7766 {
7767 /* restore the session state */
7768 mData->mSession.mState = SessionState_Unlocked;
7769 /* The failure may occur w/o any error info (from RPC), so provide one */
7770 return setError(VBOX_E_VM_ERROR,
7771 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7772 }
7773
7774 /* attach launch data to the machine */
7775 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7776 mData->mSession.mRemoteControls.push_back(aControl);
7777 mData->mSession.mProgress = aProgress;
7778 mData->mSession.mPID = pid;
7779 mData->mSession.mState = SessionState_Spawning;
7780 mData->mSession.mType = strFrontend;
7781
7782 LogFlowThisFuncLeave();
7783 return S_OK;
7784}
7785
7786/**
7787 * Returns @c true if the given machine has an open direct session and returns
7788 * the session machine instance and additional session data (on some platforms)
7789 * if so.
7790 *
7791 * Note that when the method returns @c false, the arguments remain unchanged.
7792 *
7793 * @param aMachine Session machine object.
7794 * @param aControl Direct session control object (optional).
7795 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7796 *
7797 * @note locks this object for reading.
7798 */
7799#if defined(RT_OS_WINDOWS)
7800bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7801 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7802 HANDLE *aIPCSem /*= NULL*/,
7803 bool aAllowClosing /*= false*/)
7804#elif defined(RT_OS_OS2)
7805bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7806 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7807 HMTX *aIPCSem /*= NULL*/,
7808 bool aAllowClosing /*= false*/)
7809#else
7810bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7811 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7812 bool aAllowClosing /*= false*/)
7813#endif
7814{
7815 AutoLimitedCaller autoCaller(this);
7816 AssertComRCReturn(autoCaller.rc(), false);
7817
7818 /* just return false for inaccessible machines */
7819 if (autoCaller.state() != Ready)
7820 return false;
7821
7822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7823
7824 if ( mData->mSession.mState == SessionState_Locked
7825 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7826 )
7827 {
7828 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7829
7830 aMachine = mData->mSession.mMachine;
7831
7832 if (aControl != NULL)
7833 *aControl = mData->mSession.mDirectControl;
7834
7835#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7836 /* Additional session data */
7837 if (aIPCSem != NULL)
7838 *aIPCSem = aMachine->mIPCSem;
7839#endif
7840 return true;
7841 }
7842
7843 return false;
7844}
7845
7846/**
7847 * Returns @c true if the given machine has an spawning direct session and
7848 * returns and additional session data (on some platforms) if so.
7849 *
7850 * Note that when the method returns @c false, the arguments remain unchanged.
7851 *
7852 * @param aPID PID of the spawned direct session process.
7853 *
7854 * @note locks this object for reading.
7855 */
7856#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7857bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7858#else
7859bool Machine::isSessionSpawning()
7860#endif
7861{
7862 AutoLimitedCaller autoCaller(this);
7863 AssertComRCReturn(autoCaller.rc(), false);
7864
7865 /* just return false for inaccessible machines */
7866 if (autoCaller.state() != Ready)
7867 return false;
7868
7869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7870
7871 if (mData->mSession.mState == SessionState_Spawning)
7872 {
7873#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7874 /* Additional session data */
7875 if (aPID != NULL)
7876 {
7877 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7878 *aPID = mData->mSession.mPID;
7879 }
7880#endif
7881 return true;
7882 }
7883
7884 return false;
7885}
7886
7887/**
7888 * Called from the client watcher thread to check for unexpected client process
7889 * death during Session_Spawning state (e.g. before it successfully opened a
7890 * direct session).
7891 *
7892 * On Win32 and on OS/2, this method is called only when we've got the
7893 * direct client's process termination notification, so it always returns @c
7894 * true.
7895 *
7896 * On other platforms, this method returns @c true if the client process is
7897 * terminated and @c false if it's still alive.
7898 *
7899 * @note Locks this object for writing.
7900 */
7901bool Machine::checkForSpawnFailure()
7902{
7903 AutoCaller autoCaller(this);
7904 if (!autoCaller.isOk())
7905 {
7906 /* nothing to do */
7907 LogFlowThisFunc(("Already uninitialized!\n"));
7908 return true;
7909 }
7910
7911 /* VirtualBox::addProcessToReap() needs a write lock */
7912 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7913
7914 if (mData->mSession.mState != SessionState_Spawning)
7915 {
7916 /* nothing to do */
7917 LogFlowThisFunc(("Not spawning any more!\n"));
7918 return true;
7919 }
7920
7921 HRESULT rc = S_OK;
7922
7923#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7924
7925 /* the process was already unexpectedly terminated, we just need to set an
7926 * error and finalize session spawning */
7927 rc = setError(E_FAIL,
7928 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7929 getName().c_str());
7930#else
7931
7932 /* PID not yet initialized, skip check. */
7933 if (mData->mSession.mPID == NIL_RTPROCESS)
7934 return false;
7935
7936 RTPROCSTATUS status;
7937 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7938 &status);
7939
7940 if (vrc != VERR_PROCESS_RUNNING)
7941 {
7942 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7943 rc = setError(E_FAIL,
7944 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7945 getName().c_str(), status.iStatus);
7946 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7947 rc = setError(E_FAIL,
7948 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7949 getName().c_str(), status.iStatus);
7950 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7951 rc = setError(E_FAIL,
7952 tr("The virtual machine '%s' has terminated abnormally"),
7953 getName().c_str(), status.iStatus);
7954 else
7955 rc = setError(E_FAIL,
7956 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7957 getName().c_str(), rc);
7958 }
7959
7960#endif
7961
7962 if (FAILED(rc))
7963 {
7964 /* Close the remote session, remove the remote control from the list
7965 * and reset session state to Closed (@note keep the code in sync with
7966 * the relevant part in checkForSpawnFailure()). */
7967
7968 Assert(mData->mSession.mRemoteControls.size() == 1);
7969 if (mData->mSession.mRemoteControls.size() == 1)
7970 {
7971 ErrorInfoKeeper eik;
7972 mData->mSession.mRemoteControls.front()->Uninitialize();
7973 }
7974
7975 mData->mSession.mRemoteControls.clear();
7976 mData->mSession.mState = SessionState_Unlocked;
7977
7978 /* finalize the progress after setting the state */
7979 if (!mData->mSession.mProgress.isNull())
7980 {
7981 mData->mSession.mProgress->notifyComplete(rc);
7982 mData->mSession.mProgress.setNull();
7983 }
7984
7985 mParent->addProcessToReap(mData->mSession.mPID);
7986 mData->mSession.mPID = NIL_RTPROCESS;
7987
7988 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7989 return true;
7990 }
7991
7992 return false;
7993}
7994
7995/**
7996 * Checks whether the machine can be registered. If so, commits and saves
7997 * all settings.
7998 *
7999 * @note Must be called from mParent's write lock. Locks this object and
8000 * children for writing.
8001 */
8002HRESULT Machine::prepareRegister()
8003{
8004 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8005
8006 AutoLimitedCaller autoCaller(this);
8007 AssertComRCReturnRC(autoCaller.rc());
8008
8009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8010
8011 /* wait for state dependents to drop to zero */
8012 ensureNoStateDependencies();
8013
8014 if (!mData->mAccessible)
8015 return setError(VBOX_E_INVALID_OBJECT_STATE,
8016 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8017 mUserData->s.strName.c_str(),
8018 mData->mUuid.toString().c_str());
8019
8020 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8021
8022 if (mData->mRegistered)
8023 return setError(VBOX_E_INVALID_OBJECT_STATE,
8024 tr("The machine '%s' with UUID {%s} is already registered"),
8025 mUserData->s.strName.c_str(),
8026 mData->mUuid.toString().c_str());
8027
8028 HRESULT rc = S_OK;
8029
8030 // Ensure the settings are saved. If we are going to be registered and
8031 // no config file exists yet, create it by calling saveSettings() too.
8032 if ( (mData->flModifications)
8033 || (!mData->pMachineConfigFile->fileExists())
8034 )
8035 {
8036 rc = saveSettings(NULL);
8037 // no need to check whether VirtualBox.xml needs saving too since
8038 // we can't have a machine XML file rename pending
8039 if (FAILED(rc)) return rc;
8040 }
8041
8042 /* more config checking goes here */
8043
8044 if (SUCCEEDED(rc))
8045 {
8046 /* we may have had implicit modifications we want to fix on success */
8047 commit();
8048
8049 mData->mRegistered = true;
8050 }
8051 else
8052 {
8053 /* we may have had implicit modifications we want to cancel on failure*/
8054 rollback(false /* aNotify */);
8055 }
8056
8057 return rc;
8058}
8059
8060/**
8061 * Increases the number of objects dependent on the machine state or on the
8062 * registered state. Guarantees that these two states will not change at least
8063 * until #releaseStateDependency() is called.
8064 *
8065 * Depending on the @a aDepType value, additional state checks may be made.
8066 * These checks will set extended error info on failure. See
8067 * #checkStateDependency() for more info.
8068 *
8069 * If this method returns a failure, the dependency is not added and the caller
8070 * is not allowed to rely on any particular machine state or registration state
8071 * value and may return the failed result code to the upper level.
8072 *
8073 * @param aDepType Dependency type to add.
8074 * @param aState Current machine state (NULL if not interested).
8075 * @param aRegistered Current registered state (NULL if not interested).
8076 *
8077 * @note Locks this object for writing.
8078 */
8079HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8080 MachineState_T *aState /* = NULL */,
8081 BOOL *aRegistered /* = NULL */)
8082{
8083 AutoCaller autoCaller(this);
8084 AssertComRCReturnRC(autoCaller.rc());
8085
8086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8087
8088 HRESULT rc = checkStateDependency(aDepType);
8089 if (FAILED(rc)) return rc;
8090
8091 {
8092 if (mData->mMachineStateChangePending != 0)
8093 {
8094 /* ensureNoStateDependencies() is waiting for state dependencies to
8095 * drop to zero so don't add more. It may make sense to wait a bit
8096 * and retry before reporting an error (since the pending state
8097 * transition should be really quick) but let's just assert for
8098 * now to see if it ever happens on practice. */
8099
8100 AssertFailed();
8101
8102 return setError(E_ACCESSDENIED,
8103 tr("Machine state change is in progress. Please retry the operation later."));
8104 }
8105
8106 ++mData->mMachineStateDeps;
8107 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8108 }
8109
8110 if (aState)
8111 *aState = mData->mMachineState;
8112 if (aRegistered)
8113 *aRegistered = mData->mRegistered;
8114
8115 return S_OK;
8116}
8117
8118/**
8119 * Decreases the number of objects dependent on the machine state.
8120 * Must always complete the #addStateDependency() call after the state
8121 * dependency is no more necessary.
8122 */
8123void Machine::releaseStateDependency()
8124{
8125 AutoCaller autoCaller(this);
8126 AssertComRCReturnVoid(autoCaller.rc());
8127
8128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8129
8130 /* releaseStateDependency() w/o addStateDependency()? */
8131 AssertReturnVoid(mData->mMachineStateDeps != 0);
8132 -- mData->mMachineStateDeps;
8133
8134 if (mData->mMachineStateDeps == 0)
8135 {
8136 /* inform ensureNoStateDependencies() that there are no more deps */
8137 if (mData->mMachineStateChangePending != 0)
8138 {
8139 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8140 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8141 }
8142 }
8143}
8144
8145Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8146{
8147 /* start with nothing found */
8148 Utf8Str strResult("");
8149
8150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8151
8152 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8153 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8154 // found:
8155 strResult = it->second; // source is a Utf8Str
8156
8157 return strResult;
8158}
8159
8160// protected methods
8161/////////////////////////////////////////////////////////////////////////////
8162
8163/**
8164 * Performs machine state checks based on the @a aDepType value. If a check
8165 * fails, this method will set extended error info, otherwise it will return
8166 * S_OK. It is supposed, that on failure, the caller will immediately return
8167 * the return value of this method to the upper level.
8168 *
8169 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8170 *
8171 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8172 * current state of this machine object allows to change settings of the
8173 * machine (i.e. the machine is not registered, or registered but not running
8174 * and not saved). It is useful to call this method from Machine setters
8175 * before performing any change.
8176 *
8177 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8178 * as for MutableStateDep except that if the machine is saved, S_OK is also
8179 * returned. This is useful in setters which allow changing machine
8180 * properties when it is in the saved state.
8181 *
8182 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8183 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8184 * Aborted).
8185 *
8186 * @param aDepType Dependency type to check.
8187 *
8188 * @note Non Machine based classes should use #addStateDependency() and
8189 * #releaseStateDependency() methods or the smart AutoStateDependency
8190 * template.
8191 *
8192 * @note This method must be called from under this object's read or write
8193 * lock.
8194 */
8195HRESULT Machine::checkStateDependency(StateDependency aDepType)
8196{
8197 switch (aDepType)
8198 {
8199 case AnyStateDep:
8200 {
8201 break;
8202 }
8203 case MutableStateDep:
8204 {
8205 if ( mData->mRegistered
8206 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8207 || ( mData->mMachineState != MachineState_Paused
8208 && mData->mMachineState != MachineState_Running
8209 && mData->mMachineState != MachineState_Aborted
8210 && mData->mMachineState != MachineState_Teleported
8211 && mData->mMachineState != MachineState_PoweredOff
8212 )
8213 )
8214 )
8215 return setError(VBOX_E_INVALID_VM_STATE,
8216 tr("The machine is not mutable (state is %s)"),
8217 Global::stringifyMachineState(mData->mMachineState));
8218 break;
8219 }
8220 case MutableOrSavedStateDep:
8221 {
8222 if ( mData->mRegistered
8223 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8224 || ( mData->mMachineState != MachineState_Paused
8225 && mData->mMachineState != MachineState_Running
8226 && mData->mMachineState != MachineState_Aborted
8227 && mData->mMachineState != MachineState_Teleported
8228 && mData->mMachineState != MachineState_Saved
8229 && mData->mMachineState != MachineState_PoweredOff
8230 )
8231 )
8232 )
8233 return setError(VBOX_E_INVALID_VM_STATE,
8234 tr("The machine is not mutable (state is %s)"),
8235 Global::stringifyMachineState(mData->mMachineState));
8236 break;
8237 }
8238 case OfflineStateDep:
8239 {
8240 if ( mData->mRegistered
8241 && ( !isSessionMachine()
8242 || ( mData->mMachineState != MachineState_PoweredOff
8243 && mData->mMachineState != MachineState_Saved
8244 && mData->mMachineState != MachineState_Aborted
8245 && mData->mMachineState != MachineState_Teleported
8246 )
8247 )
8248 )
8249 return setError(VBOX_E_INVALID_VM_STATE,
8250 tr("The machine is not offline (state is %s)"),
8251 Global::stringifyMachineState(mData->mMachineState));
8252 break;
8253 }
8254 }
8255
8256 return S_OK;
8257}
8258
8259/**
8260 * Helper to initialize all associated child objects and allocate data
8261 * structures.
8262 *
8263 * This method must be called as a part of the object's initialization procedure
8264 * (usually done in the #init() method).
8265 *
8266 * @note Must be called only from #init() or from #registeredInit().
8267 */
8268HRESULT Machine::initDataAndChildObjects()
8269{
8270 AutoCaller autoCaller(this);
8271 AssertComRCReturnRC(autoCaller.rc());
8272 AssertComRCReturn(autoCaller.state() == InInit ||
8273 autoCaller.state() == Limited, E_FAIL);
8274
8275 AssertReturn(!mData->mAccessible, E_FAIL);
8276
8277 /* allocate data structures */
8278 mSSData.allocate();
8279 mUserData.allocate();
8280 mHWData.allocate();
8281 mMediaData.allocate();
8282 mStorageControllers.allocate();
8283
8284 /* initialize mOSTypeId */
8285 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8286
8287 /* create associated BIOS settings object */
8288 unconst(mBIOSSettings).createObject();
8289 mBIOSSettings->init(this);
8290
8291 /* create an associated VRDE object (default is disabled) */
8292 unconst(mVRDEServer).createObject();
8293 mVRDEServer->init(this);
8294
8295 /* create associated serial port objects */
8296 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8297 {
8298 unconst(mSerialPorts[slot]).createObject();
8299 mSerialPorts[slot]->init(this, slot);
8300 }
8301
8302 /* create associated parallel port objects */
8303 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8304 {
8305 unconst(mParallelPorts[slot]).createObject();
8306 mParallelPorts[slot]->init(this, slot);
8307 }
8308
8309 /* create the audio adapter object (always present, default is disabled) */
8310 unconst(mAudioAdapter).createObject();
8311 mAudioAdapter->init(this);
8312
8313 /* create the USB controller object (always present, default is disabled) */
8314 unconst(mUSBController).createObject();
8315 mUSBController->init(this);
8316
8317 /* create associated network adapter objects */
8318 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8319 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8320 {
8321 unconst(mNetworkAdapters[slot]).createObject();
8322 mNetworkAdapters[slot]->init(this, slot);
8323 }
8324
8325 /* create the bandwidth control */
8326 unconst(mBandwidthControl).createObject();
8327 mBandwidthControl->init(this);
8328
8329 return S_OK;
8330}
8331
8332/**
8333 * Helper to uninitialize all associated child objects and to free all data
8334 * structures.
8335 *
8336 * This method must be called as a part of the object's uninitialization
8337 * procedure (usually done in the #uninit() method).
8338 *
8339 * @note Must be called only from #uninit() or from #registeredInit().
8340 */
8341void Machine::uninitDataAndChildObjects()
8342{
8343 AutoCaller autoCaller(this);
8344 AssertComRCReturnVoid(autoCaller.rc());
8345 AssertComRCReturnVoid( autoCaller.state() == InUninit
8346 || autoCaller.state() == Limited);
8347
8348 /* tell all our other child objects we've been uninitialized */
8349 if (mBandwidthControl)
8350 {
8351 mBandwidthControl->uninit();
8352 unconst(mBandwidthControl).setNull();
8353 }
8354
8355 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8356 {
8357 if (mNetworkAdapters[slot])
8358 {
8359 mNetworkAdapters[slot]->uninit();
8360 unconst(mNetworkAdapters[slot]).setNull();
8361 }
8362 }
8363
8364 if (mUSBController)
8365 {
8366 mUSBController->uninit();
8367 unconst(mUSBController).setNull();
8368 }
8369
8370 if (mAudioAdapter)
8371 {
8372 mAudioAdapter->uninit();
8373 unconst(mAudioAdapter).setNull();
8374 }
8375
8376 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8377 {
8378 if (mParallelPorts[slot])
8379 {
8380 mParallelPorts[slot]->uninit();
8381 unconst(mParallelPorts[slot]).setNull();
8382 }
8383 }
8384
8385 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8386 {
8387 if (mSerialPorts[slot])
8388 {
8389 mSerialPorts[slot]->uninit();
8390 unconst(mSerialPorts[slot]).setNull();
8391 }
8392 }
8393
8394 if (mVRDEServer)
8395 {
8396 mVRDEServer->uninit();
8397 unconst(mVRDEServer).setNull();
8398 }
8399
8400 if (mBIOSSettings)
8401 {
8402 mBIOSSettings->uninit();
8403 unconst(mBIOSSettings).setNull();
8404 }
8405
8406 /* Deassociate media (only when a real Machine or a SnapshotMachine
8407 * instance is uninitialized; SessionMachine instances refer to real
8408 * Machine media). This is necessary for a clean re-initialization of
8409 * the VM after successfully re-checking the accessibility state. Note
8410 * that in case of normal Machine or SnapshotMachine uninitialization (as
8411 * a result of unregistering or deleting the snapshot), outdated media
8412 * attachments will already be uninitialized and deleted, so this
8413 * code will not affect them. */
8414 if ( !!mMediaData
8415 && (!isSessionMachine())
8416 )
8417 {
8418 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8419 it != mMediaData->mAttachments.end();
8420 ++it)
8421 {
8422 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8423 if (pMedium.isNull())
8424 continue;
8425 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8426 AssertComRC(rc);
8427 }
8428 }
8429
8430 if (!isSessionMachine() && !isSnapshotMachine())
8431 {
8432 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8433 if (mData->mFirstSnapshot)
8434 {
8435 // snapshots tree is protected by machine write lock; strictly
8436 // this isn't necessary here since we're deleting the entire
8437 // machine, but otherwise we assert in Snapshot::uninit()
8438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8439 mData->mFirstSnapshot->uninit();
8440 mData->mFirstSnapshot.setNull();
8441 }
8442
8443 mData->mCurrentSnapshot.setNull();
8444 }
8445
8446 /* free data structures (the essential mData structure is not freed here
8447 * since it may be still in use) */
8448 mMediaData.free();
8449 mStorageControllers.free();
8450 mHWData.free();
8451 mUserData.free();
8452 mSSData.free();
8453}
8454
8455/**
8456 * Returns a pointer to the Machine object for this machine that acts like a
8457 * parent for complex machine data objects such as shared folders, etc.
8458 *
8459 * For primary Machine objects and for SnapshotMachine objects, returns this
8460 * object's pointer itself. For SessionMachine objects, returns the peer
8461 * (primary) machine pointer.
8462 */
8463Machine* Machine::getMachine()
8464{
8465 if (isSessionMachine())
8466 return (Machine*)mPeer;
8467 return this;
8468}
8469
8470/**
8471 * Makes sure that there are no machine state dependents. If necessary, waits
8472 * for the number of dependents to drop to zero.
8473 *
8474 * Make sure this method is called from under this object's write lock to
8475 * guarantee that no new dependents may be added when this method returns
8476 * control to the caller.
8477 *
8478 * @note Locks this object for writing. The lock will be released while waiting
8479 * (if necessary).
8480 *
8481 * @warning To be used only in methods that change the machine state!
8482 */
8483void Machine::ensureNoStateDependencies()
8484{
8485 AssertReturnVoid(isWriteLockOnCurrentThread());
8486
8487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8488
8489 /* Wait for all state dependents if necessary */
8490 if (mData->mMachineStateDeps != 0)
8491 {
8492 /* lazy semaphore creation */
8493 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8494 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8495
8496 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8497 mData->mMachineStateDeps));
8498
8499 ++mData->mMachineStateChangePending;
8500
8501 /* reset the semaphore before waiting, the last dependent will signal
8502 * it */
8503 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8504
8505 alock.release();
8506
8507 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8508
8509 alock.acquire();
8510
8511 -- mData->mMachineStateChangePending;
8512 }
8513}
8514
8515/**
8516 * Changes the machine state and informs callbacks.
8517 *
8518 * This method is not intended to fail so it either returns S_OK or asserts (and
8519 * returns a failure).
8520 *
8521 * @note Locks this object for writing.
8522 */
8523HRESULT Machine::setMachineState(MachineState_T aMachineState)
8524{
8525 LogFlowThisFuncEnter();
8526 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8527
8528 AutoCaller autoCaller(this);
8529 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8530
8531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8532
8533 /* wait for state dependents to drop to zero */
8534 ensureNoStateDependencies();
8535
8536 if (mData->mMachineState != aMachineState)
8537 {
8538 mData->mMachineState = aMachineState;
8539
8540 RTTimeNow(&mData->mLastStateChange);
8541
8542 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8543 }
8544
8545 LogFlowThisFuncLeave();
8546 return S_OK;
8547}
8548
8549/**
8550 * Searches for a shared folder with the given logical name
8551 * in the collection of shared folders.
8552 *
8553 * @param aName logical name of the shared folder
8554 * @param aSharedFolder where to return the found object
8555 * @param aSetError whether to set the error info if the folder is
8556 * not found
8557 * @return
8558 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8559 *
8560 * @note
8561 * must be called from under the object's lock!
8562 */
8563HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8564 ComObjPtr<SharedFolder> &aSharedFolder,
8565 bool aSetError /* = false */)
8566{
8567 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8568 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8569 it != mHWData->mSharedFolders.end();
8570 ++it)
8571 {
8572 SharedFolder *pSF = *it;
8573 AutoCaller autoCaller(pSF);
8574 if (pSF->getName() == aName)
8575 {
8576 aSharedFolder = pSF;
8577 rc = S_OK;
8578 break;
8579 }
8580 }
8581
8582 if (aSetError && FAILED(rc))
8583 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8584
8585 return rc;
8586}
8587
8588/**
8589 * Initializes all machine instance data from the given settings structures
8590 * from XML. The exception is the machine UUID which needs special handling
8591 * depending on the caller's use case, so the caller needs to set that herself.
8592 *
8593 * This gets called in several contexts during machine initialization:
8594 *
8595 * -- When machine XML exists on disk already and needs to be loaded into memory,
8596 * for example, from registeredInit() to load all registered machines on
8597 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8598 * attached to the machine should be part of some media registry already.
8599 *
8600 * -- During OVF import, when a machine config has been constructed from an
8601 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8602 * ensure that the media listed as attachments in the config (which have
8603 * been imported from the OVF) receive the correct registry ID.
8604 *
8605 * -- During VM cloning.
8606 *
8607 * @param config Machine settings from XML.
8608 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8609 * @return
8610 */
8611HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8612 const Guid *puuidRegistry)
8613{
8614 // copy name, description, OS type, teleporter, UTC etc.
8615 mUserData->s = config.machineUserData;
8616
8617 // look up the object by Id to check it is valid
8618 ComPtr<IGuestOSType> guestOSType;
8619 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8620 guestOSType.asOutParam());
8621 if (FAILED(rc)) return rc;
8622
8623 // stateFile (optional)
8624 if (config.strStateFile.isEmpty())
8625 mSSData->strStateFilePath.setNull();
8626 else
8627 {
8628 Utf8Str stateFilePathFull(config.strStateFile);
8629 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8630 if (RT_FAILURE(vrc))
8631 return setError(E_FAIL,
8632 tr("Invalid saved state file path '%s' (%Rrc)"),
8633 config.strStateFile.c_str(),
8634 vrc);
8635 mSSData->strStateFilePath = stateFilePathFull;
8636 }
8637
8638 // snapshot folder needs special processing so set it again
8639 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8640 if (FAILED(rc)) return rc;
8641
8642 /* Copy the extra data items (Not in any case config is already the same as
8643 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8644 * make sure the extra data map is copied). */
8645 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8646
8647 /* currentStateModified (optional, default is true) */
8648 mData->mCurrentStateModified = config.fCurrentStateModified;
8649
8650 mData->mLastStateChange = config.timeLastStateChange;
8651
8652 /*
8653 * note: all mUserData members must be assigned prior this point because
8654 * we need to commit changes in order to let mUserData be shared by all
8655 * snapshot machine instances.
8656 */
8657 mUserData.commitCopy();
8658
8659 // machine registry, if present (must be loaded before snapshots)
8660 if (config.canHaveOwnMediaRegistry())
8661 {
8662 // determine machine folder
8663 Utf8Str strMachineFolder = getSettingsFileFull();
8664 strMachineFolder.stripFilename();
8665 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8666 config.mediaRegistry,
8667 strMachineFolder);
8668 if (FAILED(rc)) return rc;
8669 }
8670
8671 /* Snapshot node (optional) */
8672 size_t cRootSnapshots;
8673 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8674 {
8675 // there must be only one root snapshot
8676 Assert(cRootSnapshots == 1);
8677
8678 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8679
8680 rc = loadSnapshot(snap,
8681 config.uuidCurrentSnapshot,
8682 NULL); // no parent == first snapshot
8683 if (FAILED(rc)) return rc;
8684 }
8685
8686 // hardware data
8687 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8688 if (FAILED(rc)) return rc;
8689
8690 // load storage controllers
8691 rc = loadStorageControllers(config.storageMachine,
8692 puuidRegistry,
8693 NULL /* puuidSnapshot */);
8694 if (FAILED(rc)) return rc;
8695
8696 /*
8697 * NOTE: the assignment below must be the last thing to do,
8698 * otherwise it will be not possible to change the settings
8699 * somewhere in the code above because all setters will be
8700 * blocked by checkStateDependency(MutableStateDep).
8701 */
8702
8703 /* set the machine state to Aborted or Saved when appropriate */
8704 if (config.fAborted)
8705 {
8706 mSSData->strStateFilePath.setNull();
8707
8708 /* no need to use setMachineState() during init() */
8709 mData->mMachineState = MachineState_Aborted;
8710 }
8711 else if (!mSSData->strStateFilePath.isEmpty())
8712 {
8713 /* no need to use setMachineState() during init() */
8714 mData->mMachineState = MachineState_Saved;
8715 }
8716
8717 // after loading settings, we are no longer different from the XML on disk
8718 mData->flModifications = 0;
8719
8720 return S_OK;
8721}
8722
8723/**
8724 * Recursively loads all snapshots starting from the given.
8725 *
8726 * @param aNode <Snapshot> node.
8727 * @param aCurSnapshotId Current snapshot ID from the settings file.
8728 * @param aParentSnapshot Parent snapshot.
8729 */
8730HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8731 const Guid &aCurSnapshotId,
8732 Snapshot *aParentSnapshot)
8733{
8734 AssertReturn(!isSnapshotMachine(), E_FAIL);
8735 AssertReturn(!isSessionMachine(), E_FAIL);
8736
8737 HRESULT rc = S_OK;
8738
8739 Utf8Str strStateFile;
8740 if (!data.strStateFile.isEmpty())
8741 {
8742 /* optional */
8743 strStateFile = data.strStateFile;
8744 int vrc = calculateFullPath(strStateFile, strStateFile);
8745 if (RT_FAILURE(vrc))
8746 return setError(E_FAIL,
8747 tr("Invalid saved state file path '%s' (%Rrc)"),
8748 strStateFile.c_str(),
8749 vrc);
8750 }
8751
8752 /* create a snapshot machine object */
8753 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8754 pSnapshotMachine.createObject();
8755 rc = pSnapshotMachine->initFromSettings(this,
8756 data.hardware,
8757 &data.debugging,
8758 &data.autostart,
8759 data.storage,
8760 data.uuid.ref(),
8761 strStateFile);
8762 if (FAILED(rc)) return rc;
8763
8764 /* create a snapshot object */
8765 ComObjPtr<Snapshot> pSnapshot;
8766 pSnapshot.createObject();
8767 /* initialize the snapshot */
8768 rc = pSnapshot->init(mParent, // VirtualBox object
8769 data.uuid,
8770 data.strName,
8771 data.strDescription,
8772 data.timestamp,
8773 pSnapshotMachine,
8774 aParentSnapshot);
8775 if (FAILED(rc)) return rc;
8776
8777 /* memorize the first snapshot if necessary */
8778 if (!mData->mFirstSnapshot)
8779 mData->mFirstSnapshot = pSnapshot;
8780
8781 /* memorize the current snapshot when appropriate */
8782 if ( !mData->mCurrentSnapshot
8783 && pSnapshot->getId() == aCurSnapshotId
8784 )
8785 mData->mCurrentSnapshot = pSnapshot;
8786
8787 // now create the children
8788 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8789 it != data.llChildSnapshots.end();
8790 ++it)
8791 {
8792 const settings::Snapshot &childData = *it;
8793 // recurse
8794 rc = loadSnapshot(childData,
8795 aCurSnapshotId,
8796 pSnapshot); // parent = the one we created above
8797 if (FAILED(rc)) return rc;
8798 }
8799
8800 return rc;
8801}
8802
8803/**
8804 * Loads settings into mHWData.
8805 *
8806 * @param data Reference to the hardware settings.
8807 * @param pDbg Pointer to the debugging settings.
8808 * @param pAutostart Pointer to the autostart settings.
8809 */
8810HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8811 const settings::Autostart *pAutostart)
8812{
8813 AssertReturn(!isSessionMachine(), E_FAIL);
8814
8815 HRESULT rc = S_OK;
8816
8817 try
8818 {
8819 /* The hardware version attribute (optional). */
8820 mHWData->mHWVersion = data.strVersion;
8821 mHWData->mHardwareUUID = data.uuid;
8822
8823 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8824 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8825 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8826 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8827 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8828 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8829 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8830 mHWData->mPAEEnabled = data.fPAE;
8831 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8832 mHWData->mLongMode = data.enmLongMode;
8833 mHWData->mCPUCount = data.cCPUs;
8834 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8835 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8836
8837 // cpu
8838 if (mHWData->mCPUHotPlugEnabled)
8839 {
8840 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8841 it != data.llCpus.end();
8842 ++it)
8843 {
8844 const settings::Cpu &cpu = *it;
8845
8846 mHWData->mCPUAttached[cpu.ulId] = true;
8847 }
8848 }
8849
8850 // cpuid leafs
8851 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8852 it != data.llCpuIdLeafs.end();
8853 ++it)
8854 {
8855 const settings::CpuIdLeaf &leaf = *it;
8856
8857 switch (leaf.ulId)
8858 {
8859 case 0x0:
8860 case 0x1:
8861 case 0x2:
8862 case 0x3:
8863 case 0x4:
8864 case 0x5:
8865 case 0x6:
8866 case 0x7:
8867 case 0x8:
8868 case 0x9:
8869 case 0xA:
8870 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8871 break;
8872
8873 case 0x80000000:
8874 case 0x80000001:
8875 case 0x80000002:
8876 case 0x80000003:
8877 case 0x80000004:
8878 case 0x80000005:
8879 case 0x80000006:
8880 case 0x80000007:
8881 case 0x80000008:
8882 case 0x80000009:
8883 case 0x8000000A:
8884 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8885 break;
8886
8887 default:
8888 /* just ignore */
8889 break;
8890 }
8891 }
8892
8893 mHWData->mMemorySize = data.ulMemorySizeMB;
8894 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8895
8896 // boot order
8897 for (size_t i = 0;
8898 i < RT_ELEMENTS(mHWData->mBootOrder);
8899 i++)
8900 {
8901 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8902 if (it == data.mapBootOrder.end())
8903 mHWData->mBootOrder[i] = DeviceType_Null;
8904 else
8905 mHWData->mBootOrder[i] = it->second;
8906 }
8907
8908 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8909 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8910 mHWData->mMonitorCount = data.cMonitors;
8911 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8912 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8913 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8914 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8915 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8916 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
8917 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8918 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8919 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8920 mHWData->mVideoCaptureFps = data.ulVideoCaptureFps;
8921 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8922 mHWData->mFirmwareType = data.firmwareType;
8923 mHWData->mPointingHIDType = data.pointingHIDType;
8924 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8925 mHWData->mChipsetType = data.chipsetType;
8926 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8927 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8928 mHWData->mHPETEnabled = data.fHPETEnabled;
8929
8930 /* VRDEServer */
8931 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8932 if (FAILED(rc)) return rc;
8933
8934 /* BIOS */
8935 rc = mBIOSSettings->loadSettings(data.biosSettings);
8936 if (FAILED(rc)) return rc;
8937
8938 // Bandwidth control (must come before network adapters)
8939 rc = mBandwidthControl->loadSettings(data.ioSettings);
8940 if (FAILED(rc)) return rc;
8941
8942 /* USB Controller */
8943 rc = mUSBController->loadSettings(data.usbController);
8944 if (FAILED(rc)) return rc;
8945
8946 // network adapters
8947 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8948 uint32_t oldCount = mNetworkAdapters.size();
8949 if (newCount > oldCount)
8950 {
8951 mNetworkAdapters.resize(newCount);
8952 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8953 {
8954 unconst(mNetworkAdapters[slot]).createObject();
8955 mNetworkAdapters[slot]->init(this, slot);
8956 }
8957 }
8958 else if (newCount < oldCount)
8959 mNetworkAdapters.resize(newCount);
8960 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8961 it != data.llNetworkAdapters.end();
8962 ++it)
8963 {
8964 const settings::NetworkAdapter &nic = *it;
8965
8966 /* slot unicity is guaranteed by XML Schema */
8967 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8968 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8969 if (FAILED(rc)) return rc;
8970 }
8971
8972 // serial ports
8973 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8974 it != data.llSerialPorts.end();
8975 ++it)
8976 {
8977 const settings::SerialPort &s = *it;
8978
8979 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8980 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8981 if (FAILED(rc)) return rc;
8982 }
8983
8984 // parallel ports (optional)
8985 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8986 it != data.llParallelPorts.end();
8987 ++it)
8988 {
8989 const settings::ParallelPort &p = *it;
8990
8991 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8992 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8993 if (FAILED(rc)) return rc;
8994 }
8995
8996 /* AudioAdapter */
8997 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8998 if (FAILED(rc)) return rc;
8999
9000 /* Shared folders */
9001 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9002 it != data.llSharedFolders.end();
9003 ++it)
9004 {
9005 const settings::SharedFolder &sf = *it;
9006
9007 ComObjPtr<SharedFolder> sharedFolder;
9008 /* Check for double entries. Not allowed! */
9009 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9010 if (SUCCEEDED(rc))
9011 return setError(VBOX_E_OBJECT_IN_USE,
9012 tr("Shared folder named '%s' already exists"),
9013 sf.strName.c_str());
9014
9015 /* Create the new shared folder. Don't break on error. This will be
9016 * reported when the machine starts. */
9017 sharedFolder.createObject();
9018 rc = sharedFolder->init(getMachine(),
9019 sf.strName,
9020 sf.strHostPath,
9021 RT_BOOL(sf.fWritable),
9022 RT_BOOL(sf.fAutoMount),
9023 false /* fFailOnError */);
9024 if (FAILED(rc)) return rc;
9025 mHWData->mSharedFolders.push_back(sharedFolder);
9026 }
9027
9028 // Clipboard
9029 mHWData->mClipboardMode = data.clipboardMode;
9030
9031 // drag'n'drop
9032 mHWData->mDragAndDropMode = data.dragAndDropMode;
9033
9034 // guest settings
9035 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9036
9037 // IO settings
9038 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9039 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9040
9041 // Host PCI devices
9042 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9043 it != data.pciAttachments.end();
9044 ++it)
9045 {
9046 const settings::HostPCIDeviceAttachment &hpda = *it;
9047 ComObjPtr<PCIDeviceAttachment> pda;
9048
9049 pda.createObject();
9050 pda->loadSettings(this, hpda);
9051 mHWData->mPCIDeviceAssignments.push_back(pda);
9052 }
9053
9054 /*
9055 * (The following isn't really real hardware, but it lives in HWData
9056 * for reasons of convenience.)
9057 */
9058
9059#ifdef VBOX_WITH_GUEST_PROPS
9060 /* Guest properties (optional) */
9061 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9062 it != data.llGuestProperties.end();
9063 ++it)
9064 {
9065 const settings::GuestProperty &prop = *it;
9066 uint32_t fFlags = guestProp::NILFLAG;
9067 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9068 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9069 mHWData->mGuestProperties[prop.strName] = property;
9070 }
9071
9072 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9073#endif /* VBOX_WITH_GUEST_PROPS defined */
9074
9075 rc = loadDebugging(pDbg);
9076 if (FAILED(rc))
9077 return rc;
9078
9079 mHWData->mAutostart = *pAutostart;
9080
9081 /* default frontend */
9082 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9083 }
9084 catch(std::bad_alloc &)
9085 {
9086 return E_OUTOFMEMORY;
9087 }
9088
9089 AssertComRC(rc);
9090 return rc;
9091}
9092
9093/**
9094 * Called from Machine::loadHardware() to load the debugging settings of the
9095 * machine.
9096 *
9097 * @param pDbg Pointer to the settings.
9098 */
9099HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9100{
9101 mHWData->mDebugging = *pDbg;
9102 /* no more processing currently required, this will probably change. */
9103 return S_OK;
9104}
9105
9106/**
9107 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9108 *
9109 * @param data
9110 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9111 * @param puuidSnapshot
9112 * @return
9113 */
9114HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9115 const Guid *puuidRegistry,
9116 const Guid *puuidSnapshot)
9117{
9118 AssertReturn(!isSessionMachine(), E_FAIL);
9119
9120 HRESULT rc = S_OK;
9121
9122 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9123 it != data.llStorageControllers.end();
9124 ++it)
9125 {
9126 const settings::StorageController &ctlData = *it;
9127
9128 ComObjPtr<StorageController> pCtl;
9129 /* Try to find one with the name first. */
9130 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9131 if (SUCCEEDED(rc))
9132 return setError(VBOX_E_OBJECT_IN_USE,
9133 tr("Storage controller named '%s' already exists"),
9134 ctlData.strName.c_str());
9135
9136 pCtl.createObject();
9137 rc = pCtl->init(this,
9138 ctlData.strName,
9139 ctlData.storageBus,
9140 ctlData.ulInstance,
9141 ctlData.fBootable);
9142 if (FAILED(rc)) return rc;
9143
9144 mStorageControllers->push_back(pCtl);
9145
9146 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9147 if (FAILED(rc)) return rc;
9148
9149 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9150 if (FAILED(rc)) return rc;
9151
9152 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9153 if (FAILED(rc)) return rc;
9154
9155 /* Set IDE emulation settings (only for AHCI controller). */
9156 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9157 {
9158 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9159 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9160 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9161 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9162 )
9163 return rc;
9164 }
9165
9166 /* Load the attached devices now. */
9167 rc = loadStorageDevices(pCtl,
9168 ctlData,
9169 puuidRegistry,
9170 puuidSnapshot);
9171 if (FAILED(rc)) return rc;
9172 }
9173
9174 return S_OK;
9175}
9176
9177/**
9178 * Called from loadStorageControllers for a controller's devices.
9179 *
9180 * @param aStorageController
9181 * @param data
9182 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9183 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9184 * @return
9185 */
9186HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9187 const settings::StorageController &data,
9188 const Guid *puuidRegistry,
9189 const Guid *puuidSnapshot)
9190{
9191 HRESULT rc = S_OK;
9192
9193 /* paranoia: detect duplicate attachments */
9194 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9195 it != data.llAttachedDevices.end();
9196 ++it)
9197 {
9198 const settings::AttachedDevice &ad = *it;
9199
9200 for (settings::AttachedDevicesList::const_iterator it2 = it;
9201 it2 != data.llAttachedDevices.end();
9202 ++it2)
9203 {
9204 if (it == it2)
9205 continue;
9206
9207 const settings::AttachedDevice &ad2 = *it2;
9208
9209 if ( ad.lPort == ad2.lPort
9210 && ad.lDevice == ad2.lDevice)
9211 {
9212 return setError(E_FAIL,
9213 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9214 aStorageController->getName().c_str(),
9215 ad.lPort,
9216 ad.lDevice,
9217 mUserData->s.strName.c_str());
9218 }
9219 }
9220 }
9221
9222 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9223 it != data.llAttachedDevices.end();
9224 ++it)
9225 {
9226 const settings::AttachedDevice &dev = *it;
9227 ComObjPtr<Medium> medium;
9228
9229 switch (dev.deviceType)
9230 {
9231 case DeviceType_Floppy:
9232 case DeviceType_DVD:
9233 if (dev.strHostDriveSrc.isNotEmpty())
9234 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9235 else
9236 rc = mParent->findRemoveableMedium(dev.deviceType,
9237 dev.uuid,
9238 false /* fRefresh */,
9239 false /* aSetError */,
9240 medium);
9241 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9242 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9243 rc = S_OK;
9244 break;
9245
9246 case DeviceType_HardDisk:
9247 {
9248 /* find a hard disk by UUID */
9249 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9250 if (FAILED(rc))
9251 {
9252 if (isSnapshotMachine())
9253 {
9254 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9255 // so the user knows that the bad disk is in a snapshot somewhere
9256 com::ErrorInfo info;
9257 return setError(E_FAIL,
9258 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9259 puuidSnapshot->raw(),
9260 info.getText().raw());
9261 }
9262 else
9263 return rc;
9264 }
9265
9266 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9267
9268 if (medium->getType() == MediumType_Immutable)
9269 {
9270 if (isSnapshotMachine())
9271 return setError(E_FAIL,
9272 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9273 "of the virtual machine '%s' ('%s')"),
9274 medium->getLocationFull().c_str(),
9275 dev.uuid.raw(),
9276 puuidSnapshot->raw(),
9277 mUserData->s.strName.c_str(),
9278 mData->m_strConfigFileFull.c_str());
9279
9280 return setError(E_FAIL,
9281 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9282 medium->getLocationFull().c_str(),
9283 dev.uuid.raw(),
9284 mUserData->s.strName.c_str(),
9285 mData->m_strConfigFileFull.c_str());
9286 }
9287
9288 if (medium->getType() == MediumType_MultiAttach)
9289 {
9290 if (isSnapshotMachine())
9291 return setError(E_FAIL,
9292 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9293 "of the virtual machine '%s' ('%s')"),
9294 medium->getLocationFull().c_str(),
9295 dev.uuid.raw(),
9296 puuidSnapshot->raw(),
9297 mUserData->s.strName.c_str(),
9298 mData->m_strConfigFileFull.c_str());
9299
9300 return setError(E_FAIL,
9301 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9302 medium->getLocationFull().c_str(),
9303 dev.uuid.raw(),
9304 mUserData->s.strName.c_str(),
9305 mData->m_strConfigFileFull.c_str());
9306 }
9307
9308 if ( !isSnapshotMachine()
9309 && medium->getChildren().size() != 0
9310 )
9311 return setError(E_FAIL,
9312 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9313 "because it has %d differencing child hard disks"),
9314 medium->getLocationFull().c_str(),
9315 dev.uuid.raw(),
9316 mUserData->s.strName.c_str(),
9317 mData->m_strConfigFileFull.c_str(),
9318 medium->getChildren().size());
9319
9320 if (findAttachment(mMediaData->mAttachments,
9321 medium))
9322 return setError(E_FAIL,
9323 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9324 medium->getLocationFull().c_str(),
9325 dev.uuid.raw(),
9326 mUserData->s.strName.c_str(),
9327 mData->m_strConfigFileFull.c_str());
9328
9329 break;
9330 }
9331
9332 default:
9333 return setError(E_FAIL,
9334 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9335 medium->getLocationFull().c_str(),
9336 mUserData->s.strName.c_str(),
9337 mData->m_strConfigFileFull.c_str());
9338 }
9339
9340 if (FAILED(rc))
9341 break;
9342
9343 /* Bandwidth groups are loaded at this point. */
9344 ComObjPtr<BandwidthGroup> pBwGroup;
9345
9346 if (!dev.strBwGroup.isEmpty())
9347 {
9348 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9349 if (FAILED(rc))
9350 return setError(E_FAIL,
9351 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9352 medium->getLocationFull().c_str(),
9353 dev.strBwGroup.c_str(),
9354 mUserData->s.strName.c_str(),
9355 mData->m_strConfigFileFull.c_str());
9356 pBwGroup->reference();
9357 }
9358
9359 const Bstr controllerName = aStorageController->getName();
9360 ComObjPtr<MediumAttachment> pAttachment;
9361 pAttachment.createObject();
9362 rc = pAttachment->init(this,
9363 medium,
9364 controllerName,
9365 dev.lPort,
9366 dev.lDevice,
9367 dev.deviceType,
9368 false,
9369 dev.fPassThrough,
9370 dev.fTempEject,
9371 dev.fNonRotational,
9372 dev.fDiscard,
9373 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9374 if (FAILED(rc)) break;
9375
9376 /* associate the medium with this machine and snapshot */
9377 if (!medium.isNull())
9378 {
9379 AutoCaller medCaller(medium);
9380 if (FAILED(medCaller.rc())) return medCaller.rc();
9381 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9382
9383 if (isSnapshotMachine())
9384 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9385 else
9386 rc = medium->addBackReference(mData->mUuid);
9387 /* If the medium->addBackReference fails it sets an appropriate
9388 * error message, so no need to do any guesswork here. */
9389
9390 if (puuidRegistry)
9391 // caller wants registry ID to be set on all attached media (OVF import case)
9392 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9393 }
9394
9395 if (FAILED(rc))
9396 break;
9397
9398 /* back up mMediaData to let registeredInit() properly rollback on failure
9399 * (= limited accessibility) */
9400 setModified(IsModified_Storage);
9401 mMediaData.backup();
9402 mMediaData->mAttachments.push_back(pAttachment);
9403 }
9404
9405 return rc;
9406}
9407
9408/**
9409 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9410 *
9411 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9412 * @param aSnapshot where to return the found snapshot
9413 * @param aSetError true to set extended error info on failure
9414 */
9415HRESULT Machine::findSnapshotById(const Guid &aId,
9416 ComObjPtr<Snapshot> &aSnapshot,
9417 bool aSetError /* = false */)
9418{
9419 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9420
9421 if (!mData->mFirstSnapshot)
9422 {
9423 if (aSetError)
9424 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9425 return E_FAIL;
9426 }
9427
9428 if (aId.isZero())
9429 aSnapshot = mData->mFirstSnapshot;
9430 else
9431 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9432
9433 if (!aSnapshot)
9434 {
9435 if (aSetError)
9436 return setError(E_FAIL,
9437 tr("Could not find a snapshot with UUID {%s}"),
9438 aId.toString().c_str());
9439 return E_FAIL;
9440 }
9441
9442 return S_OK;
9443}
9444
9445/**
9446 * Returns the snapshot with the given name or fails of no such snapshot.
9447 *
9448 * @param aName snapshot name to find
9449 * @param aSnapshot where to return the found snapshot
9450 * @param aSetError true to set extended error info on failure
9451 */
9452HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9453 ComObjPtr<Snapshot> &aSnapshot,
9454 bool aSetError /* = false */)
9455{
9456 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9457
9458 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9459
9460 if (!mData->mFirstSnapshot)
9461 {
9462 if (aSetError)
9463 return setError(VBOX_E_OBJECT_NOT_FOUND,
9464 tr("This machine does not have any snapshots"));
9465 return VBOX_E_OBJECT_NOT_FOUND;
9466 }
9467
9468 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9469
9470 if (!aSnapshot)
9471 {
9472 if (aSetError)
9473 return setError(VBOX_E_OBJECT_NOT_FOUND,
9474 tr("Could not find a snapshot named '%s'"), strName.c_str());
9475 return VBOX_E_OBJECT_NOT_FOUND;
9476 }
9477
9478 return S_OK;
9479}
9480
9481/**
9482 * Returns a storage controller object with the given name.
9483 *
9484 * @param aName storage controller name to find
9485 * @param aStorageController where to return the found storage controller
9486 * @param aSetError true to set extended error info on failure
9487 */
9488HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9489 ComObjPtr<StorageController> &aStorageController,
9490 bool aSetError /* = false */)
9491{
9492 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9493
9494 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9495 it != mStorageControllers->end();
9496 ++it)
9497 {
9498 if ((*it)->getName() == aName)
9499 {
9500 aStorageController = (*it);
9501 return S_OK;
9502 }
9503 }
9504
9505 if (aSetError)
9506 return setError(VBOX_E_OBJECT_NOT_FOUND,
9507 tr("Could not find a storage controller named '%s'"),
9508 aName.c_str());
9509 return VBOX_E_OBJECT_NOT_FOUND;
9510}
9511
9512HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9513 MediaData::AttachmentList &atts)
9514{
9515 AutoCaller autoCaller(this);
9516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9517
9518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9519
9520 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9521 it != mMediaData->mAttachments.end();
9522 ++it)
9523 {
9524 const ComObjPtr<MediumAttachment> &pAtt = *it;
9525
9526 // should never happen, but deal with NULL pointers in the list.
9527 AssertStmt(!pAtt.isNull(), continue);
9528
9529 // getControllerName() needs caller+read lock
9530 AutoCaller autoAttCaller(pAtt);
9531 if (FAILED(autoAttCaller.rc()))
9532 {
9533 atts.clear();
9534 return autoAttCaller.rc();
9535 }
9536 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9537
9538 if (pAtt->getControllerName() == aName)
9539 atts.push_back(pAtt);
9540 }
9541
9542 return S_OK;
9543}
9544
9545/**
9546 * Helper for #saveSettings. Cares about renaming the settings directory and
9547 * file if the machine name was changed and about creating a new settings file
9548 * if this is a new machine.
9549 *
9550 * @note Must be never called directly but only from #saveSettings().
9551 */
9552HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9553{
9554 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9555
9556 HRESULT rc = S_OK;
9557
9558 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9559
9560 /// @todo need to handle primary group change, too
9561
9562 /* attempt to rename the settings file if machine name is changed */
9563 if ( mUserData->s.fNameSync
9564 && mUserData.isBackedUp()
9565 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9566 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9567 )
9568 {
9569 bool dirRenamed = false;
9570 bool fileRenamed = false;
9571
9572 Utf8Str configFile, newConfigFile;
9573 Utf8Str configFilePrev, newConfigFilePrev;
9574 Utf8Str configDir, newConfigDir;
9575
9576 do
9577 {
9578 int vrc = VINF_SUCCESS;
9579
9580 Utf8Str name = mUserData.backedUpData()->s.strName;
9581 Utf8Str newName = mUserData->s.strName;
9582 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9583 if (group == "/")
9584 group.setNull();
9585 Utf8Str newGroup = mUserData->s.llGroups.front();
9586 if (newGroup == "/")
9587 newGroup.setNull();
9588
9589 configFile = mData->m_strConfigFileFull;
9590
9591 /* first, rename the directory if it matches the group and machine name */
9592 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9593 group.c_str(), RTPATH_DELIMITER, name.c_str());
9594 /** @todo hack, make somehow use of ComposeMachineFilename */
9595 if (mUserData->s.fDirectoryIncludesUUID)
9596 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9597 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9598 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9599 /** @todo hack, make somehow use of ComposeMachineFilename */
9600 if (mUserData->s.fDirectoryIncludesUUID)
9601 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9602 configDir = configFile;
9603 configDir.stripFilename();
9604 newConfigDir = configDir;
9605 if ( configDir.length() >= groupPlusName.length()
9606 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9607 {
9608 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9609 Utf8Str newConfigBaseDir(newConfigDir);
9610 newConfigDir.append(newGroupPlusName);
9611 /* consistency: use \ if appropriate on the platform */
9612 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9613 /* new dir and old dir cannot be equal here because of 'if'
9614 * above and because name != newName */
9615 Assert(configDir != newConfigDir);
9616 if (!fSettingsFileIsNew)
9617 {
9618 /* perform real rename only if the machine is not new */
9619 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9620 if ( vrc == VERR_FILE_NOT_FOUND
9621 || vrc == VERR_PATH_NOT_FOUND)
9622 {
9623 /* create the parent directory, then retry renaming */
9624 Utf8Str parent(newConfigDir);
9625 parent.stripFilename();
9626 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9627 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9628 }
9629 if (RT_FAILURE(vrc))
9630 {
9631 rc = setError(E_FAIL,
9632 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9633 configDir.c_str(),
9634 newConfigDir.c_str(),
9635 vrc);
9636 break;
9637 }
9638 /* delete subdirectories which are no longer needed */
9639 Utf8Str dir(configDir);
9640 dir.stripFilename();
9641 while (dir != newConfigBaseDir && dir != ".")
9642 {
9643 vrc = RTDirRemove(dir.c_str());
9644 if (RT_FAILURE(vrc))
9645 break;
9646 dir.stripFilename();
9647 }
9648 dirRenamed = true;
9649 }
9650 }
9651
9652 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9653 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9654
9655 /* then try to rename the settings file itself */
9656 if (newConfigFile != configFile)
9657 {
9658 /* get the path to old settings file in renamed directory */
9659 configFile = Utf8StrFmt("%s%c%s",
9660 newConfigDir.c_str(),
9661 RTPATH_DELIMITER,
9662 RTPathFilename(configFile.c_str()));
9663 if (!fSettingsFileIsNew)
9664 {
9665 /* perform real rename only if the machine is not new */
9666 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9667 if (RT_FAILURE(vrc))
9668 {
9669 rc = setError(E_FAIL,
9670 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9671 configFile.c_str(),
9672 newConfigFile.c_str(),
9673 vrc);
9674 break;
9675 }
9676 fileRenamed = true;
9677 configFilePrev = configFile;
9678 configFilePrev += "-prev";
9679 newConfigFilePrev = newConfigFile;
9680 newConfigFilePrev += "-prev";
9681 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9682 }
9683 }
9684
9685 // update m_strConfigFileFull amd mConfigFile
9686 mData->m_strConfigFileFull = newConfigFile;
9687 // compute the relative path too
9688 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9689
9690 // store the old and new so that VirtualBox::saveSettings() can update
9691 // the media registry
9692 if ( mData->mRegistered
9693 && configDir != newConfigDir)
9694 {
9695 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9696
9697 if (pfNeedsGlobalSaveSettings)
9698 *pfNeedsGlobalSaveSettings = true;
9699 }
9700
9701 // in the saved state file path, replace the old directory with the new directory
9702 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9703 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9704
9705 // and do the same thing for the saved state file paths of all the online snapshots
9706 if (mData->mFirstSnapshot)
9707 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9708 newConfigDir.c_str());
9709 }
9710 while (0);
9711
9712 if (FAILED(rc))
9713 {
9714 /* silently try to rename everything back */
9715 if (fileRenamed)
9716 {
9717 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9718 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9719 }
9720 if (dirRenamed)
9721 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9722 }
9723
9724 if (FAILED(rc)) return rc;
9725 }
9726
9727 if (fSettingsFileIsNew)
9728 {
9729 /* create a virgin config file */
9730 int vrc = VINF_SUCCESS;
9731
9732 /* ensure the settings directory exists */
9733 Utf8Str path(mData->m_strConfigFileFull);
9734 path.stripFilename();
9735 if (!RTDirExists(path.c_str()))
9736 {
9737 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9738 if (RT_FAILURE(vrc))
9739 {
9740 return setError(E_FAIL,
9741 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9742 path.c_str(),
9743 vrc);
9744 }
9745 }
9746
9747 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9748 path = Utf8Str(mData->m_strConfigFileFull);
9749 RTFILE f = NIL_RTFILE;
9750 vrc = RTFileOpen(&f, path.c_str(),
9751 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9752 if (RT_FAILURE(vrc))
9753 return setError(E_FAIL,
9754 tr("Could not create the settings file '%s' (%Rrc)"),
9755 path.c_str(),
9756 vrc);
9757 RTFileClose(f);
9758 }
9759
9760 return rc;
9761}
9762
9763/**
9764 * Saves and commits machine data, user data and hardware data.
9765 *
9766 * Note that on failure, the data remains uncommitted.
9767 *
9768 * @a aFlags may combine the following flags:
9769 *
9770 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9771 * Used when saving settings after an operation that makes them 100%
9772 * correspond to the settings from the current snapshot.
9773 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9774 * #isReallyModified() returns false. This is necessary for cases when we
9775 * change machine data directly, not through the backup()/commit() mechanism.
9776 * - SaveS_Force: settings will be saved without doing a deep compare of the
9777 * settings structures. This is used when this is called because snapshots
9778 * have changed to avoid the overhead of the deep compare.
9779 *
9780 * @note Must be called from under this object's write lock. Locks children for
9781 * writing.
9782 *
9783 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9784 * initialized to false and that will be set to true by this function if
9785 * the caller must invoke VirtualBox::saveSettings() because the global
9786 * settings have changed. This will happen if a machine rename has been
9787 * saved and the global machine and media registries will therefore need
9788 * updating.
9789 */
9790HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9791 int aFlags /*= 0*/)
9792{
9793 LogFlowThisFuncEnter();
9794
9795 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9796
9797 /* make sure child objects are unable to modify the settings while we are
9798 * saving them */
9799 ensureNoStateDependencies();
9800
9801 AssertReturn(!isSnapshotMachine(),
9802 E_FAIL);
9803
9804 HRESULT rc = S_OK;
9805 bool fNeedsWrite = false;
9806
9807 /* First, prepare to save settings. It will care about renaming the
9808 * settings directory and file if the machine name was changed and about
9809 * creating a new settings file if this is a new machine. */
9810 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9811 if (FAILED(rc)) return rc;
9812
9813 // keep a pointer to the current settings structures
9814 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9815 settings::MachineConfigFile *pNewConfig = NULL;
9816
9817 try
9818 {
9819 // make a fresh one to have everyone write stuff into
9820 pNewConfig = new settings::MachineConfigFile(NULL);
9821 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9822
9823 // now go and copy all the settings data from COM to the settings structures
9824 // (this calles saveSettings() on all the COM objects in the machine)
9825 copyMachineDataToSettings(*pNewConfig);
9826
9827 if (aFlags & SaveS_ResetCurStateModified)
9828 {
9829 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9830 mData->mCurrentStateModified = FALSE;
9831 fNeedsWrite = true; // always, no need to compare
9832 }
9833 else if (aFlags & SaveS_Force)
9834 {
9835 fNeedsWrite = true; // always, no need to compare
9836 }
9837 else
9838 {
9839 if (!mData->mCurrentStateModified)
9840 {
9841 // do a deep compare of the settings that we just saved with the settings
9842 // previously stored in the config file; this invokes MachineConfigFile::operator==
9843 // which does a deep compare of all the settings, which is expensive but less expensive
9844 // than writing out XML in vain
9845 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9846
9847 // could still be modified if any settings changed
9848 mData->mCurrentStateModified = fAnySettingsChanged;
9849
9850 fNeedsWrite = fAnySettingsChanged;
9851 }
9852 else
9853 fNeedsWrite = true;
9854 }
9855
9856 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9857
9858 if (fNeedsWrite)
9859 // now spit it all out!
9860 pNewConfig->write(mData->m_strConfigFileFull);
9861
9862 mData->pMachineConfigFile = pNewConfig;
9863 delete pOldConfig;
9864 commit();
9865
9866 // after saving settings, we are no longer different from the XML on disk
9867 mData->flModifications = 0;
9868 }
9869 catch (HRESULT err)
9870 {
9871 // we assume that error info is set by the thrower
9872 rc = err;
9873
9874 // restore old config
9875 delete pNewConfig;
9876 mData->pMachineConfigFile = pOldConfig;
9877 }
9878 catch (...)
9879 {
9880 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9881 }
9882
9883 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9884 {
9885 /* Fire the data change event, even on failure (since we've already
9886 * committed all data). This is done only for SessionMachines because
9887 * mutable Machine instances are always not registered (i.e. private
9888 * to the client process that creates them) and thus don't need to
9889 * inform callbacks. */
9890 if (isSessionMachine())
9891 mParent->onMachineDataChange(mData->mUuid);
9892 }
9893
9894 LogFlowThisFunc(("rc=%08X\n", rc));
9895 LogFlowThisFuncLeave();
9896 return rc;
9897}
9898
9899/**
9900 * Implementation for saving the machine settings into the given
9901 * settings::MachineConfigFile instance. This copies machine extradata
9902 * from the previous machine config file in the instance data, if any.
9903 *
9904 * This gets called from two locations:
9905 *
9906 * -- Machine::saveSettings(), during the regular XML writing;
9907 *
9908 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9909 * exported to OVF and we write the VirtualBox proprietary XML
9910 * into a <vbox:Machine> tag.
9911 *
9912 * This routine fills all the fields in there, including snapshots, *except*
9913 * for the following:
9914 *
9915 * -- fCurrentStateModified. There is some special logic associated with that.
9916 *
9917 * The caller can then call MachineConfigFile::write() or do something else
9918 * with it.
9919 *
9920 * Caller must hold the machine lock!
9921 *
9922 * This throws XML errors and HRESULT, so the caller must have a catch block!
9923 */
9924void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9925{
9926 // deep copy extradata
9927 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9928
9929 config.uuid = mData->mUuid;
9930
9931 // copy name, description, OS type, teleport, UTC etc.
9932 config.machineUserData = mUserData->s;
9933
9934 if ( mData->mMachineState == MachineState_Saved
9935 || mData->mMachineState == MachineState_Restoring
9936 // when deleting a snapshot we may or may not have a saved state in the current state,
9937 // so let's not assert here please
9938 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9939 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9940 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9941 && (!mSSData->strStateFilePath.isEmpty())
9942 )
9943 )
9944 {
9945 Assert(!mSSData->strStateFilePath.isEmpty());
9946 /* try to make the file name relative to the settings file dir */
9947 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9948 }
9949 else
9950 {
9951 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9952 config.strStateFile.setNull();
9953 }
9954
9955 if (mData->mCurrentSnapshot)
9956 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9957 else
9958 config.uuidCurrentSnapshot.clear();
9959
9960 config.timeLastStateChange = mData->mLastStateChange;
9961 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9962 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9963
9964 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9965 if (FAILED(rc)) throw rc;
9966
9967 rc = saveStorageControllers(config.storageMachine);
9968 if (FAILED(rc)) throw rc;
9969
9970 // save machine's media registry if this is VirtualBox 4.0 or later
9971 if (config.canHaveOwnMediaRegistry())
9972 {
9973 // determine machine folder
9974 Utf8Str strMachineFolder = getSettingsFileFull();
9975 strMachineFolder.stripFilename();
9976 mParent->saveMediaRegistry(config.mediaRegistry,
9977 getId(), // only media with registry ID == machine UUID
9978 strMachineFolder);
9979 // this throws HRESULT
9980 }
9981
9982 // save snapshots
9983 rc = saveAllSnapshots(config);
9984 if (FAILED(rc)) throw rc;
9985}
9986
9987/**
9988 * Saves all snapshots of the machine into the given machine config file. Called
9989 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9990 * @param config
9991 * @return
9992 */
9993HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9994{
9995 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9996
9997 HRESULT rc = S_OK;
9998
9999 try
10000 {
10001 config.llFirstSnapshot.clear();
10002
10003 if (mData->mFirstSnapshot)
10004 {
10005 settings::Snapshot snapNew;
10006 config.llFirstSnapshot.push_back(snapNew);
10007
10008 // get reference to the fresh copy of the snapshot on the list and
10009 // work on that copy directly to avoid excessive copying later
10010 settings::Snapshot &snap = config.llFirstSnapshot.front();
10011
10012 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10013 if (FAILED(rc)) throw rc;
10014 }
10015
10016// if (mType == IsSessionMachine)
10017// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10018
10019 }
10020 catch (HRESULT err)
10021 {
10022 /* we assume that error info is set by the thrower */
10023 rc = err;
10024 }
10025 catch (...)
10026 {
10027 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10028 }
10029
10030 return rc;
10031}
10032
10033/**
10034 * Saves the VM hardware configuration. It is assumed that the
10035 * given node is empty.
10036 *
10037 * @param data Reference to the settings object for the hardware config.
10038 * @param pDbg Pointer to the settings object for the debugging config
10039 * which happens to live in mHWData.
10040 * @param pAutostart Pointer to the settings object for the autostart config
10041 * which happens to live in mHWData.
10042 */
10043HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10044 settings::Autostart *pAutostart)
10045{
10046 HRESULT rc = S_OK;
10047
10048 try
10049 {
10050 /* The hardware version attribute (optional).
10051 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10052 if ( mHWData->mHWVersion == "1"
10053 && mSSData->strStateFilePath.isEmpty()
10054 )
10055 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. */
10056
10057 data.strVersion = mHWData->mHWVersion;
10058 data.uuid = mHWData->mHardwareUUID;
10059
10060 // CPU
10061 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10062 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10063 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10064 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10065 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10066 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10067 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10068 data.fPAE = !!mHWData->mPAEEnabled;
10069 data.enmLongMode = mHWData->mLongMode;
10070 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10071
10072 /* Standard and Extended CPUID leafs. */
10073 data.llCpuIdLeafs.clear();
10074 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10075 {
10076 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10077 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10078 }
10079 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10080 {
10081 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10082 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10083 }
10084
10085 data.cCPUs = mHWData->mCPUCount;
10086 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10087 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10088
10089 data.llCpus.clear();
10090 if (data.fCpuHotPlug)
10091 {
10092 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10093 {
10094 if (mHWData->mCPUAttached[idx])
10095 {
10096 settings::Cpu cpu;
10097 cpu.ulId = idx;
10098 data.llCpus.push_back(cpu);
10099 }
10100 }
10101 }
10102
10103 // memory
10104 data.ulMemorySizeMB = mHWData->mMemorySize;
10105 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10106
10107 // firmware
10108 data.firmwareType = mHWData->mFirmwareType;
10109
10110 // HID
10111 data.pointingHIDType = mHWData->mPointingHIDType;
10112 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10113
10114 // chipset
10115 data.chipsetType = mHWData->mChipsetType;
10116
10117 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10118 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10119
10120 // HPET
10121 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10122
10123 // boot order
10124 data.mapBootOrder.clear();
10125 for (size_t i = 0;
10126 i < RT_ELEMENTS(mHWData->mBootOrder);
10127 ++i)
10128 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10129
10130 // display
10131 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10132 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10133 data.cMonitors = mHWData->mMonitorCount;
10134 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10135 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10136 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10137 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10138 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10139 data.ulVideoCaptureFps = mHWData->mVideoCaptureFps;
10140 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10141 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10142 {
10143 if (mHWData->maVideoCaptureScreens[i])
10144 ASMBitSet(&data.u64VideoCaptureScreens, i);
10145 else
10146 ASMBitClear(&data.u64VideoCaptureScreens, i);
10147 }
10148 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
10149
10150 /* VRDEServer settings (optional) */
10151 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10152 if (FAILED(rc)) throw rc;
10153
10154 /* BIOS (required) */
10155 rc = mBIOSSettings->saveSettings(data.biosSettings);
10156 if (FAILED(rc)) throw rc;
10157
10158 /* USB Controller (required) */
10159 rc = mUSBController->saveSettings(data.usbController);
10160 if (FAILED(rc)) throw rc;
10161
10162 /* Network adapters (required) */
10163 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10164 data.llNetworkAdapters.clear();
10165 /* Write out only the nominal number of network adapters for this
10166 * chipset type. Since Machine::commit() hasn't been called there
10167 * may be extra NIC settings in the vector. */
10168 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10169 {
10170 settings::NetworkAdapter nic;
10171 nic.ulSlot = slot;
10172 /* paranoia check... must not be NULL, but must not crash either. */
10173 if (mNetworkAdapters[slot])
10174 {
10175 rc = mNetworkAdapters[slot]->saveSettings(nic);
10176 if (FAILED(rc)) throw rc;
10177
10178 data.llNetworkAdapters.push_back(nic);
10179 }
10180 }
10181
10182 /* Serial ports */
10183 data.llSerialPorts.clear();
10184 for (ULONG slot = 0;
10185 slot < RT_ELEMENTS(mSerialPorts);
10186 ++slot)
10187 {
10188 settings::SerialPort s;
10189 s.ulSlot = slot;
10190 rc = mSerialPorts[slot]->saveSettings(s);
10191 if (FAILED(rc)) return rc;
10192
10193 data.llSerialPorts.push_back(s);
10194 }
10195
10196 /* Parallel ports */
10197 data.llParallelPorts.clear();
10198 for (ULONG slot = 0;
10199 slot < RT_ELEMENTS(mParallelPorts);
10200 ++slot)
10201 {
10202 settings::ParallelPort p;
10203 p.ulSlot = slot;
10204 rc = mParallelPorts[slot]->saveSettings(p);
10205 if (FAILED(rc)) return rc;
10206
10207 data.llParallelPorts.push_back(p);
10208 }
10209
10210 /* Audio adapter */
10211 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10212 if (FAILED(rc)) return rc;
10213
10214 /* Shared folders */
10215 data.llSharedFolders.clear();
10216 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10217 it != mHWData->mSharedFolders.end();
10218 ++it)
10219 {
10220 SharedFolder *pSF = *it;
10221 AutoCaller sfCaller(pSF);
10222 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10223 settings::SharedFolder sf;
10224 sf.strName = pSF->getName();
10225 sf.strHostPath = pSF->getHostPath();
10226 sf.fWritable = !!pSF->isWritable();
10227 sf.fAutoMount = !!pSF->isAutoMounted();
10228
10229 data.llSharedFolders.push_back(sf);
10230 }
10231
10232 // clipboard
10233 data.clipboardMode = mHWData->mClipboardMode;
10234
10235 // drag'n'drop
10236 data.dragAndDropMode = mHWData->mDragAndDropMode;
10237
10238 /* Guest */
10239 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10240
10241 // IO settings
10242 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10243 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10244
10245 /* BandwidthControl (required) */
10246 rc = mBandwidthControl->saveSettings(data.ioSettings);
10247 if (FAILED(rc)) throw rc;
10248
10249 /* Host PCI devices */
10250 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10251 it != mHWData->mPCIDeviceAssignments.end();
10252 ++it)
10253 {
10254 ComObjPtr<PCIDeviceAttachment> pda = *it;
10255 settings::HostPCIDeviceAttachment hpda;
10256
10257 rc = pda->saveSettings(hpda);
10258 if (FAILED(rc)) throw rc;
10259
10260 data.pciAttachments.push_back(hpda);
10261 }
10262
10263
10264 // guest properties
10265 data.llGuestProperties.clear();
10266#ifdef VBOX_WITH_GUEST_PROPS
10267 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10268 it != mHWData->mGuestProperties.end();
10269 ++it)
10270 {
10271 HWData::GuestProperty property = it->second;
10272
10273 /* Remove transient guest properties at shutdown unless we
10274 * are saving state */
10275 if ( ( mData->mMachineState == MachineState_PoweredOff
10276 || mData->mMachineState == MachineState_Aborted
10277 || mData->mMachineState == MachineState_Teleported)
10278 && ( property.mFlags & guestProp::TRANSIENT
10279 || property.mFlags & guestProp::TRANSRESET))
10280 continue;
10281 settings::GuestProperty prop;
10282 prop.strName = it->first;
10283 prop.strValue = property.strValue;
10284 prop.timestamp = property.mTimestamp;
10285 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10286 guestProp::writeFlags(property.mFlags, szFlags);
10287 prop.strFlags = szFlags;
10288
10289 data.llGuestProperties.push_back(prop);
10290 }
10291
10292 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10293 /* I presume this doesn't require a backup(). */
10294 mData->mGuestPropertiesModified = FALSE;
10295#endif /* VBOX_WITH_GUEST_PROPS defined */
10296
10297 *pDbg = mHWData->mDebugging;
10298 *pAutostart = mHWData->mAutostart;
10299
10300 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10301 }
10302 catch(std::bad_alloc &)
10303 {
10304 return E_OUTOFMEMORY;
10305 }
10306
10307 AssertComRC(rc);
10308 return rc;
10309}
10310
10311/**
10312 * Saves the storage controller configuration.
10313 *
10314 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10315 */
10316HRESULT Machine::saveStorageControllers(settings::Storage &data)
10317{
10318 data.llStorageControllers.clear();
10319
10320 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10321 it != mStorageControllers->end();
10322 ++it)
10323 {
10324 HRESULT rc;
10325 ComObjPtr<StorageController> pCtl = *it;
10326
10327 settings::StorageController ctl;
10328 ctl.strName = pCtl->getName();
10329 ctl.controllerType = pCtl->getControllerType();
10330 ctl.storageBus = pCtl->getStorageBus();
10331 ctl.ulInstance = pCtl->getInstance();
10332 ctl.fBootable = pCtl->getBootable();
10333
10334 /* Save the port count. */
10335 ULONG portCount;
10336 rc = pCtl->COMGETTER(PortCount)(&portCount);
10337 ComAssertComRCRet(rc, rc);
10338 ctl.ulPortCount = portCount;
10339
10340 /* Save fUseHostIOCache */
10341 BOOL fUseHostIOCache;
10342 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10343 ComAssertComRCRet(rc, rc);
10344 ctl.fUseHostIOCache = !!fUseHostIOCache;
10345
10346 /* Save IDE emulation settings. */
10347 if (ctl.controllerType == StorageControllerType_IntelAhci)
10348 {
10349 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10350 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10351 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10352 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10353 )
10354 ComAssertComRCRet(rc, rc);
10355 }
10356
10357 /* save the devices now. */
10358 rc = saveStorageDevices(pCtl, ctl);
10359 ComAssertComRCRet(rc, rc);
10360
10361 data.llStorageControllers.push_back(ctl);
10362 }
10363
10364 return S_OK;
10365}
10366
10367/**
10368 * Saves the hard disk configuration.
10369 */
10370HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10371 settings::StorageController &data)
10372{
10373 MediaData::AttachmentList atts;
10374
10375 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10376 if (FAILED(rc)) return rc;
10377
10378 data.llAttachedDevices.clear();
10379 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10380 it != atts.end();
10381 ++it)
10382 {
10383 settings::AttachedDevice dev;
10384
10385 MediumAttachment *pAttach = *it;
10386 Medium *pMedium = pAttach->getMedium();
10387
10388 dev.deviceType = pAttach->getType();
10389 dev.lPort = pAttach->getPort();
10390 dev.lDevice = pAttach->getDevice();
10391 if (pMedium)
10392 {
10393 if (pMedium->isHostDrive())
10394 dev.strHostDriveSrc = pMedium->getLocationFull();
10395 else
10396 dev.uuid = pMedium->getId();
10397 dev.fPassThrough = pAttach->getPassthrough();
10398 dev.fTempEject = pAttach->getTempEject();
10399 dev.fNonRotational = pAttach->getNonRotational();
10400 dev.fDiscard = pAttach->getDiscard();
10401 }
10402
10403 dev.strBwGroup = pAttach->getBandwidthGroup();
10404
10405 data.llAttachedDevices.push_back(dev);
10406 }
10407
10408 return S_OK;
10409}
10410
10411/**
10412 * Saves machine state settings as defined by aFlags
10413 * (SaveSTS_* values).
10414 *
10415 * @param aFlags Combination of SaveSTS_* flags.
10416 *
10417 * @note Locks objects for writing.
10418 */
10419HRESULT Machine::saveStateSettings(int aFlags)
10420{
10421 if (aFlags == 0)
10422 return S_OK;
10423
10424 AutoCaller autoCaller(this);
10425 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10426
10427 /* This object's write lock is also necessary to serialize file access
10428 * (prevent concurrent reads and writes) */
10429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10430
10431 HRESULT rc = S_OK;
10432
10433 Assert(mData->pMachineConfigFile);
10434
10435 try
10436 {
10437 if (aFlags & SaveSTS_CurStateModified)
10438 mData->pMachineConfigFile->fCurrentStateModified = true;
10439
10440 if (aFlags & SaveSTS_StateFilePath)
10441 {
10442 if (!mSSData->strStateFilePath.isEmpty())
10443 /* try to make the file name relative to the settings file dir */
10444 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10445 else
10446 mData->pMachineConfigFile->strStateFile.setNull();
10447 }
10448
10449 if (aFlags & SaveSTS_StateTimeStamp)
10450 {
10451 Assert( mData->mMachineState != MachineState_Aborted
10452 || mSSData->strStateFilePath.isEmpty());
10453
10454 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10455
10456 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10457//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10458 }
10459
10460 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10461 }
10462 catch (...)
10463 {
10464 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10465 }
10466
10467 return rc;
10468}
10469
10470/**
10471 * Ensures that the given medium is added to a media registry. If this machine
10472 * was created with 4.0 or later, then the machine registry is used. Otherwise
10473 * the global VirtualBox media registry is used.
10474 *
10475 * Caller must NOT hold machine lock, media tree or any medium locks!
10476 *
10477 * @param pMedium
10478 */
10479void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10480{
10481 /* Paranoia checks: do not hold machine or media tree locks. */
10482 AssertReturnVoid(!isWriteLockOnCurrentThread());
10483 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10484
10485 ComObjPtr<Medium> pBase;
10486 {
10487 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10488 pBase = pMedium->getBase();
10489 }
10490
10491 /* Paranoia checks: do not hold medium locks. */
10492 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10493 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10494
10495 // decide which medium registry to use now that the medium is attached:
10496 Guid uuid;
10497 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10498 // machine XML is VirtualBox 4.0 or higher:
10499 uuid = getId(); // machine UUID
10500 else
10501 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10502
10503 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10504 mParent->markRegistryModified(uuid);
10505
10506 /* For more complex hard disk structures it can happen that the base
10507 * medium isn't yet associated with any medium registry. Do that now. */
10508 if (pMedium != pBase)
10509 {
10510 if (pBase->addRegistry(uuid, true /* fRecurse */))
10511 mParent->markRegistryModified(uuid);
10512 }
10513}
10514
10515/**
10516 * Creates differencing hard disks for all normal hard disks attached to this
10517 * machine and a new set of attachments to refer to created disks.
10518 *
10519 * Used when taking a snapshot or when deleting the current state. Gets called
10520 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10521 *
10522 * This method assumes that mMediaData contains the original hard disk attachments
10523 * it needs to create diffs for. On success, these attachments will be replaced
10524 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10525 * called to delete created diffs which will also rollback mMediaData and restore
10526 * whatever was backed up before calling this method.
10527 *
10528 * Attachments with non-normal hard disks are left as is.
10529 *
10530 * If @a aOnline is @c false then the original hard disks that require implicit
10531 * diffs will be locked for reading. Otherwise it is assumed that they are
10532 * already locked for writing (when the VM was started). Note that in the latter
10533 * case it is responsibility of the caller to lock the newly created diffs for
10534 * writing if this method succeeds.
10535 *
10536 * @param aProgress Progress object to run (must contain at least as
10537 * many operations left as the number of hard disks
10538 * attached).
10539 * @param aOnline Whether the VM was online prior to this operation.
10540 *
10541 * @note The progress object is not marked as completed, neither on success nor
10542 * on failure. This is a responsibility of the caller.
10543 *
10544 * @note Locks this object and the media tree for writing.
10545 */
10546HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10547 ULONG aWeight,
10548 bool aOnline)
10549{
10550 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10551
10552 AutoCaller autoCaller(this);
10553 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10554
10555 AutoMultiWriteLock2 alock(this->lockHandle(),
10556 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10557
10558 /* must be in a protective state because we release the lock below */
10559 AssertReturn( mData->mMachineState == MachineState_Saving
10560 || mData->mMachineState == MachineState_LiveSnapshotting
10561 || mData->mMachineState == MachineState_RestoringSnapshot
10562 || mData->mMachineState == MachineState_DeletingSnapshot
10563 , E_FAIL);
10564
10565 HRESULT rc = S_OK;
10566
10567 // use appropriate locked media map (online or offline)
10568 MediumLockListMap lockedMediaOffline;
10569 MediumLockListMap *lockedMediaMap;
10570 if (aOnline)
10571 lockedMediaMap = &mData->mSession.mLockedMedia;
10572 else
10573 lockedMediaMap = &lockedMediaOffline;
10574
10575 try
10576 {
10577 if (!aOnline)
10578 {
10579 /* lock all attached hard disks early to detect "in use"
10580 * situations before creating actual diffs */
10581 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10582 it != mMediaData->mAttachments.end();
10583 ++it)
10584 {
10585 MediumAttachment* pAtt = *it;
10586 if (pAtt->getType() == DeviceType_HardDisk)
10587 {
10588 Medium* pMedium = pAtt->getMedium();
10589 Assert(pMedium);
10590
10591 MediumLockList *pMediumLockList(new MediumLockList());
10592 alock.release();
10593 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10594 false /* fMediumLockWrite */,
10595 NULL,
10596 *pMediumLockList);
10597 alock.acquire();
10598 if (FAILED(rc))
10599 {
10600 delete pMediumLockList;
10601 throw rc;
10602 }
10603 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10604 if (FAILED(rc))
10605 {
10606 throw setError(rc,
10607 tr("Collecting locking information for all attached media failed"));
10608 }
10609 }
10610 }
10611
10612 /* Now lock all media. If this fails, nothing is locked. */
10613 alock.release();
10614 rc = lockedMediaMap->Lock();
10615 alock.acquire();
10616 if (FAILED(rc))
10617 {
10618 throw setError(rc,
10619 tr("Locking of attached media failed"));
10620 }
10621 }
10622
10623 /* remember the current list (note that we don't use backup() since
10624 * mMediaData may be already backed up) */
10625 MediaData::AttachmentList atts = mMediaData->mAttachments;
10626
10627 /* start from scratch */
10628 mMediaData->mAttachments.clear();
10629
10630 /* go through remembered attachments and create diffs for normal hard
10631 * disks and attach them */
10632 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10633 it != atts.end();
10634 ++it)
10635 {
10636 MediumAttachment* pAtt = *it;
10637
10638 DeviceType_T devType = pAtt->getType();
10639 Medium* pMedium = pAtt->getMedium();
10640
10641 if ( devType != DeviceType_HardDisk
10642 || pMedium == NULL
10643 || pMedium->getType() != MediumType_Normal)
10644 {
10645 /* copy the attachment as is */
10646
10647 /** @todo the progress object created in Console::TakeSnaphot
10648 * only expects operations for hard disks. Later other
10649 * device types need to show up in the progress as well. */
10650 if (devType == DeviceType_HardDisk)
10651 {
10652 if (pMedium == NULL)
10653 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10654 aWeight); // weight
10655 else
10656 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10657 pMedium->getBase()->getName().c_str()).raw(),
10658 aWeight); // weight
10659 }
10660
10661 mMediaData->mAttachments.push_back(pAtt);
10662 continue;
10663 }
10664
10665 /* need a diff */
10666 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10667 pMedium->getBase()->getName().c_str()).raw(),
10668 aWeight); // weight
10669
10670 Utf8Str strFullSnapshotFolder;
10671 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10672
10673 ComObjPtr<Medium> diff;
10674 diff.createObject();
10675 // store the diff in the same registry as the parent
10676 // (this cannot fail here because we can't create implicit diffs for
10677 // unregistered images)
10678 Guid uuidRegistryParent;
10679 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10680 Assert(fInRegistry); NOREF(fInRegistry);
10681 rc = diff->init(mParent,
10682 pMedium->getPreferredDiffFormat(),
10683 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10684 uuidRegistryParent);
10685 if (FAILED(rc)) throw rc;
10686
10687 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10688 * the push_back? Looks like we're going to release medium with the
10689 * wrong kind of lock (general issue with if we fail anywhere at all)
10690 * and an orphaned VDI in the snapshots folder. */
10691
10692 /* update the appropriate lock list */
10693 MediumLockList *pMediumLockList;
10694 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10695 AssertComRCThrowRC(rc);
10696 if (aOnline)
10697 {
10698 alock.release();
10699 /* The currently attached medium will be read-only, change
10700 * the lock type to read. */
10701 rc = pMediumLockList->Update(pMedium, false);
10702 alock.acquire();
10703 AssertComRCThrowRC(rc);
10704 }
10705
10706 /* release the locks before the potentially lengthy operation */
10707 alock.release();
10708 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10709 pMediumLockList,
10710 NULL /* aProgress */,
10711 true /* aWait */);
10712 alock.acquire();
10713 if (FAILED(rc)) throw rc;
10714
10715 /* actual lock list update is done in Medium::commitMedia */
10716
10717 rc = diff->addBackReference(mData->mUuid);
10718 AssertComRCThrowRC(rc);
10719
10720 /* add a new attachment */
10721 ComObjPtr<MediumAttachment> attachment;
10722 attachment.createObject();
10723 rc = attachment->init(this,
10724 diff,
10725 pAtt->getControllerName(),
10726 pAtt->getPort(),
10727 pAtt->getDevice(),
10728 DeviceType_HardDisk,
10729 true /* aImplicit */,
10730 false /* aPassthrough */,
10731 false /* aTempEject */,
10732 pAtt->getNonRotational(),
10733 pAtt->getDiscard(),
10734 pAtt->getBandwidthGroup());
10735 if (FAILED(rc)) throw rc;
10736
10737 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10738 AssertComRCThrowRC(rc);
10739 mMediaData->mAttachments.push_back(attachment);
10740 }
10741 }
10742 catch (HRESULT aRC) { rc = aRC; }
10743
10744 /* unlock all hard disks we locked when there is no VM */
10745 if (!aOnline)
10746 {
10747 ErrorInfoKeeper eik;
10748
10749 HRESULT rc1 = lockedMediaMap->Clear();
10750 AssertComRC(rc1);
10751 }
10752
10753 return rc;
10754}
10755
10756/**
10757 * Deletes implicit differencing hard disks created either by
10758 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10759 *
10760 * Note that to delete hard disks created by #AttachDevice() this method is
10761 * called from #fixupMedia() when the changes are rolled back.
10762 *
10763 * @note Locks this object and the media tree for writing.
10764 */
10765HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10766{
10767 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10768
10769 AutoCaller autoCaller(this);
10770 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10771
10772 AutoMultiWriteLock2 alock(this->lockHandle(),
10773 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10774
10775 /* We absolutely must have backed up state. */
10776 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10777
10778 /* Check if there are any implicitly created diff images. */
10779 bool fImplicitDiffs = false;
10780 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10781 it != mMediaData->mAttachments.end();
10782 ++it)
10783 {
10784 const ComObjPtr<MediumAttachment> &pAtt = *it;
10785 if (pAtt->isImplicit())
10786 {
10787 fImplicitDiffs = true;
10788 break;
10789 }
10790 }
10791 /* If there is nothing to do, leave early. This saves lots of image locking
10792 * effort. It also avoids a MachineStateChanged event without real reason.
10793 * This is important e.g. when loading a VM config, because there should be
10794 * no events. Otherwise API clients can become thoroughly confused for
10795 * inaccessible VMs (the code for loading VM configs uses this method for
10796 * cleanup if the config makes no sense), as they take such events as an
10797 * indication that the VM is alive, and they would force the VM config to
10798 * be reread, leading to an endless loop. */
10799 if (!fImplicitDiffs)
10800 return S_OK;
10801
10802 HRESULT rc = S_OK;
10803 MachineState_T oldState = mData->mMachineState;
10804
10805 /* will release the lock before the potentially lengthy operation,
10806 * so protect with the special state (unless already protected) */
10807 if ( oldState != MachineState_Saving
10808 && oldState != MachineState_LiveSnapshotting
10809 && oldState != MachineState_RestoringSnapshot
10810 && oldState != MachineState_DeletingSnapshot
10811 && oldState != MachineState_DeletingSnapshotOnline
10812 && oldState != MachineState_DeletingSnapshotPaused
10813 )
10814 setMachineState(MachineState_SettingUp);
10815
10816 // use appropriate locked media map (online or offline)
10817 MediumLockListMap lockedMediaOffline;
10818 MediumLockListMap *lockedMediaMap;
10819 if (aOnline)
10820 lockedMediaMap = &mData->mSession.mLockedMedia;
10821 else
10822 lockedMediaMap = &lockedMediaOffline;
10823
10824 try
10825 {
10826 if (!aOnline)
10827 {
10828 /* lock all attached hard disks early to detect "in use"
10829 * situations before deleting actual diffs */
10830 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10831 it != mMediaData->mAttachments.end();
10832 ++it)
10833 {
10834 MediumAttachment* pAtt = *it;
10835 if (pAtt->getType() == DeviceType_HardDisk)
10836 {
10837 Medium* pMedium = pAtt->getMedium();
10838 Assert(pMedium);
10839
10840 MediumLockList *pMediumLockList(new MediumLockList());
10841 alock.release();
10842 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10843 false /* fMediumLockWrite */,
10844 NULL,
10845 *pMediumLockList);
10846 alock.acquire();
10847
10848 if (FAILED(rc))
10849 {
10850 delete pMediumLockList;
10851 throw rc;
10852 }
10853
10854 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10855 if (FAILED(rc))
10856 throw rc;
10857 }
10858 }
10859
10860 if (FAILED(rc))
10861 throw rc;
10862 } // end of offline
10863
10864 /* Lock lists are now up to date and include implicitly created media */
10865
10866 /* Go through remembered attachments and delete all implicitly created
10867 * diffs and fix up the attachment information */
10868 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10869 MediaData::AttachmentList implicitAtts;
10870 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10871 it != mMediaData->mAttachments.end();
10872 ++it)
10873 {
10874 ComObjPtr<MediumAttachment> pAtt = *it;
10875 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10876 if (pMedium.isNull())
10877 continue;
10878
10879 // Implicit attachments go on the list for deletion and back references are removed.
10880 if (pAtt->isImplicit())
10881 {
10882 /* Deassociate and mark for deletion */
10883 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10884 rc = pMedium->removeBackReference(mData->mUuid);
10885 if (FAILED(rc))
10886 throw rc;
10887 implicitAtts.push_back(pAtt);
10888 continue;
10889 }
10890
10891 /* Was this medium attached before? */
10892 if (!findAttachment(oldAtts, pMedium))
10893 {
10894 /* no: de-associate */
10895 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10896 rc = pMedium->removeBackReference(mData->mUuid);
10897 if (FAILED(rc))
10898 throw rc;
10899 continue;
10900 }
10901 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10902 }
10903
10904 /* If there are implicit attachments to delete, throw away the lock
10905 * map contents (which will unlock all media) since the medium
10906 * attachments will be rolled back. Below we need to completely
10907 * recreate the lock map anyway since it is infinitely complex to
10908 * do this incrementally (would need reconstructing each attachment
10909 * change, which would be extremely hairy). */
10910 if (implicitAtts.size() != 0)
10911 {
10912 ErrorInfoKeeper eik;
10913
10914 HRESULT rc1 = lockedMediaMap->Clear();
10915 AssertComRC(rc1);
10916 }
10917
10918 /* rollback hard disk changes */
10919 mMediaData.rollback();
10920
10921 MultiResult mrc(S_OK);
10922
10923 // Delete unused implicit diffs.
10924 if (implicitAtts.size() != 0)
10925 {
10926 alock.release();
10927
10928 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10929 it != implicitAtts.end();
10930 ++it)
10931 {
10932 // Remove medium associated with this attachment.
10933 ComObjPtr<MediumAttachment> pAtt = *it;
10934 Assert(pAtt);
10935 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10936 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10937 Assert(pMedium);
10938
10939 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10940 // continue on delete failure, just collect error messages
10941 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10942 mrc = rc;
10943 }
10944
10945 alock.acquire();
10946
10947 /* if there is a VM recreate media lock map as mentioned above,
10948 * otherwise it is a waste of time and we leave things unlocked */
10949 if (aOnline)
10950 {
10951 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10952 /* must never be NULL, but better safe than sorry */
10953 if (!pMachine.isNull())
10954 {
10955 alock.release();
10956 rc = mData->mSession.mMachine->lockMedia();
10957 alock.acquire();
10958 if (FAILED(rc))
10959 throw rc;
10960 }
10961 }
10962 }
10963 }
10964 catch (HRESULT aRC) {rc = aRC;}
10965
10966 if (mData->mMachineState == MachineState_SettingUp)
10967 setMachineState(oldState);
10968
10969 /* unlock all hard disks we locked when there is no VM */
10970 if (!aOnline)
10971 {
10972 ErrorInfoKeeper eik;
10973
10974 HRESULT rc1 = lockedMediaMap->Clear();
10975 AssertComRC(rc1);
10976 }
10977
10978 return rc;
10979}
10980
10981
10982/**
10983 * Looks through the given list of media attachments for one with the given parameters
10984 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10985 * can be searched as well if needed.
10986 *
10987 * @param list
10988 * @param aControllerName
10989 * @param aControllerPort
10990 * @param aDevice
10991 * @return
10992 */
10993MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10994 IN_BSTR aControllerName,
10995 LONG aControllerPort,
10996 LONG aDevice)
10997{
10998 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10999 it != ll.end();
11000 ++it)
11001 {
11002 MediumAttachment *pAttach = *it;
11003 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11004 return pAttach;
11005 }
11006
11007 return NULL;
11008}
11009
11010/**
11011 * Looks through the given list of media attachments for one with the given parameters
11012 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11013 * can be searched as well if needed.
11014 *
11015 * @param list
11016 * @param aControllerName
11017 * @param aControllerPort
11018 * @param aDevice
11019 * @return
11020 */
11021MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11022 ComObjPtr<Medium> pMedium)
11023{
11024 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11025 it != ll.end();
11026 ++it)
11027 {
11028 MediumAttachment *pAttach = *it;
11029 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11030 if (pMediumThis == pMedium)
11031 return pAttach;
11032 }
11033
11034 return NULL;
11035}
11036
11037/**
11038 * Looks through the given list of media attachments for one with the given parameters
11039 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11040 * can be searched as well if needed.
11041 *
11042 * @param list
11043 * @param aControllerName
11044 * @param aControllerPort
11045 * @param aDevice
11046 * @return
11047 */
11048MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11049 Guid &id)
11050{
11051 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11052 it != ll.end();
11053 ++it)
11054 {
11055 MediumAttachment *pAttach = *it;
11056 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11057 if (pMediumThis->getId() == id)
11058 return pAttach;
11059 }
11060
11061 return NULL;
11062}
11063
11064/**
11065 * Main implementation for Machine::DetachDevice. This also gets called
11066 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11067 *
11068 * @param pAttach Medium attachment to detach.
11069 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11070 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11071 * @return
11072 */
11073HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11074 AutoWriteLock &writeLock,
11075 Snapshot *pSnapshot)
11076{
11077 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11078 DeviceType_T mediumType = pAttach->getType();
11079
11080 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11081
11082 if (pAttach->isImplicit())
11083 {
11084 /* attempt to implicitly delete the implicitly created diff */
11085
11086 /// @todo move the implicit flag from MediumAttachment to Medium
11087 /// and forbid any hard disk operation when it is implicit. Or maybe
11088 /// a special media state for it to make it even more simple.
11089
11090 Assert(mMediaData.isBackedUp());
11091
11092 /* will release the lock before the potentially lengthy operation, so
11093 * protect with the special state */
11094 MachineState_T oldState = mData->mMachineState;
11095 setMachineState(MachineState_SettingUp);
11096
11097 writeLock.release();
11098
11099 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11100 true /*aWait*/);
11101
11102 writeLock.acquire();
11103
11104 setMachineState(oldState);
11105
11106 if (FAILED(rc)) return rc;
11107 }
11108
11109 setModified(IsModified_Storage);
11110 mMediaData.backup();
11111 mMediaData->mAttachments.remove(pAttach);
11112
11113 if (!oldmedium.isNull())
11114 {
11115 // if this is from a snapshot, do not defer detachment to commitMedia()
11116 if (pSnapshot)
11117 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11118 // else if non-hard disk media, do not defer detachment to commitMedia() either
11119 else if (mediumType != DeviceType_HardDisk)
11120 oldmedium->removeBackReference(mData->mUuid);
11121 }
11122
11123 return S_OK;
11124}
11125
11126/**
11127 * Goes thru all media of the given list and
11128 *
11129 * 1) calls detachDevice() on each of them for this machine and
11130 * 2) adds all Medium objects found in the process to the given list,
11131 * depending on cleanupMode.
11132 *
11133 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11134 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11135 * media to the list.
11136 *
11137 * This gets called from Machine::Unregister, both for the actual Machine and
11138 * the SnapshotMachine objects that might be found in the snapshots.
11139 *
11140 * Requires caller and locking. The machine lock must be passed in because it
11141 * will be passed on to detachDevice which needs it for temporary unlocking.
11142 *
11143 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11144 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11145 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11146 * otherwise no media get added.
11147 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11148 * @return
11149 */
11150HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11151 Snapshot *pSnapshot,
11152 CleanupMode_T cleanupMode,
11153 MediaList &llMedia)
11154{
11155 Assert(isWriteLockOnCurrentThread());
11156
11157 HRESULT rc;
11158
11159 // make a temporary list because detachDevice invalidates iterators into
11160 // mMediaData->mAttachments
11161 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11162
11163 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11164 it != llAttachments2.end();
11165 ++it)
11166 {
11167 ComObjPtr<MediumAttachment> &pAttach = *it;
11168 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11169
11170 if (!pMedium.isNull())
11171 {
11172 AutoCaller mac(pMedium);
11173 if (FAILED(mac.rc())) return mac.rc();
11174 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11175 DeviceType_T devType = pMedium->getDeviceType();
11176 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11177 && devType == DeviceType_HardDisk)
11178 || (cleanupMode == CleanupMode_Full)
11179 )
11180 {
11181 llMedia.push_back(pMedium);
11182 ComObjPtr<Medium> pParent = pMedium->getParent();
11183 /*
11184 * Search for medias which are not attached to any machine, but
11185 * in the chain to an attached disk. Mediums are only consided
11186 * if they are:
11187 * - have only one child
11188 * - no references to any machines
11189 * - are of normal medium type
11190 */
11191 while (!pParent.isNull())
11192 {
11193 AutoCaller mac1(pParent);
11194 if (FAILED(mac1.rc())) return mac1.rc();
11195 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11196 if (pParent->getChildren().size() == 1)
11197 {
11198 if ( pParent->getMachineBackRefCount() == 0
11199 && pParent->getType() == MediumType_Normal
11200 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11201 llMedia.push_back(pParent);
11202 }
11203 else
11204 break;
11205 pParent = pParent->getParent();
11206 }
11207 }
11208 }
11209
11210 // real machine: then we need to use the proper method
11211 rc = detachDevice(pAttach, writeLock, pSnapshot);
11212
11213 if (FAILED(rc))
11214 return rc;
11215 }
11216
11217 return S_OK;
11218}
11219
11220/**
11221 * Perform deferred hard disk detachments.
11222 *
11223 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11224 * backed up).
11225 *
11226 * If @a aOnline is @c true then this method will also unlock the old hard disks
11227 * for which the new implicit diffs were created and will lock these new diffs for
11228 * writing.
11229 *
11230 * @param aOnline Whether the VM was online prior to this operation.
11231 *
11232 * @note Locks this object for writing!
11233 */
11234void Machine::commitMedia(bool aOnline /*= false*/)
11235{
11236 AutoCaller autoCaller(this);
11237 AssertComRCReturnVoid(autoCaller.rc());
11238
11239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11240
11241 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11242
11243 HRESULT rc = S_OK;
11244
11245 /* no attach/detach operations -- nothing to do */
11246 if (!mMediaData.isBackedUp())
11247 return;
11248
11249 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11250 bool fMediaNeedsLocking = false;
11251
11252 /* enumerate new attachments */
11253 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11254 it != mMediaData->mAttachments.end();
11255 ++it)
11256 {
11257 MediumAttachment *pAttach = *it;
11258
11259 pAttach->commit();
11260
11261 Medium* pMedium = pAttach->getMedium();
11262 bool fImplicit = pAttach->isImplicit();
11263
11264 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11265 (pMedium) ? pMedium->getName().c_str() : "NULL",
11266 fImplicit));
11267
11268 /** @todo convert all this Machine-based voodoo to MediumAttachment
11269 * based commit logic. */
11270 if (fImplicit)
11271 {
11272 /* convert implicit attachment to normal */
11273 pAttach->setImplicit(false);
11274
11275 if ( aOnline
11276 && pMedium
11277 && pAttach->getType() == DeviceType_HardDisk
11278 )
11279 {
11280 ComObjPtr<Medium> parent = pMedium->getParent();
11281 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11282
11283 /* update the appropriate lock list */
11284 MediumLockList *pMediumLockList;
11285 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11286 AssertComRC(rc);
11287 if (pMediumLockList)
11288 {
11289 /* unlock if there's a need to change the locking */
11290 if (!fMediaNeedsLocking)
11291 {
11292 rc = mData->mSession.mLockedMedia.Unlock();
11293 AssertComRC(rc);
11294 fMediaNeedsLocking = true;
11295 }
11296 rc = pMediumLockList->Update(parent, false);
11297 AssertComRC(rc);
11298 rc = pMediumLockList->Append(pMedium, true);
11299 AssertComRC(rc);
11300 }
11301 }
11302
11303 continue;
11304 }
11305
11306 if (pMedium)
11307 {
11308 /* was this medium attached before? */
11309 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11310 oldIt != oldAtts.end();
11311 ++oldIt)
11312 {
11313 MediumAttachment *pOldAttach = *oldIt;
11314 if (pOldAttach->getMedium() == pMedium)
11315 {
11316 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11317
11318 /* yes: remove from old to avoid de-association */
11319 oldAtts.erase(oldIt);
11320 break;
11321 }
11322 }
11323 }
11324 }
11325
11326 /* enumerate remaining old attachments and de-associate from the
11327 * current machine state */
11328 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11329 it != oldAtts.end();
11330 ++it)
11331 {
11332 MediumAttachment *pAttach = *it;
11333 Medium* pMedium = pAttach->getMedium();
11334
11335 /* Detach only hard disks, since DVD/floppy media is detached
11336 * instantly in MountMedium. */
11337 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11338 {
11339 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11340
11341 /* now de-associate from the current machine state */
11342 rc = pMedium->removeBackReference(mData->mUuid);
11343 AssertComRC(rc);
11344
11345 if (aOnline)
11346 {
11347 /* unlock since medium is not used anymore */
11348 MediumLockList *pMediumLockList;
11349 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11350 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11351 {
11352 /* this happens for online snapshots, there the attachment
11353 * is changing, but only to a diff image created under
11354 * the old one, so there is no separate lock list */
11355 Assert(!pMediumLockList);
11356 }
11357 else
11358 {
11359 AssertComRC(rc);
11360 if (pMediumLockList)
11361 {
11362 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11363 AssertComRC(rc);
11364 }
11365 }
11366 }
11367 }
11368 }
11369
11370 /* take media locks again so that the locking state is consistent */
11371 if (fMediaNeedsLocking)
11372 {
11373 Assert(aOnline);
11374 rc = mData->mSession.mLockedMedia.Lock();
11375 AssertComRC(rc);
11376 }
11377
11378 /* commit the hard disk changes */
11379 mMediaData.commit();
11380
11381 if (isSessionMachine())
11382 {
11383 /*
11384 * Update the parent machine to point to the new owner.
11385 * This is necessary because the stored parent will point to the
11386 * session machine otherwise and cause crashes or errors later
11387 * when the session machine gets invalid.
11388 */
11389 /** @todo Change the MediumAttachment class to behave like any other
11390 * class in this regard by creating peer MediumAttachment
11391 * objects for session machines and share the data with the peer
11392 * machine.
11393 */
11394 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11395 it != mMediaData->mAttachments.end();
11396 ++it)
11397 {
11398 (*it)->updateParentMachine(mPeer);
11399 }
11400
11401 /* attach new data to the primary machine and reshare it */
11402 mPeer->mMediaData.attach(mMediaData);
11403 }
11404
11405 return;
11406}
11407
11408/**
11409 * Perform deferred deletion of implicitly created diffs.
11410 *
11411 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11412 * backed up).
11413 *
11414 * @note Locks this object for writing!
11415 */
11416void Machine::rollbackMedia()
11417{
11418 AutoCaller autoCaller(this);
11419 AssertComRCReturnVoid(autoCaller.rc());
11420
11421 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11422 LogFlowThisFunc(("Entering rollbackMedia\n"));
11423
11424 HRESULT rc = S_OK;
11425
11426 /* no attach/detach operations -- nothing to do */
11427 if (!mMediaData.isBackedUp())
11428 return;
11429
11430 /* enumerate new attachments */
11431 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11432 it != mMediaData->mAttachments.end();
11433 ++it)
11434 {
11435 MediumAttachment *pAttach = *it;
11436 /* Fix up the backrefs for DVD/floppy media. */
11437 if (pAttach->getType() != DeviceType_HardDisk)
11438 {
11439 Medium* pMedium = pAttach->getMedium();
11440 if (pMedium)
11441 {
11442 rc = pMedium->removeBackReference(mData->mUuid);
11443 AssertComRC(rc);
11444 }
11445 }
11446
11447 (*it)->rollback();
11448
11449 pAttach = *it;
11450 /* Fix up the backrefs for DVD/floppy media. */
11451 if (pAttach->getType() != DeviceType_HardDisk)
11452 {
11453 Medium* pMedium = pAttach->getMedium();
11454 if (pMedium)
11455 {
11456 rc = pMedium->addBackReference(mData->mUuid);
11457 AssertComRC(rc);
11458 }
11459 }
11460 }
11461
11462 /** @todo convert all this Machine-based voodoo to MediumAttachment
11463 * based rollback logic. */
11464 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11465
11466 return;
11467}
11468
11469/**
11470 * Returns true if the settings file is located in the directory named exactly
11471 * as the machine; this means, among other things, that the machine directory
11472 * should be auto-renamed.
11473 *
11474 * @param aSettingsDir if not NULL, the full machine settings file directory
11475 * name will be assigned there.
11476 *
11477 * @note Doesn't lock anything.
11478 * @note Not thread safe (must be called from this object's lock).
11479 */
11480bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11481{
11482 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11483 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11484 if (aSettingsDir)
11485 *aSettingsDir = strMachineDirName;
11486 strMachineDirName.stripPath(); // vmname
11487 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11488 strConfigFileOnly.stripPath() // vmname.vbox
11489 .stripExt(); // vmname
11490 /** @todo hack, make somehow use of ComposeMachineFilename */
11491 if (mUserData->s.fDirectoryIncludesUUID)
11492 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11493
11494 AssertReturn(!strMachineDirName.isEmpty(), false);
11495 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11496
11497 return strMachineDirName == strConfigFileOnly;
11498}
11499
11500/**
11501 * Discards all changes to machine settings.
11502 *
11503 * @param aNotify Whether to notify the direct session about changes or not.
11504 *
11505 * @note Locks objects for writing!
11506 */
11507void Machine::rollback(bool aNotify)
11508{
11509 AutoCaller autoCaller(this);
11510 AssertComRCReturn(autoCaller.rc(), (void)0);
11511
11512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11513
11514 if (!mStorageControllers.isNull())
11515 {
11516 if (mStorageControllers.isBackedUp())
11517 {
11518 /* unitialize all new devices (absent in the backed up list). */
11519 StorageControllerList::const_iterator it = mStorageControllers->begin();
11520 StorageControllerList *backedList = mStorageControllers.backedUpData();
11521 while (it != mStorageControllers->end())
11522 {
11523 if ( std::find(backedList->begin(), backedList->end(), *it)
11524 == backedList->end()
11525 )
11526 {
11527 (*it)->uninit();
11528 }
11529 ++it;
11530 }
11531
11532 /* restore the list */
11533 mStorageControllers.rollback();
11534 }
11535
11536 /* rollback any changes to devices after restoring the list */
11537 if (mData->flModifications & IsModified_Storage)
11538 {
11539 StorageControllerList::const_iterator it = mStorageControllers->begin();
11540 while (it != mStorageControllers->end())
11541 {
11542 (*it)->rollback();
11543 ++it;
11544 }
11545 }
11546 }
11547
11548 mUserData.rollback();
11549
11550 mHWData.rollback();
11551
11552 if (mData->flModifications & IsModified_Storage)
11553 rollbackMedia();
11554
11555 if (mBIOSSettings)
11556 mBIOSSettings->rollback();
11557
11558 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11559 mVRDEServer->rollback();
11560
11561 if (mAudioAdapter)
11562 mAudioAdapter->rollback();
11563
11564 if (mUSBController && (mData->flModifications & IsModified_USB))
11565 mUSBController->rollback();
11566
11567 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11568 mBandwidthControl->rollback();
11569
11570 if (!mHWData.isNull())
11571 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11572 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11573 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11574 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11575
11576 if (mData->flModifications & IsModified_NetworkAdapters)
11577 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11578 if ( mNetworkAdapters[slot]
11579 && mNetworkAdapters[slot]->isModified())
11580 {
11581 mNetworkAdapters[slot]->rollback();
11582 networkAdapters[slot] = mNetworkAdapters[slot];
11583 }
11584
11585 if (mData->flModifications & IsModified_SerialPorts)
11586 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11587 if ( mSerialPorts[slot]
11588 && mSerialPorts[slot]->isModified())
11589 {
11590 mSerialPorts[slot]->rollback();
11591 serialPorts[slot] = mSerialPorts[slot];
11592 }
11593
11594 if (mData->flModifications & IsModified_ParallelPorts)
11595 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11596 if ( mParallelPorts[slot]
11597 && mParallelPorts[slot]->isModified())
11598 {
11599 mParallelPorts[slot]->rollback();
11600 parallelPorts[slot] = mParallelPorts[slot];
11601 }
11602
11603 if (aNotify)
11604 {
11605 /* inform the direct session about changes */
11606
11607 ComObjPtr<Machine> that = this;
11608 uint32_t flModifications = mData->flModifications;
11609 alock.release();
11610
11611 if (flModifications & IsModified_SharedFolders)
11612 that->onSharedFolderChange();
11613
11614 if (flModifications & IsModified_VRDEServer)
11615 that->onVRDEServerChange(/* aRestart */ TRUE);
11616 if (flModifications & IsModified_USB)
11617 that->onUSBControllerChange();
11618
11619 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11620 if (networkAdapters[slot])
11621 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11622 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11623 if (serialPorts[slot])
11624 that->onSerialPortChange(serialPorts[slot]);
11625 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11626 if (parallelPorts[slot])
11627 that->onParallelPortChange(parallelPorts[slot]);
11628
11629 if (flModifications & IsModified_Storage)
11630 that->onStorageControllerChange();
11631
11632#if 0
11633 if (flModifications & IsModified_BandwidthControl)
11634 that->onBandwidthControlChange();
11635#endif
11636 }
11637}
11638
11639/**
11640 * Commits all the changes to machine settings.
11641 *
11642 * Note that this operation is supposed to never fail.
11643 *
11644 * @note Locks this object and children for writing.
11645 */
11646void Machine::commit()
11647{
11648 AutoCaller autoCaller(this);
11649 AssertComRCReturnVoid(autoCaller.rc());
11650
11651 AutoCaller peerCaller(mPeer);
11652 AssertComRCReturnVoid(peerCaller.rc());
11653
11654 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11655
11656 /*
11657 * use safe commit to ensure Snapshot machines (that share mUserData)
11658 * will still refer to a valid memory location
11659 */
11660 mUserData.commitCopy();
11661
11662 mHWData.commit();
11663
11664 if (mMediaData.isBackedUp())
11665 commitMedia(Global::IsOnline(mData->mMachineState));
11666
11667 mBIOSSettings->commit();
11668 mVRDEServer->commit();
11669 mAudioAdapter->commit();
11670 mUSBController->commit();
11671 mBandwidthControl->commit();
11672
11673 /* Since mNetworkAdapters is a list which might have been changed (resized)
11674 * without using the Backupable<> template we need to handle the copying
11675 * of the list entries manually, including the creation of peers for the
11676 * new objects. */
11677 bool commitNetworkAdapters = false;
11678 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11679 if (mPeer)
11680 {
11681 /* commit everything, even the ones which will go away */
11682 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11683 mNetworkAdapters[slot]->commit();
11684 /* copy over the new entries, creating a peer and uninit the original */
11685 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11686 for (size_t slot = 0; slot < newSize; slot++)
11687 {
11688 /* look if this adapter has a peer device */
11689 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11690 if (!peer)
11691 {
11692 /* no peer means the adapter is a newly created one;
11693 * create a peer owning data this data share it with */
11694 peer.createObject();
11695 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11696 }
11697 mPeer->mNetworkAdapters[slot] = peer;
11698 }
11699 /* uninit any no longer needed network adapters */
11700 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11701 mNetworkAdapters[slot]->uninit();
11702 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11703 {
11704 if (mPeer->mNetworkAdapters[slot])
11705 mPeer->mNetworkAdapters[slot]->uninit();
11706 }
11707 /* Keep the original network adapter count until this point, so that
11708 * discarding a chipset type change will not lose settings. */
11709 mNetworkAdapters.resize(newSize);
11710 mPeer->mNetworkAdapters.resize(newSize);
11711 }
11712 else
11713 {
11714 /* we have no peer (our parent is the newly created machine);
11715 * just commit changes to the network adapters */
11716 commitNetworkAdapters = true;
11717 }
11718 if (commitNetworkAdapters)
11719 {
11720 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11721 mNetworkAdapters[slot]->commit();
11722 }
11723
11724 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11725 mSerialPorts[slot]->commit();
11726 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11727 mParallelPorts[slot]->commit();
11728
11729 bool commitStorageControllers = false;
11730
11731 if (mStorageControllers.isBackedUp())
11732 {
11733 mStorageControllers.commit();
11734
11735 if (mPeer)
11736 {
11737 /* Commit all changes to new controllers (this will reshare data with
11738 * peers for those who have peers) */
11739 StorageControllerList *newList = new StorageControllerList();
11740 StorageControllerList::const_iterator it = mStorageControllers->begin();
11741 while (it != mStorageControllers->end())
11742 {
11743 (*it)->commit();
11744
11745 /* look if this controller has a peer device */
11746 ComObjPtr<StorageController> peer = (*it)->getPeer();
11747 if (!peer)
11748 {
11749 /* no peer means the device is a newly created one;
11750 * create a peer owning data this device share it with */
11751 peer.createObject();
11752 peer->init(mPeer, *it, true /* aReshare */);
11753 }
11754 else
11755 {
11756 /* remove peer from the old list */
11757 mPeer->mStorageControllers->remove(peer);
11758 }
11759 /* and add it to the new list */
11760 newList->push_back(peer);
11761
11762 ++it;
11763 }
11764
11765 /* uninit old peer's controllers that are left */
11766 it = mPeer->mStorageControllers->begin();
11767 while (it != mPeer->mStorageControllers->end())
11768 {
11769 (*it)->uninit();
11770 ++it;
11771 }
11772
11773 /* attach new list of controllers to our peer */
11774 mPeer->mStorageControllers.attach(newList);
11775 }
11776 else
11777 {
11778 /* we have no peer (our parent is the newly created machine);
11779 * just commit changes to devices */
11780 commitStorageControllers = true;
11781 }
11782 }
11783 else
11784 {
11785 /* the list of controllers itself is not changed,
11786 * just commit changes to controllers themselves */
11787 commitStorageControllers = true;
11788 }
11789
11790 if (commitStorageControllers)
11791 {
11792 StorageControllerList::const_iterator it = mStorageControllers->begin();
11793 while (it != mStorageControllers->end())
11794 {
11795 (*it)->commit();
11796 ++it;
11797 }
11798 }
11799
11800 if (isSessionMachine())
11801 {
11802 /* attach new data to the primary machine and reshare it */
11803 mPeer->mUserData.attach(mUserData);
11804 mPeer->mHWData.attach(mHWData);
11805 /* mMediaData is reshared by fixupMedia */
11806 // mPeer->mMediaData.attach(mMediaData);
11807 Assert(mPeer->mMediaData.data() == mMediaData.data());
11808 }
11809}
11810
11811/**
11812 * Copies all the hardware data from the given machine.
11813 *
11814 * Currently, only called when the VM is being restored from a snapshot. In
11815 * particular, this implies that the VM is not running during this method's
11816 * call.
11817 *
11818 * @note This method must be called from under this object's lock.
11819 *
11820 * @note This method doesn't call #commit(), so all data remains backed up and
11821 * unsaved.
11822 */
11823void Machine::copyFrom(Machine *aThat)
11824{
11825 AssertReturnVoid(!isSnapshotMachine());
11826 AssertReturnVoid(aThat->isSnapshotMachine());
11827
11828 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11829
11830 mHWData.assignCopy(aThat->mHWData);
11831
11832 // create copies of all shared folders (mHWData after attaching a copy
11833 // contains just references to original objects)
11834 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11835 it != mHWData->mSharedFolders.end();
11836 ++it)
11837 {
11838 ComObjPtr<SharedFolder> folder;
11839 folder.createObject();
11840 HRESULT rc = folder->initCopy(getMachine(), *it);
11841 AssertComRC(rc);
11842 *it = folder;
11843 }
11844
11845 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11846 mVRDEServer->copyFrom(aThat->mVRDEServer);
11847 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11848 mUSBController->copyFrom(aThat->mUSBController);
11849 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11850
11851 /* create private copies of all controllers */
11852 mStorageControllers.backup();
11853 mStorageControllers->clear();
11854 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11855 it != aThat->mStorageControllers->end();
11856 ++it)
11857 {
11858 ComObjPtr<StorageController> ctrl;
11859 ctrl.createObject();
11860 ctrl->initCopy(this, *it);
11861 mStorageControllers->push_back(ctrl);
11862 }
11863
11864 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11865 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11866 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11867 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11868 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11869 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11870 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11871}
11872
11873/**
11874 * Returns whether the given storage controller is hotplug capable.
11875 *
11876 * @returns true if the controller supports hotplugging
11877 * false otherwise.
11878 * @param enmCtrlType The controller type to check for.
11879 */
11880bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11881{
11882 switch (enmCtrlType)
11883 {
11884 case StorageControllerType_IntelAhci:
11885 return true;
11886 case StorageControllerType_LsiLogic:
11887 case StorageControllerType_LsiLogicSas:
11888 case StorageControllerType_BusLogic:
11889 case StorageControllerType_PIIX3:
11890 case StorageControllerType_PIIX4:
11891 case StorageControllerType_ICH6:
11892 case StorageControllerType_I82078:
11893 default:
11894 return false;
11895 }
11896}
11897
11898#ifdef VBOX_WITH_RESOURCE_USAGE_API
11899
11900void Machine::getDiskList(MediaList &list)
11901{
11902 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11903 it != mMediaData->mAttachments.end();
11904 ++it)
11905 {
11906 MediumAttachment* pAttach = *it;
11907 /* just in case */
11908 AssertStmt(pAttach, continue);
11909
11910 AutoCaller localAutoCallerA(pAttach);
11911 if (FAILED(localAutoCallerA.rc())) continue;
11912
11913 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11914
11915 if (pAttach->getType() == DeviceType_HardDisk)
11916 list.push_back(pAttach->getMedium());
11917 }
11918}
11919
11920void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11921{
11922 AssertReturnVoid(isWriteLockOnCurrentThread());
11923 AssertPtrReturnVoid(aCollector);
11924
11925 pm::CollectorHAL *hal = aCollector->getHAL();
11926 /* Create sub metrics */
11927 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11928 "Percentage of processor time spent in user mode by the VM process.");
11929 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11930 "Percentage of processor time spent in kernel mode by the VM process.");
11931 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11932 "Size of resident portion of VM process in memory.");
11933 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11934 "Actual size of all VM disks combined.");
11935 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11936 "Network receive rate.");
11937 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11938 "Network transmit rate.");
11939 /* Create and register base metrics */
11940 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11941 cpuLoadUser, cpuLoadKernel);
11942 aCollector->registerBaseMetric(cpuLoad);
11943 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11944 ramUsageUsed);
11945 aCollector->registerBaseMetric(ramUsage);
11946 MediaList disks;
11947 getDiskList(disks);
11948 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11949 diskUsageUsed);
11950 aCollector->registerBaseMetric(diskUsage);
11951
11952 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11953 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11954 new pm::AggregateAvg()));
11955 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11956 new pm::AggregateMin()));
11957 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11958 new pm::AggregateMax()));
11959 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11960 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11961 new pm::AggregateAvg()));
11962 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11963 new pm::AggregateMin()));
11964 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11965 new pm::AggregateMax()));
11966
11967 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11968 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11969 new pm::AggregateAvg()));
11970 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11971 new pm::AggregateMin()));
11972 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11973 new pm::AggregateMax()));
11974
11975 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11976 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11977 new pm::AggregateAvg()));
11978 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11979 new pm::AggregateMin()));
11980 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11981 new pm::AggregateMax()));
11982
11983
11984 /* Guest metrics collector */
11985 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11986 aCollector->registerGuest(mCollectorGuest);
11987 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11988 this, __PRETTY_FUNCTION__, mCollectorGuest));
11989
11990 /* Create sub metrics */
11991 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11992 "Percentage of processor time spent in user mode as seen by the guest.");
11993 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11994 "Percentage of processor time spent in kernel mode as seen by the guest.");
11995 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11996 "Percentage of processor time spent idling as seen by the guest.");
11997
11998 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11999 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12000 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12001 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12002 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12003 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12004
12005 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12006
12007 /* Create and register base metrics */
12008 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12009 machineNetRx, machineNetTx);
12010 aCollector->registerBaseMetric(machineNetRate);
12011
12012 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12013 guestLoadUser, guestLoadKernel, guestLoadIdle);
12014 aCollector->registerBaseMetric(guestCpuLoad);
12015
12016 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12017 guestMemTotal, guestMemFree,
12018 guestMemBalloon, guestMemShared,
12019 guestMemCache, guestPagedTotal);
12020 aCollector->registerBaseMetric(guestCpuMem);
12021
12022 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12023 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12024 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12025 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12026
12027 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12028 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12029 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12030 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12031
12032 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12033 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12034 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12035 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12036
12037 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12038 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12039 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12040 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12041
12042 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12043 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12044 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12045 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12046
12047 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12048 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12049 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12050 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12051
12052 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12053 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12054 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12055 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12056
12057 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12058 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12059 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12060 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12061
12062 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12063 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12064 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12065 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12066
12067 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12068 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12069 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12071
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12073 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12074 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12075 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12076}
12077
12078void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12079{
12080 AssertReturnVoid(isWriteLockOnCurrentThread());
12081
12082 if (aCollector)
12083 {
12084 aCollector->unregisterMetricsFor(aMachine);
12085 aCollector->unregisterBaseMetricsFor(aMachine);
12086 }
12087}
12088
12089#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12090
12091
12092////////////////////////////////////////////////////////////////////////////////
12093
12094DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12095
12096HRESULT SessionMachine::FinalConstruct()
12097{
12098 LogFlowThisFunc(("\n"));
12099
12100#if defined(RT_OS_WINDOWS)
12101 mIPCSem = NULL;
12102#elif defined(RT_OS_OS2)
12103 mIPCSem = NULLHANDLE;
12104#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12105 mIPCSem = -1;
12106#else
12107# error "Port me!"
12108#endif
12109
12110 return BaseFinalConstruct();
12111}
12112
12113void SessionMachine::FinalRelease()
12114{
12115 LogFlowThisFunc(("\n"));
12116
12117 uninit(Uninit::Unexpected);
12118
12119 BaseFinalRelease();
12120}
12121
12122/**
12123 * @note Must be called only by Machine::openSession() from its own write lock.
12124 */
12125HRESULT SessionMachine::init(Machine *aMachine)
12126{
12127 LogFlowThisFuncEnter();
12128 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12129
12130 AssertReturn(aMachine, E_INVALIDARG);
12131
12132 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12133
12134 /* Enclose the state transition NotReady->InInit->Ready */
12135 AutoInitSpan autoInitSpan(this);
12136 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12137
12138 /* create the interprocess semaphore */
12139#if defined(RT_OS_WINDOWS)
12140 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12141 for (size_t i = 0; i < mIPCSemName.length(); i++)
12142 if (mIPCSemName.raw()[i] == '\\')
12143 mIPCSemName.raw()[i] = '/';
12144 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12145 ComAssertMsgRet(mIPCSem,
12146 ("Cannot create IPC mutex '%ls', err=%d",
12147 mIPCSemName.raw(), ::GetLastError()),
12148 E_FAIL);
12149#elif defined(RT_OS_OS2)
12150 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12151 aMachine->mData->mUuid.raw());
12152 mIPCSemName = ipcSem;
12153 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12154 ComAssertMsgRet(arc == NO_ERROR,
12155 ("Cannot create IPC mutex '%s', arc=%ld",
12156 ipcSem.c_str(), arc),
12157 E_FAIL);
12158#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12159# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12160# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12161 /** @todo Check that this still works correctly. */
12162 AssertCompileSize(key_t, 8);
12163# else
12164 AssertCompileSize(key_t, 4);
12165# endif
12166 key_t key;
12167 mIPCSem = -1;
12168 mIPCKey = "0";
12169 for (uint32_t i = 0; i < 1 << 24; i++)
12170 {
12171 key = ((uint32_t)'V' << 24) | i;
12172 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12173 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12174 {
12175 mIPCSem = sem;
12176 if (sem >= 0)
12177 mIPCKey = BstrFmt("%u", key);
12178 break;
12179 }
12180 }
12181# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12182 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12183 char *pszSemName = NULL;
12184 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12185 key_t key = ::ftok(pszSemName, 'V');
12186 RTStrFree(pszSemName);
12187
12188 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12189# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12190
12191 int errnoSave = errno;
12192 if (mIPCSem < 0 && errnoSave == ENOSYS)
12193 {
12194 setError(E_FAIL,
12195 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12196 "support for SysV IPC. Check the host kernel configuration for "
12197 "CONFIG_SYSVIPC=y"));
12198 return E_FAIL;
12199 }
12200 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12201 * the IPC semaphores */
12202 if (mIPCSem < 0 && errnoSave == ENOSPC)
12203 {
12204#ifdef RT_OS_LINUX
12205 setError(E_FAIL,
12206 tr("Cannot create IPC semaphore because the system limit for the "
12207 "maximum number of semaphore sets (SEMMNI), or the system wide "
12208 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12209 "current set of SysV IPC semaphores can be determined from "
12210 "the file /proc/sysvipc/sem"));
12211#else
12212 setError(E_FAIL,
12213 tr("Cannot create IPC semaphore because the system-imposed limit "
12214 "on the maximum number of allowed semaphores or semaphore "
12215 "identifiers system-wide would be exceeded"));
12216#endif
12217 return E_FAIL;
12218 }
12219 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12220 E_FAIL);
12221 /* set the initial value to 1 */
12222 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12223 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12224 E_FAIL);
12225#else
12226# error "Port me!"
12227#endif
12228
12229 /* memorize the peer Machine */
12230 unconst(mPeer) = aMachine;
12231 /* share the parent pointer */
12232 unconst(mParent) = aMachine->mParent;
12233
12234 /* take the pointers to data to share */
12235 mData.share(aMachine->mData);
12236 mSSData.share(aMachine->mSSData);
12237
12238 mUserData.share(aMachine->mUserData);
12239 mHWData.share(aMachine->mHWData);
12240 mMediaData.share(aMachine->mMediaData);
12241
12242 mStorageControllers.allocate();
12243 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12244 it != aMachine->mStorageControllers->end();
12245 ++it)
12246 {
12247 ComObjPtr<StorageController> ctl;
12248 ctl.createObject();
12249 ctl->init(this, *it);
12250 mStorageControllers->push_back(ctl);
12251 }
12252
12253 unconst(mBIOSSettings).createObject();
12254 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12255 /* create another VRDEServer object that will be mutable */
12256 unconst(mVRDEServer).createObject();
12257 mVRDEServer->init(this, aMachine->mVRDEServer);
12258 /* create another audio adapter object that will be mutable */
12259 unconst(mAudioAdapter).createObject();
12260 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12261 /* create a list of serial ports that will be mutable */
12262 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12263 {
12264 unconst(mSerialPorts[slot]).createObject();
12265 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12266 }
12267 /* create a list of parallel ports that will be mutable */
12268 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12269 {
12270 unconst(mParallelPorts[slot]).createObject();
12271 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12272 }
12273 /* create another USB controller object that will be mutable */
12274 unconst(mUSBController).createObject();
12275 mUSBController->init(this, aMachine->mUSBController);
12276
12277 /* create a list of network adapters that will be mutable */
12278 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12279 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12280 {
12281 unconst(mNetworkAdapters[slot]).createObject();
12282 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12283 }
12284
12285 /* create another bandwidth control object that will be mutable */
12286 unconst(mBandwidthControl).createObject();
12287 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12288
12289 /* default is to delete saved state on Saved -> PoweredOff transition */
12290 mRemoveSavedState = true;
12291
12292 /* Confirm a successful initialization when it's the case */
12293 autoInitSpan.setSucceeded();
12294
12295 LogFlowThisFuncLeave();
12296 return S_OK;
12297}
12298
12299/**
12300 * Uninitializes this session object. If the reason is other than
12301 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12302 *
12303 * @param aReason uninitialization reason
12304 *
12305 * @note Locks mParent + this object for writing.
12306 */
12307void SessionMachine::uninit(Uninit::Reason aReason)
12308{
12309 LogFlowThisFuncEnter();
12310 LogFlowThisFunc(("reason=%d\n", aReason));
12311
12312 /*
12313 * Strongly reference ourselves to prevent this object deletion after
12314 * mData->mSession.mMachine.setNull() below (which can release the last
12315 * reference and call the destructor). Important: this must be done before
12316 * accessing any members (and before AutoUninitSpan that does it as well).
12317 * This self reference will be released as the very last step on return.
12318 */
12319 ComObjPtr<SessionMachine> selfRef = this;
12320
12321 /* Enclose the state transition Ready->InUninit->NotReady */
12322 AutoUninitSpan autoUninitSpan(this);
12323 if (autoUninitSpan.uninitDone())
12324 {
12325 LogFlowThisFunc(("Already uninitialized\n"));
12326 LogFlowThisFuncLeave();
12327 return;
12328 }
12329
12330 if (autoUninitSpan.initFailed())
12331 {
12332 /* We've been called by init() because it's failed. It's not really
12333 * necessary (nor it's safe) to perform the regular uninit sequence
12334 * below, the following is enough.
12335 */
12336 LogFlowThisFunc(("Initialization failed.\n"));
12337#if defined(RT_OS_WINDOWS)
12338 if (mIPCSem)
12339 ::CloseHandle(mIPCSem);
12340 mIPCSem = NULL;
12341#elif defined(RT_OS_OS2)
12342 if (mIPCSem != NULLHANDLE)
12343 ::DosCloseMutexSem(mIPCSem);
12344 mIPCSem = NULLHANDLE;
12345#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12346 if (mIPCSem >= 0)
12347 ::semctl(mIPCSem, 0, IPC_RMID);
12348 mIPCSem = -1;
12349# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12350 mIPCKey = "0";
12351# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12352#else
12353# error "Port me!"
12354#endif
12355 uninitDataAndChildObjects();
12356 mData.free();
12357 unconst(mParent) = NULL;
12358 unconst(mPeer) = NULL;
12359 LogFlowThisFuncLeave();
12360 return;
12361 }
12362
12363 MachineState_T lastState;
12364 {
12365 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12366 lastState = mData->mMachineState;
12367 }
12368 NOREF(lastState);
12369
12370#ifdef VBOX_WITH_USB
12371 // release all captured USB devices, but do this before requesting the locks below
12372 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12373 {
12374 /* Console::captureUSBDevices() is called in the VM process only after
12375 * setting the machine state to Starting or Restoring.
12376 * Console::detachAllUSBDevices() will be called upon successful
12377 * termination. So, we need to release USB devices only if there was
12378 * an abnormal termination of a running VM.
12379 *
12380 * This is identical to SessionMachine::DetachAllUSBDevices except
12381 * for the aAbnormal argument. */
12382 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12383 AssertComRC(rc);
12384 NOREF(rc);
12385
12386 USBProxyService *service = mParent->host()->usbProxyService();
12387 if (service)
12388 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12389 }
12390#endif /* VBOX_WITH_USB */
12391
12392 // we need to lock this object in uninit() because the lock is shared
12393 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12394 // and others need mParent lock, and USB needs host lock.
12395 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12396
12397#if 0
12398 // Trigger async cleanup tasks, avoid doing things here which are not
12399 // vital to be done immediately and maybe need more locks. This calls
12400 // Machine::unregisterMetrics().
12401 mParent->onMachineUninit(mPeer);
12402#else
12403 /*
12404 * It is safe to call Machine::unregisterMetrics() here because
12405 * PerformanceCollector::samplerCallback no longer accesses guest methods
12406 * holding the lock.
12407 */
12408 unregisterMetrics(mParent->performanceCollector(), mPeer);
12409#endif
12410 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12411 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12412 this, __PRETTY_FUNCTION__, mCollectorGuest));
12413 if (mCollectorGuest)
12414 {
12415 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12416 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12417 mCollectorGuest = NULL;
12418 }
12419
12420 if (aReason == Uninit::Abnormal)
12421 {
12422 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12423 Global::IsOnlineOrTransient(lastState)));
12424
12425 /* reset the state to Aborted */
12426 if (mData->mMachineState != MachineState_Aborted)
12427 setMachineState(MachineState_Aborted);
12428 }
12429
12430 // any machine settings modified?
12431 if (mData->flModifications)
12432 {
12433 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12434 rollback(false /* aNotify */);
12435 }
12436
12437 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12438 || !mConsoleTaskData.mSnapshot);
12439 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12440 {
12441 LogWarningThisFunc(("canceling failed save state request!\n"));
12442 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12443 }
12444 else if (!mConsoleTaskData.mSnapshot.isNull())
12445 {
12446 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12447
12448 /* delete all differencing hard disks created (this will also attach
12449 * their parents back by rolling back mMediaData) */
12450 rollbackMedia();
12451
12452 // delete the saved state file (it might have been already created)
12453 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12454 // think it's still in use
12455 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12456 mConsoleTaskData.mSnapshot->uninit();
12457 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12458 }
12459
12460 if (!mData->mSession.mType.isEmpty())
12461 {
12462 /* mType is not null when this machine's process has been started by
12463 * Machine::LaunchVMProcess(), therefore it is our child. We
12464 * need to queue the PID to reap the process (and avoid zombies on
12465 * Linux). */
12466 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12467 mParent->addProcessToReap(mData->mSession.mPID);
12468 }
12469
12470 mData->mSession.mPID = NIL_RTPROCESS;
12471
12472 if (aReason == Uninit::Unexpected)
12473 {
12474 /* Uninitialization didn't come from #checkForDeath(), so tell the
12475 * client watcher thread to update the set of machines that have open
12476 * sessions. */
12477 mParent->updateClientWatcher();
12478 }
12479
12480 /* uninitialize all remote controls */
12481 if (mData->mSession.mRemoteControls.size())
12482 {
12483 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12484 mData->mSession.mRemoteControls.size()));
12485
12486 Data::Session::RemoteControlList::iterator it =
12487 mData->mSession.mRemoteControls.begin();
12488 while (it != mData->mSession.mRemoteControls.end())
12489 {
12490 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12491 HRESULT rc = (*it)->Uninitialize();
12492 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12493 if (FAILED(rc))
12494 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12495 ++it;
12496 }
12497 mData->mSession.mRemoteControls.clear();
12498 }
12499
12500 /*
12501 * An expected uninitialization can come only from #checkForDeath().
12502 * Otherwise it means that something's gone really wrong (for example,
12503 * the Session implementation has released the VirtualBox reference
12504 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12505 * etc). However, it's also possible, that the client releases the IPC
12506 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12507 * but the VirtualBox release event comes first to the server process.
12508 * This case is practically possible, so we should not assert on an
12509 * unexpected uninit, just log a warning.
12510 */
12511
12512 if ((aReason == Uninit::Unexpected))
12513 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12514
12515 if (aReason != Uninit::Normal)
12516 {
12517 mData->mSession.mDirectControl.setNull();
12518 }
12519 else
12520 {
12521 /* this must be null here (see #OnSessionEnd()) */
12522 Assert(mData->mSession.mDirectControl.isNull());
12523 Assert(mData->mSession.mState == SessionState_Unlocking);
12524 Assert(!mData->mSession.mProgress.isNull());
12525 }
12526 if (mData->mSession.mProgress)
12527 {
12528 if (aReason == Uninit::Normal)
12529 mData->mSession.mProgress->notifyComplete(S_OK);
12530 else
12531 mData->mSession.mProgress->notifyComplete(E_FAIL,
12532 COM_IIDOF(ISession),
12533 getComponentName(),
12534 tr("The VM session was aborted"));
12535 mData->mSession.mProgress.setNull();
12536 }
12537
12538 /* remove the association between the peer machine and this session machine */
12539 Assert( (SessionMachine*)mData->mSession.mMachine == this
12540 || aReason == Uninit::Unexpected);
12541
12542 /* reset the rest of session data */
12543 mData->mSession.mMachine.setNull();
12544 mData->mSession.mState = SessionState_Unlocked;
12545 mData->mSession.mType.setNull();
12546
12547 /* close the interprocess semaphore before leaving the exclusive lock */
12548#if defined(RT_OS_WINDOWS)
12549 if (mIPCSem)
12550 ::CloseHandle(mIPCSem);
12551 mIPCSem = NULL;
12552#elif defined(RT_OS_OS2)
12553 if (mIPCSem != NULLHANDLE)
12554 ::DosCloseMutexSem(mIPCSem);
12555 mIPCSem = NULLHANDLE;
12556#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12557 if (mIPCSem >= 0)
12558 ::semctl(mIPCSem, 0, IPC_RMID);
12559 mIPCSem = -1;
12560# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12561 mIPCKey = "0";
12562# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12563#else
12564# error "Port me!"
12565#endif
12566
12567 /* fire an event */
12568 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12569
12570 uninitDataAndChildObjects();
12571
12572 /* free the essential data structure last */
12573 mData.free();
12574
12575 /* release the exclusive lock before setting the below two to NULL */
12576 multilock.release();
12577
12578 unconst(mParent) = NULL;
12579 unconst(mPeer) = NULL;
12580
12581 LogFlowThisFuncLeave();
12582}
12583
12584// util::Lockable interface
12585////////////////////////////////////////////////////////////////////////////////
12586
12587/**
12588 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12589 * with the primary Machine instance (mPeer).
12590 */
12591RWLockHandle *SessionMachine::lockHandle() const
12592{
12593 AssertReturn(mPeer != NULL, NULL);
12594 return mPeer->lockHandle();
12595}
12596
12597// IInternalMachineControl methods
12598////////////////////////////////////////////////////////////////////////////////
12599
12600/**
12601 * Passes collected guest statistics to performance collector object
12602 */
12603STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12604 ULONG aCpuKernel, ULONG aCpuIdle,
12605 ULONG aMemTotal, ULONG aMemFree,
12606 ULONG aMemBalloon, ULONG aMemShared,
12607 ULONG aMemCache, ULONG aPageTotal,
12608 ULONG aAllocVMM, ULONG aFreeVMM,
12609 ULONG aBalloonedVMM, ULONG aSharedVMM,
12610 ULONG aVmNetRx, ULONG aVmNetTx)
12611{
12612 if (mCollectorGuest)
12613 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12614 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12615 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12616 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12617
12618 return S_OK;
12619}
12620
12621/**
12622 * @note Locks this object for writing.
12623 */
12624STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12625{
12626 AutoCaller autoCaller(this);
12627 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12628
12629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12630
12631 mRemoveSavedState = aRemove;
12632
12633 return S_OK;
12634}
12635
12636/**
12637 * @note Locks the same as #setMachineState() does.
12638 */
12639STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12640{
12641 return setMachineState(aMachineState);
12642}
12643
12644/**
12645 * @note Locks this object for reading.
12646 */
12647STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12648{
12649 AutoCaller autoCaller(this);
12650 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12651
12652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12653
12654#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12655 mIPCSemName.cloneTo(aId);
12656 return S_OK;
12657#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12658# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12659 mIPCKey.cloneTo(aId);
12660# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12661 mData->m_strConfigFileFull.cloneTo(aId);
12662# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12663 return S_OK;
12664#else
12665# error "Port me!"
12666#endif
12667}
12668
12669/**
12670 * @note Locks this object for writing.
12671 */
12672STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12673{
12674 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12675 AutoCaller autoCaller(this);
12676 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12677
12678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12679
12680 if (mData->mSession.mState != SessionState_Locked)
12681 return VBOX_E_INVALID_OBJECT_STATE;
12682
12683 if (!mData->mSession.mProgress.isNull())
12684 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12685
12686 LogFlowThisFunc(("returns S_OK.\n"));
12687 return S_OK;
12688}
12689
12690/**
12691 * @note Locks this object for writing.
12692 */
12693STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12694{
12695 AutoCaller autoCaller(this);
12696 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12697
12698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12699
12700 if (mData->mSession.mState != SessionState_Locked)
12701 return VBOX_E_INVALID_OBJECT_STATE;
12702
12703 /* Finalize the LaunchVMProcess progress object. */
12704 if (mData->mSession.mProgress)
12705 {
12706 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12707 mData->mSession.mProgress.setNull();
12708 }
12709
12710 if (SUCCEEDED((HRESULT)iResult))
12711 {
12712#ifdef VBOX_WITH_RESOURCE_USAGE_API
12713 /* The VM has been powered up successfully, so it makes sense
12714 * now to offer the performance metrics for a running machine
12715 * object. Doing it earlier wouldn't be safe. */
12716 registerMetrics(mParent->performanceCollector(), mPeer,
12717 mData->mSession.mPID);
12718#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12719 }
12720
12721 return S_OK;
12722}
12723
12724/**
12725 * @note Locks this object for writing.
12726 */
12727STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12728{
12729 LogFlowThisFuncEnter();
12730
12731 CheckComArgOutPointerValid(aProgress);
12732
12733 AutoCaller autoCaller(this);
12734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12735
12736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12737
12738 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12739 E_FAIL);
12740
12741 /* create a progress object to track operation completion */
12742 ComObjPtr<Progress> pProgress;
12743 pProgress.createObject();
12744 pProgress->init(getVirtualBox(),
12745 static_cast<IMachine *>(this) /* aInitiator */,
12746 Bstr(tr("Stopping the virtual machine")).raw(),
12747 FALSE /* aCancelable */);
12748
12749 /* fill in the console task data */
12750 mConsoleTaskData.mLastState = mData->mMachineState;
12751 mConsoleTaskData.mProgress = pProgress;
12752
12753 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12754 setMachineState(MachineState_Stopping);
12755
12756 pProgress.queryInterfaceTo(aProgress);
12757
12758 return S_OK;
12759}
12760
12761/**
12762 * @note Locks this object for writing.
12763 */
12764STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12765{
12766 LogFlowThisFuncEnter();
12767
12768 AutoCaller autoCaller(this);
12769 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12770
12771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12772
12773 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12774 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12775 && mConsoleTaskData.mLastState != MachineState_Null,
12776 E_FAIL);
12777
12778 /*
12779 * On failure, set the state to the state we had when BeginPoweringDown()
12780 * was called (this is expected by Console::PowerDown() and the associated
12781 * task). On success the VM process already changed the state to
12782 * MachineState_PoweredOff, so no need to do anything.
12783 */
12784 if (FAILED(iResult))
12785 setMachineState(mConsoleTaskData.mLastState);
12786
12787 /* notify the progress object about operation completion */
12788 Assert(mConsoleTaskData.mProgress);
12789 if (SUCCEEDED(iResult))
12790 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12791 else
12792 {
12793 Utf8Str strErrMsg(aErrMsg);
12794 if (strErrMsg.length())
12795 mConsoleTaskData.mProgress->notifyComplete(iResult,
12796 COM_IIDOF(ISession),
12797 getComponentName(),
12798 strErrMsg.c_str());
12799 else
12800 mConsoleTaskData.mProgress->notifyComplete(iResult);
12801 }
12802
12803 /* clear out the temporary saved state data */
12804 mConsoleTaskData.mLastState = MachineState_Null;
12805 mConsoleTaskData.mProgress.setNull();
12806
12807 LogFlowThisFuncLeave();
12808 return S_OK;
12809}
12810
12811
12812/**
12813 * Goes through the USB filters of the given machine to see if the given
12814 * device matches any filter or not.
12815 *
12816 * @note Locks the same as USBController::hasMatchingFilter() does.
12817 */
12818STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12819 BOOL *aMatched,
12820 ULONG *aMaskedIfs)
12821{
12822 LogFlowThisFunc(("\n"));
12823
12824 CheckComArgNotNull(aUSBDevice);
12825 CheckComArgOutPointerValid(aMatched);
12826
12827 AutoCaller autoCaller(this);
12828 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12829
12830#ifdef VBOX_WITH_USB
12831 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12832#else
12833 NOREF(aUSBDevice);
12834 NOREF(aMaskedIfs);
12835 *aMatched = FALSE;
12836#endif
12837
12838 return S_OK;
12839}
12840
12841/**
12842 * @note Locks the same as Host::captureUSBDevice() does.
12843 */
12844STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12845{
12846 LogFlowThisFunc(("\n"));
12847
12848 AutoCaller autoCaller(this);
12849 AssertComRCReturnRC(autoCaller.rc());
12850
12851#ifdef VBOX_WITH_USB
12852 /* if captureDeviceForVM() fails, it must have set extended error info */
12853 clearError();
12854 MultiResult rc = mParent->host()->checkUSBProxyService();
12855 if (FAILED(rc)) return rc;
12856
12857 USBProxyService *service = mParent->host()->usbProxyService();
12858 AssertReturn(service, E_FAIL);
12859 return service->captureDeviceForVM(this, Guid(aId).ref());
12860#else
12861 NOREF(aId);
12862 return E_NOTIMPL;
12863#endif
12864}
12865
12866/**
12867 * @note Locks the same as Host::detachUSBDevice() does.
12868 */
12869STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12870{
12871 LogFlowThisFunc(("\n"));
12872
12873 AutoCaller autoCaller(this);
12874 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12875
12876#ifdef VBOX_WITH_USB
12877 USBProxyService *service = mParent->host()->usbProxyService();
12878 AssertReturn(service, E_FAIL);
12879 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12880#else
12881 NOREF(aId);
12882 NOREF(aDone);
12883 return E_NOTIMPL;
12884#endif
12885}
12886
12887/**
12888 * Inserts all machine filters to the USB proxy service and then calls
12889 * Host::autoCaptureUSBDevices().
12890 *
12891 * Called by Console from the VM process upon VM startup.
12892 *
12893 * @note Locks what called methods lock.
12894 */
12895STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12896{
12897 LogFlowThisFunc(("\n"));
12898
12899 AutoCaller autoCaller(this);
12900 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12901
12902#ifdef VBOX_WITH_USB
12903 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12904 AssertComRC(rc);
12905 NOREF(rc);
12906
12907 USBProxyService *service = mParent->host()->usbProxyService();
12908 AssertReturn(service, E_FAIL);
12909 return service->autoCaptureDevicesForVM(this);
12910#else
12911 return S_OK;
12912#endif
12913}
12914
12915/**
12916 * Removes all machine filters from the USB proxy service and then calls
12917 * Host::detachAllUSBDevices().
12918 *
12919 * Called by Console from the VM process upon normal VM termination or by
12920 * SessionMachine::uninit() upon abnormal VM termination (from under the
12921 * Machine/SessionMachine lock).
12922 *
12923 * @note Locks what called methods lock.
12924 */
12925STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12926{
12927 LogFlowThisFunc(("\n"));
12928
12929 AutoCaller autoCaller(this);
12930 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12931
12932#ifdef VBOX_WITH_USB
12933 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12934 AssertComRC(rc);
12935 NOREF(rc);
12936
12937 USBProxyService *service = mParent->host()->usbProxyService();
12938 AssertReturn(service, E_FAIL);
12939 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12940#else
12941 NOREF(aDone);
12942 return S_OK;
12943#endif
12944}
12945
12946/**
12947 * @note Locks this object for writing.
12948 */
12949STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12950 IProgress **aProgress)
12951{
12952 LogFlowThisFuncEnter();
12953
12954 AssertReturn(aSession, E_INVALIDARG);
12955 AssertReturn(aProgress, E_INVALIDARG);
12956
12957 AutoCaller autoCaller(this);
12958
12959 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12960 /*
12961 * We don't assert below because it might happen that a non-direct session
12962 * informs us it is closed right after we've been uninitialized -- it's ok.
12963 */
12964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12965
12966 /* get IInternalSessionControl interface */
12967 ComPtr<IInternalSessionControl> control(aSession);
12968
12969 ComAssertRet(!control.isNull(), E_INVALIDARG);
12970
12971 /* Creating a Progress object requires the VirtualBox lock, and
12972 * thus locking it here is required by the lock order rules. */
12973 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12974
12975 if (control == mData->mSession.mDirectControl)
12976 {
12977 ComAssertRet(aProgress, E_POINTER);
12978
12979 /* The direct session is being normally closed by the client process
12980 * ----------------------------------------------------------------- */
12981
12982 /* go to the closing state (essential for all open*Session() calls and
12983 * for #checkForDeath()) */
12984 Assert(mData->mSession.mState == SessionState_Locked);
12985 mData->mSession.mState = SessionState_Unlocking;
12986
12987 /* set direct control to NULL to release the remote instance */
12988 mData->mSession.mDirectControl.setNull();
12989 LogFlowThisFunc(("Direct control is set to NULL\n"));
12990
12991 if (mData->mSession.mProgress)
12992 {
12993 /* finalize the progress, someone might wait if a frontend
12994 * closes the session before powering on the VM. */
12995 mData->mSession.mProgress->notifyComplete(E_FAIL,
12996 COM_IIDOF(ISession),
12997 getComponentName(),
12998 tr("The VM session was closed before any attempt to power it on"));
12999 mData->mSession.mProgress.setNull();
13000 }
13001
13002 /* Create the progress object the client will use to wait until
13003 * #checkForDeath() is called to uninitialize this session object after
13004 * it releases the IPC semaphore.
13005 * Note! Because we're "reusing" mProgress here, this must be a proxy
13006 * object just like for LaunchVMProcess. */
13007 Assert(mData->mSession.mProgress.isNull());
13008 ComObjPtr<ProgressProxy> progress;
13009 progress.createObject();
13010 ComPtr<IUnknown> pPeer(mPeer);
13011 progress->init(mParent, pPeer,
13012 Bstr(tr("Closing session")).raw(),
13013 FALSE /* aCancelable */);
13014 progress.queryInterfaceTo(aProgress);
13015 mData->mSession.mProgress = progress;
13016 }
13017 else
13018 {
13019 /* the remote session is being normally closed */
13020 Data::Session::RemoteControlList::iterator it =
13021 mData->mSession.mRemoteControls.begin();
13022 while (it != mData->mSession.mRemoteControls.end())
13023 {
13024 if (control == *it)
13025 break;
13026 ++it;
13027 }
13028 BOOL found = it != mData->mSession.mRemoteControls.end();
13029 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13030 E_INVALIDARG);
13031 // This MUST be erase(it), not remove(*it) as the latter triggers a
13032 // very nasty use after free due to the place where the value "lives".
13033 mData->mSession.mRemoteControls.erase(it);
13034 }
13035
13036 /* signal the client watcher thread, because the client is going away */
13037 mParent->updateClientWatcher();
13038
13039 LogFlowThisFuncLeave();
13040 return S_OK;
13041}
13042
13043/**
13044 * @note Locks this object for writing.
13045 */
13046STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13047{
13048 LogFlowThisFuncEnter();
13049
13050 CheckComArgOutPointerValid(aProgress);
13051 CheckComArgOutPointerValid(aStateFilePath);
13052
13053 AutoCaller autoCaller(this);
13054 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13055
13056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13057
13058 AssertReturn( mData->mMachineState == MachineState_Paused
13059 && mConsoleTaskData.mLastState == MachineState_Null
13060 && mConsoleTaskData.strStateFilePath.isEmpty(),
13061 E_FAIL);
13062
13063 /* create a progress object to track operation completion */
13064 ComObjPtr<Progress> pProgress;
13065 pProgress.createObject();
13066 pProgress->init(getVirtualBox(),
13067 static_cast<IMachine *>(this) /* aInitiator */,
13068 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13069 FALSE /* aCancelable */);
13070
13071 Utf8Str strStateFilePath;
13072 /* stateFilePath is null when the machine is not running */
13073 if (mData->mMachineState == MachineState_Paused)
13074 composeSavedStateFilename(strStateFilePath);
13075
13076 /* fill in the console task data */
13077 mConsoleTaskData.mLastState = mData->mMachineState;
13078 mConsoleTaskData.strStateFilePath = strStateFilePath;
13079 mConsoleTaskData.mProgress = pProgress;
13080
13081 /* set the state to Saving (this is expected by Console::SaveState()) */
13082 setMachineState(MachineState_Saving);
13083
13084 strStateFilePath.cloneTo(aStateFilePath);
13085 pProgress.queryInterfaceTo(aProgress);
13086
13087 return S_OK;
13088}
13089
13090/**
13091 * @note Locks mParent + this object for writing.
13092 */
13093STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13094{
13095 LogFlowThisFunc(("\n"));
13096
13097 AutoCaller autoCaller(this);
13098 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13099
13100 /* endSavingState() need mParent lock */
13101 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13102
13103 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13104 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13105 && mConsoleTaskData.mLastState != MachineState_Null
13106 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13107 E_FAIL);
13108
13109 /*
13110 * On failure, set the state to the state we had when BeginSavingState()
13111 * was called (this is expected by Console::SaveState() and the associated
13112 * task). On success the VM process already changed the state to
13113 * MachineState_Saved, so no need to do anything.
13114 */
13115 if (FAILED(iResult))
13116 setMachineState(mConsoleTaskData.mLastState);
13117
13118 return endSavingState(iResult, aErrMsg);
13119}
13120
13121/**
13122 * @note Locks this object for writing.
13123 */
13124STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13125{
13126 LogFlowThisFunc(("\n"));
13127
13128 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13129
13130 AutoCaller autoCaller(this);
13131 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13132
13133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13134
13135 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13136 || mData->mMachineState == MachineState_Teleported
13137 || mData->mMachineState == MachineState_Aborted
13138 , E_FAIL); /** @todo setError. */
13139
13140 Utf8Str stateFilePathFull = aSavedStateFile;
13141 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13142 if (RT_FAILURE(vrc))
13143 return setError(VBOX_E_FILE_ERROR,
13144 tr("Invalid saved state file path '%ls' (%Rrc)"),
13145 aSavedStateFile,
13146 vrc);
13147
13148 mSSData->strStateFilePath = stateFilePathFull;
13149
13150 /* The below setMachineState() will detect the state transition and will
13151 * update the settings file */
13152
13153 return setMachineState(MachineState_Saved);
13154}
13155
13156STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13157 ComSafeArrayOut(BSTR, aValues),
13158 ComSafeArrayOut(LONG64, aTimestamps),
13159 ComSafeArrayOut(BSTR, aFlags))
13160{
13161 LogFlowThisFunc(("\n"));
13162
13163#ifdef VBOX_WITH_GUEST_PROPS
13164 using namespace guestProp;
13165
13166 AutoCaller autoCaller(this);
13167 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13168
13169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13170
13171 CheckComArgOutSafeArrayPointerValid(aNames);
13172 CheckComArgOutSafeArrayPointerValid(aValues);
13173 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13174 CheckComArgOutSafeArrayPointerValid(aFlags);
13175
13176 size_t cEntries = mHWData->mGuestProperties.size();
13177 com::SafeArray<BSTR> names(cEntries);
13178 com::SafeArray<BSTR> values(cEntries);
13179 com::SafeArray<LONG64> timestamps(cEntries);
13180 com::SafeArray<BSTR> flags(cEntries);
13181 unsigned i = 0;
13182 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13183 it != mHWData->mGuestProperties.end();
13184 ++it)
13185 {
13186 char szFlags[MAX_FLAGS_LEN + 1];
13187 it->first.cloneTo(&names[i]);
13188 it->second.strValue.cloneTo(&values[i]);
13189 timestamps[i] = it->second.mTimestamp;
13190 /* If it is NULL, keep it NULL. */
13191 if (it->second.mFlags)
13192 {
13193 writeFlags(it->second.mFlags, szFlags);
13194 Bstr(szFlags).cloneTo(&flags[i]);
13195 }
13196 else
13197 flags[i] = NULL;
13198 ++i;
13199 }
13200 names.detachTo(ComSafeArrayOutArg(aNames));
13201 values.detachTo(ComSafeArrayOutArg(aValues));
13202 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13203 flags.detachTo(ComSafeArrayOutArg(aFlags));
13204 return S_OK;
13205#else
13206 ReturnComNotImplemented();
13207#endif
13208}
13209
13210STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13211 IN_BSTR aValue,
13212 LONG64 aTimestamp,
13213 IN_BSTR aFlags)
13214{
13215 LogFlowThisFunc(("\n"));
13216
13217#ifdef VBOX_WITH_GUEST_PROPS
13218 using namespace guestProp;
13219
13220 CheckComArgStrNotEmptyOrNull(aName);
13221 CheckComArgNotNull(aValue);
13222 CheckComArgNotNull(aFlags);
13223
13224 try
13225 {
13226 /*
13227 * Convert input up front.
13228 */
13229 Utf8Str utf8Name(aName);
13230 uint32_t fFlags = NILFLAG;
13231 if (aFlags)
13232 {
13233 Utf8Str utf8Flags(aFlags);
13234 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13235 AssertRCReturn(vrc, E_INVALIDARG);
13236 }
13237
13238 /*
13239 * Now grab the object lock, validate the state and do the update.
13240 */
13241 AutoCaller autoCaller(this);
13242 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13243
13244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13245
13246 switch (mData->mMachineState)
13247 {
13248 case MachineState_Paused:
13249 case MachineState_Running:
13250 case MachineState_Teleporting:
13251 case MachineState_TeleportingPausedVM:
13252 case MachineState_LiveSnapshotting:
13253 case MachineState_DeletingSnapshotOnline:
13254 case MachineState_DeletingSnapshotPaused:
13255 case MachineState_Saving:
13256 case MachineState_Stopping:
13257 break;
13258
13259 default:
13260 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13261 VBOX_E_INVALID_VM_STATE);
13262 }
13263
13264 setModified(IsModified_MachineData);
13265 mHWData.backup();
13266
13267 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13268 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13269 if (it != mHWData->mGuestProperties.end())
13270 {
13271 if (!fDelete)
13272 {
13273 it->second.strValue = aValue;
13274 it->second.mTimestamp = aTimestamp;
13275 it->second.mFlags = fFlags;
13276 }
13277 else
13278 mHWData->mGuestProperties.erase(it);
13279
13280 mData->mGuestPropertiesModified = TRUE;
13281 }
13282 else if (!fDelete)
13283 {
13284 HWData::GuestProperty prop;
13285 prop.strValue = aValue;
13286 prop.mTimestamp = aTimestamp;
13287 prop.mFlags = fFlags;
13288
13289 mHWData->mGuestProperties[utf8Name] = prop;
13290 mData->mGuestPropertiesModified = TRUE;
13291 }
13292
13293 /*
13294 * Send a callback notification if appropriate
13295 */
13296 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13297 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13298 RTSTR_MAX,
13299 utf8Name.c_str(),
13300 RTSTR_MAX, NULL)
13301 )
13302 {
13303 alock.release();
13304
13305 mParent->onGuestPropertyChange(mData->mUuid,
13306 aName,
13307 aValue,
13308 aFlags);
13309 }
13310 }
13311 catch (...)
13312 {
13313 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13314 }
13315 return S_OK;
13316#else
13317 ReturnComNotImplemented();
13318#endif
13319}
13320
13321STDMETHODIMP SessionMachine::LockMedia()
13322{
13323 AutoCaller autoCaller(this);
13324 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13325
13326 AutoMultiWriteLock2 alock(this->lockHandle(),
13327 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13328
13329 AssertReturn( mData->mMachineState == MachineState_Starting
13330 || mData->mMachineState == MachineState_Restoring
13331 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13332
13333 clearError();
13334 alock.release();
13335 return lockMedia();
13336}
13337
13338STDMETHODIMP SessionMachine::UnlockMedia()
13339{
13340 unlockMedia();
13341 return S_OK;
13342}
13343
13344STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13345 IMediumAttachment **aNewAttachment)
13346{
13347 CheckComArgNotNull(aAttachment);
13348 CheckComArgOutPointerValid(aNewAttachment);
13349
13350 AutoCaller autoCaller(this);
13351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13352
13353 // request the host lock first, since might be calling Host methods for getting host drives;
13354 // next, protect the media tree all the while we're in here, as well as our member variables
13355 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13356 this->lockHandle(),
13357 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13358
13359 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13360
13361 Bstr ctrlName;
13362 LONG lPort;
13363 LONG lDevice;
13364 bool fTempEject;
13365 {
13366 AutoCaller autoAttachCaller(this);
13367 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13368
13369 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13370
13371 /* Need to query the details first, as the IMediumAttachment reference
13372 * might be to the original settings, which we are going to change. */
13373 ctrlName = pAttach->getControllerName();
13374 lPort = pAttach->getPort();
13375 lDevice = pAttach->getDevice();
13376 fTempEject = pAttach->getTempEject();
13377 }
13378
13379 if (!fTempEject)
13380 {
13381 /* Remember previously mounted medium. The medium before taking the
13382 * backup is not necessarily the same thing. */
13383 ComObjPtr<Medium> oldmedium;
13384 oldmedium = pAttach->getMedium();
13385
13386 setModified(IsModified_Storage);
13387 mMediaData.backup();
13388
13389 // The backup operation makes the pAttach reference point to the
13390 // old settings. Re-get the correct reference.
13391 pAttach = findAttachment(mMediaData->mAttachments,
13392 ctrlName.raw(),
13393 lPort,
13394 lDevice);
13395
13396 {
13397 AutoCaller autoAttachCaller(this);
13398 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13399
13400 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13401 if (!oldmedium.isNull())
13402 oldmedium->removeBackReference(mData->mUuid);
13403
13404 pAttach->updateMedium(NULL);
13405 pAttach->updateEjected();
13406 }
13407
13408 setModified(IsModified_Storage);
13409 }
13410 else
13411 {
13412 {
13413 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13414 pAttach->updateEjected();
13415 }
13416 }
13417
13418 pAttach.queryInterfaceTo(aNewAttachment);
13419
13420 return S_OK;
13421}
13422
13423// public methods only for internal purposes
13424/////////////////////////////////////////////////////////////////////////////
13425
13426/**
13427 * Called from the client watcher thread to check for expected or unexpected
13428 * death of the client process that has a direct session to this machine.
13429 *
13430 * On Win32 and on OS/2, this method is called only when we've got the
13431 * mutex (i.e. the client has either died or terminated normally) so it always
13432 * returns @c true (the client is terminated, the session machine is
13433 * uninitialized).
13434 *
13435 * On other platforms, the method returns @c true if the client process has
13436 * terminated normally or abnormally and the session machine was uninitialized,
13437 * and @c false if the client process is still alive.
13438 *
13439 * @note Locks this object for writing.
13440 */
13441bool SessionMachine::checkForDeath()
13442{
13443 Uninit::Reason reason;
13444 bool terminated = false;
13445
13446 /* Enclose autoCaller with a block because calling uninit() from under it
13447 * will deadlock. */
13448 {
13449 AutoCaller autoCaller(this);
13450 if (!autoCaller.isOk())
13451 {
13452 /* return true if not ready, to cause the client watcher to exclude
13453 * the corresponding session from watching */
13454 LogFlowThisFunc(("Already uninitialized!\n"));
13455 return true;
13456 }
13457
13458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13459
13460 /* Determine the reason of death: if the session state is Closing here,
13461 * everything is fine. Otherwise it means that the client did not call
13462 * OnSessionEnd() before it released the IPC semaphore. This may happen
13463 * either because the client process has abnormally terminated, or
13464 * because it simply forgot to call ISession::Close() before exiting. We
13465 * threat the latter also as an abnormal termination (see
13466 * Session::uninit() for details). */
13467 reason = mData->mSession.mState == SessionState_Unlocking ?
13468 Uninit::Normal :
13469 Uninit::Abnormal;
13470
13471#if defined(RT_OS_WINDOWS)
13472
13473 AssertMsg(mIPCSem, ("semaphore must be created"));
13474
13475 /* release the IPC mutex */
13476 ::ReleaseMutex(mIPCSem);
13477
13478 terminated = true;
13479
13480#elif defined(RT_OS_OS2)
13481
13482 AssertMsg(mIPCSem, ("semaphore must be created"));
13483
13484 /* release the IPC mutex */
13485 ::DosReleaseMutexSem(mIPCSem);
13486
13487 terminated = true;
13488
13489#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13490
13491 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13492
13493 int val = ::semctl(mIPCSem, 0, GETVAL);
13494 if (val > 0)
13495 {
13496 /* the semaphore is signaled, meaning the session is terminated */
13497 terminated = true;
13498 }
13499
13500#else
13501# error "Port me!"
13502#endif
13503
13504 } /* AutoCaller block */
13505
13506 if (terminated)
13507 uninit(reason);
13508
13509 return terminated;
13510}
13511
13512/**
13513 * @note Locks this object for reading.
13514 */
13515HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13516{
13517 LogFlowThisFunc(("\n"));
13518
13519 AutoCaller autoCaller(this);
13520 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13521
13522 ComPtr<IInternalSessionControl> directControl;
13523 {
13524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13525 directControl = mData->mSession.mDirectControl;
13526 }
13527
13528 /* ignore notifications sent after #OnSessionEnd() is called */
13529 if (!directControl)
13530 return S_OK;
13531
13532 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13533}
13534
13535/**
13536 * @note Locks this object for reading.
13537 */
13538HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13539 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13540{
13541 LogFlowThisFunc(("\n"));
13542
13543 AutoCaller autoCaller(this);
13544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13545
13546 ComPtr<IInternalSessionControl> directControl;
13547 {
13548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13549 directControl = mData->mSession.mDirectControl;
13550 }
13551
13552 /* ignore notifications sent after #OnSessionEnd() is called */
13553 if (!directControl)
13554 return S_OK;
13555 /*
13556 * instead acting like callback we ask IVirtualBox deliver corresponding event
13557 */
13558
13559 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13560 return S_OK;
13561}
13562
13563/**
13564 * @note Locks this object for reading.
13565 */
13566HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13567{
13568 LogFlowThisFunc(("\n"));
13569
13570 AutoCaller autoCaller(this);
13571 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13572
13573 ComPtr<IInternalSessionControl> directControl;
13574 {
13575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13576 directControl = mData->mSession.mDirectControl;
13577 }
13578
13579 /* ignore notifications sent after #OnSessionEnd() is called */
13580 if (!directControl)
13581 return S_OK;
13582
13583 return directControl->OnSerialPortChange(serialPort);
13584}
13585
13586/**
13587 * @note Locks this object for reading.
13588 */
13589HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13590{
13591 LogFlowThisFunc(("\n"));
13592
13593 AutoCaller autoCaller(this);
13594 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13595
13596 ComPtr<IInternalSessionControl> directControl;
13597 {
13598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13599 directControl = mData->mSession.mDirectControl;
13600 }
13601
13602 /* ignore notifications sent after #OnSessionEnd() is called */
13603 if (!directControl)
13604 return S_OK;
13605
13606 return directControl->OnParallelPortChange(parallelPort);
13607}
13608
13609/**
13610 * @note Locks this object for reading.
13611 */
13612HRESULT SessionMachine::onStorageControllerChange()
13613{
13614 LogFlowThisFunc(("\n"));
13615
13616 AutoCaller autoCaller(this);
13617 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13618
13619 ComPtr<IInternalSessionControl> directControl;
13620 {
13621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13622 directControl = mData->mSession.mDirectControl;
13623 }
13624
13625 /* ignore notifications sent after #OnSessionEnd() is called */
13626 if (!directControl)
13627 return S_OK;
13628
13629 return directControl->OnStorageControllerChange();
13630}
13631
13632/**
13633 * @note Locks this object for reading.
13634 */
13635HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13636{
13637 LogFlowThisFunc(("\n"));
13638
13639 AutoCaller autoCaller(this);
13640 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13641
13642 ComPtr<IInternalSessionControl> directControl;
13643 {
13644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13645 directControl = mData->mSession.mDirectControl;
13646 }
13647
13648 /* ignore notifications sent after #OnSessionEnd() is called */
13649 if (!directControl)
13650 return S_OK;
13651
13652 return directControl->OnMediumChange(aAttachment, aForce);
13653}
13654
13655/**
13656 * @note Locks this object for reading.
13657 */
13658HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13659{
13660 LogFlowThisFunc(("\n"));
13661
13662 AutoCaller autoCaller(this);
13663 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13664
13665 ComPtr<IInternalSessionControl> directControl;
13666 {
13667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13668 directControl = mData->mSession.mDirectControl;
13669 }
13670
13671 /* ignore notifications sent after #OnSessionEnd() is called */
13672 if (!directControl)
13673 return S_OK;
13674
13675 return directControl->OnCPUChange(aCPU, aRemove);
13676}
13677
13678HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13679{
13680 LogFlowThisFunc(("\n"));
13681
13682 AutoCaller autoCaller(this);
13683 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13684
13685 ComPtr<IInternalSessionControl> directControl;
13686 {
13687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13688 directControl = mData->mSession.mDirectControl;
13689 }
13690
13691 /* ignore notifications sent after #OnSessionEnd() is called */
13692 if (!directControl)
13693 return S_OK;
13694
13695 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13696}
13697
13698/**
13699 * @note Locks this object for reading.
13700 */
13701HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13702{
13703 LogFlowThisFunc(("\n"));
13704
13705 AutoCaller autoCaller(this);
13706 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13707
13708 ComPtr<IInternalSessionControl> directControl;
13709 {
13710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13711 directControl = mData->mSession.mDirectControl;
13712 }
13713
13714 /* ignore notifications sent after #OnSessionEnd() is called */
13715 if (!directControl)
13716 return S_OK;
13717
13718 return directControl->OnVRDEServerChange(aRestart);
13719}
13720
13721/**
13722 * @note Locks this object for reading.
13723 */
13724HRESULT SessionMachine::onUSBControllerChange()
13725{
13726 LogFlowThisFunc(("\n"));
13727
13728 AutoCaller autoCaller(this);
13729 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13730
13731 ComPtr<IInternalSessionControl> directControl;
13732 {
13733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13734 directControl = mData->mSession.mDirectControl;
13735 }
13736
13737 /* ignore notifications sent after #OnSessionEnd() is called */
13738 if (!directControl)
13739 return S_OK;
13740
13741 return directControl->OnUSBControllerChange();
13742}
13743
13744/**
13745 * @note Locks this object for reading.
13746 */
13747HRESULT SessionMachine::onSharedFolderChange()
13748{
13749 LogFlowThisFunc(("\n"));
13750
13751 AutoCaller autoCaller(this);
13752 AssertComRCReturnRC(autoCaller.rc());
13753
13754 ComPtr<IInternalSessionControl> directControl;
13755 {
13756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13757 directControl = mData->mSession.mDirectControl;
13758 }
13759
13760 /* ignore notifications sent after #OnSessionEnd() is called */
13761 if (!directControl)
13762 return S_OK;
13763
13764 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13765}
13766
13767/**
13768 * @note Locks this object for reading.
13769 */
13770HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13771{
13772 LogFlowThisFunc(("\n"));
13773
13774 AutoCaller autoCaller(this);
13775 AssertComRCReturnRC(autoCaller.rc());
13776
13777 ComPtr<IInternalSessionControl> directControl;
13778 {
13779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13780 directControl = mData->mSession.mDirectControl;
13781 }
13782
13783 /* ignore notifications sent after #OnSessionEnd() is called */
13784 if (!directControl)
13785 return S_OK;
13786
13787 return directControl->OnClipboardModeChange(aClipboardMode);
13788}
13789
13790/**
13791 * @note Locks this object for reading.
13792 */
13793HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13794{
13795 LogFlowThisFunc(("\n"));
13796
13797 AutoCaller autoCaller(this);
13798 AssertComRCReturnRC(autoCaller.rc());
13799
13800 ComPtr<IInternalSessionControl> directControl;
13801 {
13802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13803 directControl = mData->mSession.mDirectControl;
13804 }
13805
13806 /* ignore notifications sent after #OnSessionEnd() is called */
13807 if (!directControl)
13808 return S_OK;
13809
13810 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13811}
13812
13813/**
13814 * @note Locks this object for reading.
13815 */
13816HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13817{
13818 LogFlowThisFunc(("\n"));
13819
13820 AutoCaller autoCaller(this);
13821 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13822
13823 ComPtr<IInternalSessionControl> directControl;
13824 {
13825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13826 directControl = mData->mSession.mDirectControl;
13827 }
13828
13829 /* ignore notifications sent after #OnSessionEnd() is called */
13830 if (!directControl)
13831 return S_OK;
13832
13833 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13834}
13835
13836/**
13837 * @note Locks this object for reading.
13838 */
13839HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13840{
13841 LogFlowThisFunc(("\n"));
13842
13843 AutoCaller autoCaller(this);
13844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13845
13846 ComPtr<IInternalSessionControl> directControl;
13847 {
13848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13849 directControl = mData->mSession.mDirectControl;
13850 }
13851
13852 /* ignore notifications sent after #OnSessionEnd() is called */
13853 if (!directControl)
13854 return S_OK;
13855
13856 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13857}
13858
13859/**
13860 * Returns @c true if this machine's USB controller reports it has a matching
13861 * filter for the given USB device and @c false otherwise.
13862 *
13863 * @note locks this object for reading.
13864 */
13865bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13866{
13867 AutoCaller autoCaller(this);
13868 /* silently return if not ready -- this method may be called after the
13869 * direct machine session has been called */
13870 if (!autoCaller.isOk())
13871 return false;
13872
13873#ifdef VBOX_WITH_USB
13874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13875
13876 switch (mData->mMachineState)
13877 {
13878 case MachineState_Starting:
13879 case MachineState_Restoring:
13880 case MachineState_TeleportingIn:
13881 case MachineState_Paused:
13882 case MachineState_Running:
13883 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13884 * elsewhere... */
13885 alock.release();
13886 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13887 default: break;
13888 }
13889#else
13890 NOREF(aDevice);
13891 NOREF(aMaskedIfs);
13892#endif
13893 return false;
13894}
13895
13896/**
13897 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13898 */
13899HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13900 IVirtualBoxErrorInfo *aError,
13901 ULONG aMaskedIfs)
13902{
13903 LogFlowThisFunc(("\n"));
13904
13905 AutoCaller autoCaller(this);
13906
13907 /* This notification may happen after the machine object has been
13908 * uninitialized (the session was closed), so don't assert. */
13909 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13910
13911 ComPtr<IInternalSessionControl> directControl;
13912 {
13913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13914 directControl = mData->mSession.mDirectControl;
13915 }
13916
13917 /* fail on notifications sent after #OnSessionEnd() is called, it is
13918 * expected by the caller */
13919 if (!directControl)
13920 return E_FAIL;
13921
13922 /* No locks should be held at this point. */
13923 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13924 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13925
13926 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13927}
13928
13929/**
13930 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13931 */
13932HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13933 IVirtualBoxErrorInfo *aError)
13934{
13935 LogFlowThisFunc(("\n"));
13936
13937 AutoCaller autoCaller(this);
13938
13939 /* This notification may happen after the machine object has been
13940 * uninitialized (the session was closed), so don't assert. */
13941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13942
13943 ComPtr<IInternalSessionControl> directControl;
13944 {
13945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13946 directControl = mData->mSession.mDirectControl;
13947 }
13948
13949 /* fail on notifications sent after #OnSessionEnd() is called, it is
13950 * expected by the caller */
13951 if (!directControl)
13952 return E_FAIL;
13953
13954 /* No locks should be held at this point. */
13955 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13956 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13957
13958 return directControl->OnUSBDeviceDetach(aId, aError);
13959}
13960
13961// protected methods
13962/////////////////////////////////////////////////////////////////////////////
13963
13964/**
13965 * Helper method to finalize saving the state.
13966 *
13967 * @note Must be called from under this object's lock.
13968 *
13969 * @param aRc S_OK if the snapshot has been taken successfully
13970 * @param aErrMsg human readable error message for failure
13971 *
13972 * @note Locks mParent + this objects for writing.
13973 */
13974HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13975{
13976 LogFlowThisFuncEnter();
13977
13978 AutoCaller autoCaller(this);
13979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13980
13981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13982
13983 HRESULT rc = S_OK;
13984
13985 if (SUCCEEDED(aRc))
13986 {
13987 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13988
13989 /* save all VM settings */
13990 rc = saveSettings(NULL);
13991 // no need to check whether VirtualBox.xml needs saving also since
13992 // we can't have a name change pending at this point
13993 }
13994 else
13995 {
13996 // delete the saved state file (it might have been already created);
13997 // we need not check whether this is shared with a snapshot here because
13998 // we certainly created this saved state file here anew
13999 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14000 }
14001
14002 /* notify the progress object about operation completion */
14003 Assert(mConsoleTaskData.mProgress);
14004 if (SUCCEEDED(aRc))
14005 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14006 else
14007 {
14008 if (aErrMsg.length())
14009 mConsoleTaskData.mProgress->notifyComplete(aRc,
14010 COM_IIDOF(ISession),
14011 getComponentName(),
14012 aErrMsg.c_str());
14013 else
14014 mConsoleTaskData.mProgress->notifyComplete(aRc);
14015 }
14016
14017 /* clear out the temporary saved state data */
14018 mConsoleTaskData.mLastState = MachineState_Null;
14019 mConsoleTaskData.strStateFilePath.setNull();
14020 mConsoleTaskData.mProgress.setNull();
14021
14022 LogFlowThisFuncLeave();
14023 return rc;
14024}
14025
14026/**
14027 * Deletes the given file if it is no longer in use by either the current machine state
14028 * (if the machine is "saved") or any of the machine's snapshots.
14029 *
14030 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14031 * but is different for each SnapshotMachine. When calling this, the order of calling this
14032 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14033 * is therefore critical. I know, it's all rather messy.
14034 *
14035 * @param strStateFile
14036 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14037 */
14038void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14039 Snapshot *pSnapshotToIgnore)
14040{
14041 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14042 if ( (strStateFile.isNotEmpty())
14043 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14044 )
14045 // ... and it must also not be shared with other snapshots
14046 if ( !mData->mFirstSnapshot
14047 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14048 // this checks the SnapshotMachine's state file paths
14049 )
14050 RTFileDelete(strStateFile.c_str());
14051}
14052
14053/**
14054 * Locks the attached media.
14055 *
14056 * All attached hard disks are locked for writing and DVD/floppy are locked for
14057 * reading. Parents of attached hard disks (if any) are locked for reading.
14058 *
14059 * This method also performs accessibility check of all media it locks: if some
14060 * media is inaccessible, the method will return a failure and a bunch of
14061 * extended error info objects per each inaccessible medium.
14062 *
14063 * Note that this method is atomic: if it returns a success, all media are
14064 * locked as described above; on failure no media is locked at all (all
14065 * succeeded individual locks will be undone).
14066 *
14067 * The caller is responsible for doing the necessary state sanity checks.
14068 *
14069 * The locks made by this method must be undone by calling #unlockMedia() when
14070 * no more needed.
14071 */
14072HRESULT SessionMachine::lockMedia()
14073{
14074 AutoCaller autoCaller(this);
14075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14076
14077 AutoMultiWriteLock2 alock(this->lockHandle(),
14078 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14079
14080 /* bail out if trying to lock things with already set up locking */
14081 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14082
14083 MultiResult mrc(S_OK);
14084
14085 /* Collect locking information for all medium objects attached to the VM. */
14086 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14087 it != mMediaData->mAttachments.end();
14088 ++it)
14089 {
14090 MediumAttachment* pAtt = *it;
14091 DeviceType_T devType = pAtt->getType();
14092 Medium *pMedium = pAtt->getMedium();
14093
14094 MediumLockList *pMediumLockList(new MediumLockList());
14095 // There can be attachments without a medium (floppy/dvd), and thus
14096 // it's impossible to create a medium lock list. It still makes sense
14097 // to have the empty medium lock list in the map in case a medium is
14098 // attached later.
14099 if (pMedium != NULL)
14100 {
14101 MediumType_T mediumType = pMedium->getType();
14102 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14103 || mediumType == MediumType_Shareable;
14104 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14105
14106 alock.release();
14107 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14108 !fIsReadOnlyLock /* fMediumLockWrite */,
14109 NULL,
14110 *pMediumLockList);
14111 alock.acquire();
14112 if (FAILED(mrc))
14113 {
14114 delete pMediumLockList;
14115 mData->mSession.mLockedMedia.Clear();
14116 break;
14117 }
14118 }
14119
14120 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14121 if (FAILED(rc))
14122 {
14123 mData->mSession.mLockedMedia.Clear();
14124 mrc = setError(rc,
14125 tr("Collecting locking information for all attached media failed"));
14126 break;
14127 }
14128 }
14129
14130 if (SUCCEEDED(mrc))
14131 {
14132 /* Now lock all media. If this fails, nothing is locked. */
14133 alock.release();
14134 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14135 alock.acquire();
14136 if (FAILED(rc))
14137 {
14138 mrc = setError(rc,
14139 tr("Locking of attached media failed"));
14140 }
14141 }
14142
14143 return mrc;
14144}
14145
14146/**
14147 * Undoes the locks made by by #lockMedia().
14148 */
14149void SessionMachine::unlockMedia()
14150{
14151 AutoCaller autoCaller(this);
14152 AssertComRCReturnVoid(autoCaller.rc());
14153
14154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14155
14156 /* we may be holding important error info on the current thread;
14157 * preserve it */
14158 ErrorInfoKeeper eik;
14159
14160 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14161 AssertComRC(rc);
14162}
14163
14164/**
14165 * Helper to change the machine state (reimplementation).
14166 *
14167 * @note Locks this object for writing.
14168 * @note This method must not call saveSettings or SaveSettings, otherwise
14169 * it can cause crashes in random places due to unexpectedly committing
14170 * the current settings. The caller is responsible for that. The call
14171 * to saveStateSettings is fine, because this method does not commit.
14172 */
14173HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14174{
14175 LogFlowThisFuncEnter();
14176 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14177
14178 AutoCaller autoCaller(this);
14179 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14180
14181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14182
14183 MachineState_T oldMachineState = mData->mMachineState;
14184
14185 AssertMsgReturn(oldMachineState != aMachineState,
14186 ("oldMachineState=%s, aMachineState=%s\n",
14187 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14188 E_FAIL);
14189
14190 HRESULT rc = S_OK;
14191
14192 int stsFlags = 0;
14193 bool deleteSavedState = false;
14194
14195 /* detect some state transitions */
14196
14197 if ( ( oldMachineState == MachineState_Saved
14198 && aMachineState == MachineState_Restoring)
14199 || ( ( oldMachineState == MachineState_PoweredOff
14200 || oldMachineState == MachineState_Teleported
14201 || oldMachineState == MachineState_Aborted
14202 )
14203 && ( aMachineState == MachineState_TeleportingIn
14204 || aMachineState == MachineState_Starting
14205 )
14206 )
14207 )
14208 {
14209 /* The EMT thread is about to start */
14210
14211 /* Nothing to do here for now... */
14212
14213 /// @todo NEWMEDIA don't let mDVDDrive and other children
14214 /// change anything when in the Starting/Restoring state
14215 }
14216 else if ( ( oldMachineState == MachineState_Running
14217 || oldMachineState == MachineState_Paused
14218 || oldMachineState == MachineState_Teleporting
14219 || oldMachineState == MachineState_LiveSnapshotting
14220 || oldMachineState == MachineState_Stuck
14221 || oldMachineState == MachineState_Starting
14222 || oldMachineState == MachineState_Stopping
14223 || oldMachineState == MachineState_Saving
14224 || oldMachineState == MachineState_Restoring
14225 || oldMachineState == MachineState_TeleportingPausedVM
14226 || oldMachineState == MachineState_TeleportingIn
14227 )
14228 && ( aMachineState == MachineState_PoweredOff
14229 || aMachineState == MachineState_Saved
14230 || aMachineState == MachineState_Teleported
14231 || aMachineState == MachineState_Aborted
14232 )
14233 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14234 * snapshot */
14235 && ( mConsoleTaskData.mSnapshot.isNull()
14236 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14237 )
14238 )
14239 {
14240 /* The EMT thread has just stopped, unlock attached media. Note that as
14241 * opposed to locking that is done from Console, we do unlocking here
14242 * because the VM process may have aborted before having a chance to
14243 * properly unlock all media it locked. */
14244
14245 unlockMedia();
14246 }
14247
14248 if (oldMachineState == MachineState_Restoring)
14249 {
14250 if (aMachineState != MachineState_Saved)
14251 {
14252 /*
14253 * delete the saved state file once the machine has finished
14254 * restoring from it (note that Console sets the state from
14255 * Restoring to Saved if the VM couldn't restore successfully,
14256 * to give the user an ability to fix an error and retry --
14257 * we keep the saved state file in this case)
14258 */
14259 deleteSavedState = true;
14260 }
14261 }
14262 else if ( oldMachineState == MachineState_Saved
14263 && ( aMachineState == MachineState_PoweredOff
14264 || aMachineState == MachineState_Aborted
14265 || aMachineState == MachineState_Teleported
14266 )
14267 )
14268 {
14269 /*
14270 * delete the saved state after Console::ForgetSavedState() is called
14271 * or if the VM process (owning a direct VM session) crashed while the
14272 * VM was Saved
14273 */
14274
14275 /// @todo (dmik)
14276 // Not sure that deleting the saved state file just because of the
14277 // client death before it attempted to restore the VM is a good
14278 // thing. But when it crashes we need to go to the Aborted state
14279 // which cannot have the saved state file associated... The only
14280 // way to fix this is to make the Aborted condition not a VM state
14281 // but a bool flag: i.e., when a crash occurs, set it to true and
14282 // change the state to PoweredOff or Saved depending on the
14283 // saved state presence.
14284
14285 deleteSavedState = true;
14286 mData->mCurrentStateModified = TRUE;
14287 stsFlags |= SaveSTS_CurStateModified;
14288 }
14289
14290 if ( aMachineState == MachineState_Starting
14291 || aMachineState == MachineState_Restoring
14292 || aMachineState == MachineState_TeleportingIn
14293 )
14294 {
14295 /* set the current state modified flag to indicate that the current
14296 * state is no more identical to the state in the
14297 * current snapshot */
14298 if (!mData->mCurrentSnapshot.isNull())
14299 {
14300 mData->mCurrentStateModified = TRUE;
14301 stsFlags |= SaveSTS_CurStateModified;
14302 }
14303 }
14304
14305 if (deleteSavedState)
14306 {
14307 if (mRemoveSavedState)
14308 {
14309 Assert(!mSSData->strStateFilePath.isEmpty());
14310
14311 // it is safe to delete the saved state file if ...
14312 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14313 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14314 // ... none of the snapshots share the saved state file
14315 )
14316 RTFileDelete(mSSData->strStateFilePath.c_str());
14317 }
14318
14319 mSSData->strStateFilePath.setNull();
14320 stsFlags |= SaveSTS_StateFilePath;
14321 }
14322
14323 /* redirect to the underlying peer machine */
14324 mPeer->setMachineState(aMachineState);
14325
14326 if ( aMachineState == MachineState_PoweredOff
14327 || aMachineState == MachineState_Teleported
14328 || aMachineState == MachineState_Aborted
14329 || aMachineState == MachineState_Saved)
14330 {
14331 /* the machine has stopped execution
14332 * (or the saved state file was adopted) */
14333 stsFlags |= SaveSTS_StateTimeStamp;
14334 }
14335
14336 if ( ( oldMachineState == MachineState_PoweredOff
14337 || oldMachineState == MachineState_Aborted
14338 || oldMachineState == MachineState_Teleported
14339 )
14340 && aMachineState == MachineState_Saved)
14341 {
14342 /* the saved state file was adopted */
14343 Assert(!mSSData->strStateFilePath.isEmpty());
14344 stsFlags |= SaveSTS_StateFilePath;
14345 }
14346
14347#ifdef VBOX_WITH_GUEST_PROPS
14348 if ( aMachineState == MachineState_PoweredOff
14349 || aMachineState == MachineState_Aborted
14350 || aMachineState == MachineState_Teleported)
14351 {
14352 /* Make sure any transient guest properties get removed from the
14353 * property store on shutdown. */
14354
14355 HWData::GuestPropertyMap::const_iterator it;
14356 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14357 if (!fNeedsSaving)
14358 for (it = mHWData->mGuestProperties.begin();
14359 it != mHWData->mGuestProperties.end(); ++it)
14360 if ( (it->second.mFlags & guestProp::TRANSIENT)
14361 || (it->second.mFlags & guestProp::TRANSRESET))
14362 {
14363 fNeedsSaving = true;
14364 break;
14365 }
14366 if (fNeedsSaving)
14367 {
14368 mData->mCurrentStateModified = TRUE;
14369 stsFlags |= SaveSTS_CurStateModified;
14370 }
14371 }
14372#endif
14373
14374 rc = saveStateSettings(stsFlags);
14375
14376 if ( ( oldMachineState != MachineState_PoweredOff
14377 && oldMachineState != MachineState_Aborted
14378 && oldMachineState != MachineState_Teleported
14379 )
14380 && ( aMachineState == MachineState_PoweredOff
14381 || aMachineState == MachineState_Aborted
14382 || aMachineState == MachineState_Teleported
14383 )
14384 )
14385 {
14386 /* we've been shut down for any reason */
14387 /* no special action so far */
14388 }
14389
14390 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14391 LogFlowThisFuncLeave();
14392 return rc;
14393}
14394
14395/**
14396 * Sends the current machine state value to the VM process.
14397 *
14398 * @note Locks this object for reading, then calls a client process.
14399 */
14400HRESULT SessionMachine::updateMachineStateOnClient()
14401{
14402 AutoCaller autoCaller(this);
14403 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14404
14405 ComPtr<IInternalSessionControl> directControl;
14406 {
14407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14408 AssertReturn(!!mData, E_FAIL);
14409 directControl = mData->mSession.mDirectControl;
14410
14411 /* directControl may be already set to NULL here in #OnSessionEnd()
14412 * called too early by the direct session process while there is still
14413 * some operation (like deleting the snapshot) in progress. The client
14414 * process in this case is waiting inside Session::close() for the
14415 * "end session" process object to complete, while #uninit() called by
14416 * #checkForDeath() on the Watcher thread is waiting for the pending
14417 * operation to complete. For now, we accept this inconsistent behavior
14418 * and simply do nothing here. */
14419
14420 if (mData->mSession.mState == SessionState_Unlocking)
14421 return S_OK;
14422
14423 AssertReturn(!directControl.isNull(), E_FAIL);
14424 }
14425
14426 return directControl->UpdateMachineState(mData->mMachineState);
14427}
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