VirtualBox

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

Last change on this file since 51584 was 51567, checked in by vboxsync, 11 years ago

6813 MachineImpl.cpp server side com wrapper API useage - further fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 492.5 KB
Line 
1/* $Id: MachineImpl.cpp 51567 2014-06-06 11:51:41Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureEnabled = false;
169 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
170 maVideoCaptureScreens[i] = true;
171
172 mHWVirtExEnabled = true;
173 mHWVirtExNestedPagingEnabled = true;
174#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
175 mHWVirtExLargePagesEnabled = true;
176#else
177 /* Not supported on 32 bits hosts. */
178 mHWVirtExLargePagesEnabled = false;
179#endif
180 mHWVirtExVPIDEnabled = true;
181 mHWVirtExUXEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
189 mSyntheticCpu = false;
190 mTripleFaultReset = false;
191 mHPETEnabled = false;
192
193 /* default boot order: floppy - DVD - HDD */
194 mBootOrder[0] = DeviceType_Floppy;
195 mBootOrder[1] = DeviceType_DVD;
196 mBootOrder[2] = DeviceType_HardDisk;
197 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
198 mBootOrder[i] = DeviceType_Null;
199
200 mClipboardMode = ClipboardMode_Disabled;
201 mDnDMode = DnDMode_Disabled;
202 mGuestPropertyNotificationPatterns = "";
203
204 mFirmwareType = FirmwareType_BIOS;
205 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
206 mPointingHIDType = PointingHIDType_PS2Mouse;
207 mChipsetType = ChipsetType_PIIX3;
208 mParavirtProvider = ParavirtProvider_Default;
209 mEmulatedUSBCardReaderEnabled = FALSE;
210
211 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
212 mCPUAttached[i] = false;
213
214 mIOCacheEnabled = true;
215 mIOCacheSize = 5; /* 5MB */
216
217 /* Maximum CPU execution cap by default. */
218 mCpuExecutionCap = 100;
219}
220
221Machine::HWData::~HWData()
222{
223}
224
225/////////////////////////////////////////////////////////////////////////////
226// Machine::HDData structure
227/////////////////////////////////////////////////////////////////////////////
228
229Machine::MediaData::MediaData()
230{
231}
232
233Machine::MediaData::~MediaData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 GuestOSType *aOsType,
291 const Guid &aId,
292 bool fForceOverwrite,
293 bool fDirectoryIncludesUUID)
294{
295 LogFlowThisFuncEnter();
296 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
297
298 /* Enclose the state transition NotReady->InInit->Ready */
299 AutoInitSpan autoInitSpan(this);
300 AssertReturn(autoInitSpan.isOk(), E_FAIL);
301
302 HRESULT rc = initImpl(aParent, strConfigFile);
303 if (FAILED(rc)) return rc;
304
305 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
306 if (FAILED(rc)) return rc;
307
308 if (SUCCEEDED(rc))
309 {
310 // create an empty machine config
311 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
312
313 rc = initDataAndChildObjects();
314 }
315
316 if (SUCCEEDED(rc))
317 {
318 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
319 mData->mAccessible = TRUE;
320
321 unconst(mData->mUuid) = aId;
322
323 mUserData->s.strName = strName;
324
325 mUserData->s.llGroups = llGroups;
326
327 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
328 // the "name sync" flag determines whether the machine directory gets renamed along
329 // with the machine file; say so if the settings file name is the same as the
330 // settings file parent directory (machine directory)
331 mUserData->s.fNameSync = i_isInOwnDir();
332
333 // initialize the default snapshots folder
334 rc = COMSETTER(SnapshotFolder)(NULL);
335 AssertComRC(rc);
336
337 if (aOsType)
338 {
339 /* Store OS type */
340 mUserData->s.strOsType = aOsType->i_id();
341
342 /* Apply BIOS defaults */
343 mBIOSSettings->i_applyDefaults(aOsType);
344
345 /* Apply network adapters defaults */
346 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
347 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
348
349 /* Apply serial port defaults */
350 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
351 mSerialPorts[slot]->i_applyDefaults(aOsType);
352
353 /* Let the OS type select 64-bit ness. */
354 mHWData->mLongMode = aOsType->i_is64Bit()
355 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
356 }
357
358 /* At this point the changing of the current state modification
359 * flag is allowed. */
360 i_allowStateModification();
361
362 /* commit all changes made during the initialization */
363 i_commit();
364 }
365
366 /* Confirm a successful initialization when it's the case */
367 if (SUCCEEDED(rc))
368 {
369 if (mData->mAccessible)
370 autoInitSpan.setSucceeded();
371 else
372 autoInitSpan.setLimited();
373 }
374
375 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
376 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
377 mData->mRegistered,
378 mData->mAccessible,
379 rc));
380
381 LogFlowThisFuncLeave();
382
383 return rc;
384}
385
386/**
387 * Initializes a new instance with data from machine XML (formerly Init_Registered).
388 * Gets called in two modes:
389 *
390 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
391 * UUID is specified and we mark the machine as "registered";
392 *
393 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
394 * and the machine remains unregistered until RegisterMachine() is called.
395 *
396 * @param aParent Associated parent object
397 * @param aConfigFile Local file system path to the VM settings file (can
398 * be relative to the VirtualBox config directory).
399 * @param aId UUID of the machine or NULL (see above).
400 *
401 * @return Success indicator. if not S_OK, the machine object is invalid
402 */
403HRESULT Machine::initFromSettings(VirtualBox *aParent,
404 const Utf8Str &strConfigFile,
405 const Guid *aId)
406{
407 LogFlowThisFuncEnter();
408 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
409
410 /* Enclose the state transition NotReady->InInit->Ready */
411 AutoInitSpan autoInitSpan(this);
412 AssertReturn(autoInitSpan.isOk(), E_FAIL);
413
414 HRESULT rc = initImpl(aParent, strConfigFile);
415 if (FAILED(rc)) return rc;
416
417 if (aId)
418 {
419 // loading a registered VM:
420 unconst(mData->mUuid) = *aId;
421 mData->mRegistered = TRUE;
422 // now load the settings from XML:
423 rc = i_registeredInit();
424 // this calls initDataAndChildObjects() and loadSettings()
425 }
426 else
427 {
428 // opening an unregistered VM (VirtualBox::OpenMachine()):
429 rc = initDataAndChildObjects();
430
431 if (SUCCEEDED(rc))
432 {
433 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
434 mData->mAccessible = TRUE;
435
436 try
437 {
438 // load and parse machine XML; this will throw on XML or logic errors
439 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
440
441 // reject VM UUID duplicates, they can happen if someone
442 // tries to register an already known VM config again
443 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
444 true /* fPermitInaccessible */,
445 false /* aDoSetError */,
446 NULL) != VBOX_E_OBJECT_NOT_FOUND)
447 {
448 throw setError(E_FAIL,
449 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
450 mData->m_strConfigFile.c_str());
451 }
452
453 // use UUID from machine config
454 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
455
456 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
457 NULL /* puuidRegistry */);
458 if (FAILED(rc)) throw rc;
459
460 /* At this point the changing of the current state modification
461 * flag is allowed. */
462 i_allowStateModification();
463
464 i_commit();
465 }
466 catch (HRESULT err)
467 {
468 /* we assume that error info is set by the thrower */
469 rc = err;
470 }
471 catch (...)
472 {
473 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
474 }
475 }
476 }
477
478 /* Confirm a successful initialization when it's the case */
479 if (SUCCEEDED(rc))
480 {
481 if (mData->mAccessible)
482 autoInitSpan.setSucceeded();
483 else
484 {
485 autoInitSpan.setLimited();
486
487 // uninit media from this machine's media registry, or else
488 // reloading the settings will fail
489 mParent->i_unregisterMachineMedia(i_getId());
490 }
491 }
492
493 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
494 "rc=%08X\n",
495 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
496 mData->mRegistered, mData->mAccessible, rc));
497
498 LogFlowThisFuncLeave();
499
500 return rc;
501}
502
503/**
504 * Initializes a new instance from a machine config that is already in memory
505 * (import OVF case). Since we are importing, the UUID in the machine
506 * config is ignored and we always generate a fresh one.
507 *
508 * @param strName Name for the new machine; this overrides what is specified in config and is used
509 * for the settings file as well.
510 * @param config Machine configuration loaded and parsed from XML.
511 *
512 * @return Success indicator. if not S_OK, the machine object is invalid
513 */
514HRESULT Machine::init(VirtualBox *aParent,
515 const Utf8Str &strName,
516 const settings::MachineConfigFile &config)
517{
518 LogFlowThisFuncEnter();
519
520 /* Enclose the state transition NotReady->InInit->Ready */
521 AutoInitSpan autoInitSpan(this);
522 AssertReturn(autoInitSpan.isOk(), E_FAIL);
523
524 Utf8Str strConfigFile;
525 aParent->i_getDefaultMachineFolder(strConfigFile);
526 strConfigFile.append(RTPATH_DELIMITER);
527 strConfigFile.append(strName);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(".vbox");
531
532 HRESULT rc = initImpl(aParent, strConfigFile);
533 if (FAILED(rc)) return rc;
534
535 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
536 if (FAILED(rc)) return rc;
537
538 rc = initDataAndChildObjects();
539
540 if (SUCCEEDED(rc))
541 {
542 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
543 mData->mAccessible = TRUE;
544
545 // create empty machine config for instance data
546 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
547
548 // generate fresh UUID, ignore machine config
549 unconst(mData->mUuid).create();
550
551 rc = i_loadMachineDataFromSettings(config,
552 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
553
554 // override VM name as well, it may be different
555 mUserData->s.strName = strName;
556
557 if (SUCCEEDED(rc))
558 {
559 /* At this point the changing of the current state modification
560 * flag is allowed. */
561 i_allowStateModification();
562
563 /* commit all changes made during the initialization */
564 i_commit();
565 }
566 }
567
568 /* Confirm a successful initialization when it's the case */
569 if (SUCCEEDED(rc))
570 {
571 if (mData->mAccessible)
572 autoInitSpan.setSucceeded();
573 else
574 {
575 /* Ignore all errors from unregistering, they would destroy
576- * the more interesting error information we already have,
577- * pinpointing the issue with the VM config. */
578 ErrorInfoKeeper eik;
579
580 autoInitSpan.setLimited();
581
582 // uninit media from this machine's media registry, or else
583 // reloading the settings will fail
584 mParent->i_unregisterMachineMedia(i_getId());
585 }
586 }
587
588 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
589 "rc=%08X\n",
590 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
591 mData->mRegistered, mData->mAccessible, rc));
592
593 LogFlowThisFuncLeave();
594
595 return rc;
596}
597
598/**
599 * Shared code between the various init() implementations.
600 * @param aParent
601 * @return
602 */
603HRESULT Machine::initImpl(VirtualBox *aParent,
604 const Utf8Str &strConfigFile)
605{
606 LogFlowThisFuncEnter();
607
608 AssertReturn(aParent, E_INVALIDARG);
609 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
610
611 HRESULT rc = S_OK;
612
613 /* share the parent weakly */
614 unconst(mParent) = aParent;
615
616 /* allocate the essential machine data structure (the rest will be
617 * allocated later by initDataAndChildObjects() */
618 mData.allocate();
619
620 /* memorize the config file name (as provided) */
621 mData->m_strConfigFile = strConfigFile;
622
623 /* get the full file name */
624 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
625 if (RT_FAILURE(vrc1))
626 return setError(VBOX_E_FILE_ERROR,
627 tr("Invalid machine settings file name '%s' (%Rrc)"),
628 strConfigFile.c_str(),
629 vrc1);
630
631 LogFlowThisFuncLeave();
632
633 return rc;
634}
635
636/**
637 * Tries to create a machine settings file in the path stored in the machine
638 * instance data. Used when a new machine is created to fail gracefully if
639 * the settings file could not be written (e.g. because machine dir is read-only).
640 * @return
641 */
642HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
643{
644 HRESULT rc = S_OK;
645
646 // when we create a new machine, we must be able to create the settings file
647 RTFILE f = NIL_RTFILE;
648 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
649 if ( RT_SUCCESS(vrc)
650 || vrc == VERR_SHARING_VIOLATION
651 )
652 {
653 if (RT_SUCCESS(vrc))
654 RTFileClose(f);
655 if (!fForceOverwrite)
656 rc = setError(VBOX_E_FILE_ERROR,
657 tr("Machine settings file '%s' already exists"),
658 mData->m_strConfigFileFull.c_str());
659 else
660 {
661 /* try to delete the config file, as otherwise the creation
662 * of a new settings file will fail. */
663 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
664 if (RT_FAILURE(vrc2))
665 rc = setError(VBOX_E_FILE_ERROR,
666 tr("Could not delete the existing settings file '%s' (%Rrc)"),
667 mData->m_strConfigFileFull.c_str(), vrc2);
668 }
669 }
670 else if ( vrc != VERR_FILE_NOT_FOUND
671 && vrc != VERR_PATH_NOT_FOUND
672 )
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Invalid machine settings file name '%s' (%Rrc)"),
675 mData->m_strConfigFileFull.c_str(),
676 vrc);
677 return rc;
678}
679
680/**
681 * Initializes the registered machine by loading the settings file.
682 * This method is separated from #init() in order to make it possible to
683 * retry the operation after VirtualBox startup instead of refusing to
684 * startup the whole VirtualBox server in case if the settings file of some
685 * registered VM is invalid or inaccessible.
686 *
687 * @note Must be always called from this object's write lock
688 * (unless called from #init() that doesn't need any locking).
689 * @note Locks the mUSBController method for writing.
690 * @note Subclasses must not call this method.
691 */
692HRESULT Machine::i_registeredInit()
693{
694 AssertReturn(!i_isSessionMachine(), E_FAIL);
695 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
696 AssertReturn(mData->mUuid.isValid(), E_FAIL);
697 AssertReturn(!mData->mAccessible, E_FAIL);
698
699 HRESULT rc = initDataAndChildObjects();
700
701 if (SUCCEEDED(rc))
702 {
703 /* Temporarily reset the registered flag in order to let setters
704 * potentially called from loadSettings() succeed (isMutable() used in
705 * all setters will return FALSE for a Machine instance if mRegistered
706 * is TRUE). */
707 mData->mRegistered = FALSE;
708
709 try
710 {
711 // load and parse machine XML; this will throw on XML or logic errors
712 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
713
714 if (mData->mUuid != mData->pMachineConfigFile->uuid)
715 throw setError(E_FAIL,
716 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
717 mData->pMachineConfigFile->uuid.raw(),
718 mData->m_strConfigFileFull.c_str(),
719 mData->mUuid.toString().c_str(),
720 mParent->i_settingsFilePath().c_str());
721
722 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
723 NULL /* const Guid *puuidRegistry */);
724 if (FAILED(rc)) throw rc;
725 }
726 catch (HRESULT err)
727 {
728 /* we assume that error info is set by the thrower */
729 rc = err;
730 }
731 catch (...)
732 {
733 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
734 }
735
736 /* Restore the registered flag (even on failure) */
737 mData->mRegistered = TRUE;
738 }
739
740 if (SUCCEEDED(rc))
741 {
742 /* Set mAccessible to TRUE only if we successfully locked and loaded
743 * the settings file */
744 mData->mAccessible = TRUE;
745
746 /* commit all changes made during loading the settings file */
747 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
748 /// @todo r=klaus for some reason the settings loading logic backs up
749 // the settings, and therefore a commit is needed. Should probably be changed.
750 }
751 else
752 {
753 /* If the machine is registered, then, instead of returning a
754 * failure, we mark it as inaccessible and set the result to
755 * success to give it a try later */
756
757 /* fetch the current error info */
758 mData->mAccessError = com::ErrorInfo();
759 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
760 mData->mUuid.raw(),
761 mData->mAccessError.getText().raw()));
762
763 /* rollback all changes */
764 i_rollback(false /* aNotify */);
765
766 // uninit media from this machine's media registry, or else
767 // reloading the settings will fail
768 mParent->i_unregisterMachineMedia(i_getId());
769
770 /* uninitialize the common part to make sure all data is reset to
771 * default (null) values */
772 uninitDataAndChildObjects();
773
774 rc = S_OK;
775 }
776
777 return rc;
778}
779
780/**
781 * Uninitializes the instance.
782 * Called either from FinalRelease() or by the parent when it gets destroyed.
783 *
784 * @note The caller of this method must make sure that this object
785 * a) doesn't have active callers on the current thread and b) is not locked
786 * by the current thread; otherwise uninit() will hang either a) due to
787 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
788 * a dead-lock caused by this thread waiting for all callers on the other
789 * threads are done but preventing them from doing so by holding a lock.
790 */
791void Machine::uninit()
792{
793 LogFlowThisFuncEnter();
794
795 Assert(!isWriteLockOnCurrentThread());
796
797 Assert(!uRegistryNeedsSaving);
798 if (uRegistryNeedsSaving)
799 {
800 AutoCaller autoCaller(this);
801 if (SUCCEEDED(autoCaller.rc()))
802 {
803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
804 i_saveSettings(NULL, Machine::SaveS_Force);
805 }
806 }
807
808 /* Enclose the state transition Ready->InUninit->NotReady */
809 AutoUninitSpan autoUninitSpan(this);
810 if (autoUninitSpan.uninitDone())
811 return;
812
813 Assert(!i_isSnapshotMachine());
814 Assert(!i_isSessionMachine());
815 Assert(!!mData);
816
817 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
818 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
819
820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
821
822 if (!mData->mSession.mMachine.isNull())
823 {
824 /* Theoretically, this can only happen if the VirtualBox server has been
825 * terminated while there were clients running that owned open direct
826 * sessions. Since in this case we are definitely called by
827 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
828 * won't happen on the client watcher thread (because it does
829 * VirtualBox::addCaller() for the duration of the
830 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
831 * cannot happen until the VirtualBox caller is released). This is
832 * important, because SessionMachine::uninit() cannot correctly operate
833 * after we return from this method (it expects the Machine instance is
834 * still valid). We'll call it ourselves below.
835 */
836 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
837 (SessionMachine*)mData->mSession.mMachine));
838
839 if (Global::IsOnlineOrTransient(mData->mMachineState))
840 {
841 LogWarningThisFunc(("Setting state to Aborted!\n"));
842 /* set machine state using SessionMachine reimplementation */
843 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
844 }
845
846 /*
847 * Uninitialize SessionMachine using public uninit() to indicate
848 * an unexpected uninitialization.
849 */
850 mData->mSession.mMachine->uninit();
851 /* SessionMachine::uninit() must set mSession.mMachine to null */
852 Assert(mData->mSession.mMachine.isNull());
853 }
854
855 // uninit media from this machine's media registry, if they're still there
856 Guid uuidMachine(i_getId());
857
858 /* the lock is no more necessary (SessionMachine is uninitialized) */
859 alock.release();
860
861 /* XXX This will fail with
862 * "cannot be closed because it is still attached to 1 virtual machines"
863 * because at this point we did not call uninitDataAndChildObjects() yet
864 * and therefore also removeBackReference() for all these mediums was not called! */
865
866 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
867 mParent->i_unregisterMachineMedia(uuidMachine);
868
869 // has machine been modified?
870 if (mData->flModifications)
871 {
872 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
873 i_rollback(false /* aNotify */);
874 }
875
876 if (mData->mAccessible)
877 uninitDataAndChildObjects();
878
879 /* free the essential data structure last */
880 mData.free();
881
882 LogFlowThisFuncLeave();
883}
884
885// Wrapped IMachine properties
886/////////////////////////////////////////////////////////////////////////////
887HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
888{
889 /* mParent is constant during life time, no need to lock */
890 ComObjPtr<VirtualBox> pVirtualBox(mParent);
891 pVirtualBox.queryInterfaceTo(aParent.asOutParam());
892
893 return S_OK;
894}
895
896
897HRESULT Machine::getAccessible(BOOL *aAccessible)
898{
899 /* In some cases (medium registry related), it is necessary to be able to
900 * go through the list of all machines. Happens when an inaccessible VM
901 * has a sensible medium registry. */
902 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
904
905 HRESULT rc = S_OK;
906
907 if (!mData->mAccessible)
908 {
909 /* try to initialize the VM once more if not accessible */
910
911 AutoReinitSpan autoReinitSpan(this);
912 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
913
914#ifdef DEBUG
915 LogFlowThisFunc(("Dumping media backreferences\n"));
916 mParent->i_dumpAllBackRefs();
917#endif
918
919 if (mData->pMachineConfigFile)
920 {
921 // reset the XML file to force loadSettings() (called from registeredInit())
922 // to parse it again; the file might have changed
923 delete mData->pMachineConfigFile;
924 mData->pMachineConfigFile = NULL;
925 }
926
927 rc = i_registeredInit();
928
929 if (SUCCEEDED(rc) && mData->mAccessible)
930 {
931 autoReinitSpan.setSucceeded();
932
933 /* make sure interesting parties will notice the accessibility
934 * state change */
935 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
936 mParent->i_onMachineDataChange(mData->mUuid);
937 }
938 }
939
940 if (SUCCEEDED(rc))
941 *aAccessible = mData->mAccessible;
942
943 LogFlowThisFuncLeave();
944
945 return rc;
946}
947
948HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
949{
950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
951
952 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
953 {
954 /* return shortly */
955 aAccessError = NULL;
956 return S_OK;
957 }
958
959 HRESULT rc = S_OK;
960
961 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
962 rc = errorInfo.createObject();
963 if (SUCCEEDED(rc))
964 {
965 errorInfo->init(mData->mAccessError.getResultCode(),
966 mData->mAccessError.getInterfaceID().ref(),
967 Utf8Str(mData->mAccessError.getComponent()).c_str(),
968 Utf8Str(mData->mAccessError.getText()));
969 rc = errorInfo.queryInterfaceTo(aAccessError.asOutParam());
970 }
971
972 return rc;
973}
974
975HRESULT Machine::getName(com::Utf8Str &aName)
976{
977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
978
979 aName = mUserData->s.strName;
980
981 return S_OK;
982}
983
984HRESULT Machine::setName(const com::Utf8Str &aName)
985{
986 // prohibit setting a UUID only as the machine name, or else it can
987 // never be found by findMachine()
988 Guid test(aName);
989
990 if (test.isValid())
991 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
992
993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 HRESULT rc = i_checkStateDependency(MutableStateDep);
996 if (FAILED(rc)) return rc;
997
998 i_setModified(IsModified_MachineData);
999 mUserData.backup();
1000 mUserData->s.strName = aName;
1001
1002 return S_OK;
1003}
1004
1005HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1006{
1007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1008
1009 aDescription = mUserData->s.strDescription;
1010
1011 return S_OK;
1012}
1013
1014HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1015{
1016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 // this can be done in principle in any state as it doesn't affect the VM
1019 // significantly, but play safe by not messing around while complex
1020 // activities are going on
1021 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1022 if (FAILED(rc)) return rc;
1023
1024 i_setModified(IsModified_MachineData);
1025 mUserData.backup();
1026 mUserData->s.strDescription = aDescription;
1027
1028 return S_OK;
1029}
1030
1031HRESULT Machine::getId(com::Guid &aId)
1032{
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 aId = mData->mUuid;
1036
1037 return S_OK;
1038}
1039
1040HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1041{
1042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1043 aGroups.resize(mUserData->s.llGroups.size());
1044 size_t i = 0;
1045 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1046 it != mUserData->s.llGroups.end(); ++it, ++i)
1047 aGroups[i] = (*it);
1048
1049 return S_OK;
1050}
1051
1052HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1053{
1054 StringsList llGroups;
1055 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1056 if (FAILED(rc))
1057 return rc;
1058
1059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1060
1061 // changing machine groups is possible while the VM is offline
1062 rc = i_checkStateDependency(OfflineStateDep);
1063 if (FAILED(rc)) return rc;
1064
1065 i_setModified(IsModified_MachineData);
1066 mUserData.backup();
1067 mUserData->s.llGroups = llGroups;
1068
1069 return S_OK;
1070}
1071
1072HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1073{
1074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1075
1076 aOSTypeId = mUserData->s.strOsType;
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1082{
1083 /* look up the object by Id to check it is valid */
1084 ComPtr<IGuestOSType> guestOSType;
1085 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1086 if (FAILED(rc)) return rc;
1087
1088 /* when setting, always use the "etalon" value for consistency -- lookup
1089 * by ID is case-insensitive and the input value may have different case */
1090 Bstr osTypeId;
1091 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1092 if (FAILED(rc)) return rc;
1093
1094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1095
1096 rc = i_checkStateDependency(MutableStateDep);
1097 if (FAILED(rc)) return rc;
1098
1099 i_setModified(IsModified_MachineData);
1100 mUserData.backup();
1101 mUserData->s.strOsType = osTypeId;
1102
1103 return S_OK;
1104}
1105
1106HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1107{
1108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1109
1110 *aFirmwareType = mHWData->mFirmwareType;
1111
1112 return S_OK;
1113}
1114
1115HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1116{
1117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 HRESULT rc = i_checkStateDependency(MutableStateDep);
1120 if (FAILED(rc)) return rc;
1121
1122 i_setModified(IsModified_MachineData);
1123 mHWData.backup();
1124 mHWData->mFirmwareType = aFirmwareType;
1125
1126 return S_OK;
1127}
1128
1129HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1130{
1131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1134
1135 return S_OK;
1136}
1137
1138HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1139{
1140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1141
1142 HRESULT rc = i_checkStateDependency(MutableStateDep);
1143 if (FAILED(rc)) return rc;
1144
1145 i_setModified(IsModified_MachineData);
1146 mHWData.backup();
1147 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1153{
1154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 *aPointingHIDType = mHWData->mPointingHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1162{
1163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 HRESULT rc = i_checkStateDependency(MutableStateDep);
1166 if (FAILED(rc)) return rc;
1167
1168 i_setModified(IsModified_MachineData);
1169 mHWData.backup();
1170 mHWData->mPointingHIDType = aPointingHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1176{
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aChipsetType = mHWData->mChipsetType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1185{
1186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 HRESULT rc = i_checkStateDependency(MutableStateDep);
1189 if (FAILED(rc)) return rc;
1190
1191 if (aChipsetType != mHWData->mChipsetType)
1192 {
1193 i_setModified(IsModified_MachineData);
1194 mHWData.backup();
1195 mHWData->mChipsetType = aChipsetType;
1196
1197 // Resize network adapter array, to be finalized on commit/rollback.
1198 // We must not throw away entries yet, otherwise settings are lost
1199 // without a way to roll back.
1200 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1201 size_t oldCount = mNetworkAdapters.size();
1202 if (newCount > oldCount)
1203 {
1204 mNetworkAdapters.resize(newCount);
1205 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1206 {
1207 unconst(mNetworkAdapters[slot]).createObject();
1208 mNetworkAdapters[slot]->init(this, slot);
1209 }
1210 }
1211 }
1212
1213 return S_OK;
1214}
1215
1216HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1217{
1218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1219
1220 *aParavirtProvider = mHWData->mParavirtProvider;
1221
1222 return S_OK;
1223}
1224
1225HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1226{
1227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1228
1229 HRESULT rc = i_checkStateDependency(MutableStateDep);
1230 if (FAILED(rc)) return rc;
1231
1232 if (aParavirtProvider != mHWData->mParavirtProvider)
1233 {
1234 i_setModified(IsModified_MachineData);
1235 mHWData.backup();
1236 mHWData->mParavirtProvider = aParavirtProvider;
1237 }
1238
1239 return S_OK;
1240}
1241
1242HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1243{
1244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1245
1246 aHardwareVersion = mHWData->mHWVersion;
1247
1248 return S_OK;
1249}
1250
1251HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1252{
1253 /* check known version */
1254 Utf8Str hwVersion = aHardwareVersion;
1255 if ( hwVersion.compare("1") != 0
1256 && hwVersion.compare("2") != 0)
1257 return setError(E_INVALIDARG,
1258 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1259
1260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 HRESULT rc = i_checkStateDependency(MutableStateDep);
1263 if (FAILED(rc)) return rc;
1264
1265 i_setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mHWVersion = aHardwareVersion;
1268
1269 return S_OK;
1270}
1271
1272HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1273{
1274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 if (!mHWData->mHardwareUUID.isZero())
1277 aHardwareUUID = mHWData->mHardwareUUID;
1278 else
1279 aHardwareUUID = mData->mUuid;
1280
1281 return S_OK;
1282}
1283
1284HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1285{
1286 if (!aHardwareUUID.isValid())
1287 return E_INVALIDARG;
1288
1289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1290
1291 HRESULT rc = i_checkStateDependency(MutableStateDep);
1292 if (FAILED(rc)) return rc;
1293
1294 i_setModified(IsModified_MachineData);
1295 mHWData.backup();
1296 if (aHardwareUUID == mData->mUuid)
1297 mHWData->mHardwareUUID.clear();
1298 else
1299 mHWData->mHardwareUUID = aHardwareUUID;
1300
1301 return S_OK;
1302}
1303
1304HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1305{
1306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1307
1308 *aMemorySize = mHWData->mMemorySize;
1309
1310 return S_OK;
1311}
1312
1313HRESULT Machine::setMemorySize(ULONG aMemorySize)
1314{
1315 /* check RAM limits */
1316 if ( aMemorySize < MM_RAM_MIN_IN_MB
1317 || aMemorySize > MM_RAM_MAX_IN_MB
1318 )
1319 return setError(E_INVALIDARG,
1320 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1321 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1322
1323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1324
1325 HRESULT rc = i_checkStateDependency(MutableStateDep);
1326 if (FAILED(rc)) return rc;
1327
1328 i_setModified(IsModified_MachineData);
1329 mHWData.backup();
1330 mHWData->mMemorySize = aMemorySize;
1331
1332 return S_OK;
1333}
1334
1335HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1336{
1337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1338
1339 *aCPUCount = mHWData->mCPUCount;
1340
1341 return S_OK;
1342}
1343
1344HRESULT Machine::setCPUCount(ULONG aCPUCount)
1345{
1346 /* check CPU limits */
1347 if ( aCPUCount < SchemaDefs::MinCPUCount
1348 || aCPUCount > SchemaDefs::MaxCPUCount
1349 )
1350 return setError(E_INVALIDARG,
1351 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1352 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1353
1354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1355
1356 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1357 if (mHWData->mCPUHotPlugEnabled)
1358 {
1359 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1360 {
1361 if (mHWData->mCPUAttached[idx])
1362 return setError(E_INVALIDARG,
1363 tr("There is still a CPU attached to socket %lu."
1364 "Detach the CPU before removing the socket"),
1365 aCPUCount, idx+1);
1366 }
1367 }
1368
1369 HRESULT rc = i_checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 i_setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 mHWData->mCPUCount = aCPUCount;
1375
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1384
1385 return S_OK;
1386}
1387
1388HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1389{
1390 HRESULT rc = S_OK;
1391
1392 /* check throttle limits */
1393 if ( aCPUExecutionCap < 1
1394 || aCPUExecutionCap > 100
1395 )
1396 return setError(E_INVALIDARG,
1397 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1398 aCPUExecutionCap, 1, 100);
1399
1400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1401
1402 alock.release();
1403 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1404 alock.acquire();
1405 if (FAILED(rc)) return rc;
1406
1407 i_setModified(IsModified_MachineData);
1408 mHWData.backup();
1409 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1410
1411 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1412 if (Global::IsOnline(mData->mMachineState))
1413 i_saveSettings(NULL);
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1419{
1420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1423
1424 return S_OK;
1425}
1426
1427HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1428{
1429 HRESULT rc = S_OK;
1430
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 rc = i_checkStateDependency(MutableStateDep);
1434 if (FAILED(rc)) return rc;
1435
1436 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1437 {
1438 if (aCPUHotPlugEnabled)
1439 {
1440 i_setModified(IsModified_MachineData);
1441 mHWData.backup();
1442
1443 /* Add the amount of CPUs currently attached */
1444 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1445 mHWData->mCPUAttached[i] = true;
1446 }
1447 else
1448 {
1449 /*
1450 * We can disable hotplug only if the amount of maximum CPUs is equal
1451 * to the amount of attached CPUs
1452 */
1453 unsigned cCpusAttached = 0;
1454 unsigned iHighestId = 0;
1455
1456 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1457 {
1458 if (mHWData->mCPUAttached[i])
1459 {
1460 cCpusAttached++;
1461 iHighestId = i;
1462 }
1463 }
1464
1465 if ( (cCpusAttached != mHWData->mCPUCount)
1466 || (iHighestId >= mHWData->mCPUCount))
1467 return setError(E_INVALIDARG,
1468 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1469
1470 i_setModified(IsModified_MachineData);
1471 mHWData.backup();
1472 }
1473 }
1474
1475 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1476
1477 return rc;
1478}
1479
1480HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1481{
1482#ifdef VBOX_WITH_USB_CARDREADER
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1486
1487 return S_OK;
1488#else
1489 NOREF(aEmulatedUSBCardReaderEnabled);
1490 return E_NOTIMPL;
1491#endif
1492}
1493
1494HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1495{
1496#ifdef VBOX_WITH_USB_CARDREADER
1497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1498
1499 HRESULT rc = i_checkStateDependency(MutableStateDep);
1500 if (FAILED(rc)) return rc;
1501
1502 i_setModified(IsModified_MachineData);
1503 mHWData.backup();
1504 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1505
1506 return S_OK;
1507#else
1508 NOREF(aEmulatedUSBCardReaderEnabled);
1509 return E_NOTIMPL;
1510#endif
1511}
1512
1513HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1514{
1515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1516
1517 *aHPETEnabled = mHWData->mHPETEnabled;
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1523{
1524 HRESULT rc = S_OK;
1525
1526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1527
1528 rc = i_checkStateDependency(MutableStateDep);
1529 if (FAILED(rc)) return rc;
1530
1531 i_setModified(IsModified_MachineData);
1532 mHWData.backup();
1533
1534 mHWData->mHPETEnabled = aHPETEnabled;
1535
1536 return rc;
1537}
1538
1539HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1540{
1541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1544 return S_OK;
1545}
1546
1547HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1548{
1549 HRESULT rc = S_OK;
1550
1551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1552
1553 i_setModified(IsModified_MachineData);
1554 mHWData.backup();
1555 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1556
1557 alock.release();
1558 rc = i_onVideoCaptureChange();
1559 alock.acquire();
1560 if (FAILED(rc))
1561 {
1562 /*
1563 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1564 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1565 * determine if it should start or stop capturing. Therefore we need to manually
1566 * undo change.
1567 */
1568 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1569 return rc;
1570 }
1571
1572 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1573 if (Global::IsOnline(mData->mMachineState))
1574 i_saveSettings(NULL);
1575
1576 return rc;
1577}
1578
1579HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1580{
1581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1582 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1583 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1584 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1585 return S_OK;
1586}
1587
1588HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1589{
1590 SafeArray<BOOL> screens(aVideoCaptureScreens);
1591 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1592 bool fChanged = false;
1593
1594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1595
1596 for (unsigned i = 0; i < screens.size(); ++i)
1597 {
1598 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1599 {
1600 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1601 fChanged = true;
1602 }
1603 }
1604 if (fChanged)
1605 {
1606 alock.release();
1607 HRESULT rc = i_onVideoCaptureChange();
1608 alock.acquire();
1609 if (FAILED(rc)) return rc;
1610 i_setModified(IsModified_MachineData);
1611
1612 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1613 if (Global::IsOnline(mData->mMachineState))
1614 i_saveSettings(NULL);
1615 }
1616
1617 return S_OK;
1618}
1619
1620HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1621{
1622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1623 if (mHWData->mVideoCaptureFile.isEmpty())
1624 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1625 else
1626 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1627 return S_OK;
1628}
1629
1630HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1631{
1632 Utf8Str strFile(aVideoCaptureFile);
1633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1634
1635 if ( Global::IsOnline(mData->mMachineState)
1636 && mHWData->mVideoCaptureEnabled)
1637 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1638
1639 if (!RTPathStartsWithRoot(strFile.c_str()))
1640 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1641
1642 if (!strFile.isEmpty())
1643 {
1644 Utf8Str defaultFile;
1645 i_getDefaultVideoCaptureFile(defaultFile);
1646 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1647 strFile.setNull();
1648 }
1649
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 mHWData->mVideoCaptureFile = strFile;
1653
1654 return S_OK;
1655}
1656
1657HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1658{
1659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1660 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1661 return S_OK;
1662}
1663
1664HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1665{
1666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1667
1668 if ( Global::IsOnline(mData->mMachineState)
1669 && mHWData->mVideoCaptureEnabled)
1670 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1671
1672 i_setModified(IsModified_MachineData);
1673 mHWData.backup();
1674 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1675
1676 return S_OK;
1677}
1678
1679HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1680{
1681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1682 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1683 return S_OK;
1684}
1685
1686HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1687{
1688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1689
1690 if ( Global::IsOnline(mData->mMachineState)
1691 && mHWData->mVideoCaptureEnabled)
1692 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1693
1694 i_setModified(IsModified_MachineData);
1695 mHWData.backup();
1696 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1697
1698 return S_OK;
1699}
1700
1701HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1702{
1703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1704 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1705 return S_OK;
1706}
1707
1708HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1709{
1710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 if ( Global::IsOnline(mData->mMachineState)
1713 && mHWData->mVideoCaptureEnabled)
1714 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1715
1716 i_setModified(IsModified_MachineData);
1717 mHWData.backup();
1718 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1719
1720 return S_OK;
1721}
1722
1723HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1724{
1725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1726 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1727 return S_OK;
1728}
1729
1730HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1731{
1732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 if ( Global::IsOnline(mData->mMachineState)
1735 && mHWData->mVideoCaptureEnabled)
1736 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1737
1738 i_setModified(IsModified_MachineData);
1739 mHWData.backup();
1740 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1746{
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1750
1751 return S_OK;
1752}
1753
1754HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1755{
1756 switch (aGraphicsControllerType)
1757 {
1758 case GraphicsControllerType_Null:
1759 case GraphicsControllerType_VBoxVGA:
1760#ifdef VBOX_WITH_VMSVGA
1761 case GraphicsControllerType_VMSVGA:
1762#endif
1763 break;
1764 default:
1765 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1766 }
1767
1768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1769
1770 HRESULT rc = i_checkStateDependency(MutableStateDep);
1771 if (FAILED(rc)) return rc;
1772
1773 i_setModified(IsModified_MachineData);
1774 mHWData.backup();
1775 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1776
1777 return S_OK;
1778}
1779
1780HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1781{
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 *aVRAMSize = mHWData->mVRAMSize;
1785
1786 return S_OK;
1787}
1788
1789HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1790{
1791 /* check VRAM limits */
1792 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1793 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1794 return setError(E_INVALIDARG,
1795 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1796 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1797
1798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1799
1800 HRESULT rc = i_checkStateDependency(MutableStateDep);
1801 if (FAILED(rc)) return rc;
1802
1803 i_setModified(IsModified_MachineData);
1804 mHWData.backup();
1805 mHWData->mVRAMSize = aVRAMSize;
1806
1807 return S_OK;
1808}
1809
1810/** @todo this method should not be public */
1811HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1812{
1813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1816
1817 return S_OK;
1818}
1819
1820/**
1821 * Set the memory balloon size.
1822 *
1823 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1824 * we have to make sure that we never call IGuest from here.
1825 */
1826HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1827{
1828 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1829#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1830 /* check limits */
1831 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1832 return setError(E_INVALIDARG,
1833 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1834 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1835
1836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1837
1838 i_setModified(IsModified_MachineData);
1839 mHWData.backup();
1840 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1841
1842 return S_OK;
1843#else
1844 NOREF(aMemoryBalloonSize);
1845 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1846#endif
1847}
1848
1849HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1850{
1851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1852
1853 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1854 return S_OK;
1855}
1856
1857HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1858{
1859#ifdef VBOX_WITH_PAGE_SHARING
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1863 i_setModified(IsModified_MachineData);
1864 mHWData.backup();
1865 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1866 return S_OK;
1867#else
1868 NOREF(aPageFusionEnabled);
1869 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1870#endif
1871}
1872
1873HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1878
1879 return S_OK;
1880}
1881
1882HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1883{
1884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 HRESULT rc = i_checkStateDependency(MutableStateDep);
1887 if (FAILED(rc)) return rc;
1888
1889 /** @todo check validity! */
1890
1891 i_setModified(IsModified_MachineData);
1892 mHWData.backup();
1893 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1894
1895 return S_OK;
1896}
1897
1898
1899HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1900{
1901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1902
1903 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1904
1905 return S_OK;
1906}
1907
1908HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1909{
1910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1911
1912 HRESULT rc = i_checkStateDependency(MutableStateDep);
1913 if (FAILED(rc)) return rc;
1914
1915 /** @todo check validity! */
1916 i_setModified(IsModified_MachineData);
1917 mHWData.backup();
1918 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1919
1920 return S_OK;
1921}
1922
1923HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1924{
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 *aMonitorCount = mHWData->mMonitorCount;
1928
1929 return S_OK;
1930}
1931
1932HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1933{
1934 /* make sure monitor count is a sensible number */
1935 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1936 return setError(E_INVALIDARG,
1937 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1938 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1939
1940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1941
1942 HRESULT rc = i_checkStateDependency(MutableStateDep);
1943 if (FAILED(rc)) return rc;
1944
1945 i_setModified(IsModified_MachineData);
1946 mHWData.backup();
1947 mHWData->mMonitorCount = aMonitorCount;
1948
1949 return S_OK;
1950}
1951
1952HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1953{
1954 /* mBIOSSettings is constant during life time, no need to lock */
1955 mBIOSSettings.queryInterfaceTo(aBIOSSettings.asOutParam());
1956
1957 return S_OK;
1958}
1959
1960HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1961{
1962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1963
1964 switch (aProperty)
1965 {
1966 case CPUPropertyType_PAE:
1967 *aValue = mHWData->mPAEEnabled;
1968 break;
1969
1970 case CPUPropertyType_Synthetic:
1971 *aValue = mHWData->mSyntheticCpu;
1972 break;
1973
1974 case CPUPropertyType_LongMode:
1975 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1976 *aValue = TRUE;
1977 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1978 *aValue = FALSE;
1979#if HC_ARCH_BITS == 64
1980 else
1981 *aValue = TRUE;
1982#else
1983 else
1984 {
1985 *aValue = FALSE;
1986
1987 ComPtr<IGuestOSType> ptrGuestOSType;
1988 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1989 if (SUCCEEDED(hrc2))
1990 {
1991 BOOL fIs64Bit = FALSE;
1992 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
1993 if (SUCCEEDED(hrc2) && fIs64Bit)
1994 {
1995 ComObjPtr<Host> ptrHost = mParent->i_host();
1996 alock.release();
1997
1998 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1999 if (FAILED(hrc2))
2000 *aValue = FALSE;
2001 }
2002 }
2003 }
2004#endif
2005 break;
2006
2007 case CPUPropertyType_TripleFaultReset:
2008 *aValue = mHWData->mTripleFaultReset;
2009 break;
2010
2011 default:
2012 return E_INVALIDARG;
2013 }
2014 return S_OK;
2015}
2016
2017HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2018{
2019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2020
2021 HRESULT rc = i_checkStateDependency(MutableStateDep);
2022 if (FAILED(rc)) return rc;
2023
2024 switch (aProperty)
2025 {
2026 case CPUPropertyType_PAE:
2027 i_setModified(IsModified_MachineData);
2028 mHWData.backup();
2029 mHWData->mPAEEnabled = !!aValue;
2030 break;
2031
2032 case CPUPropertyType_Synthetic:
2033 i_setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mSyntheticCpu = !!aValue;
2036 break;
2037
2038 case CPUPropertyType_LongMode:
2039 i_setModified(IsModified_MachineData);
2040 mHWData.backup();
2041 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2042 break;
2043
2044 case CPUPropertyType_TripleFaultReset:
2045 i_setModified(IsModified_MachineData);
2046 mHWData.backup();
2047 mHWData->mTripleFaultReset = !!aValue;
2048 break;
2049
2050 default:
2051 return E_INVALIDARG;
2052 }
2053 return S_OK;
2054}
2055
2056HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2057{
2058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2059
2060 switch(aId)
2061 {
2062 case 0x0:
2063 case 0x1:
2064 case 0x2:
2065 case 0x3:
2066 case 0x4:
2067 case 0x5:
2068 case 0x6:
2069 case 0x7:
2070 case 0x8:
2071 case 0x9:
2072 case 0xA:
2073 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2074 return E_INVALIDARG;
2075
2076 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2077 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2078 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2079 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2080 break;
2081
2082 case 0x80000000:
2083 case 0x80000001:
2084 case 0x80000002:
2085 case 0x80000003:
2086 case 0x80000004:
2087 case 0x80000005:
2088 case 0x80000006:
2089 case 0x80000007:
2090 case 0x80000008:
2091 case 0x80000009:
2092 case 0x8000000A:
2093 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2094 return E_INVALIDARG;
2095
2096 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2097 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2098 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2099 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2100 break;
2101
2102 default:
2103 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2104 }
2105 return S_OK;
2106}
2107
2108
2109HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2110{
2111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2112
2113 HRESULT rc = i_checkStateDependency(MutableStateDep);
2114 if (FAILED(rc)) return rc;
2115
2116 switch(aId)
2117 {
2118 case 0x0:
2119 case 0x1:
2120 case 0x2:
2121 case 0x3:
2122 case 0x4:
2123 case 0x5:
2124 case 0x6:
2125 case 0x7:
2126 case 0x8:
2127 case 0x9:
2128 case 0xA:
2129 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2130 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2131 i_setModified(IsModified_MachineData);
2132 mHWData.backup();
2133 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2134 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2135 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2136 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2137 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2138 break;
2139
2140 case 0x80000000:
2141 case 0x80000001:
2142 case 0x80000002:
2143 case 0x80000003:
2144 case 0x80000004:
2145 case 0x80000005:
2146 case 0x80000006:
2147 case 0x80000007:
2148 case 0x80000008:
2149 case 0x80000009:
2150 case 0x8000000A:
2151 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2152 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2153 i_setModified(IsModified_MachineData);
2154 mHWData.backup();
2155 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2156 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2157 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2158 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2159 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2160 break;
2161
2162 default:
2163 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2164 }
2165 return S_OK;
2166}
2167
2168HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2169{
2170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2171
2172 HRESULT rc = i_checkStateDependency(MutableStateDep);
2173 if (FAILED(rc)) return rc;
2174
2175 switch(aId)
2176 {
2177 case 0x0:
2178 case 0x1:
2179 case 0x2:
2180 case 0x3:
2181 case 0x4:
2182 case 0x5:
2183 case 0x6:
2184 case 0x7:
2185 case 0x8:
2186 case 0x9:
2187 case 0xA:
2188 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2189 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2190 i_setModified(IsModified_MachineData);
2191 mHWData.backup();
2192 /* Invalidate leaf. */
2193 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2194 break;
2195
2196 case 0x80000000:
2197 case 0x80000001:
2198 case 0x80000002:
2199 case 0x80000003:
2200 case 0x80000004:
2201 case 0x80000005:
2202 case 0x80000006:
2203 case 0x80000007:
2204 case 0x80000008:
2205 case 0x80000009:
2206 case 0x8000000A:
2207 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2208 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2209 i_setModified(IsModified_MachineData);
2210 mHWData.backup();
2211 /* Invalidate leaf. */
2212 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2213 break;
2214
2215 default:
2216 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2217 }
2218 return S_OK;
2219}
2220
2221HRESULT Machine::removeAllCPUIDLeaves()
2222{
2223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2224
2225 HRESULT rc = i_checkStateDependency(MutableStateDep);
2226 if (FAILED(rc)) return rc;
2227
2228 i_setModified(IsModified_MachineData);
2229 mHWData.backup();
2230
2231 /* Invalidate all standard leafs. */
2232 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2233 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2234
2235 /* Invalidate all extended leafs. */
2236 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2237 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2238
2239 return S_OK;
2240}
2241HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2242{
2243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2244
2245 switch(aProperty)
2246 {
2247 case HWVirtExPropertyType_Enabled:
2248 *aValue = mHWData->mHWVirtExEnabled;
2249 break;
2250
2251 case HWVirtExPropertyType_VPID:
2252 *aValue = mHWData->mHWVirtExVPIDEnabled;
2253 break;
2254
2255 case HWVirtExPropertyType_NestedPaging:
2256 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2257 break;
2258
2259 case HWVirtExPropertyType_UnrestrictedExecution:
2260 *aValue = mHWData->mHWVirtExUXEnabled;
2261 break;
2262
2263 case HWVirtExPropertyType_LargePages:
2264 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2265#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2266 *aValue = FALSE;
2267#endif
2268 break;
2269
2270 case HWVirtExPropertyType_Force:
2271 *aValue = mHWData->mHWVirtExForceEnabled;
2272 break;
2273
2274 default:
2275 return E_INVALIDARG;
2276 }
2277 return S_OK;
2278}
2279
2280HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2281{
2282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2283
2284 HRESULT rc = i_checkStateDependency(MutableStateDep);
2285 if (FAILED(rc)) return rc;
2286
2287 switch(aProperty)
2288 {
2289 case HWVirtExPropertyType_Enabled:
2290 i_setModified(IsModified_MachineData);
2291 mHWData.backup();
2292 mHWData->mHWVirtExEnabled = !!aValue;
2293 break;
2294
2295 case HWVirtExPropertyType_VPID:
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2299 break;
2300
2301 case HWVirtExPropertyType_NestedPaging:
2302 i_setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2305 break;
2306
2307 case HWVirtExPropertyType_UnrestrictedExecution:
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mHWVirtExUXEnabled = !!aValue;
2311 break;
2312
2313 case HWVirtExPropertyType_LargePages:
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2317 break;
2318
2319 case HWVirtExPropertyType_Force:
2320 i_setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mHWVirtExForceEnabled = !!aValue;
2323 break;
2324
2325 default:
2326 return E_INVALIDARG;
2327 }
2328
2329 return S_OK;
2330}
2331
2332HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2333{
2334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2335
2336 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2337
2338 return S_OK;
2339}
2340
2341HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2342{
2343 /* @todo (r=dmik):
2344 * 1. Allow to change the name of the snapshot folder containing snapshots
2345 * 2. Rename the folder on disk instead of just changing the property
2346 * value (to be smart and not to leave garbage). Note that it cannot be
2347 * done here because the change may be rolled back. Thus, the right
2348 * place is #saveSettings().
2349 */
2350
2351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2352
2353 HRESULT rc = i_checkStateDependency(MutableStateDep);
2354 if (FAILED(rc)) return rc;
2355
2356 if (!mData->mCurrentSnapshot.isNull())
2357 return setError(E_FAIL,
2358 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2359
2360 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2361
2362 if (strSnapshotFolder.isEmpty())
2363 strSnapshotFolder = "Snapshots";
2364 int vrc = i_calculateFullPath(strSnapshotFolder,
2365 strSnapshotFolder);
2366 if (RT_FAILURE(vrc))
2367 return setError(E_FAIL,
2368 tr("Invalid snapshot folder '%s' (%Rrc)"),
2369 strSnapshotFolder.c_str(), vrc);
2370
2371 i_setModified(IsModified_MachineData);
2372 mUserData.backup();
2373
2374 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2375
2376 return S_OK;
2377}
2378
2379HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2380{
2381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2382
2383 aMediumAttachments.resize(mMediaData->mAttachments.size());
2384 size_t i = 0;
2385 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2386 it != mMediaData->mAttachments.end(); ++it, ++i)
2387 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
2388
2389 return S_OK;
2390}
2391
2392HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2393{
2394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2395
2396 Assert(!!mVRDEServer);
2397
2398 mVRDEServer.queryInterfaceTo(aVRDEServer.asOutParam());
2399
2400 return S_OK;
2401}
2402
2403HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2404{
2405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2406
2407 mAudioAdapter.queryInterfaceTo(aAudioAdapter.asOutParam());
2408
2409 return S_OK;
2410}
2411
2412HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2413{
2414#ifdef VBOX_WITH_VUSB
2415 clearError();
2416 MultiResult rc(S_OK);
2417
2418# ifdef VBOX_WITH_USB
2419 rc = mParent->i_host()->i_checkUSBProxyService();
2420 if (FAILED(rc)) return rc;
2421# endif
2422
2423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2424
2425 USBControllerList data = *mUSBControllers.data();
2426 aUSBControllers.resize(data.size());
2427 size_t i = 0;
2428 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2429 (*it).queryInterfaceTo(aUSBControllers[i].asOutParam());
2430
2431 return S_OK;
2432#else
2433 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2434 * extended error info to indicate that USB is simply not available
2435 * (w/o treating it as a failure), for example, as in OSE */
2436 NOREF(aUSBControllers);
2437 ReturnComNotImplemented();
2438#endif /* VBOX_WITH_VUSB */
2439}
2440
2441HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2442{
2443#ifdef VBOX_WITH_VUSB
2444 clearError();
2445 MultiResult rc(S_OK);
2446
2447# ifdef VBOX_WITH_USB
2448 rc = mParent->i_host()->i_checkUSBProxyService();
2449 if (FAILED(rc)) return rc;
2450# endif
2451
2452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters.asOutParam());
2455#else
2456 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2457 * extended error info to indicate that USB is simply not available
2458 * (w/o treating it as a failure), for example, as in OSE */
2459 NOREF(aUSBDeviceFilters);
2460 ReturnComNotImplemented();
2461#endif /* VBOX_WITH_VUSB */
2462}
2463
2464HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2465{
2466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 aSettingsFilePath = mData->m_strConfigFileFull;
2469
2470 return S_OK;
2471}
2472
2473HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2474{
2475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2476
2477 HRESULT rc = i_checkStateDependency(MutableStateDep);
2478 if (FAILED(rc)) return rc;
2479
2480 if (!mData->pMachineConfigFile->fileExists())
2481 // this is a new machine, and no config file exists yet:
2482 *aSettingsModified = TRUE;
2483 else
2484 *aSettingsModified = (mData->flModifications != 0);
2485
2486 return S_OK;
2487}
2488
2489HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2490{
2491
2492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2493
2494 *aSessionState = mData->mSession.mState;
2495
2496 return S_OK;
2497}
2498
2499HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2500{
2501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2502
2503 aSessionType = mData->mSession.mType;
2504
2505 return S_OK;
2506}
2507
2508HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2509{
2510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2511
2512 *aSessionPID = mData->mSession.mPID;
2513
2514 return S_OK;
2515}
2516
2517HRESULT Machine::getState(MachineState_T *aState)
2518{
2519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2520
2521 *aState = mData->mMachineState;
2522
2523 return S_OK;
2524}
2525
2526HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2527{
2528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2529
2530 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2531
2532 return S_OK;
2533}
2534
2535HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2536{
2537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2538
2539 aStateFilePath = mSSData->strStateFilePath;
2540
2541 return S_OK;
2542}
2543
2544HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2545{
2546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 i_getLogFolder(aLogFolder);
2549
2550 return S_OK;
2551}
2552
2553HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2554{
2555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2556
2557 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot.asOutParam());
2558
2559 return S_OK;
2560}
2561
2562HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2563{
2564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2565
2566 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2567 ? 0
2568 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2569
2570 return S_OK;
2571}
2572
2573HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2574{
2575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 /* Note: for machines with no snapshots, we always return FALSE
2578 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2579 * reasons :) */
2580
2581 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2582 ? FALSE
2583 : mData->mCurrentStateModified;
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 aSharedFolders.resize(mHWData->mSharedFolders.size());
2593 size_t i = 0;
2594 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2595 it != mHWData->mSharedFolders.end(); ++i, ++it)
2596 (*it).queryInterfaceTo(aSharedFolders[i].asOutParam());
2597
2598 return S_OK;
2599}
2600
2601HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2602{
2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2604
2605 *aClipboardMode = mHWData->mClipboardMode;
2606
2607 return S_OK;
2608}
2609
2610HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2611{
2612 HRESULT rc = S_OK;
2613
2614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 alock.release();
2617 rc = i_onClipboardModeChange(aClipboardMode);
2618 alock.acquire();
2619 if (FAILED(rc)) return rc;
2620
2621 i_setModified(IsModified_MachineData);
2622 mHWData.backup();
2623 mHWData->mClipboardMode = aClipboardMode;
2624
2625 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2626 if (Global::IsOnline(mData->mMachineState))
2627 i_saveSettings(NULL);
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 *aDnDMode = mHWData->mDnDMode;
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2642{
2643 HRESULT rc = S_OK;
2644
2645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2646
2647 alock.release();
2648 rc = i_onDnDModeChange(aDnDMode);
2649
2650 alock.acquire();
2651 if (FAILED(rc)) return rc;
2652
2653 i_setModified(IsModified_MachineData);
2654 mHWData.backup();
2655 mHWData->mDnDMode = aDnDMode;
2656
2657 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2658 if (Global::IsOnline(mData->mMachineState))
2659 i_saveSettings(NULL);
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 try
2669 {
2670 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2671 }
2672 catch (...)
2673 {
2674 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2675 }
2676
2677 return S_OK;
2678}
2679
2680HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2681{
2682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 HRESULT rc = i_checkStateDependency(MutableStateDep);
2685 if (FAILED(rc)) return rc;
2686
2687 i_setModified(IsModified_MachineData);
2688 mHWData.backup();
2689 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2690 return rc;
2691}
2692
2693HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696 StorageControllerList data = *mStorageControllers.data();
2697 size_t i = 0;
2698 aStorageControllers.resize(data.size());
2699 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2700 (*it).queryInterfaceTo(aStorageControllers[i].asOutParam());
2701 return S_OK;
2702}
2703
2704HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2705{
2706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2707
2708 *aEnabled = mUserData->s.fTeleporterEnabled;
2709
2710 return S_OK;
2711}
2712
2713HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2714{
2715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 /* Only allow it to be set to true when PoweredOff or Aborted.
2718 (Clearing it is always permitted.) */
2719 if ( aTeleporterEnabled
2720 && mData->mRegistered
2721 && ( !i_isSessionMachine()
2722 || ( mData->mMachineState != MachineState_PoweredOff
2723 && mData->mMachineState != MachineState_Teleported
2724 && mData->mMachineState != MachineState_Aborted
2725 )
2726 )
2727 )
2728 return setError(VBOX_E_INVALID_VM_STATE,
2729 tr("The machine is not powered off (state is %s)"),
2730 Global::stringifyMachineState(mData->mMachineState));
2731
2732 i_setModified(IsModified_MachineData);
2733 mUserData.backup();
2734 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2749{
2750 if (aTeleporterPort >= _64K)
2751 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2752
2753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 HRESULT rc = i_checkStateDependency(MutableStateDep);
2756 if (FAILED(rc)) return rc;
2757
2758 i_setModified(IsModified_MachineData);
2759 mUserData.backup();
2760 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2761
2762 return S_OK;
2763}
2764
2765HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2766{
2767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2775{
2776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 HRESULT rc = i_checkStateDependency(MutableStateDep);
2779 if (FAILED(rc)) return rc;
2780
2781 i_setModified(IsModified_MachineData);
2782 mUserData.backup();
2783 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2797{
2798 /*
2799 * Hash the password first.
2800 */
2801 com::Utf8Str aT = aTeleporterPassword;
2802
2803 if (!aT.isEmpty())
2804 {
2805 if (VBoxIsPasswordHashed(&aT))
2806 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2807 VBoxHashPassword(&aT);
2808 }
2809
2810 /*
2811 * Do the update.
2812 */
2813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2814 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2815 if (SUCCEEDED(hrc))
2816 {
2817 i_setModified(IsModified_MachineData);
2818 mUserData.backup();
2819 mUserData->s.strTeleporterPassword = aT;
2820 }
2821
2822 return hrc;
2823}
2824
2825HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2826{
2827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2830 return S_OK;
2831}
2832
2833HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2834{
2835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2836
2837 /* @todo deal with running state change. */
2838 HRESULT rc = i_checkStateDependency(MutableStateDep);
2839 if (FAILED(rc)) return rc;
2840
2841 i_setModified(IsModified_MachineData);
2842 mUserData.backup();
2843 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2844 return S_OK;
2845}
2846
2847HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2848{
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2852 return S_OK;
2853}
2854
2855HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2856{
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 /* @todo deal with running state change. */
2860 HRESULT rc = i_checkStateDependency(MutableStateDep);
2861 if (FAILED(rc)) return rc;
2862
2863 i_setModified(IsModified_MachineData);
2864 mUserData.backup();
2865 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2866 return S_OK;
2867}
2868
2869HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 /* @todo deal with running state change. */
2882 HRESULT rc = i_checkStateDependency(MutableStateDep);
2883 if (FAILED(rc)) return rc;
2884
2885 i_setModified(IsModified_MachineData);
2886 mUserData.backup();
2887 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2888 return S_OK;
2889}
2890
2891HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2892{
2893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2896
2897 return S_OK;
2898}
2899
2900HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2901{
2902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2903
2904 /* @todo deal with running state change. */
2905 HRESULT rc = i_checkStateDependency(MutableStateDep);
2906 if (FAILED(rc)) return rc;
2907
2908 i_setModified(IsModified_MachineData);
2909 mUserData.backup();
2910 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2911
2912 return S_OK;
2913}
2914
2915HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2916{
2917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2920 return S_OK;
2921}
2922
2923HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2924{
2925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 /* @todo deal with running state change. */
2928 HRESULT rc = i_checkStateDependency(MutableStateDep);
2929 if (FAILED(rc)) return rc;
2930
2931 i_setModified(IsModified_MachineData);
2932 mUserData.backup();
2933 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2934 return S_OK;
2935}
2936
2937HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2938{
2939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2940
2941 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2942
2943 return S_OK;
2944}
2945
2946HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2947{
2948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 /* Only allow it to be set to true when PoweredOff or Aborted.
2951 (Clearing it is always permitted.) */
2952 if ( aRTCUseUTC
2953 && mData->mRegistered
2954 && ( !i_isSessionMachine()
2955 || ( mData->mMachineState != MachineState_PoweredOff
2956 && mData->mMachineState != MachineState_Teleported
2957 && mData->mMachineState != MachineState_Aborted
2958 )
2959 )
2960 )
2961 return setError(VBOX_E_INVALID_VM_STATE,
2962 tr("The machine is not powered off (state is %s)"),
2963 Global::stringifyMachineState(mData->mMachineState));
2964
2965 i_setModified(IsModified_MachineData);
2966 mUserData.backup();
2967 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2968
2969 return S_OK;
2970}
2971
2972HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2973{
2974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2975
2976 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2977
2978 return S_OK;
2979}
2980
2981HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2982{
2983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 HRESULT rc = i_checkStateDependency(MutableStateDep);
2986 if (FAILED(rc)) return rc;
2987
2988 i_setModified(IsModified_MachineData);
2989 mHWData.backup();
2990 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2991
2992 return S_OK;
2993}
2994
2995HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2996{
2997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2998
2999 *aIOCacheSize = mHWData->mIOCacheSize;
3000
3001 return S_OK;
3002}
3003
3004HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3005{
3006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3007
3008 HRESULT rc = i_checkStateDependency(MutableStateDep);
3009 if (FAILED(rc)) return rc;
3010
3011 i_setModified(IsModified_MachineData);
3012 mHWData.backup();
3013 mHWData->mIOCacheSize = aIOCacheSize;
3014
3015 return S_OK;
3016}
3017
3018
3019/**
3020 * @note Locks objects!
3021 */
3022HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3023 LockType_T aLockType)
3024
3025{
3026 /* check the session state */
3027 SessionState_T state;
3028 HRESULT rc = aSession->COMGETTER(State)(&state);
3029 if (FAILED(rc)) return rc;
3030
3031 if (state != SessionState_Unlocked)
3032 return setError(VBOX_E_INVALID_OBJECT_STATE,
3033 tr("The given session is busy"));
3034
3035 // get the client's IInternalSessionControl interface
3036 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3037 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3038 E_INVALIDARG);
3039
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 if (!mData->mRegistered)
3043 return setError(E_UNEXPECTED,
3044 tr("The machine '%s' is not registered"),
3045 mUserData->s.strName.c_str());
3046
3047 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3048
3049 SessionState_T oldState = mData->mSession.mState;
3050 /* Hack: in case the session is closing and there is a progress object
3051 * which allows waiting for the session to be closed, take the opportunity
3052 * and do a limited wait (max. 1 second). This helps a lot when the system
3053 * is busy and thus session closing can take a little while. */
3054 if ( mData->mSession.mState == SessionState_Unlocking
3055 && mData->mSession.mProgress)
3056 {
3057 alock.release();
3058 mData->mSession.mProgress->WaitForCompletion(1000);
3059 alock.acquire();
3060 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3061 }
3062
3063 // try again now
3064 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3065 // (i.e. session machine exists)
3066 && (aLockType == LockType_Shared) // caller wants a shared link to the
3067 // existing session that holds the write lock:
3068 )
3069 {
3070 // OK, share the session... we are now dealing with three processes:
3071 // 1) VBoxSVC (where this code runs);
3072 // 2) process C: the caller's client process (who wants a shared session);
3073 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3074
3075 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3076 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3077 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3078 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3079 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3080
3081 /*
3082 * Release the lock before calling the client process. It's safe here
3083 * since the only thing to do after we get the lock again is to add
3084 * the remote control to the list (which doesn't directly influence
3085 * anything).
3086 */
3087 alock.release();
3088
3089 // get the console of the session holding the write lock (this is a remote call)
3090 ComPtr<IConsole> pConsoleW;
3091 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3092 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3093 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3094 if (FAILED(rc))
3095 // the failure may occur w/o any error info (from RPC), so provide one
3096 return setError(VBOX_E_VM_ERROR,
3097 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3098
3099 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3100
3101 // share the session machine and W's console with the caller's session
3102 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3103 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3104 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3105
3106 if (FAILED(rc))
3107 // the failure may occur w/o any error info (from RPC), so provide one
3108 return setError(VBOX_E_VM_ERROR,
3109 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3110 alock.acquire();
3111
3112 // need to revalidate the state after acquiring the lock again
3113 if (mData->mSession.mState != SessionState_Locked)
3114 {
3115 pSessionControl->Uninitialize();
3116 return setError(VBOX_E_INVALID_SESSION_STATE,
3117 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3118 mUserData->s.strName.c_str());
3119 }
3120
3121 // add the caller's session to the list
3122 mData->mSession.mRemoteControls.push_back(pSessionControl);
3123 }
3124 else if ( mData->mSession.mState == SessionState_Locked
3125 || mData->mSession.mState == SessionState_Unlocking
3126 )
3127 {
3128 // sharing not permitted, or machine still unlocking:
3129 return setError(VBOX_E_INVALID_OBJECT_STATE,
3130 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3131 mUserData->s.strName.c_str());
3132 }
3133 else
3134 {
3135 // machine is not locked: then write-lock the machine (create the session machine)
3136
3137 // must not be busy
3138 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3139
3140 // get the caller's session PID
3141 RTPROCESS pid = NIL_RTPROCESS;
3142 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3143 pSessionControl->GetPID((ULONG*)&pid);
3144 Assert(pid != NIL_RTPROCESS);
3145
3146 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3147
3148 if (fLaunchingVMProcess)
3149 {
3150 if (mData->mSession.mPID == NIL_RTPROCESS)
3151 {
3152 // two or more clients racing for a lock, the one which set the
3153 // session state to Spawning will win, the others will get an
3154 // error as we can't decide here if waiting a little would help
3155 // (only for shared locks this would avoid an error)
3156 return setError(VBOX_E_INVALID_OBJECT_STATE,
3157 tr("The machine '%s' already has a lock request pending"),
3158 mUserData->s.strName.c_str());
3159 }
3160
3161 // this machine is awaiting for a spawning session to be opened:
3162 // then the calling process must be the one that got started by
3163 // LaunchVMProcess()
3164
3165 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3166 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3167
3168 if (mData->mSession.mPID != pid)
3169 return setError(E_ACCESSDENIED,
3170 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3171 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3172 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3173 }
3174
3175 // create the mutable SessionMachine from the current machine
3176 ComObjPtr<SessionMachine> sessionMachine;
3177 sessionMachine.createObject();
3178 rc = sessionMachine->init(this);
3179 AssertComRC(rc);
3180
3181 /* NOTE: doing return from this function after this point but
3182 * before the end is forbidden since it may call SessionMachine::uninit()
3183 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3184 * lock while still holding the Machine lock in alock so that a deadlock
3185 * is possible due to the wrong lock order. */
3186
3187 if (SUCCEEDED(rc))
3188 {
3189 /*
3190 * Set the session state to Spawning to protect against subsequent
3191 * attempts to open a session and to unregister the machine after
3192 * we release the lock.
3193 */
3194 SessionState_T origState = mData->mSession.mState;
3195 mData->mSession.mState = SessionState_Spawning;
3196
3197#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3198 /* Get the client token ID to be passed to the client process */
3199 Utf8Str strTokenId;
3200 sessionMachine->i_getTokenId(strTokenId);
3201 Assert(!strTokenId.isEmpty());
3202#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3203 /* Get the client token to be passed to the client process */
3204 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3205 /* The token is now "owned" by pToken, fix refcount */
3206 if (!pToken.isNull())
3207 pToken->Release();
3208#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3209
3210 /*
3211 * Release the lock before calling the client process -- it will call
3212 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3213 * because the state is Spawning, so that LaunchVMProcess() and
3214 * LockMachine() calls will fail. This method, called before we
3215 * acquire the lock again, will fail because of the wrong PID.
3216 *
3217 * Note that mData->mSession.mRemoteControls accessed outside
3218 * the lock may not be modified when state is Spawning, so it's safe.
3219 */
3220 alock.release();
3221
3222 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3223#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3224 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3225#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3226 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3227 /* Now the token is owned by the client process. */
3228 pToken.setNull();
3229#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3230 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3231
3232 /* The failure may occur w/o any error info (from RPC), so provide one */
3233 if (FAILED(rc))
3234 setError(VBOX_E_VM_ERROR,
3235 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3236
3237 if ( SUCCEEDED(rc)
3238 && fLaunchingVMProcess
3239 )
3240 {
3241 /* complete the remote session initialization */
3242
3243 /* get the console from the direct session */
3244 ComPtr<IConsole> console;
3245 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3246 ComAssertComRC(rc);
3247
3248 if (SUCCEEDED(rc) && !console)
3249 {
3250 ComAssert(!!console);
3251 rc = E_FAIL;
3252 }
3253
3254 /* assign machine & console to the remote session */
3255 if (SUCCEEDED(rc))
3256 {
3257 /*
3258 * after LaunchVMProcess(), the first and the only
3259 * entry in remoteControls is that remote session
3260 */
3261 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3262 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3263 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3264
3265 /* The failure may occur w/o any error info (from RPC), so provide one */
3266 if (FAILED(rc))
3267 setError(VBOX_E_VM_ERROR,
3268 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3269 }
3270
3271 if (FAILED(rc))
3272 pSessionControl->Uninitialize();
3273 }
3274
3275 /* acquire the lock again */
3276 alock.acquire();
3277
3278 /* Restore the session state */
3279 mData->mSession.mState = origState;
3280 }
3281
3282 // finalize spawning anyway (this is why we don't return on errors above)
3283 if (fLaunchingVMProcess)
3284 {
3285 /* Note that the progress object is finalized later */
3286 /** @todo Consider checking mData->mSession.mProgress for cancellation
3287 * around here. */
3288
3289 /* We don't reset mSession.mPID here because it is necessary for
3290 * SessionMachine::uninit() to reap the child process later. */
3291
3292 if (FAILED(rc))
3293 {
3294 /* Close the remote session, remove the remote control from the list
3295 * and reset session state to Closed (@note keep the code in sync
3296 * with the relevant part in checkForSpawnFailure()). */
3297
3298 Assert(mData->mSession.mRemoteControls.size() == 1);
3299 if (mData->mSession.mRemoteControls.size() == 1)
3300 {
3301 ErrorInfoKeeper eik;
3302 mData->mSession.mRemoteControls.front()->Uninitialize();
3303 }
3304
3305 mData->mSession.mRemoteControls.clear();
3306 mData->mSession.mState = SessionState_Unlocked;
3307 }
3308 }
3309 else
3310 {
3311 /* memorize PID of the directly opened session */
3312 if (SUCCEEDED(rc))
3313 mData->mSession.mPID = pid;
3314 }
3315
3316 if (SUCCEEDED(rc))
3317 {
3318 /* memorize the direct session control and cache IUnknown for it */
3319 mData->mSession.mDirectControl = pSessionControl;
3320 mData->mSession.mState = SessionState_Locked;
3321 /* associate the SessionMachine with this Machine */
3322 mData->mSession.mMachine = sessionMachine;
3323
3324 /* request an IUnknown pointer early from the remote party for later
3325 * identity checks (it will be internally cached within mDirectControl
3326 * at least on XPCOM) */
3327 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3328 NOREF(unk);
3329 }
3330
3331 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3332 * would break the lock order */
3333 alock.release();
3334
3335 /* uninitialize the created session machine on failure */
3336 if (FAILED(rc))
3337 sessionMachine->uninit();
3338
3339 }
3340
3341 if (SUCCEEDED(rc))
3342 {
3343 /*
3344 * tell the client watcher thread to update the set of
3345 * machines that have open sessions
3346 */
3347 mParent->i_updateClientWatcher();
3348
3349 if (oldState != SessionState_Locked)
3350 /* fire an event */
3351 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3352 }
3353
3354 return rc;
3355}
3356
3357/**
3358 * @note Locks objects!
3359 */
3360HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3361 const com::Utf8Str &aType,
3362 const com::Utf8Str &aEnvironment,
3363 ComPtr<IProgress> &aProgress)
3364{
3365 Utf8Str strFrontend(aType);
3366 Utf8Str strEnvironment(aEnvironment);
3367 /* "emergencystop" doesn't need the session, so skip the checks/interface
3368 * retrieval. This code doesn't quite fit in here, but introducing a
3369 * special API method would be even more effort, and would require explicit
3370 * support by every API client. It's better to hide the feature a bit. */
3371 if (strFrontend != "emergencystop")
3372 CheckComArgNotNull(aSession);
3373
3374 HRESULT rc = S_OK;
3375 if (strFrontend.isEmpty())
3376 {
3377 Bstr bstrFrontend;
3378 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3379 if (FAILED(rc))
3380 return rc;
3381 strFrontend = bstrFrontend;
3382 if (strFrontend.isEmpty())
3383 {
3384 ComPtr<ISystemProperties> systemProperties;
3385 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3386 if (FAILED(rc))
3387 return rc;
3388 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3389 if (FAILED(rc))
3390 return rc;
3391 strFrontend = bstrFrontend;
3392 }
3393 /* paranoia - emergencystop is not a valid default */
3394 if (strFrontend == "emergencystop")
3395 strFrontend = Utf8Str::Empty;
3396 }
3397 /* default frontend: Qt GUI */
3398 if (strFrontend.isEmpty())
3399 strFrontend = "GUI/Qt";
3400
3401 if (strFrontend != "emergencystop")
3402 {
3403 /* check the session state */
3404 SessionState_T state;
3405 rc = aSession->COMGETTER(State)(&state);
3406 if (FAILED(rc))
3407 return rc;
3408
3409 if (state != SessionState_Unlocked)
3410 return setError(VBOX_E_INVALID_OBJECT_STATE,
3411 tr("The given session is busy"));
3412
3413 /* get the IInternalSessionControl interface */
3414 ComPtr<IInternalSessionControl> control(aSession);
3415 ComAssertMsgRet(!control.isNull(),
3416 ("No IInternalSessionControl interface"),
3417 E_INVALIDARG);
3418
3419 /* get the teleporter enable state for the progress object init. */
3420 BOOL fTeleporterEnabled;
3421 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3422 if (FAILED(rc))
3423 return rc;
3424
3425 /* create a progress object */
3426 ComObjPtr<ProgressProxy> progress;
3427 progress.createObject();
3428 rc = progress->init(mParent,
3429 static_cast<IMachine*>(this),
3430 Bstr(tr("Starting VM")).raw(),
3431 TRUE /* aCancelable */,
3432 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3433 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3434 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3435 2 /* uFirstOperationWeight */,
3436 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3437
3438 if (SUCCEEDED(rc))
3439 {
3440 rc = i_launchVMProcess(control, strFrontend, strEnvironment, progress);
3441 if (SUCCEEDED(rc))
3442 {
3443 progress.queryInterfaceTo(aProgress.asOutParam());
3444
3445 /* signal the client watcher thread */
3446 mParent->i_updateClientWatcher();
3447
3448 /* fire an event */
3449 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3450 }
3451 }
3452 }
3453 else
3454 {
3455 /* no progress object - either instant success or failure */
3456 aProgress = NULL;
3457
3458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3459
3460 if (mData->mSession.mState != SessionState_Locked)
3461 return setError(VBOX_E_INVALID_OBJECT_STATE,
3462 tr("The machine '%s' is not locked by a session"),
3463 mUserData->s.strName.c_str());
3464
3465 /* must have a VM process associated - do not kill normal API clients
3466 * with an open session */
3467 if (!Global::IsOnline(mData->mMachineState))
3468 return setError(VBOX_E_INVALID_OBJECT_STATE,
3469 tr("The machine '%s' does not have a VM process"),
3470 mUserData->s.strName.c_str());
3471
3472 /* forcibly terminate the VM process */
3473 if (mData->mSession.mPID != NIL_RTPROCESS)
3474 RTProcTerminate(mData->mSession.mPID);
3475
3476 /* signal the client watcher thread, as most likely the client has
3477 * been terminated */
3478 mParent->i_updateClientWatcher();
3479 }
3480
3481 return rc;
3482}
3483
3484HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3485{
3486 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3487 return setError(E_INVALIDARG,
3488 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3489 aPosition, SchemaDefs::MaxBootPosition);
3490
3491 if (aDevice == DeviceType_USB)
3492 return setError(E_NOTIMPL,
3493 tr("Booting from USB device is currently not supported"));
3494
3495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3496
3497 HRESULT rc = i_checkStateDependency(MutableStateDep);
3498 if (FAILED(rc)) return rc;
3499
3500 i_setModified(IsModified_MachineData);
3501 mHWData.backup();
3502 mHWData->mBootOrder[aPosition - 1] = aDevice;
3503
3504 return S_OK;
3505}
3506
3507HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3508{
3509 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3510 return setError(E_INVALIDARG,
3511 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3512 aPosition, SchemaDefs::MaxBootPosition);
3513
3514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3515
3516 *aDevice = mHWData->mBootOrder[aPosition - 1];
3517
3518 return S_OK;
3519}
3520
3521HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3522 LONG aControllerPort,
3523 LONG aDevice,
3524 DeviceType_T aType,
3525 const ComPtr<IMedium> &aMedium)
3526{
3527 IMedium *aM = aMedium;
3528 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3529 aName.c_str(), aControllerPort, aDevice, aType, aM));
3530
3531 // request the host lock first, since might be calling Host methods for getting host drives;
3532 // next, protect the media tree all the while we're in here, as well as our member variables
3533 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3534 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3535
3536 HRESULT rc = i_checkStateDependency(MutableStateDep);
3537 if (FAILED(rc)) return rc;
3538
3539 /// @todo NEWMEDIA implicit machine registration
3540 if (!mData->mRegistered)
3541 return setError(VBOX_E_INVALID_OBJECT_STATE,
3542 tr("Cannot attach storage devices to an unregistered machine"));
3543
3544 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3545
3546 /* Check for an existing controller. */
3547 ComObjPtr<StorageController> ctl;
3548 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3549 if (FAILED(rc)) return rc;
3550
3551 StorageControllerType_T ctrlType;
3552 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3553 if (FAILED(rc))
3554 return setError(E_FAIL,
3555 tr("Could not get type of controller '%s'"),
3556 aName.c_str());
3557
3558 bool fSilent = false;
3559 Utf8Str strReconfig;
3560
3561 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3562 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3563 if ( mData->mMachineState == MachineState_Paused
3564 && strReconfig == "1")
3565 fSilent = true;
3566
3567 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3568 bool fHotplug = false;
3569 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3570 fHotplug = true;
3571
3572 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3573 return setError(VBOX_E_INVALID_VM_STATE,
3574 tr("Controller '%s' does not support hotplugging"),
3575 aName.c_str());
3576
3577 // check that the port and device are not out of range
3578 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3579 if (FAILED(rc)) return rc;
3580
3581 /* check if the device slot is already busy */
3582 MediumAttachment *pAttachTemp;
3583 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3584 Bstr(aName).raw(),
3585 aControllerPort,
3586 aDevice)))
3587 {
3588 Medium *pMedium = pAttachTemp->i_getMedium();
3589 if (pMedium)
3590 {
3591 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3592 return setError(VBOX_E_OBJECT_IN_USE,
3593 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3594 pMedium->i_getLocationFull().c_str(),
3595 aControllerPort,
3596 aDevice,
3597 aName.c_str());
3598 }
3599 else
3600 return setError(VBOX_E_OBJECT_IN_USE,
3601 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3602 aControllerPort, aDevice, aName.c_str());
3603 }
3604
3605 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3606 if (aMedium && medium.isNull())
3607 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3608
3609 AutoCaller mediumCaller(medium);
3610 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3611
3612 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3613
3614 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3615 && !medium.isNull()
3616 )
3617 return setError(VBOX_E_OBJECT_IN_USE,
3618 tr("Medium '%s' is already attached to this virtual machine"),
3619 medium->i_getLocationFull().c_str());
3620
3621 if (!medium.isNull())
3622 {
3623 MediumType_T mtype = medium->i_getType();
3624 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3625 // For DVDs it's not written to the config file, so needs no global config
3626 // version bump. For floppies it's a new attribute "type", which is ignored
3627 // by older VirtualBox version, so needs no global config version bump either.
3628 // For hard disks this type is not accepted.
3629 if (mtype == MediumType_MultiAttach)
3630 {
3631 // This type is new with VirtualBox 4.0 and therefore requires settings
3632 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3633 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3634 // two reasons: The medium type is a property of the media registry tree, which
3635 // can reside in the global config file (for pre-4.0 media); we would therefore
3636 // possibly need to bump the global config version. We don't want to do that though
3637 // because that might make downgrading to pre-4.0 impossible.
3638 // As a result, we can only use these two new types if the medium is NOT in the
3639 // global registry:
3640 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3641 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3642 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3643 )
3644 return setError(VBOX_E_INVALID_OBJECT_STATE,
3645 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3646 "to machines that were created with VirtualBox 4.0 or later"),
3647 medium->i_getLocationFull().c_str());
3648 }
3649 }
3650
3651 bool fIndirect = false;
3652 if (!medium.isNull())
3653 fIndirect = medium->i_isReadOnly();
3654 bool associate = true;
3655
3656 do
3657 {
3658 if ( aType == DeviceType_HardDisk
3659 && mMediaData.isBackedUp())
3660 {
3661 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3662
3663 /* check if the medium was attached to the VM before we started
3664 * changing attachments in which case the attachment just needs to
3665 * be restored */
3666 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3667 {
3668 AssertReturn(!fIndirect, E_FAIL);
3669
3670 /* see if it's the same bus/channel/device */
3671 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3672 {
3673 /* the simplest case: restore the whole attachment
3674 * and return, nothing else to do */
3675 mMediaData->mAttachments.push_back(pAttachTemp);
3676
3677 /* Reattach the medium to the VM. */
3678 if (fHotplug || fSilent)
3679 {
3680 mediumLock.release();
3681 treeLock.release();
3682 alock.release();
3683
3684 MediumLockList *pMediumLockList(new MediumLockList());
3685
3686 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3687 true /* fMediumLockWrite */,
3688 NULL,
3689 *pMediumLockList);
3690 alock.acquire();
3691 if (FAILED(rc))
3692 delete pMediumLockList;
3693 else
3694 {
3695 mData->mSession.mLockedMedia.Unlock();
3696 alock.release();
3697 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3698 mData->mSession.mLockedMedia.Lock();
3699 alock.acquire();
3700 }
3701 alock.release();
3702
3703 if (SUCCEEDED(rc))
3704 {
3705 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3706 /* Remove lock list in case of error. */
3707 if (FAILED(rc))
3708 {
3709 mData->mSession.mLockedMedia.Unlock();
3710 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3711 mData->mSession.mLockedMedia.Lock();
3712 }
3713 }
3714 }
3715
3716 return S_OK;
3717 }
3718
3719 /* bus/channel/device differ; we need a new attachment object,
3720 * but don't try to associate it again */
3721 associate = false;
3722 break;
3723 }
3724 }
3725
3726 /* go further only if the attachment is to be indirect */
3727 if (!fIndirect)
3728 break;
3729
3730 /* perform the so called smart attachment logic for indirect
3731 * attachments. Note that smart attachment is only applicable to base
3732 * hard disks. */
3733
3734 if (medium->i_getParent().isNull())
3735 {
3736 /* first, investigate the backup copy of the current hard disk
3737 * attachments to make it possible to re-attach existing diffs to
3738 * another device slot w/o losing their contents */
3739 if (mMediaData.isBackedUp())
3740 {
3741 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3742
3743 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3744 uint32_t foundLevel = 0;
3745
3746 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3747 {
3748 uint32_t level = 0;
3749 MediumAttachment *pAttach = *it;
3750 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3751 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3752 if (pMedium.isNull())
3753 continue;
3754
3755 if (pMedium->i_getBase(&level) == medium)
3756 {
3757 /* skip the hard disk if its currently attached (we
3758 * cannot attach the same hard disk twice) */
3759 if (i_findAttachment(mMediaData->mAttachments,
3760 pMedium))
3761 continue;
3762
3763 /* matched device, channel and bus (i.e. attached to the
3764 * same place) will win and immediately stop the search;
3765 * otherwise the attachment that has the youngest
3766 * descendant of medium will be used
3767 */
3768 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3769 {
3770 /* the simplest case: restore the whole attachment
3771 * and return, nothing else to do */
3772 mMediaData->mAttachments.push_back(*it);
3773
3774 /* Reattach the medium to the VM. */
3775 if (fHotplug || fSilent)
3776 {
3777 mediumLock.release();
3778 treeLock.release();
3779 alock.release();
3780
3781 MediumLockList *pMediumLockList(new MediumLockList());
3782
3783 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3784 true /* fMediumLockWrite */,
3785 NULL,
3786 *pMediumLockList);
3787 alock.acquire();
3788 if (FAILED(rc))
3789 delete pMediumLockList;
3790 else
3791 {
3792 mData->mSession.mLockedMedia.Unlock();
3793 alock.release();
3794 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3795 mData->mSession.mLockedMedia.Lock();
3796 alock.acquire();
3797 }
3798 alock.release();
3799
3800 if (SUCCEEDED(rc))
3801 {
3802 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3803 /* Remove lock list in case of error. */
3804 if (FAILED(rc))
3805 {
3806 mData->mSession.mLockedMedia.Unlock();
3807 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3808 mData->mSession.mLockedMedia.Lock();
3809 }
3810 }
3811 }
3812
3813 return S_OK;
3814 }
3815 else if ( foundIt == oldAtts.end()
3816 || level > foundLevel /* prefer younger */
3817 )
3818 {
3819 foundIt = it;
3820 foundLevel = level;
3821 }
3822 }
3823 }
3824
3825 if (foundIt != oldAtts.end())
3826 {
3827 /* use the previously attached hard disk */
3828 medium = (*foundIt)->i_getMedium();
3829 mediumCaller.attach(medium);
3830 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3831 mediumLock.attach(medium);
3832 /* not implicit, doesn't require association with this VM */
3833 fIndirect = false;
3834 associate = false;
3835 /* go right to the MediumAttachment creation */
3836 break;
3837 }
3838 }
3839
3840 /* must give up the medium lock and medium tree lock as below we
3841 * go over snapshots, which needs a lock with higher lock order. */
3842 mediumLock.release();
3843 treeLock.release();
3844
3845 /* then, search through snapshots for the best diff in the given
3846 * hard disk's chain to base the new diff on */
3847
3848 ComObjPtr<Medium> base;
3849 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3850 while (snap)
3851 {
3852 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3853
3854 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
3855
3856 MediumAttachment *pAttachFound = NULL;
3857 uint32_t foundLevel = 0;
3858
3859 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
3860 {
3861 MediumAttachment *pAttach = *it;
3862 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3863 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3864 if (pMedium.isNull())
3865 continue;
3866
3867 uint32_t level = 0;
3868 if (pMedium->i_getBase(&level) == medium)
3869 {
3870 /* matched device, channel and bus (i.e. attached to the
3871 * same place) will win and immediately stop the search;
3872 * otherwise the attachment that has the youngest
3873 * descendant of medium will be used
3874 */
3875 if ( pAttach->i_getDevice() == aDevice
3876 && pAttach->i_getPort() == aControllerPort
3877 && pAttach->i_getControllerName() == aName
3878 )
3879 {
3880 pAttachFound = pAttach;
3881 break;
3882 }
3883 else if ( !pAttachFound
3884 || level > foundLevel /* prefer younger */
3885 )
3886 {
3887 pAttachFound = pAttach;
3888 foundLevel = level;
3889 }
3890 }
3891 }
3892
3893 if (pAttachFound)
3894 {
3895 base = pAttachFound->i_getMedium();
3896 break;
3897 }
3898
3899 snap = snap->i_getParent();
3900 }
3901
3902 /* re-lock medium tree and the medium, as we need it below */
3903 treeLock.acquire();
3904 mediumLock.acquire();
3905
3906 /* found a suitable diff, use it as a base */
3907 if (!base.isNull())
3908 {
3909 medium = base;
3910 mediumCaller.attach(medium);
3911 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3912 mediumLock.attach(medium);
3913 }
3914 }
3915
3916 Utf8Str strFullSnapshotFolder;
3917 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3918
3919 ComObjPtr<Medium> diff;
3920 diff.createObject();
3921 // store this diff in the same registry as the parent
3922 Guid uuidRegistryParent;
3923 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3924 {
3925 // parent image has no registry: this can happen if we're attaching a new immutable
3926 // image that has not yet been attached (medium then points to the base and we're
3927 // creating the diff image for the immutable, and the parent is not yet registered);
3928 // put the parent in the machine registry then
3929 mediumLock.release();
3930 treeLock.release();
3931 alock.release();
3932 i_addMediumToRegistry(medium);
3933 alock.acquire();
3934 treeLock.acquire();
3935 mediumLock.acquire();
3936 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3937 }
3938 rc = diff->init(mParent,
3939 medium->i_getPreferredDiffFormat(),
3940 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3941 uuidRegistryParent);
3942 if (FAILED(rc)) return rc;
3943
3944 /* Apply the normal locking logic to the entire chain. */
3945 MediumLockList *pMediumLockList(new MediumLockList());
3946 mediumLock.release();
3947 treeLock.release();
3948 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3949 true /* fMediumLockWrite */,
3950 medium,
3951 *pMediumLockList);
3952 treeLock.acquire();
3953 mediumLock.acquire();
3954 if (SUCCEEDED(rc))
3955 {
3956 mediumLock.release();
3957 treeLock.release();
3958 rc = pMediumLockList->Lock();
3959 treeLock.acquire();
3960 mediumLock.acquire();
3961 if (FAILED(rc))
3962 setError(rc,
3963 tr("Could not lock medium when creating diff '%s'"),
3964 diff->i_getLocationFull().c_str());
3965 else
3966 {
3967 /* will release the lock before the potentially lengthy
3968 * operation, so protect with the special state */
3969 MachineState_T oldState = mData->mMachineState;
3970 i_setMachineState(MachineState_SettingUp);
3971
3972 mediumLock.release();
3973 treeLock.release();
3974 alock.release();
3975
3976 rc = medium->i_createDiffStorage(diff,
3977 MediumVariant_Standard,
3978 pMediumLockList,
3979 NULL /* aProgress */,
3980 true /* aWait */);
3981
3982 alock.acquire();
3983 treeLock.acquire();
3984 mediumLock.acquire();
3985
3986 i_setMachineState(oldState);
3987 }
3988 }
3989
3990 /* Unlock the media and free the associated memory. */
3991 delete pMediumLockList;
3992
3993 if (FAILED(rc)) return rc;
3994
3995 /* use the created diff for the actual attachment */
3996 medium = diff;
3997 mediumCaller.attach(medium);
3998 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3999 mediumLock.attach(medium);
4000 }
4001 while (0);
4002
4003 ComObjPtr<MediumAttachment> attachment;
4004 attachment.createObject();
4005 rc = attachment->init(this,
4006 medium,
4007 aName,
4008 aControllerPort,
4009 aDevice,
4010 aType,
4011 fIndirect,
4012 false /* fPassthrough */,
4013 false /* fTempEject */,
4014 false /* fNonRotational */,
4015 false /* fDiscard */,
4016 fHotplug /* fHotPluggable */,
4017 Utf8Str::Empty);
4018 if (FAILED(rc)) return rc;
4019
4020 if (associate && !medium.isNull())
4021 {
4022 // as the last step, associate the medium to the VM
4023 rc = medium->i_addBackReference(mData->mUuid);
4024 // here we can fail because of Deleting, or being in process of creating a Diff
4025 if (FAILED(rc)) return rc;
4026
4027 mediumLock.release();
4028 treeLock.release();
4029 alock.release();
4030 i_addMediumToRegistry(medium);
4031 alock.acquire();
4032 treeLock.acquire();
4033 mediumLock.acquire();
4034 }
4035
4036 /* success: finally remember the attachment */
4037 i_setModified(IsModified_Storage);
4038 mMediaData.backup();
4039 mMediaData->mAttachments.push_back(attachment);
4040
4041 mediumLock.release();
4042 treeLock.release();
4043 alock.release();
4044
4045 if (fHotplug || fSilent)
4046 {
4047 if (!medium.isNull())
4048 {
4049 MediumLockList *pMediumLockList(new MediumLockList());
4050
4051 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4052 true /* fMediumLockWrite */,
4053 NULL,
4054 *pMediumLockList);
4055 alock.acquire();
4056 if (FAILED(rc))
4057 delete pMediumLockList;
4058 else
4059 {
4060 mData->mSession.mLockedMedia.Unlock();
4061 alock.release();
4062 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4063 mData->mSession.mLockedMedia.Lock();
4064 alock.acquire();
4065 }
4066 alock.release();
4067 }
4068
4069 if (SUCCEEDED(rc))
4070 {
4071 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4072 /* Remove lock list in case of error. */
4073 if (FAILED(rc))
4074 {
4075 mData->mSession.mLockedMedia.Unlock();
4076 mData->mSession.mLockedMedia.Remove(attachment);
4077 mData->mSession.mLockedMedia.Lock();
4078 }
4079 }
4080 }
4081
4082 mParent->i_saveModifiedRegistries();
4083
4084 return rc;
4085}
4086
4087HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4088 LONG aDevice)
4089{
4090 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4091 aName.c_str(), aControllerPort, aDevice));
4092
4093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4094
4095 HRESULT rc = i_checkStateDependency(MutableStateDep);
4096 if (FAILED(rc)) return rc;
4097
4098 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4099
4100 /* Check for an existing controller. */
4101 ComObjPtr<StorageController> ctl;
4102 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4103 if (FAILED(rc)) return rc;
4104
4105 StorageControllerType_T ctrlType;
4106 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4107 if (FAILED(rc))
4108 return setError(E_FAIL,
4109 tr("Could not get type of controller '%s'"),
4110 aName.c_str());
4111
4112 bool fSilent = false;
4113 Utf8Str strReconfig;
4114
4115 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4116 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4117 if ( mData->mMachineState == MachineState_Paused
4118 && strReconfig == "1")
4119 fSilent = true;
4120
4121 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4122 bool fHotplug = false;
4123 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4124 fHotplug = true;
4125
4126 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4127 return setError(VBOX_E_INVALID_VM_STATE,
4128 tr("Controller '%s' does not support hotplugging"),
4129 aName.c_str());
4130
4131 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4132 Bstr(aName).raw(),
4133 aControllerPort,
4134 aDevice);
4135 if (!pAttach)
4136 return setError(VBOX_E_OBJECT_NOT_FOUND,
4137 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4138 aDevice, aControllerPort, aName.c_str());
4139
4140 if (fHotplug && !pAttach->i_getHotPluggable())
4141 return setError(VBOX_E_NOT_SUPPORTED,
4142 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4143 aDevice, aControllerPort, aName.c_str());
4144
4145 /*
4146 * The VM has to detach the device before we delete any implicit diffs.
4147 * If this fails we can roll back without loosing data.
4148 */
4149 if (fHotplug || fSilent)
4150 {
4151 alock.release();
4152 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4153 alock.acquire();
4154 }
4155 if (FAILED(rc)) return rc;
4156
4157 /* If we are here everything went well and we can delete the implicit now. */
4158 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4159
4160 alock.release();
4161
4162 mParent->i_saveModifiedRegistries();
4163
4164 return rc;
4165}
4166
4167HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4168 LONG aDevice, BOOL aPassthrough)
4169{
4170 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4171 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4172
4173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4174
4175 HRESULT rc = i_checkStateDependency(MutableStateDep);
4176 if (FAILED(rc)) return rc;
4177
4178 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4179
4180 if (Global::IsOnlineOrTransient(mData->mMachineState))
4181 return setError(VBOX_E_INVALID_VM_STATE,
4182 tr("Invalid machine state: %s"),
4183 Global::stringifyMachineState(mData->mMachineState));
4184
4185 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4186 Bstr(aName).raw(),
4187 aControllerPort,
4188 aDevice);
4189 if (!pAttach)
4190 return setError(VBOX_E_OBJECT_NOT_FOUND,
4191 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4192 aDevice, aControllerPort, aName.c_str());
4193
4194
4195 i_setModified(IsModified_Storage);
4196 mMediaData.backup();
4197
4198 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4199
4200 if (pAttach->i_getType() != DeviceType_DVD)
4201 return setError(E_INVALIDARG,
4202 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4203 aDevice, aControllerPort, aName.c_str());
4204 pAttach->i_updatePassthrough(!!aPassthrough);
4205
4206 return S_OK;
4207}
4208
4209HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4210 LONG aDevice, BOOL aTemporaryEject)
4211{
4212
4213 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4214 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4215
4216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4217
4218 HRESULT rc = i_checkStateDependency(MutableStateDep);
4219 if (FAILED(rc)) return rc;
4220
4221 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4222 Bstr(aName).raw(),
4223 aControllerPort,
4224 aDevice);
4225 if (!pAttach)
4226 return setError(VBOX_E_OBJECT_NOT_FOUND,
4227 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4228 aDevice, aControllerPort, aName.c_str());
4229
4230
4231 i_setModified(IsModified_Storage);
4232 mMediaData.backup();
4233
4234 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4235
4236 if (pAttach->i_getType() != DeviceType_DVD)
4237 return setError(E_INVALIDARG,
4238 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4239 aDevice, aControllerPort, aName.c_str());
4240 pAttach->i_updateTempEject(!!aTemporaryEject);
4241
4242 return S_OK;
4243}
4244
4245HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4246 LONG aDevice, BOOL aNonRotational)
4247{
4248
4249 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4250 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4251
4252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4253
4254 HRESULT rc = i_checkStateDependency(MutableStateDep);
4255 if (FAILED(rc)) return rc;
4256
4257 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4258
4259 if (Global::IsOnlineOrTransient(mData->mMachineState))
4260 return setError(VBOX_E_INVALID_VM_STATE,
4261 tr("Invalid machine state: %s"),
4262 Global::stringifyMachineState(mData->mMachineState));
4263
4264 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4265 Bstr(aName).raw(),
4266 aControllerPort,
4267 aDevice);
4268 if (!pAttach)
4269 return setError(VBOX_E_OBJECT_NOT_FOUND,
4270 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4271 aDevice, aControllerPort, aName.c_str());
4272
4273
4274 i_setModified(IsModified_Storage);
4275 mMediaData.backup();
4276
4277 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4278
4279 if (pAttach->i_getType() != DeviceType_HardDisk)
4280 return setError(E_INVALIDARG,
4281 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4282 aDevice, aControllerPort, aName.c_str());
4283 pAttach->i_updateNonRotational(!!aNonRotational);
4284
4285 return S_OK;
4286}
4287
4288HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4289 LONG aDevice, BOOL aDiscard)
4290{
4291
4292 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4293 aName.c_str(), aControllerPort, aDevice, aDiscard));
4294
4295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4296
4297 HRESULT rc = i_checkStateDependency(MutableStateDep);
4298 if (FAILED(rc)) return rc;
4299
4300 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4301
4302 if (Global::IsOnlineOrTransient(mData->mMachineState))
4303 return setError(VBOX_E_INVALID_VM_STATE,
4304 tr("Invalid machine state: %s"),
4305 Global::stringifyMachineState(mData->mMachineState));
4306
4307 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4308 Bstr(aName).raw(),
4309 aControllerPort,
4310 aDevice);
4311 if (!pAttach)
4312 return setError(VBOX_E_OBJECT_NOT_FOUND,
4313 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4314 aDevice, aControllerPort, aName.c_str());
4315
4316
4317 i_setModified(IsModified_Storage);
4318 mMediaData.backup();
4319
4320 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4321
4322 if (pAttach->i_getType() != DeviceType_HardDisk)
4323 return setError(E_INVALIDARG,
4324 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4325 aDevice, aControllerPort, aName.c_str());
4326 pAttach->i_updateDiscard(!!aDiscard);
4327
4328 return S_OK;
4329}
4330
4331HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4332 LONG aDevice, BOOL aHotPluggable)
4333{
4334 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4335 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4336
4337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4338
4339 HRESULT rc = i_checkStateDependency(MutableStateDep);
4340 if (FAILED(rc)) return rc;
4341
4342 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4343
4344 if (Global::IsOnlineOrTransient(mData->mMachineState))
4345 return setError(VBOX_E_INVALID_VM_STATE,
4346 tr("Invalid machine state: %s"),
4347 Global::stringifyMachineState(mData->mMachineState));
4348
4349 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4350 Bstr(aName).raw(),
4351 aControllerPort,
4352 aDevice);
4353 if (!pAttach)
4354 return setError(VBOX_E_OBJECT_NOT_FOUND,
4355 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4356 aDevice, aControllerPort, aName.c_str());
4357
4358 /* Check for an existing controller. */
4359 ComObjPtr<StorageController> ctl;
4360 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4361 if (FAILED(rc)) return rc;
4362
4363 StorageControllerType_T ctrlType;
4364 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4365 if (FAILED(rc))
4366 return setError(E_FAIL,
4367 tr("Could not get type of controller '%s'"),
4368 aName.c_str());
4369
4370 if (!i_isControllerHotplugCapable(ctrlType))
4371 return setError(VBOX_E_NOT_SUPPORTED,
4372 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4373 aName.c_str());
4374
4375 i_setModified(IsModified_Storage);
4376 mMediaData.backup();
4377
4378 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4379
4380 if (pAttach->i_getType() == DeviceType_Floppy)
4381 return setError(E_INVALIDARG,
4382 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4383 aDevice, aControllerPort, aName.c_str());
4384 pAttach->i_updateHotPluggable(!!aHotPluggable);
4385
4386 return S_OK;
4387}
4388
4389HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4390 LONG aDevice)
4391{
4392 int rc = S_OK;
4393 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4394 aName.c_str(), aControllerPort, aDevice));
4395
4396 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4397
4398 return rc;
4399}
4400
4401HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4402 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4403{
4404 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4405 aName.c_str(), aControllerPort, aDevice));
4406
4407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4408
4409 HRESULT rc = i_checkStateDependency(MutableStateDep);
4410 if (FAILED(rc)) return rc;
4411
4412 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4413
4414 if (Global::IsOnlineOrTransient(mData->mMachineState))
4415 return setError(VBOX_E_INVALID_VM_STATE,
4416 tr("Invalid machine state: %s"),
4417 Global::stringifyMachineState(mData->mMachineState));
4418
4419 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4420 Bstr(aName).raw(),
4421 aControllerPort,
4422 aDevice);
4423 if (!pAttach)
4424 return setError(VBOX_E_OBJECT_NOT_FOUND,
4425 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4426 aDevice, aControllerPort, aName.c_str());
4427
4428
4429 i_setModified(IsModified_Storage);
4430 mMediaData.backup();
4431
4432 IBandwidthGroup *iB = aBandwidthGroup;
4433 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4434 if (aBandwidthGroup && group.isNull())
4435 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4436
4437 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4438
4439 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4440 if (strBandwidthGroupOld.isNotEmpty())
4441 {
4442 /* Get the bandwidth group object and release it - this must not fail. */
4443 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4444 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4445 Assert(SUCCEEDED(rc));
4446
4447 pBandwidthGroupOld->i_release();
4448 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4449 }
4450
4451 if (!group.isNull())
4452 {
4453 group->i_reference();
4454 pAttach->i_updateBandwidthGroup(group->i_getName());
4455 }
4456
4457 return S_OK;
4458}
4459
4460HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4461 LONG aControllerPort,
4462 LONG aDevice,
4463 DeviceType_T aType)
4464{
4465 HRESULT rc = S_OK;
4466
4467 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4468 aName.c_str(), aControllerPort, aDevice, aType));
4469
4470 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4471
4472 return rc;
4473}
4474
4475
4476HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4477 LONG aControllerPort,
4478 LONG aDevice,
4479 BOOL aForce)
4480{
4481 int rc = S_OK;
4482 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4483 aName.c_str(), aControllerPort, aForce));
4484
4485 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4486
4487 return rc;
4488}
4489
4490HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4491 LONG aControllerPort,
4492 LONG aDevice,
4493 const ComPtr<IMedium> &aMedium,
4494 BOOL aForce)
4495{
4496 int rc = S_OK;
4497 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4498 aName.c_str(), aControllerPort, aDevice, aForce));
4499
4500 // request the host lock first, since might be calling Host methods for getting host drives;
4501 // next, protect the media tree all the while we're in here, as well as our member variables
4502 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4503 this->lockHandle(),
4504 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4505
4506 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4507 Bstr(aName).raw(),
4508 aControllerPort,
4509 aDevice);
4510 if (pAttach.isNull())
4511 return setError(VBOX_E_OBJECT_NOT_FOUND,
4512 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4513 aDevice, aControllerPort, aName.c_str());
4514
4515 /* Remember previously mounted medium. The medium before taking the
4516 * backup is not necessarily the same thing. */
4517 ComObjPtr<Medium> oldmedium;
4518 oldmedium = pAttach->i_getMedium();
4519
4520 IMedium *iM = aMedium;
4521 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4522 if (aMedium && pMedium.isNull())
4523 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4524
4525 AutoCaller mediumCaller(pMedium);
4526 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4527
4528 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4529 if (pMedium)
4530 {
4531 DeviceType_T mediumType = pAttach->i_getType();
4532 switch (mediumType)
4533 {
4534 case DeviceType_DVD:
4535 case DeviceType_Floppy:
4536 break;
4537
4538 default:
4539 return setError(VBOX_E_INVALID_OBJECT_STATE,
4540 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4541 aControllerPort,
4542 aDevice,
4543 aName.c_str());
4544 }
4545 }
4546
4547 i_setModified(IsModified_Storage);
4548 mMediaData.backup();
4549
4550 {
4551 // The backup operation makes the pAttach reference point to the
4552 // old settings. Re-get the correct reference.
4553 pAttach = i_findAttachment(mMediaData->mAttachments,
4554 Bstr(aName).raw(),
4555 aControllerPort,
4556 aDevice);
4557 if (!oldmedium.isNull())
4558 oldmedium->i_removeBackReference(mData->mUuid);
4559 if (!pMedium.isNull())
4560 {
4561 pMedium->i_addBackReference(mData->mUuid);
4562
4563 mediumLock.release();
4564 multiLock.release();
4565 i_addMediumToRegistry(pMedium);
4566 multiLock.acquire();
4567 mediumLock.acquire();
4568 }
4569
4570 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4571 pAttach->i_updateMedium(pMedium);
4572 }
4573
4574 i_setModified(IsModified_Storage);
4575
4576 mediumLock.release();
4577 multiLock.release();
4578 rc = i_onMediumChange(pAttach, aForce);
4579 multiLock.acquire();
4580 mediumLock.acquire();
4581
4582 /* On error roll back this change only. */
4583 if (FAILED(rc))
4584 {
4585 if (!pMedium.isNull())
4586 pMedium->i_removeBackReference(mData->mUuid);
4587 pAttach = i_findAttachment(mMediaData->mAttachments,
4588 Bstr(aName).raw(),
4589 aControllerPort,
4590 aDevice);
4591 /* If the attachment is gone in the meantime, bail out. */
4592 if (pAttach.isNull())
4593 return rc;
4594 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4595 if (!oldmedium.isNull())
4596 oldmedium->i_addBackReference(mData->mUuid);
4597 pAttach->i_updateMedium(oldmedium);
4598 }
4599
4600 mediumLock.release();
4601 multiLock.release();
4602
4603 mParent->i_saveModifiedRegistries();
4604
4605 return rc;
4606}
4607HRESULT Machine::getMedium(const com::Utf8Str &aName,
4608 LONG aControllerPort,
4609 LONG aDevice,
4610 ComPtr<IMedium> &aMedium)
4611{
4612 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4613 aName.c_str(), aControllerPort, aDevice));
4614
4615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4616
4617 aMedium = NULL;
4618
4619 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4620 Bstr(aName).raw(),
4621 aControllerPort,
4622 aDevice);
4623 if (pAttach.isNull())
4624 return setError(VBOX_E_OBJECT_NOT_FOUND,
4625 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4626 aDevice, aControllerPort, aName.c_str());
4627
4628 pAttach->i_getMedium().queryInterfaceTo(aMedium.asOutParam());
4629
4630 return S_OK;
4631}
4632
4633HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4634{
4635
4636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4637
4638 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4639
4640 return S_OK;
4641}
4642
4643HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4644{
4645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4646
4647 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4648
4649 return S_OK;
4650}
4651
4652HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4653{
4654 /* Do not assert if slot is out of range, just return the advertised
4655 status. testdriver/vbox.py triggers this in logVmInfo. */
4656 if (aSlot >= mNetworkAdapters.size())
4657 return setError(E_INVALIDARG,
4658 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4659 aSlot, mNetworkAdapters.size());
4660
4661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4662
4663 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4664
4665 return S_OK;
4666}
4667
4668HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4669{
4670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4671
4672 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4673 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4674 size_t i = 0;
4675 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4676 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4677 ++it, ++i)
4678 aKeys[i] = it->first;
4679
4680 return S_OK;
4681}
4682
4683 /**
4684 * @note Locks this object for reading.
4685 */
4686HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4687 com::Utf8Str &aValue)
4688{
4689 /* start with nothing found */
4690 aValue = "";
4691
4692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4693
4694 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4695 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4696 // found:
4697 aValue = it->second; // source is a Utf8Str
4698
4699 /* return the result to caller (may be empty) */
4700 return S_OK;
4701}
4702
4703 /**
4704 * @note Locks mParent for writing + this object for writing.
4705 */
4706HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4707{
4708 Utf8Str strOldValue; // empty
4709
4710 // locking note: we only hold the read lock briefly to look up the old value,
4711 // then release it and call the onExtraCanChange callbacks. There is a small
4712 // chance of a race insofar as the callback might be called twice if two callers
4713 // change the same key at the same time, but that's a much better solution
4714 // than the deadlock we had here before. The actual changing of the extradata
4715 // is then performed under the write lock and race-free.
4716
4717 // look up the old value first; if nothing has changed then we need not do anything
4718 {
4719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4720 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4721 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4722 strOldValue = it->second;
4723 }
4724
4725 bool fChanged;
4726 if ((fChanged = (strOldValue != aValue)))
4727 {
4728 // ask for permission from all listeners outside the locks;
4729 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4730 // lock to copy the list of callbacks to invoke
4731 Bstr error;
4732 Bstr bstrValue(aValue);
4733
4734 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4735 {
4736 const char *sep = error.isEmpty() ? "" : ": ";
4737 CBSTR err = error.raw();
4738 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4739 sep, err));
4740 return setError(E_ACCESSDENIED,
4741 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4742 aKey.c_str(),
4743 aValue.c_str(),
4744 sep,
4745 err);
4746 }
4747
4748 // data is changing and change not vetoed: then write it out under the lock
4749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4750
4751 if (i_isSnapshotMachine())
4752 {
4753 HRESULT rc = i_checkStateDependency(MutableStateDep);
4754 if (FAILED(rc)) return rc;
4755 }
4756
4757 if (aValue.isEmpty())
4758 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4759 else
4760 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4761 // creates a new key if needed
4762
4763 bool fNeedsGlobalSaveSettings = false;
4764 i_saveSettings(&fNeedsGlobalSaveSettings);
4765
4766 if (fNeedsGlobalSaveSettings)
4767 {
4768 // save the global settings; for that we should hold only the VirtualBox lock
4769 alock.release();
4770 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4771 mParent->i_saveSettings();
4772 }
4773 }
4774
4775 // fire notification outside the lock
4776 if (fChanged)
4777 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4778
4779 return S_OK;
4780}
4781
4782HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4783{
4784 aProgress = NULL;
4785 NOREF(aSettingsFilePath);
4786 ReturnComNotImplemented();
4787}
4788
4789HRESULT Machine::saveSettings()
4790{
4791 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4792
4793 /* when there was auto-conversion, we want to save the file even if
4794 * the VM is saved */
4795 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4796 if (FAILED(rc)) return rc;
4797
4798 /* the settings file path may never be null */
4799 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4800
4801 /* save all VM data excluding snapshots */
4802 bool fNeedsGlobalSaveSettings = false;
4803 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4804 mlock.release();
4805
4806 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4807 {
4808 // save the global settings; for that we should hold only the VirtualBox lock
4809 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4810 rc = mParent->i_saveSettings();
4811 }
4812
4813 return rc;
4814}
4815
4816
4817HRESULT Machine::discardSettings()
4818{
4819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4820
4821 HRESULT rc = i_checkStateDependency(MutableStateDep);
4822 if (FAILED(rc)) return rc;
4823
4824 /*
4825 * during this rollback, the session will be notified if data has
4826 * been actually changed
4827 */
4828 i_rollback(true /* aNotify */);
4829
4830 return S_OK;
4831}
4832
4833/** @note Locks objects! */
4834HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4835 std::vector<ComPtr<IMedium> > &aMedia)
4836{
4837 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4838 AutoLimitedCaller autoCaller(this);
4839 AssertComRCReturnRC(autoCaller.rc());
4840
4841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4842
4843 Guid id(i_getId());
4844
4845 if (mData->mSession.mState != SessionState_Unlocked)
4846 return setError(VBOX_E_INVALID_OBJECT_STATE,
4847 tr("Cannot unregister the machine '%s' while it is locked"),
4848 mUserData->s.strName.c_str());
4849
4850 // wait for state dependents to drop to zero
4851 i_ensureNoStateDependencies();
4852
4853 if (!mData->mAccessible)
4854 {
4855 // inaccessible maschines can only be unregistered; uninitialize ourselves
4856 // here because currently there may be no unregistered that are inaccessible
4857 // (this state combination is not supported). Note releasing the caller and
4858 // leaving the lock before calling uninit()
4859 alock.release();
4860 autoCaller.release();
4861
4862 uninit();
4863
4864 mParent->i_unregisterMachine(this, id);
4865 // calls VirtualBox::i_saveSettings()
4866
4867 return S_OK;
4868 }
4869
4870 HRESULT rc = S_OK;
4871
4872 // discard saved state
4873 if (mData->mMachineState == MachineState_Saved)
4874 {
4875 // add the saved state file to the list of files the caller should delete
4876 Assert(!mSSData->strStateFilePath.isEmpty());
4877 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4878
4879 mSSData->strStateFilePath.setNull();
4880
4881 // unconditionally set the machine state to powered off, we now
4882 // know no session has locked the machine
4883 mData->mMachineState = MachineState_PoweredOff;
4884 }
4885
4886 size_t cSnapshots = 0;
4887 if (mData->mFirstSnapshot)
4888 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
4889 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
4890 // fail now before we start detaching media
4891 return setError(VBOX_E_INVALID_OBJECT_STATE,
4892 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4893 mUserData->s.strName.c_str(), cSnapshots);
4894
4895 // This list collects the medium objects from all medium attachments
4896 // which we will detach from the machine and its snapshots, in a specific
4897 // order which allows for closing all media without getting "media in use"
4898 // errors, simply by going through the list from the front to the back:
4899 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4900 // and must be closed before the parent media from the snapshots, or closing the parents
4901 // will fail because they still have children);
4902 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4903 // the root ("first") snapshot of the machine.
4904 MediaList llMedia;
4905
4906 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4907 && mMediaData->mAttachments.size()
4908 )
4909 {
4910 // we have media attachments: detach them all and add the Medium objects to our list
4911 if (aCleanupMode != CleanupMode_UnregisterOnly)
4912 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4913 else
4914 return setError(VBOX_E_INVALID_OBJECT_STATE,
4915 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4916 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4917 }
4918
4919 if (cSnapshots)
4920 {
4921 // autoCleanup must be true here, or we would have failed above
4922
4923 // add the media from the medium attachments of the snapshots to llMedia
4924 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4925 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4926 // into the children first
4927
4928 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4929 MachineState_T oldState = mData->mMachineState;
4930 mData->mMachineState = MachineState_DeletingSnapshot;
4931
4932 // make a copy of the first snapshot so the refcount does not drop to 0
4933 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4934 // because of the AutoCaller voodoo)
4935 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4936
4937 // GO!
4938 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4939
4940 mData->mMachineState = oldState;
4941 }
4942
4943 if (FAILED(rc))
4944 {
4945 i_rollbackMedia();
4946 return rc;
4947 }
4948
4949 // commit all the media changes made above
4950 i_commitMedia();
4951
4952 mData->mRegistered = false;
4953
4954 // machine lock no longer needed
4955 alock.release();
4956
4957 // return media to caller
4958 size_t i = 0;
4959 aMedia.resize(llMedia.size());
4960 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
4961 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4962
4963 mParent->i_unregisterMachine(this, id);
4964 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4965
4966 return S_OK;
4967}
4968
4969struct Machine::DeleteTask
4970{
4971 ComObjPtr<Machine> pMachine;
4972 RTCList<ComPtr<IMedium> > llMediums;
4973 StringsList llFilesToDelete;
4974 ComObjPtr<Progress> pProgress;
4975};
4976
4977HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
4978{
4979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4980
4981 HRESULT rc = i_checkStateDependency(MutableStateDep);
4982 if (FAILED(rc)) return rc;
4983
4984 if (mData->mRegistered)
4985 return setError(VBOX_E_INVALID_VM_STATE,
4986 tr("Cannot delete settings of a registered machine"));
4987
4988 DeleteTask *pTask = new DeleteTask;
4989 pTask->pMachine = this;
4990
4991 // collect files to delete
4992 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
4993
4994 for (size_t i = 0; i < aMedia.size(); ++i)
4995 {
4996 IMedium *pIMedium(aMedia[i]);
4997 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4998 if (pMedium.isNull())
4999 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5000 SafeArray<BSTR> ids;
5001 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5002 if (FAILED(rc)) return rc;
5003 /* At this point the medium should not have any back references
5004 * anymore. If it has it is attached to another VM and *must* not
5005 * deleted. */
5006 if (ids.size() < 1)
5007 pTask->llMediums.append(pMedium);
5008 }
5009 if (mData->pMachineConfigFile->fileExists())
5010 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5011
5012 pTask->pProgress.createObject();
5013 pTask->pProgress->init(i_getVirtualBox(),
5014 static_cast<IMachine*>(this) /* aInitiator */,
5015 Bstr(tr("Deleting files")).raw(),
5016 true /* fCancellable */,
5017 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5018 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5019
5020 int vrc = RTThreadCreate(NULL,
5021 Machine::deleteThread,
5022 (void*)pTask,
5023 0,
5024 RTTHREADTYPE_MAIN_WORKER,
5025 0,
5026 "MachineDelete");
5027
5028 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5029
5030 if (RT_FAILURE(vrc))
5031 {
5032 delete pTask;
5033 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5034 }
5035
5036 LogFlowFuncLeave();
5037
5038 return S_OK;
5039}
5040
5041/**
5042 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5043 * calls Machine::deleteTaskWorker() on the actual machine object.
5044 * @param Thread
5045 * @param pvUser
5046 * @return
5047 */
5048/*static*/
5049DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5050{
5051 LogFlowFuncEnter();
5052
5053 DeleteTask *pTask = (DeleteTask*)pvUser;
5054 Assert(pTask);
5055 Assert(pTask->pMachine);
5056 Assert(pTask->pProgress);
5057
5058 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5059 pTask->pProgress->i_notifyComplete(rc);
5060
5061 delete pTask;
5062
5063 LogFlowFuncLeave();
5064
5065 NOREF(Thread);
5066
5067 return VINF_SUCCESS;
5068}
5069
5070/**
5071 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5072 * @param task
5073 * @return
5074 */
5075HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5076{
5077 AutoCaller autoCaller(this);
5078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5079
5080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5081
5082 HRESULT rc = S_OK;
5083
5084 try
5085 {
5086 ULONG uLogHistoryCount = 3;
5087 ComPtr<ISystemProperties> systemProperties;
5088 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5089 if (FAILED(rc)) throw rc;
5090
5091 if (!systemProperties.isNull())
5092 {
5093 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5094 if (FAILED(rc)) throw rc;
5095 }
5096
5097 MachineState_T oldState = mData->mMachineState;
5098 i_setMachineState(MachineState_SettingUp);
5099 alock.release();
5100 for (size_t i = 0; i < task.llMediums.size(); ++i)
5101 {
5102 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5103 {
5104 AutoCaller mac(pMedium);
5105 if (FAILED(mac.rc())) throw mac.rc();
5106 Utf8Str strLocation = pMedium->i_getLocationFull();
5107 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5108 if (FAILED(rc)) throw rc;
5109 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5110 }
5111 ComPtr<IProgress> pProgress2;
5112 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5113 if (FAILED(rc)) throw rc;
5114 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5115 if (FAILED(rc)) throw rc;
5116 /* Check the result of the asynchronous process. */
5117 LONG iRc;
5118 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5119 if (FAILED(rc)) throw rc;
5120 /* If the thread of the progress object has an error, then
5121 * retrieve the error info from there, or it'll be lost. */
5122 if (FAILED(iRc))
5123 throw setError(ProgressErrorInfo(pProgress2));
5124
5125 /* Close the medium, deliberately without checking the return
5126- * code, and without leaving any trace in the error info, as
5127- * a failure here is a very minor issue, which shouldn't happen
5128- * as above we even managed to delete the medium. */
5129 {
5130 ErrorInfoKeeper eik;
5131 pMedium->Close();
5132 }
5133 }
5134 i_setMachineState(oldState);
5135 alock.acquire();
5136
5137 // delete the files pushed on the task list by Machine::Delete()
5138 // (this includes saved states of the machine and snapshots and
5139 // medium storage files from the IMedium list passed in, and the
5140 // machine XML file)
5141 StringsList::const_iterator it = task.llFilesToDelete.begin();
5142 while (it != task.llFilesToDelete.end())
5143 {
5144 const Utf8Str &strFile = *it;
5145 LogFunc(("Deleting file %s\n", strFile.c_str()));
5146 int vrc = RTFileDelete(strFile.c_str());
5147 if (RT_FAILURE(vrc))
5148 throw setError(VBOX_E_IPRT_ERROR,
5149 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5150
5151 ++it;
5152 if (it == task.llFilesToDelete.end())
5153 {
5154 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5155 if (FAILED(rc)) throw rc;
5156 break;
5157 }
5158
5159 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5160 if (FAILED(rc)) throw rc;
5161 }
5162
5163 /* delete the settings only when the file actually exists */
5164 if (mData->pMachineConfigFile->fileExists())
5165 {
5166 /* Delete any backup or uncommitted XML files. Ignore failures.
5167 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5168 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5169 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5170 RTFileDelete(otherXml.c_str());
5171 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5172 RTFileDelete(otherXml.c_str());
5173
5174 /* delete the Logs folder, nothing important should be left
5175 * there (we don't check for errors because the user might have
5176 * some private files there that we don't want to delete) */
5177 Utf8Str logFolder;
5178 getLogFolder(logFolder);
5179 Assert(logFolder.length());
5180 if (RTDirExists(logFolder.c_str()))
5181 {
5182 /* Delete all VBox.log[.N] files from the Logs folder
5183 * (this must be in sync with the rotation logic in
5184 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5185 * files that may have been created by the GUI. */
5186 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5187 logFolder.c_str(), RTPATH_DELIMITER);
5188 RTFileDelete(log.c_str());
5189 log = Utf8StrFmt("%s%cVBox.png",
5190 logFolder.c_str(), RTPATH_DELIMITER);
5191 RTFileDelete(log.c_str());
5192 for (int i = uLogHistoryCount; i > 0; i--)
5193 {
5194 log = Utf8StrFmt("%s%cVBox.log.%d",
5195 logFolder.c_str(), RTPATH_DELIMITER, i);
5196 RTFileDelete(log.c_str());
5197 log = Utf8StrFmt("%s%cVBox.png.%d",
5198 logFolder.c_str(), RTPATH_DELIMITER, i);
5199 RTFileDelete(log.c_str());
5200 }
5201
5202 RTDirRemove(logFolder.c_str());
5203 }
5204
5205 /* delete the Snapshots folder, nothing important should be left
5206 * there (we don't check for errors because the user might have
5207 * some private files there that we don't want to delete) */
5208 Utf8Str strFullSnapshotFolder;
5209 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5210 Assert(!strFullSnapshotFolder.isEmpty());
5211 if (RTDirExists(strFullSnapshotFolder.c_str()))
5212 RTDirRemove(strFullSnapshotFolder.c_str());
5213
5214 // delete the directory that contains the settings file, but only
5215 // if it matches the VM name
5216 Utf8Str settingsDir;
5217 if (i_isInOwnDir(&settingsDir))
5218 RTDirRemove(settingsDir.c_str());
5219 }
5220
5221 alock.release();
5222
5223 mParent->i_saveModifiedRegistries();
5224 }
5225 catch (HRESULT aRC) { rc = aRC; }
5226
5227 return rc;
5228}
5229
5230HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5231{
5232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5233
5234 ComObjPtr<Snapshot> pSnapshot;
5235 HRESULT rc;
5236
5237 if (aNameOrId.isEmpty())
5238 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5239 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5240 else
5241 {
5242 Guid uuid(aNameOrId);
5243 if (uuid.isValid())
5244 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5245 else
5246 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5247 }
5248 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5249
5250 return rc;
5251}
5252
5253HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5254{
5255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5256
5257 HRESULT rc = i_checkStateDependency(MutableStateDep);
5258 if (FAILED(rc)) return rc;
5259
5260 ComObjPtr<SharedFolder> sharedFolder;
5261 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5262 if (SUCCEEDED(rc))
5263 return setError(VBOX_E_OBJECT_IN_USE,
5264 tr("Shared folder named '%s' already exists"),
5265 aName.c_str());
5266
5267 sharedFolder.createObject();
5268 rc = sharedFolder->init(i_getMachine(),
5269 aName,
5270 aHostPath,
5271 !!aWritable,
5272 !!aAutomount,
5273 true /* fFailOnError */);
5274 if (FAILED(rc)) return rc;
5275
5276 i_setModified(IsModified_SharedFolders);
5277 mHWData.backup();
5278 mHWData->mSharedFolders.push_back(sharedFolder);
5279
5280 /* inform the direct session if any */
5281 alock.release();
5282 i_onSharedFolderChange();
5283
5284 return S_OK;
5285}
5286
5287HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5288{
5289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5290
5291 HRESULT rc = i_checkStateDependency(MutableStateDep);
5292 if (FAILED(rc)) return rc;
5293
5294 ComObjPtr<SharedFolder> sharedFolder;
5295 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5296 if (FAILED(rc)) return rc;
5297
5298 i_setModified(IsModified_SharedFolders);
5299 mHWData.backup();
5300 mHWData->mSharedFolders.remove(sharedFolder);
5301
5302 /* inform the direct session if any */
5303 alock.release();
5304 i_onSharedFolderChange();
5305
5306 return S_OK;
5307}
5308
5309HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5310{
5311 /* start with No */
5312 *aCanShow = FALSE;
5313
5314 ComPtr<IInternalSessionControl> directControl;
5315 {
5316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5317
5318 if (mData->mSession.mState != SessionState_Locked)
5319 return setError(VBOX_E_INVALID_VM_STATE,
5320 tr("Machine is not locked for session (session state: %s)"),
5321 Global::stringifySessionState(mData->mSession.mState));
5322
5323 directControl = mData->mSession.mDirectControl;
5324 }
5325
5326 /* ignore calls made after #OnSessionEnd() is called */
5327 if (!directControl)
5328 return S_OK;
5329
5330 LONG64 dummy;
5331 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5332}
5333
5334HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5335{
5336 ComPtr<IInternalSessionControl> directControl;
5337 {
5338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5339
5340 if (mData->mSession.mState != SessionState_Locked)
5341 return setError(E_FAIL,
5342 tr("Machine is not locked for session (session state: %s)"),
5343 Global::stringifySessionState(mData->mSession.mState));
5344
5345 directControl = mData->mSession.mDirectControl;
5346 }
5347
5348 /* ignore calls made after #OnSessionEnd() is called */
5349 if (!directControl)
5350 return S_OK;
5351
5352 BOOL dummy;
5353 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5354}
5355
5356#ifdef VBOX_WITH_GUEST_PROPS
5357/**
5358 * Look up a guest property in VBoxSVC's internal structures.
5359 */
5360HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5361 com::Utf8Str &aValue,
5362 LONG64 *aTimestamp,
5363 com::Utf8Str &aFlags) const
5364{
5365 using namespace guestProp;
5366
5367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5368 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5369
5370 if (it != mHWData->mGuestProperties.end())
5371 {
5372 char szFlags[MAX_FLAGS_LEN + 1];
5373 aValue = it->second.strValue;
5374 *aTimestamp = it->second.mTimestamp;
5375 writeFlags(it->second.mFlags, szFlags);
5376 aFlags = Utf8Str(szFlags);
5377 }
5378
5379 return S_OK;
5380}
5381
5382/**
5383 * Query the VM that a guest property belongs to for the property.
5384 * @returns E_ACCESSDENIED if the VM process is not available or not
5385 * currently handling queries and the lookup should then be done in
5386 * VBoxSVC.
5387 */
5388HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5389 com::Utf8Str &aValue,
5390 LONG64 *aTimestamp,
5391 com::Utf8Str &aFlags) const
5392{
5393 HRESULT rc = S_OK;
5394 BSTR bValue = NULL;
5395 BSTR bFlags = NULL;
5396
5397 ComPtr<IInternalSessionControl> directControl;
5398 directControl = mData->mSession.mDirectControl;
5399
5400 /* fail if we were called after #OnSessionEnd() is called. This is a
5401 * silly race condition. */
5402
5403 /** @todo This code is bothering API clients (like python script clients) with
5404 * the AccessGuestProperty call, creating unncessary IPC. Need to
5405 * have a way of figuring out which kind of direct session it is... */
5406 if (!directControl)
5407 rc = E_ACCESSDENIED;
5408 else
5409 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), NULL, NULL,
5410 false /* isSetter */,
5411 &bValue, aTimestamp, &bFlags);
5412
5413 aValue = bValue;
5414 aFlags = bFlags;
5415
5416 return rc;
5417}
5418#endif // VBOX_WITH_GUEST_PROPS
5419
5420HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5421 com::Utf8Str &aValue,
5422 LONG64 *aTimestamp,
5423 com::Utf8Str &aFlags)
5424{
5425#ifndef VBOX_WITH_GUEST_PROPS
5426 ReturnComNotImplemented();
5427#else // VBOX_WITH_GUEST_PROPS
5428
5429 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5430
5431 if (rc == E_ACCESSDENIED)
5432 /* The VM is not running or the service is not (yet) accessible */
5433 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5434 return rc;
5435#endif // VBOX_WITH_GUEST_PROPS
5436}
5437
5438HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5439{
5440 LONG64 dummyTimestamp;
5441 com::Utf8Str dummyFlags;
5442 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5443 return rc;
5444
5445}
5446HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5447{
5448 com::Utf8Str dummyFlags;
5449 com::Utf8Str dummyValue;
5450 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5451 return rc;
5452}
5453
5454#ifdef VBOX_WITH_GUEST_PROPS
5455/**
5456 * Set a guest property in VBoxSVC's internal structures.
5457 */
5458HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5459 const com::Utf8Str &aFlags)
5460{
5461 using namespace guestProp;
5462
5463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5464 HRESULT rc = S_OK;
5465
5466 rc = i_checkStateDependency(MutableStateDep);
5467 if (FAILED(rc)) return rc;
5468
5469 try
5470 {
5471 uint32_t fFlags = NILFLAG;
5472 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5473 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5474
5475 bool fDelete = aValue.isEmpty();
5476 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5477 if (it == mHWData->mGuestProperties.end())
5478 {
5479 if (!fDelete)
5480 {
5481 i_setModified(IsModified_MachineData);
5482 mHWData.backupEx();
5483
5484 RTTIMESPEC time;
5485 HWData::GuestProperty prop;
5486 prop.strValue = Bstr(aValue).raw();
5487 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5488 prop.mFlags = fFlags;
5489 mHWData->mGuestProperties[aName] = prop;
5490 }
5491 }
5492 else
5493 {
5494 if (it->second.mFlags & (RDONLYHOST))
5495 {
5496 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5497 }
5498 else
5499 {
5500 i_setModified(IsModified_MachineData);
5501 mHWData.backupEx();
5502
5503 /* The backupEx() operation invalidates our iterator,
5504 * so get a new one. */
5505 it = mHWData->mGuestProperties.find(aName);
5506 Assert(it != mHWData->mGuestProperties.end());
5507
5508 if (!fDelete)
5509 {
5510 RTTIMESPEC time;
5511 it->second.strValue = aValue;
5512 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5513 it->second.mFlags = fFlags;
5514 }
5515 else
5516 mHWData->mGuestProperties.erase(it);
5517 }
5518 }
5519
5520 if ( SUCCEEDED(rc)
5521 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5522 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5523 RTSTR_MAX,
5524 aName.c_str(),
5525 RTSTR_MAX,
5526 NULL)
5527 )
5528 )
5529 {
5530 alock.release();
5531
5532 mParent->i_onGuestPropertyChange(mData->mUuid,
5533 Bstr(aName).raw(),
5534 Bstr(aValue).raw(),
5535 Bstr(aFlags).raw());
5536 }
5537 }
5538 catch (std::bad_alloc &)
5539 {
5540 rc = E_OUTOFMEMORY;
5541 }
5542
5543 return rc;
5544}
5545
5546/**
5547 * Set a property on the VM that that property belongs to.
5548 * @returns E_ACCESSDENIED if the VM process is not available or not
5549 * currently handling queries and the setting should then be done in
5550 * VBoxSVC.
5551 */
5552HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5553 const com::Utf8Str &aFlags)
5554{
5555 HRESULT rc;
5556
5557 try
5558 {
5559 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5560
5561 BSTR dummy = NULL; /* will not be changed (setter) */
5562 LONG64 dummy64;
5563 if (!directControl)
5564 rc = E_ACCESSDENIED;
5565 else
5566 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5567 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5568 true /* isSetter */,
5569 &dummy, &dummy64, &dummy);
5570 }
5571 catch (std::bad_alloc &)
5572 {
5573 rc = E_OUTOFMEMORY;
5574 }
5575
5576 return rc;
5577}
5578#endif // VBOX_WITH_GUEST_PROPS
5579
5580HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5581 const com::Utf8Str &aFlags)
5582{
5583#ifndef VBOX_WITH_GUEST_PROPS
5584 ReturnComNotImplemented();
5585#else // VBOX_WITH_GUEST_PROPS
5586 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags);
5587 if (rc == E_ACCESSDENIED)
5588 /* The VM is not running or the service is not (yet) accessible */
5589 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags);
5590 return rc;
5591#endif // VBOX_WITH_GUEST_PROPS
5592}
5593
5594HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5595{
5596 return setGuestProperty(aProperty, aValue, "");
5597}
5598
5599HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5600{
5601 return setGuestProperty(aName, "", "");
5602}
5603
5604#ifdef VBOX_WITH_GUEST_PROPS
5605/**
5606 * Enumerate the guest properties in VBoxSVC's internal structures.
5607 */
5608HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5609 std::vector<com::Utf8Str> &aNames,
5610 std::vector<com::Utf8Str> &aValues,
5611 std::vector<LONG64> &aTimestamps,
5612 std::vector<com::Utf8Str> &aFlags)
5613{
5614 using namespace guestProp;
5615
5616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5617 Utf8Str strPatterns(aPatterns);
5618
5619 HWData::GuestPropertyMap propMap;
5620
5621 /*
5622 * Look for matching patterns and build up a list.
5623 */
5624 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5625 while (it != mHWData->mGuestProperties.end())
5626 {
5627 if ( strPatterns.isEmpty()
5628 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5629 RTSTR_MAX,
5630 it->first.c_str(),
5631 RTSTR_MAX,
5632 NULL)
5633 )
5634 propMap.insert(*it);
5635 it++;
5636 }
5637
5638 alock.release();
5639
5640 /*
5641 * And build up the arrays for returning the property information.
5642 */
5643 size_t cEntries = propMap.size();
5644
5645 aNames.resize(cEntries);
5646 aValues.resize(cEntries);
5647 aTimestamps.resize(cEntries);
5648 aFlags.resize(cEntries);
5649
5650 char szFlags[MAX_FLAGS_LEN + 1];
5651 size_t i= 0;
5652 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5653 {
5654 aNames[i] = it->first;
5655 aValues[i] = it->second.strValue;
5656 aTimestamps[i] = it->second.mTimestamp;
5657 writeFlags(it->second.mFlags, szFlags);
5658 aFlags[i] = Utf8Str(szFlags);
5659 }
5660
5661 return S_OK;
5662}
5663
5664/**
5665 * Enumerate the properties managed by a VM.
5666 * @returns E_ACCESSDENIED if the VM process is not available or not
5667 * currently handling queries and the setting should then be done in
5668 * VBoxSVC.
5669 */
5670HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5671 std::vector<com::Utf8Str> &aNames,
5672 std::vector<com::Utf8Str> &aValues,
5673 std::vector<LONG64> &aTimestamps,
5674 std::vector<com::Utf8Str> &aFlags)
5675{
5676 HRESULT rc;
5677 ComPtr<IInternalSessionControl> directControl;
5678 directControl = mData->mSession.mDirectControl;
5679
5680
5681 com::SafeArray<BSTR> bNames;
5682 com::SafeArray<BSTR> bValues;
5683 com::SafeArray<LONG64> bTimestamps;
5684 com::SafeArray<BSTR> bFlags;
5685
5686 if (!directControl)
5687 rc = E_ACCESSDENIED;
5688 else
5689 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5690 ComSafeArrayAsOutParam(bNames),
5691 ComSafeArrayAsOutParam(bValues),
5692 ComSafeArrayAsOutParam(bTimestamps),
5693 ComSafeArrayAsOutParam(bFlags));
5694 size_t i;
5695 aNames.resize(bNames.size());
5696 for (i = 0; i < bNames.size(); ++i)
5697 aNames[i] = Utf8Str(bNames[i]);
5698 aValues.resize(bValues.size());
5699 for (i = 0; i < bValues.size(); ++i)
5700 aValues[i] = Utf8Str(bValues[i]);
5701 aTimestamps.resize(bTimestamps.size());
5702 for (i = 0; i < bTimestamps.size(); ++i)
5703 aTimestamps[i] = bTimestamps[i];
5704 aFlags.resize(bFlags.size());
5705 for (i = 0; i < bFlags.size(); ++i)
5706 aFlags[i] = Utf8Str(bFlags[i]);
5707
5708 return rc;
5709}
5710#endif // VBOX_WITH_GUEST_PROPS
5711HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5712 std::vector<com::Utf8Str> &aNames,
5713 std::vector<com::Utf8Str> &aValues,
5714 std::vector<LONG64> &aTimestamps,
5715 std::vector<com::Utf8Str> &aFlags)
5716{
5717#ifndef VBOX_WITH_GUEST_PROPS
5718 ReturnComNotImplemented();
5719#else // VBOX_WITH_GUEST_PROPS
5720
5721 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5722
5723 if (rc == E_ACCESSDENIED)
5724 /* The VM is not running or the service is not (yet) accessible */
5725 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5726 return rc;
5727#endif // VBOX_WITH_GUEST_PROPS
5728}
5729
5730HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5731 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5732{
5733 MediaData::AttachmentList atts;
5734
5735 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5736 if (FAILED(rc)) return rc;
5737
5738 size_t i = 0;
5739 aMediumAttachments.resize(atts.size());
5740 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5741 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5742
5743 return S_OK;
5744}
5745
5746HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5747 LONG aControllerPort,
5748 LONG aDevice,
5749 ComPtr<IMediumAttachment> &aAttachment)
5750{
5751 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5752 aName.c_str(), aControllerPort, aDevice));
5753
5754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5755
5756 aAttachment = NULL;
5757
5758 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5759 Bstr(aName).raw(),
5760 aControllerPort,
5761 aDevice);
5762 if (pAttach.isNull())
5763 return setError(VBOX_E_OBJECT_NOT_FOUND,
5764 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5765 aDevice, aControllerPort, aName.c_str());
5766
5767 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5768
5769 return S_OK;
5770}
5771
5772
5773HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5774 StorageBus_T aConnectionType,
5775 ComPtr<IStorageController> &aController)
5776{
5777 if ( (aConnectionType <= StorageBus_Null)
5778 || (aConnectionType > StorageBus_USB))
5779 return setError(E_INVALIDARG,
5780 tr("Invalid connection type: %d"),
5781 aConnectionType);
5782
5783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5784
5785 HRESULT rc = i_checkStateDependency(MutableStateDep);
5786 if (FAILED(rc)) return rc;
5787
5788 /* try to find one with the name first. */
5789 ComObjPtr<StorageController> ctrl;
5790
5791 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5792 if (SUCCEEDED(rc))
5793 return setError(VBOX_E_OBJECT_IN_USE,
5794 tr("Storage controller named '%s' already exists"),
5795 aName.c_str());
5796
5797 ctrl.createObject();
5798
5799 /* get a new instance number for the storage controller */
5800 ULONG ulInstance = 0;
5801 bool fBootable = true;
5802 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5803 it != mStorageControllers->end();
5804 ++it)
5805 {
5806 if ((*it)->i_getStorageBus() == aConnectionType)
5807 {
5808 ULONG ulCurInst = (*it)->i_getInstance();
5809
5810 if (ulCurInst >= ulInstance)
5811 ulInstance = ulCurInst + 1;
5812
5813 /* Only one controller of each type can be marked as bootable. */
5814 if ((*it)->i_getBootable())
5815 fBootable = false;
5816 }
5817 }
5818
5819 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5820 if (FAILED(rc)) return rc;
5821
5822 i_setModified(IsModified_Storage);
5823 mStorageControllers.backup();
5824 mStorageControllers->push_back(ctrl);
5825
5826 ctrl.queryInterfaceTo(aController.asOutParam());
5827
5828 /* inform the direct session if any */
5829 alock.release();
5830 i_onStorageControllerChange();
5831
5832 return S_OK;
5833}
5834
5835HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5836 ComPtr<IStorageController> &aStorageController)
5837{
5838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5839
5840 ComObjPtr<StorageController> ctrl;
5841
5842 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5843 if (SUCCEEDED(rc))
5844 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5845
5846 return rc;
5847}
5848
5849HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
5850 ComPtr<IStorageController> &aStorageController)
5851{
5852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5853
5854 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5855 it != mStorageControllers->end();
5856 ++it)
5857 {
5858 if ((*it)->i_getInstance() == aInstance)
5859 {
5860 (*it).queryInterfaceTo(aStorageController.asOutParam());
5861 return S_OK;
5862 }
5863 }
5864
5865 return setError(VBOX_E_OBJECT_NOT_FOUND,
5866 tr("Could not find a storage controller with instance number '%lu'"),
5867 aInstance);
5868}
5869
5870HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5871{
5872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5873
5874 HRESULT rc = i_checkStateDependency(MutableStateDep);
5875 if (FAILED(rc)) return rc;
5876
5877 ComObjPtr<StorageController> ctrl;
5878
5879 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5880 if (SUCCEEDED(rc))
5881 {
5882 /* Ensure that only one controller of each type is marked as bootable. */
5883 if (aBootable == TRUE)
5884 {
5885 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5886 it != mStorageControllers->end();
5887 ++it)
5888 {
5889 ComObjPtr<StorageController> aCtrl = (*it);
5890
5891 if ( (aCtrl->i_getName() != aName)
5892 && aCtrl->i_getBootable() == TRUE
5893 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5894 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5895 {
5896 aCtrl->i_setBootable(FALSE);
5897 break;
5898 }
5899 }
5900 }
5901
5902 if (SUCCEEDED(rc))
5903 {
5904 ctrl->i_setBootable(aBootable);
5905 i_setModified(IsModified_Storage);
5906 }
5907 }
5908
5909 if (SUCCEEDED(rc))
5910 {
5911 /* inform the direct session if any */
5912 alock.release();
5913 i_onStorageControllerChange();
5914 }
5915
5916 return rc;
5917}
5918
5919HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5920{
5921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5922
5923 HRESULT rc = i_checkStateDependency(MutableStateDep);
5924 if (FAILED(rc)) return rc;
5925
5926 ComObjPtr<StorageController> ctrl;
5927 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5928 if (FAILED(rc)) return rc;
5929
5930 {
5931 /* find all attached devices to the appropriate storage controller and detach them all */
5932 // make a temporary list because detachDevice invalidates iterators into
5933 // mMediaData->mAttachments
5934 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
5935
5936 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
5937 it != llAttachments2.end();
5938 ++it)
5939 {
5940 MediumAttachment *pAttachTemp = *it;
5941
5942 AutoCaller localAutoCaller(pAttachTemp);
5943 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5944
5945 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5946
5947 if (pAttachTemp->i_getControllerName() == aName)
5948 {
5949 rc = i_detachDevice(pAttachTemp, alock, NULL);
5950 if (FAILED(rc)) return rc;
5951 }
5952 }
5953 }
5954
5955 /* We can remove it now. */
5956 i_setModified(IsModified_Storage);
5957 mStorageControllers.backup();
5958
5959 ctrl->i_unshare();
5960
5961 mStorageControllers->remove(ctrl);
5962
5963 /* inform the direct session if any */
5964 alock.release();
5965 i_onStorageControllerChange();
5966
5967 return S_OK;
5968}
5969
5970HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
5971 ComPtr<IUSBController> &aController)
5972{
5973 if ( (aType <= USBControllerType_Null)
5974 || (aType >= USBControllerType_Last))
5975 return setError(E_INVALIDARG,
5976 tr("Invalid USB controller type: %d"),
5977 aType);
5978
5979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5980
5981 HRESULT rc = i_checkStateDependency(MutableStateDep);
5982 if (FAILED(rc)) return rc;
5983
5984 /* try to find one with the same type first. */
5985 ComObjPtr<USBController> ctrl;
5986
5987 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
5988 if (SUCCEEDED(rc))
5989 return setError(VBOX_E_OBJECT_IN_USE,
5990 tr("USB controller named '%s' already exists"),
5991 aName.c_str());
5992
5993 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
5994 ULONG maxInstances;
5995 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
5996 if (FAILED(rc))
5997 return rc;
5998
5999 ULONG cInstances = i_getUSBControllerCountByType(aType);
6000 if (cInstances >= maxInstances)
6001 return setError(E_INVALIDARG,
6002 tr("Too many USB controllers of this type"));
6003
6004 ctrl.createObject();
6005
6006 rc = ctrl->init(this, aName, aType);
6007 if (FAILED(rc)) return rc;
6008
6009 i_setModified(IsModified_USB);
6010 mUSBControllers.backup();
6011 mUSBControllers->push_back(ctrl);
6012
6013 ctrl.queryInterfaceTo(aController.asOutParam());
6014
6015 /* inform the direct session if any */
6016 alock.release();
6017 i_onUSBControllerChange();
6018
6019 return S_OK;
6020}
6021
6022HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6023{
6024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6025
6026 ComObjPtr<USBController> ctrl;
6027
6028 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6029 if (SUCCEEDED(rc))
6030 ctrl.queryInterfaceTo(aController.asOutParam());
6031
6032 return rc;
6033}
6034
6035HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6036 ULONG *aControllers)
6037{
6038 if ( (aType <= USBControllerType_Null)
6039 || (aType >= USBControllerType_Last))
6040 return setError(E_INVALIDARG,
6041 tr("Invalid USB controller type: %d"),
6042 aType);
6043
6044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6045
6046 ComObjPtr<USBController> ctrl;
6047
6048 *aControllers = i_getUSBControllerCountByType(aType);
6049
6050 return S_OK;
6051}
6052
6053HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6054{
6055
6056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6057
6058 HRESULT rc = i_checkStateDependency(MutableStateDep);
6059 if (FAILED(rc)) return rc;
6060
6061 ComObjPtr<USBController> ctrl;
6062 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6063 if (FAILED(rc)) return rc;
6064
6065 i_setModified(IsModified_USB);
6066 mUSBControllers.backup();
6067
6068 ctrl->i_unshare();
6069
6070 mUSBControllers->remove(ctrl);
6071
6072 /* inform the direct session if any */
6073 alock.release();
6074 i_onUSBControllerChange();
6075
6076 return S_OK;
6077}
6078
6079HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6080 ULONG *aOriginX,
6081 ULONG *aOriginY,
6082 ULONG *aWidth,
6083 ULONG *aHeight,
6084 BOOL *aEnabled)
6085{
6086 uint32_t u32OriginX= 0;
6087 uint32_t u32OriginY= 0;
6088 uint32_t u32Width = 0;
6089 uint32_t u32Height = 0;
6090 uint16_t u16Flags = 0;
6091
6092 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6093 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6094 if (RT_FAILURE(vrc))
6095 {
6096#ifdef RT_OS_WINDOWS
6097 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6098 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6099 * So just assign fEnable to TRUE again.
6100 * The right fix would be to change GUI API wrappers to make sure that parameters
6101 * are changed only if API succeeds.
6102 */
6103 *aEnabled = TRUE;
6104#endif
6105 return setError(VBOX_E_IPRT_ERROR,
6106 tr("Saved guest size is not available (%Rrc)"),
6107 vrc);
6108 }
6109
6110 *aOriginX = u32OriginX;
6111 *aOriginY = u32OriginY;
6112 *aWidth = u32Width;
6113 *aHeight = u32Height;
6114 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6115
6116 return S_OK;
6117}
6118
6119HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6120{
6121 if (aScreenId != 0)
6122 return E_NOTIMPL;
6123
6124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6125
6126 uint8_t *pu8Data = NULL;
6127 uint32_t cbData = 0;
6128 uint32_t u32Width = 0;
6129 uint32_t u32Height = 0;
6130
6131 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6132
6133 if (RT_FAILURE(vrc))
6134 return setError(VBOX_E_IPRT_ERROR,
6135 tr("Saved screenshot data is not available (%Rrc)"),
6136 vrc);
6137
6138 *aSize = cbData;
6139 *aWidth = u32Width;
6140 *aHeight = u32Height;
6141
6142 freeSavedDisplayScreenshot(pu8Data);
6143
6144 return S_OK;
6145}
6146
6147
6148HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6149{
6150 if (aScreenId != 0)
6151 return E_NOTIMPL;
6152
6153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6154
6155 uint8_t *pu8Data = NULL;
6156 uint32_t cbData = 0;
6157 uint32_t u32Width = 0;
6158 uint32_t u32Height = 0;
6159
6160 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6161
6162 if (RT_FAILURE(vrc))
6163 return setError(VBOX_E_IPRT_ERROR,
6164 tr("Saved screenshot data is not available (%Rrc)"),
6165 vrc);
6166
6167 *aWidth = u32Width;
6168 *aHeight = u32Height;
6169
6170 com::SafeArray<BYTE> bitmap(cbData);
6171 /* Convert pixels to format expected by the API caller. */
6172 if (aBGR)
6173 {
6174 /* [0] B, [1] G, [2] R, [3] A. */
6175 for (unsigned i = 0; i < cbData; i += 4)
6176 {
6177 bitmap[i] = pu8Data[i];
6178 bitmap[i + 1] = pu8Data[i + 1];
6179 bitmap[i + 2] = pu8Data[i + 2];
6180 bitmap[i + 3] = 0xff;
6181 }
6182 }
6183 else
6184 {
6185 /* [0] R, [1] G, [2] B, [3] A. */
6186 for (unsigned i = 0; i < cbData; i += 4)
6187 {
6188 bitmap[i] = pu8Data[i + 2];
6189 bitmap[i + 1] = pu8Data[i + 1];
6190 bitmap[i + 2] = pu8Data[i];
6191 bitmap[i + 3] = 0xff;
6192 }
6193 }
6194 aData.resize(bitmap.size());
6195 for (size_t i = 0; i < bitmap.size(); ++i)
6196 aData[i] = bitmap[i];
6197
6198 freeSavedDisplayScreenshot(pu8Data);
6199
6200 return S_OK;
6201}
6202
6203HRESULT Machine::readSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6204{
6205 if (aScreenId != 0)
6206 return E_NOTIMPL;
6207
6208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6209
6210 uint8_t *pu8Data = NULL;
6211 uint32_t cbData = 0;
6212 uint32_t u32Width = 0;
6213 uint32_t u32Height = 0;
6214
6215 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6216
6217 if (RT_FAILURE(vrc))
6218 return setError(VBOX_E_IPRT_ERROR,
6219 tr("Saved screenshot data is not available (%Rrc)"),
6220 vrc);
6221
6222 *aWidth = u32Width;
6223 *aHeight = u32Height;
6224
6225 HRESULT rc = S_OK;
6226 uint8_t *pu8PNG = NULL;
6227 uint32_t cbPNG = 0;
6228 uint32_t cxPNG = 0;
6229 uint32_t cyPNG = 0;
6230
6231 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6232
6233 if (RT_SUCCESS(vrc))
6234 {
6235 com::SafeArray<BYTE> screenData(cbPNG);
6236 screenData.initFrom(pu8PNG, cbPNG);
6237 if (pu8PNG)
6238 RTMemFree(pu8PNG);
6239 aData.resize(screenData.size());
6240 for (size_t i = 0; i < screenData.size(); ++i)
6241 aData[i] = screenData[i];
6242 }
6243 else
6244 {
6245 if (pu8PNG)
6246 RTMemFree(pu8PNG);
6247 return setError(VBOX_E_IPRT_ERROR,
6248 tr("Could not convert screenshot to PNG (%Rrc)"),
6249 vrc);
6250 }
6251
6252 freeSavedDisplayScreenshot(pu8Data);
6253
6254 return rc;
6255}
6256
6257HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6258{
6259 if (aScreenId != 0)
6260 return E_NOTIMPL;
6261
6262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6263
6264 uint8_t *pu8Data = NULL;
6265 uint32_t cbData = 0;
6266 uint32_t u32Width = 0;
6267 uint32_t u32Height = 0;
6268
6269 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6270
6271 if (RT_FAILURE(vrc))
6272 return setError(VBOX_E_IPRT_ERROR,
6273 tr("Saved screenshot data is not available (%Rrc)"),
6274 vrc);
6275
6276 *aSize = cbData;
6277 *aWidth = u32Width;
6278 *aHeight = u32Height;
6279
6280 freeSavedDisplayScreenshot(pu8Data);
6281
6282 return S_OK;
6283}
6284
6285HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6286{
6287 if (aScreenId != 0)
6288 return E_NOTIMPL;
6289
6290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6291
6292 uint8_t *pu8Data = NULL;
6293 uint32_t cbData = 0;
6294 uint32_t u32Width = 0;
6295 uint32_t u32Height = 0;
6296
6297 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6298
6299 if (RT_FAILURE(vrc))
6300 return setError(VBOX_E_IPRT_ERROR,
6301 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6302 vrc);
6303
6304 *aWidth = u32Width;
6305 *aHeight = u32Height;
6306
6307 com::SafeArray<BYTE> png(cbData);
6308 png.initFrom(pu8Data, cbData);
6309 aData.resize(png.size());
6310 for (size_t i = 0; i < png.size(); ++i)
6311 aData[i] = png[i];
6312
6313 freeSavedDisplayScreenshot(pu8Data);
6314
6315 return S_OK;
6316}
6317
6318HRESULT Machine::hotPlugCPU(ULONG aCpu)
6319{
6320 HRESULT rc = S_OK;
6321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6322
6323 if (!mHWData->mCPUHotPlugEnabled)
6324 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6325
6326 if (aCpu >= mHWData->mCPUCount)
6327 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6328
6329 if (mHWData->mCPUAttached[aCpu])
6330 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6331
6332 alock.release();
6333 rc = i_onCPUChange(aCpu, false);
6334 alock.acquire();
6335 if (FAILED(rc)) return rc;
6336
6337 i_setModified(IsModified_MachineData);
6338 mHWData.backup();
6339 mHWData->mCPUAttached[aCpu] = true;
6340
6341 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6342 if (Global::IsOnline(mData->mMachineState))
6343 i_saveSettings(NULL);
6344
6345 return S_OK;
6346}
6347
6348HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6349{
6350 HRESULT rc = S_OK;
6351
6352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6353
6354 if (!mHWData->mCPUHotPlugEnabled)
6355 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6356
6357 if (aCpu >= SchemaDefs::MaxCPUCount)
6358 return setError(E_INVALIDARG,
6359 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6360 SchemaDefs::MaxCPUCount);
6361
6362 if (!mHWData->mCPUAttached[aCpu])
6363 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6364
6365 /* CPU 0 can't be detached */
6366 if (aCpu == 0)
6367 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6368
6369 alock.release();
6370 rc = i_onCPUChange(aCpu, true);
6371 alock.acquire();
6372 if (FAILED(rc)) return rc;
6373
6374 i_setModified(IsModified_MachineData);
6375 mHWData.backup();
6376 mHWData->mCPUAttached[aCpu] = false;
6377
6378 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6379 if (Global::IsOnline(mData->mMachineState))
6380 i_saveSettings(NULL);
6381
6382 return S_OK;
6383}
6384
6385HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6386{
6387 *aAttached = false;
6388
6389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6390
6391 /* If hotplug is enabled the CPU is always enabled. */
6392 if (!mHWData->mCPUHotPlugEnabled)
6393 {
6394 if (aCpu < mHWData->mCPUCount)
6395 *aAttached = true;
6396 }
6397 else
6398 {
6399 if (aCpu < SchemaDefs::MaxCPUCount)
6400 *aAttached = mHWData->mCPUAttached[aCpu];
6401 }
6402
6403 return S_OK;
6404}
6405
6406HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6407{
6408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6409
6410 Utf8Str log = i_queryLogFilename(aIdx);
6411 if (!RTFileExists(log.c_str()))
6412 log.setNull();
6413 aFilename = log;
6414
6415 return S_OK;
6416}
6417
6418HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6419{
6420 if (aSize < 0)
6421 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6422
6423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6424
6425 HRESULT rc = S_OK;
6426 Utf8Str log = i_queryLogFilename(aIdx);
6427
6428 /* do not unnecessarily hold the lock while doing something which does
6429 * not need the lock and potentially takes a long time. */
6430 alock.release();
6431
6432 /* Limit the chunk size to 32K for now, as that gives better performance
6433 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6434 * One byte expands to approx. 25 bytes of breathtaking XML. */
6435 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6436 com::SafeArray<BYTE> logData(cbData);
6437
6438 RTFILE LogFile;
6439 int vrc = RTFileOpen(&LogFile, log.c_str(),
6440 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6441 if (RT_SUCCESS(vrc))
6442 {
6443 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6444 if (RT_SUCCESS(vrc))
6445 logData.resize(cbData);
6446 else
6447 rc = setError(VBOX_E_IPRT_ERROR,
6448 tr("Could not read log file '%s' (%Rrc)"),
6449 log.c_str(), vrc);
6450 RTFileClose(LogFile);
6451 }
6452 else
6453 rc = setError(VBOX_E_IPRT_ERROR,
6454 tr("Could not open log file '%s' (%Rrc)"),
6455 log.c_str(), vrc);
6456
6457 if (FAILED(rc))
6458 logData.resize(0);
6459
6460 aData.resize(logData.size());
6461 for (size_t i = 0; i < logData.size(); ++i)
6462 aData[i] = logData[i];
6463
6464 return rc;
6465}
6466
6467
6468/**
6469 * Currently this method doesn't attach device to the running VM,
6470 * just makes sure it's plugged on next VM start.
6471 */
6472HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6473{
6474 // lock scope
6475 {
6476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6477
6478 HRESULT rc = i_checkStateDependency(MutableStateDep);
6479 if (FAILED(rc)) return rc;
6480
6481 ChipsetType_T aChipset = ChipsetType_PIIX3;
6482 COMGETTER(ChipsetType)(&aChipset);
6483
6484 if (aChipset != ChipsetType_ICH9)
6485 {
6486 return setError(E_INVALIDARG,
6487 tr("Host PCI attachment only supported with ICH9 chipset"));
6488 }
6489
6490 // check if device with this host PCI address already attached
6491 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6492 it != mHWData->mPCIDeviceAssignments.end();
6493 ++it)
6494 {
6495 LONG iHostAddress = -1;
6496 ComPtr<PCIDeviceAttachment> pAttach;
6497 pAttach = *it;
6498 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6499 if (iHostAddress == aHostAddress)
6500 return setError(E_INVALIDARG,
6501 tr("Device with host PCI address already attached to this VM"));
6502 }
6503
6504 ComObjPtr<PCIDeviceAttachment> pda;
6505 char name[32];
6506
6507 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6508 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6509 Bstr bname(name);
6510 pda.createObject();
6511 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6512 i_setModified(IsModified_MachineData);
6513 mHWData.backup();
6514 mHWData->mPCIDeviceAssignments.push_back(pda);
6515 }
6516
6517 return S_OK;
6518}
6519
6520/**
6521 * Currently this method doesn't detach device from the running VM,
6522 * just makes sure it's not plugged on next VM start.
6523 */
6524HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6525{
6526 ComObjPtr<PCIDeviceAttachment> pAttach;
6527 bool fRemoved = false;
6528 HRESULT rc;
6529
6530 // lock scope
6531 {
6532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6533
6534 rc = i_checkStateDependency(MutableStateDep);
6535 if (FAILED(rc)) return rc;
6536
6537 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6538 it != mHWData->mPCIDeviceAssignments.end();
6539 ++it)
6540 {
6541 LONG iHostAddress = -1;
6542 pAttach = *it;
6543 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6544 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6545 {
6546 i_setModified(IsModified_MachineData);
6547 mHWData.backup();
6548 mHWData->mPCIDeviceAssignments.remove(pAttach);
6549 fRemoved = true;
6550 break;
6551 }
6552 }
6553 }
6554
6555
6556 /* Fire event outside of the lock */
6557 if (fRemoved)
6558 {
6559 Assert(!pAttach.isNull());
6560 ComPtr<IEventSource> es;
6561 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6562 Assert(SUCCEEDED(rc));
6563 Bstr mid;
6564 rc = this->COMGETTER(Id)(mid.asOutParam());
6565 Assert(SUCCEEDED(rc));
6566 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6567 }
6568
6569 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6570 tr("No host PCI device %08x attached"),
6571 aHostAddress
6572 );
6573}
6574
6575HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6576{
6577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6578
6579 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6580
6581 size_t i = 0;
6582 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6583 it != mHWData->mPCIDeviceAssignments.end();
6584 ++i, ++it)
6585 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6586
6587 return S_OK;
6588}
6589
6590HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6591{
6592 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6593
6594 return S_OK;
6595}
6596
6597HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6598{
6599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6600
6601 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6602
6603 return S_OK;
6604}
6605
6606HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6607{
6608 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6609 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6610 if (SUCCEEDED(hrc))
6611 {
6612 hrc = mHWData.backupEx();
6613 if (SUCCEEDED(hrc))
6614 {
6615 i_setModified(IsModified_MachineData);
6616 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6617 }
6618 }
6619 return hrc;
6620}
6621
6622HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6623{
6624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6625 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6626 return S_OK;
6627}
6628
6629HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6630{
6631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6632 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6633 if (SUCCEEDED(hrc))
6634 {
6635 hrc = mHWData.backupEx();
6636 if (SUCCEEDED(hrc))
6637 {
6638 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6639 if (SUCCEEDED(hrc))
6640 i_setModified(IsModified_MachineData);
6641 }
6642 }
6643 return hrc;
6644}
6645
6646HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6647{
6648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6649
6650 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6651
6652 return S_OK;
6653}
6654
6655HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6656{
6657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6658 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6659 if (SUCCEEDED(hrc))
6660 {
6661 hrc = mHWData.backupEx();
6662 if (SUCCEEDED(hrc))
6663 {
6664 i_setModified(IsModified_MachineData);
6665 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6666 }
6667 }
6668 return hrc;
6669}
6670
6671HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6672{
6673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6674
6675 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6676
6677 return S_OK;
6678}
6679
6680HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6681{
6682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6685 if ( SUCCEEDED(hrc)
6686 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6687 {
6688 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6689 int vrc;
6690
6691 if (aAutostartEnabled)
6692 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6693 else
6694 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6695
6696 if (RT_SUCCESS(vrc))
6697 {
6698 hrc = mHWData.backupEx();
6699 if (SUCCEEDED(hrc))
6700 {
6701 i_setModified(IsModified_MachineData);
6702 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6703 }
6704 }
6705 else if (vrc == VERR_NOT_SUPPORTED)
6706 hrc = setError(VBOX_E_NOT_SUPPORTED,
6707 tr("The VM autostart feature is not supported on this platform"));
6708 else if (vrc == VERR_PATH_NOT_FOUND)
6709 hrc = setError(E_FAIL,
6710 tr("The path to the autostart database is not set"));
6711 else
6712 hrc = setError(E_UNEXPECTED,
6713 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6714 aAutostartEnabled ? "Adding" : "Removing",
6715 mUserData->s.strName.c_str(), vrc);
6716 }
6717 return hrc;
6718}
6719
6720HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6721{
6722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6723
6724 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6725
6726 return S_OK;
6727}
6728
6729HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6730{
6731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6732 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6733 if (SUCCEEDED(hrc))
6734 {
6735 hrc = mHWData.backupEx();
6736 if (SUCCEEDED(hrc))
6737 {
6738 i_setModified(IsModified_MachineData);
6739 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6740 }
6741 }
6742 return hrc;
6743}
6744
6745HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6746{
6747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6748
6749 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6750
6751 return S_OK;
6752}
6753
6754HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6755{
6756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6757 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6758 if ( SUCCEEDED(hrc)
6759 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6760 {
6761 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6762 int vrc;
6763
6764 if (aAutostopType != AutostopType_Disabled)
6765 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6766 else
6767 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6768
6769 if (RT_SUCCESS(vrc))
6770 {
6771 hrc = mHWData.backupEx();
6772 if (SUCCEEDED(hrc))
6773 {
6774 i_setModified(IsModified_MachineData);
6775 mHWData->mAutostart.enmAutostopType = aAutostopType;
6776 }
6777 }
6778 else if (vrc == VERR_NOT_SUPPORTED)
6779 hrc = setError(VBOX_E_NOT_SUPPORTED,
6780 tr("The VM autostop feature is not supported on this platform"));
6781 else if (vrc == VERR_PATH_NOT_FOUND)
6782 hrc = setError(E_FAIL,
6783 tr("The path to the autostart database is not set"));
6784 else
6785 hrc = setError(E_UNEXPECTED,
6786 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6787 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6788 mUserData->s.strName.c_str(), vrc);
6789 }
6790 return hrc;
6791}
6792
6793HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6794{
6795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6796
6797 aDefaultFrontend = mHWData->mDefaultFrontend;
6798
6799 return S_OK;
6800}
6801
6802HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6803{
6804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6805 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6806 if (SUCCEEDED(hrc))
6807 {
6808 hrc = mHWData.backupEx();
6809 if (SUCCEEDED(hrc))
6810 {
6811 i_setModified(IsModified_MachineData);
6812 mHWData->mDefaultFrontend = aDefaultFrontend;
6813 }
6814 }
6815 return hrc;
6816}
6817
6818HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6819{
6820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6821 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
6822 aIcon.resize(mUserData->mIcon.size());
6823 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
6824 aIcon.resize(icon.size());
6825 for (size_t i = 0; i < icon.size(); ++i)
6826 aIcon[i] = icon[i];
6827 return S_OK;
6828}
6829
6830HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6831{
6832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6833 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6834 if (SUCCEEDED(hrc))
6835 {
6836 i_setModified(IsModified_MachineData);
6837 mUserData.backup();
6838 com::SafeArray<BYTE> icon(aIcon);
6839 mUserData->mIcon.resize(aIcon.size());
6840 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
6841 }
6842 return hrc;
6843}
6844
6845HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6846{
6847
6848#ifdef VBOX_WITH_USB
6849 *aUSBProxyAvailable = true;
6850#else
6851 *aUSBProxyAvailable = false;
6852#endif
6853 return S_OK;
6854}
6855
6856HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6857 ComPtr<IProgress> &aProgress)
6858{
6859 ComObjPtr<Progress> pP;
6860 Progress *ppP = pP;
6861 IProgress *iP = static_cast<IProgress *>(ppP);
6862 IProgress **pProgress = &iP;
6863
6864 IMachine *pTarget = aTarget;
6865
6866 /* Convert the options. */
6867 RTCList<CloneOptions_T> optList;
6868 if (aOptions.size())
6869 for (size_t i = 0; i < aOptions.size(); ++i)
6870 optList.append(aOptions[i]);
6871
6872 if (optList.contains(CloneOptions_Link))
6873 {
6874 if (!i_isSnapshotMachine())
6875 return setError(E_INVALIDARG,
6876 tr("Linked clone can only be created from a snapshot"));
6877 if (aMode != CloneMode_MachineState)
6878 return setError(E_INVALIDARG,
6879 tr("Linked clone can only be created for a single machine state"));
6880 }
6881 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6882
6883 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6884
6885 HRESULT rc = pWorker->start(pProgress);
6886
6887 pP = static_cast<Progress *>(*pProgress);
6888 pP.queryInterfaceTo(aProgress.asOutParam());
6889
6890 return rc;
6891
6892}
6893
6894// public methods for internal purposes
6895/////////////////////////////////////////////////////////////////////////////
6896
6897/**
6898 * Adds the given IsModified_* flag to the dirty flags of the machine.
6899 * This must be called either during i_loadSettings or under the machine write lock.
6900 * @param fl
6901 */
6902void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6903{
6904 mData->flModifications |= fl;
6905 if (fAllowStateModification && i_isStateModificationAllowed())
6906 mData->mCurrentStateModified = true;
6907}
6908
6909/**
6910 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6911 * care of the write locking.
6912 *
6913 * @param fModifications The flag to add.
6914 */
6915void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6916{
6917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6918 i_setModified(fModification, fAllowStateModification);
6919}
6920
6921/**
6922 * Saves the registry entry of this machine to the given configuration node.
6923 *
6924 * @param aEntryNode Node to save the registry entry to.
6925 *
6926 * @note locks this object for reading.
6927 */
6928HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
6929{
6930 AutoLimitedCaller autoCaller(this);
6931 AssertComRCReturnRC(autoCaller.rc());
6932
6933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6934
6935 data.uuid = mData->mUuid;
6936 data.strSettingsFile = mData->m_strConfigFile;
6937
6938 return S_OK;
6939}
6940
6941/**
6942 * Calculates the absolute path of the given path taking the directory of the
6943 * machine settings file as the current directory.
6944 *
6945 * @param aPath Path to calculate the absolute path for.
6946 * @param aResult Where to put the result (used only on success, can be the
6947 * same Utf8Str instance as passed in @a aPath).
6948 * @return IPRT result.
6949 *
6950 * @note Locks this object for reading.
6951 */
6952int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6953{
6954 AutoCaller autoCaller(this);
6955 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6956
6957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6958
6959 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6960
6961 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6962
6963 strSettingsDir.stripFilename();
6964 char folder[RTPATH_MAX];
6965 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6966 if (RT_SUCCESS(vrc))
6967 aResult = folder;
6968
6969 return vrc;
6970}
6971
6972/**
6973 * Copies strSource to strTarget, making it relative to the machine folder
6974 * if it is a subdirectory thereof, or simply copying it otherwise.
6975 *
6976 * @param strSource Path to evaluate and copy.
6977 * @param strTarget Buffer to receive target path.
6978 *
6979 * @note Locks this object for reading.
6980 */
6981void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
6982 Utf8Str &strTarget)
6983{
6984 AutoCaller autoCaller(this);
6985 AssertComRCReturn(autoCaller.rc(), (void)0);
6986
6987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6988
6989 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6990 // use strTarget as a temporary buffer to hold the machine settings dir
6991 strTarget = mData->m_strConfigFileFull;
6992 strTarget.stripFilename();
6993 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6994 {
6995 // is relative: then append what's left
6996 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6997 // for empty paths (only possible for subdirs) use "." to avoid
6998 // triggering default settings for not present config attributes.
6999 if (strTarget.isEmpty())
7000 strTarget = ".";
7001 }
7002 else
7003 // is not relative: then overwrite
7004 strTarget = strSource;
7005}
7006
7007/**
7008 * Returns the full path to the machine's log folder in the
7009 * \a aLogFolder argument.
7010 */
7011void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7012{
7013 AutoCaller autoCaller(this);
7014 AssertComRCReturnVoid(autoCaller.rc());
7015
7016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7017
7018 char szTmp[RTPATH_MAX];
7019 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7020 if (RT_SUCCESS(vrc))
7021 {
7022 if (szTmp[0] && !mUserData.isNull())
7023 {
7024 char szTmp2[RTPATH_MAX];
7025 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7026 if (RT_SUCCESS(vrc))
7027 aLogFolder = BstrFmt("%s%c%s",
7028 szTmp2,
7029 RTPATH_DELIMITER,
7030 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7031 }
7032 else
7033 vrc = VERR_PATH_IS_RELATIVE;
7034 }
7035
7036 if (RT_FAILURE(vrc))
7037 {
7038 // fallback if VBOX_USER_LOGHOME is not set or invalid
7039 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7040 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7041 aLogFolder.append(RTPATH_DELIMITER);
7042 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7043 }
7044}
7045
7046/**
7047 * Returns the full path to the machine's log file for an given index.
7048 */
7049Utf8Str Machine::i_queryLogFilename(ULONG idx)
7050{
7051 Utf8Str logFolder;
7052 getLogFolder(logFolder);
7053 Assert(logFolder.length());
7054 Utf8Str log;
7055 if (idx == 0)
7056 log = Utf8StrFmt("%s%cVBox.log",
7057 logFolder.c_str(), RTPATH_DELIMITER);
7058 else
7059 log = Utf8StrFmt("%s%cVBox.log.%d",
7060 logFolder.c_str(), RTPATH_DELIMITER, idx);
7061 return log;
7062}
7063
7064/**
7065 * Composes a unique saved state filename based on the current system time. The filename is
7066 * granular to the second so this will work so long as no more than one snapshot is taken on
7067 * a machine per second.
7068 *
7069 * Before version 4.1, we used this formula for saved state files:
7070 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7071 * which no longer works because saved state files can now be shared between the saved state of the
7072 * "saved" machine and an online snapshot, and the following would cause problems:
7073 * 1) save machine
7074 * 2) create online snapshot from that machine state --> reusing saved state file
7075 * 3) save machine again --> filename would be reused, breaking the online snapshot
7076 *
7077 * So instead we now use a timestamp.
7078 *
7079 * @param str
7080 */
7081
7082void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7083{
7084 AutoCaller autoCaller(this);
7085 AssertComRCReturnVoid(autoCaller.rc());
7086
7087 {
7088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7089 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7090 }
7091
7092 RTTIMESPEC ts;
7093 RTTimeNow(&ts);
7094 RTTIME time;
7095 RTTimeExplode(&time, &ts);
7096
7097 strStateFilePath += RTPATH_DELIMITER;
7098 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7099 time.i32Year, time.u8Month, time.u8MonthDay,
7100 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7101}
7102
7103/**
7104 * Returns the full path to the default video capture file.
7105 */
7106void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7107{
7108 AutoCaller autoCaller(this);
7109 AssertComRCReturnVoid(autoCaller.rc());
7110
7111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7112
7113 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7114 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7115 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7116}
7117
7118/**
7119 * Returns whether at least one USB controller is present for the VM.
7120 */
7121bool Machine::i_isUSBControllerPresent()
7122{
7123 AutoCaller autoCaller(this);
7124 AssertComRCReturn(autoCaller.rc(), false);
7125
7126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7127
7128 return (mUSBControllers->size() > 0);
7129}
7130
7131/**
7132 * @note Locks this object for writing, calls the client process
7133 * (inside the lock).
7134 */
7135HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7136 const Utf8Str &strFrontend,
7137 const Utf8Str &strEnvironment,
7138 ProgressProxy *aProgress)
7139{
7140 LogFlowThisFuncEnter();
7141
7142 AssertReturn(aControl, E_FAIL);
7143 AssertReturn(aProgress, E_FAIL);
7144 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7145
7146 AutoCaller autoCaller(this);
7147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7148
7149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7150
7151 if (!mData->mRegistered)
7152 return setError(E_UNEXPECTED,
7153 tr("The machine '%s' is not registered"),
7154 mUserData->s.strName.c_str());
7155
7156 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7157
7158 if ( mData->mSession.mState == SessionState_Locked
7159 || mData->mSession.mState == SessionState_Spawning
7160 || mData->mSession.mState == SessionState_Unlocking)
7161 return setError(VBOX_E_INVALID_OBJECT_STATE,
7162 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7163 mUserData->s.strName.c_str());
7164
7165 /* may not be busy */
7166 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7167
7168 /* get the path to the executable */
7169 char szPath[RTPATH_MAX];
7170 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7171 size_t sz = strlen(szPath);
7172 szPath[sz++] = RTPATH_DELIMITER;
7173 szPath[sz] = 0;
7174 char *cmd = szPath + sz;
7175 sz = sizeof(szPath) - sz;
7176
7177 int vrc = VINF_SUCCESS;
7178 RTPROCESS pid = NIL_RTPROCESS;
7179
7180 RTENV env = RTENV_DEFAULT;
7181
7182 if (!strEnvironment.isEmpty())
7183 {
7184 char *newEnvStr = NULL;
7185
7186 do
7187 {
7188 /* clone the current environment */
7189 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7190 AssertRCBreakStmt(vrc2, vrc = vrc2);
7191
7192 newEnvStr = RTStrDup(strEnvironment.c_str());
7193 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7194
7195 /* put new variables to the environment
7196 * (ignore empty variable names here since RTEnv API
7197 * intentionally doesn't do that) */
7198 char *var = newEnvStr;
7199 for (char *p = newEnvStr; *p; ++p)
7200 {
7201 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7202 {
7203 *p = '\0';
7204 if (*var)
7205 {
7206 char *val = strchr(var, '=');
7207 if (val)
7208 {
7209 *val++ = '\0';
7210 vrc2 = RTEnvSetEx(env, var, val);
7211 }
7212 else
7213 vrc2 = RTEnvUnsetEx(env, var);
7214 if (RT_FAILURE(vrc2))
7215 break;
7216 }
7217 var = p + 1;
7218 }
7219 }
7220 if (RT_SUCCESS(vrc2) && *var)
7221 vrc2 = RTEnvPutEx(env, var);
7222
7223 AssertRCBreakStmt(vrc2, vrc = vrc2);
7224 }
7225 while (0);
7226
7227 if (newEnvStr != NULL)
7228 RTStrFree(newEnvStr);
7229 }
7230
7231#ifdef VBOX_WITH_QTGUI
7232 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7233 {
7234# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7235 /* Modify the base path so that we don't need to use ".." below. */
7236 RTPathStripTrailingSlash(szPath);
7237 RTPathStripFilename(szPath);
7238 sz = strlen(szPath);
7239 cmd = szPath + sz;
7240 sz = sizeof(szPath) - sz;
7241
7242#define OSX_APP_NAME "VirtualBoxVM"
7243#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7244
7245 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7246 if ( strAppOverride.contains(".")
7247 || strAppOverride.contains("/")
7248 || strAppOverride.contains("\\")
7249 || strAppOverride.contains(":"))
7250 strAppOverride.setNull();
7251 Utf8Str strAppPath;
7252 if (!strAppOverride.isEmpty())
7253 {
7254 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7255 Utf8Str strFullPath(szPath);
7256 strFullPath.append(strAppPath);
7257 /* there is a race, but people using this deserve the failure */
7258 if (!RTFileExists(strFullPath.c_str()))
7259 strAppOverride.setNull();
7260 }
7261 if (strAppOverride.isEmpty())
7262 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7263 const char *VirtualBox_exe = strAppPath.c_str();
7264 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
7265# else
7266 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7267 Assert(sz >= sizeof(VirtualBox_exe));
7268# endif
7269 strcpy(cmd, VirtualBox_exe);
7270
7271 Utf8Str idStr = mData->mUuid.toString();
7272 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7273 "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7274 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7275 }
7276#else /* !VBOX_WITH_QTGUI */
7277 if (0)
7278 ;
7279#endif /* VBOX_WITH_QTGUI */
7280
7281 else
7282
7283#ifdef VBOX_WITH_VBOXSDL
7284 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7285 {
7286 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7287 Assert(sz >= sizeof(VBoxSDL_exe));
7288 strcpy(cmd, VBoxSDL_exe);
7289
7290 Utf8Str idStr = mData->mUuid.toString();
7291 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7292 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7293 }
7294#else /* !VBOX_WITH_VBOXSDL */
7295 if (0)
7296 ;
7297#endif /* !VBOX_WITH_VBOXSDL */
7298
7299 else
7300
7301#ifdef VBOX_WITH_HEADLESS
7302 if ( strFrontend == "headless"
7303 || strFrontend == "capture"
7304 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7305 )
7306 {
7307 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7308 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7309 * and a VM works even if the server has not been installed.
7310 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7311 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7312 * differently in 4.0 and 3.x.
7313 */
7314 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7315 Assert(sz >= sizeof(VBoxHeadless_exe));
7316 strcpy(cmd, VBoxHeadless_exe);
7317
7318 Utf8Str idStr = mData->mUuid.toString();
7319 /* Leave space for "--capture" arg. */
7320 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7321 "--startvm", idStr.c_str(),
7322 "--vrde", "config",
7323 0, /* For "--capture". */
7324 0 };
7325 if (strFrontend == "capture")
7326 {
7327 unsigned pos = RT_ELEMENTS(args) - 2;
7328 args[pos] = "--capture";
7329 }
7330 vrc = RTProcCreate(szPath, args, env,
7331#ifdef RT_OS_WINDOWS
7332 RTPROC_FLAGS_NO_WINDOW
7333#else
7334 0
7335#endif
7336 , &pid);
7337 }
7338#else /* !VBOX_WITH_HEADLESS */
7339 if (0)
7340 ;
7341#endif /* !VBOX_WITH_HEADLESS */
7342 else
7343 {
7344 RTEnvDestroy(env);
7345 return setError(E_INVALIDARG,
7346 tr("Invalid frontend name: '%s'"),
7347 strFrontend.c_str());
7348 }
7349
7350 RTEnvDestroy(env);
7351
7352 if (RT_FAILURE(vrc))
7353 return setError(VBOX_E_IPRT_ERROR,
7354 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7355 mUserData->s.strName.c_str(), vrc);
7356
7357 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7358
7359 /*
7360 * Note that we don't release the lock here before calling the client,
7361 * because it doesn't need to call us back if called with a NULL argument.
7362 * Releasing the lock here is dangerous because we didn't prepare the
7363 * launch data yet, but the client we've just started may happen to be
7364 * too fast and call LockMachine() that will fail (because of PID, etc.),
7365 * so that the Machine will never get out of the Spawning session state.
7366 */
7367
7368 /* inform the session that it will be a remote one */
7369 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7370#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7371 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7372#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7373 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7374#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7375 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7376
7377 if (FAILED(rc))
7378 {
7379 /* restore the session state */
7380 mData->mSession.mState = SessionState_Unlocked;
7381 alock.release();
7382 mParent->i_addProcessToReap(pid);
7383 /* The failure may occur w/o any error info (from RPC), so provide one */
7384 return setError(VBOX_E_VM_ERROR,
7385 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7386 }
7387
7388 /* attach launch data to the machine */
7389 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7390 mData->mSession.mRemoteControls.push_back(aControl);
7391 mData->mSession.mProgress = aProgress;
7392 mData->mSession.mPID = pid;
7393 mData->mSession.mState = SessionState_Spawning;
7394 mData->mSession.mType = strFrontend;
7395
7396 alock.release();
7397 mParent->i_addProcessToReap(pid);
7398
7399 LogFlowThisFuncLeave();
7400 return S_OK;
7401}
7402
7403/**
7404 * Returns @c true if the given session machine instance has an open direct
7405 * session (and optionally also for direct sessions which are closing) and
7406 * returns the session control machine instance if so.
7407 *
7408 * Note that when the method returns @c false, the arguments remain unchanged.
7409 *
7410 * @param aMachine Session machine object.
7411 * @param aControl Direct session control object (optional).
7412 * @param aAllowClosing If true then additionally a session which is currently
7413 * being closed will also be allowed.
7414 *
7415 * @note locks this object for reading.
7416 */
7417bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7418 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7419 bool aAllowClosing /*= false*/)
7420{
7421 AutoLimitedCaller autoCaller(this);
7422 AssertComRCReturn(autoCaller.rc(), false);
7423
7424 /* just return false for inaccessible machines */
7425 if (autoCaller.state() != Ready)
7426 return false;
7427
7428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7429
7430 if ( mData->mSession.mState == SessionState_Locked
7431 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7432 )
7433 {
7434 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7435
7436 aMachine = mData->mSession.mMachine;
7437
7438 if (aControl != NULL)
7439 *aControl = mData->mSession.mDirectControl;
7440
7441 return true;
7442 }
7443
7444 return false;
7445}
7446
7447/**
7448 * Returns @c true if the given machine has an spawning direct session.
7449 *
7450 * @note locks this object for reading.
7451 */
7452bool Machine::i_isSessionSpawning()
7453{
7454 AutoLimitedCaller autoCaller(this);
7455 AssertComRCReturn(autoCaller.rc(), false);
7456
7457 /* just return false for inaccessible machines */
7458 if (autoCaller.state() != Ready)
7459 return false;
7460
7461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7462
7463 if (mData->mSession.mState == SessionState_Spawning)
7464 return true;
7465
7466 return false;
7467}
7468
7469/**
7470 * Called from the client watcher thread to check for unexpected client process
7471 * death during Session_Spawning state (e.g. before it successfully opened a
7472 * direct session).
7473 *
7474 * On Win32 and on OS/2, this method is called only when we've got the
7475 * direct client's process termination notification, so it always returns @c
7476 * true.
7477 *
7478 * On other platforms, this method returns @c true if the client process is
7479 * terminated and @c false if it's still alive.
7480 *
7481 * @note Locks this object for writing.
7482 */
7483bool Machine::i_checkForSpawnFailure()
7484{
7485 AutoCaller autoCaller(this);
7486 if (!autoCaller.isOk())
7487 {
7488 /* nothing to do */
7489 LogFlowThisFunc(("Already uninitialized!\n"));
7490 return true;
7491 }
7492
7493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7494
7495 if (mData->mSession.mState != SessionState_Spawning)
7496 {
7497 /* nothing to do */
7498 LogFlowThisFunc(("Not spawning any more!\n"));
7499 return true;
7500 }
7501
7502 HRESULT rc = S_OK;
7503
7504 /* PID not yet initialized, skip check. */
7505 if (mData->mSession.mPID == NIL_RTPROCESS)
7506 return false;
7507
7508 RTPROCSTATUS status;
7509 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7510
7511 if (vrc != VERR_PROCESS_RUNNING)
7512 {
7513 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7514 rc = setError(E_FAIL,
7515 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7516 i_getName().c_str(), status.iStatus);
7517 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7518 rc = setError(E_FAIL,
7519 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7520 i_getName().c_str(), status.iStatus);
7521 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7522 rc = setError(E_FAIL,
7523 tr("The virtual machine '%s' has terminated abnormally"),
7524 i_getName().c_str(), status.iStatus);
7525 else
7526 rc = setError(E_FAIL,
7527 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7528 i_getName().c_str(), vrc);
7529 }
7530
7531 if (FAILED(rc))
7532 {
7533 /* Close the remote session, remove the remote control from the list
7534 * and reset session state to Closed (@note keep the code in sync with
7535 * the relevant part in LockMachine()). */
7536
7537 Assert(mData->mSession.mRemoteControls.size() == 1);
7538 if (mData->mSession.mRemoteControls.size() == 1)
7539 {
7540 ErrorInfoKeeper eik;
7541 mData->mSession.mRemoteControls.front()->Uninitialize();
7542 }
7543
7544 mData->mSession.mRemoteControls.clear();
7545 mData->mSession.mState = SessionState_Unlocked;
7546
7547 /* finalize the progress after setting the state */
7548 if (!mData->mSession.mProgress.isNull())
7549 {
7550 mData->mSession.mProgress->notifyComplete(rc);
7551 mData->mSession.mProgress.setNull();
7552 }
7553
7554 mData->mSession.mPID = NIL_RTPROCESS;
7555
7556 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7557 return true;
7558 }
7559
7560 return false;
7561}
7562
7563/**
7564 * Checks whether the machine can be registered. If so, commits and saves
7565 * all settings.
7566 *
7567 * @note Must be called from mParent's write lock. Locks this object and
7568 * children for writing.
7569 */
7570HRESULT Machine::i_prepareRegister()
7571{
7572 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7573
7574 AutoLimitedCaller autoCaller(this);
7575 AssertComRCReturnRC(autoCaller.rc());
7576
7577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7578
7579 /* wait for state dependents to drop to zero */
7580 i_ensureNoStateDependencies();
7581
7582 if (!mData->mAccessible)
7583 return setError(VBOX_E_INVALID_OBJECT_STATE,
7584 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7585 mUserData->s.strName.c_str(),
7586 mData->mUuid.toString().c_str());
7587
7588 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7589
7590 if (mData->mRegistered)
7591 return setError(VBOX_E_INVALID_OBJECT_STATE,
7592 tr("The machine '%s' with UUID {%s} is already registered"),
7593 mUserData->s.strName.c_str(),
7594 mData->mUuid.toString().c_str());
7595
7596 HRESULT rc = S_OK;
7597
7598 // Ensure the settings are saved. If we are going to be registered and
7599 // no config file exists yet, create it by calling i_saveSettings() too.
7600 if ( (mData->flModifications)
7601 || (!mData->pMachineConfigFile->fileExists())
7602 )
7603 {
7604 rc = i_saveSettings(NULL);
7605 // no need to check whether VirtualBox.xml needs saving too since
7606 // we can't have a machine XML file rename pending
7607 if (FAILED(rc)) return rc;
7608 }
7609
7610 /* more config checking goes here */
7611
7612 if (SUCCEEDED(rc))
7613 {
7614 /* we may have had implicit modifications we want to fix on success */
7615 i_commit();
7616
7617 mData->mRegistered = true;
7618 }
7619 else
7620 {
7621 /* we may have had implicit modifications we want to cancel on failure*/
7622 i_rollback(false /* aNotify */);
7623 }
7624
7625 return rc;
7626}
7627
7628/**
7629 * Increases the number of objects dependent on the machine state or on the
7630 * registered state. Guarantees that these two states will not change at least
7631 * until #releaseStateDependency() is called.
7632 *
7633 * Depending on the @a aDepType value, additional state checks may be made.
7634 * These checks will set extended error info on failure. See
7635 * #checkStateDependency() for more info.
7636 *
7637 * If this method returns a failure, the dependency is not added and the caller
7638 * is not allowed to rely on any particular machine state or registration state
7639 * value and may return the failed result code to the upper level.
7640 *
7641 * @param aDepType Dependency type to add.
7642 * @param aState Current machine state (NULL if not interested).
7643 * @param aRegistered Current registered state (NULL if not interested).
7644 *
7645 * @note Locks this object for writing.
7646 */
7647HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7648 MachineState_T *aState /* = NULL */,
7649 BOOL *aRegistered /* = NULL */)
7650{
7651 AutoCaller autoCaller(this);
7652 AssertComRCReturnRC(autoCaller.rc());
7653
7654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7655
7656 HRESULT rc = i_checkStateDependency(aDepType);
7657 if (FAILED(rc)) return rc;
7658
7659 {
7660 if (mData->mMachineStateChangePending != 0)
7661 {
7662 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7663 * drop to zero so don't add more. It may make sense to wait a bit
7664 * and retry before reporting an error (since the pending state
7665 * transition should be really quick) but let's just assert for
7666 * now to see if it ever happens on practice. */
7667
7668 AssertFailed();
7669
7670 return setError(E_ACCESSDENIED,
7671 tr("Machine state change is in progress. Please retry the operation later."));
7672 }
7673
7674 ++mData->mMachineStateDeps;
7675 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7676 }
7677
7678 if (aState)
7679 *aState = mData->mMachineState;
7680 if (aRegistered)
7681 *aRegistered = mData->mRegistered;
7682
7683 return S_OK;
7684}
7685
7686/**
7687 * Decreases the number of objects dependent on the machine state.
7688 * Must always complete the #addStateDependency() call after the state
7689 * dependency is no more necessary.
7690 */
7691void Machine::i_releaseStateDependency()
7692{
7693 AutoCaller autoCaller(this);
7694 AssertComRCReturnVoid(autoCaller.rc());
7695
7696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7697
7698 /* releaseStateDependency() w/o addStateDependency()? */
7699 AssertReturnVoid(mData->mMachineStateDeps != 0);
7700 -- mData->mMachineStateDeps;
7701
7702 if (mData->mMachineStateDeps == 0)
7703 {
7704 /* inform i_ensureNoStateDependencies() that there are no more deps */
7705 if (mData->mMachineStateChangePending != 0)
7706 {
7707 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7708 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7709 }
7710 }
7711}
7712
7713Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7714{
7715 /* start with nothing found */
7716 Utf8Str strResult("");
7717
7718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7719
7720 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7721 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7722 // found:
7723 strResult = it->second; // source is a Utf8Str
7724
7725 return strResult;
7726}
7727
7728// protected methods
7729/////////////////////////////////////////////////////////////////////////////
7730
7731/**
7732 * Performs machine state checks based on the @a aDepType value. If a check
7733 * fails, this method will set extended error info, otherwise it will return
7734 * S_OK. It is supposed, that on failure, the caller will immediately return
7735 * the return value of this method to the upper level.
7736 *
7737 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7738 *
7739 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7740 * current state of this machine object allows to change settings of the
7741 * machine (i.e. the machine is not registered, or registered but not running
7742 * and not saved). It is useful to call this method from Machine setters
7743 * before performing any change.
7744 *
7745 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7746 * as for MutableStateDep except that if the machine is saved, S_OK is also
7747 * returned. This is useful in setters which allow changing machine
7748 * properties when it is in the saved state.
7749 *
7750 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7751 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7752 * Aborted).
7753 *
7754 * @param aDepType Dependency type to check.
7755 *
7756 * @note Non Machine based classes should use #addStateDependency() and
7757 * #releaseStateDependency() methods or the smart AutoStateDependency
7758 * template.
7759 *
7760 * @note This method must be called from under this object's read or write
7761 * lock.
7762 */
7763HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7764{
7765 switch (aDepType)
7766 {
7767 case AnyStateDep:
7768 {
7769 break;
7770 }
7771 case MutableStateDep:
7772 {
7773 if ( mData->mRegistered
7774 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7775 Paused should actually be included here... (Live Migration) */
7776 || ( mData->mMachineState != MachineState_Paused
7777 && mData->mMachineState != MachineState_Running
7778 && mData->mMachineState != MachineState_Aborted
7779 && mData->mMachineState != MachineState_Teleported
7780 && mData->mMachineState != MachineState_PoweredOff
7781 )
7782 )
7783 )
7784 return setError(VBOX_E_INVALID_VM_STATE,
7785 tr("The machine is not mutable (state is %s)"),
7786 Global::stringifyMachineState(mData->mMachineState));
7787 break;
7788 }
7789 case MutableOrSavedStateDep:
7790 {
7791 if ( mData->mRegistered
7792 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7793 Paused should actually be included here... (Live Migration) */
7794 || ( mData->mMachineState != MachineState_Paused
7795 && mData->mMachineState != MachineState_Running
7796 && mData->mMachineState != MachineState_Aborted
7797 && mData->mMachineState != MachineState_Teleported
7798 && mData->mMachineState != MachineState_Saved
7799 && mData->mMachineState != MachineState_PoweredOff
7800 )
7801 )
7802 )
7803 return setError(VBOX_E_INVALID_VM_STATE,
7804 tr("The machine is not mutable (state is %s)"),
7805 Global::stringifyMachineState(mData->mMachineState));
7806 break;
7807 }
7808 case OfflineStateDep:
7809 {
7810 if ( mData->mRegistered
7811 && ( !i_isSessionMachine()
7812 || ( mData->mMachineState != MachineState_PoweredOff
7813 && mData->mMachineState != MachineState_Saved
7814 && mData->mMachineState != MachineState_Aborted
7815 && mData->mMachineState != MachineState_Teleported
7816 )
7817 )
7818 )
7819 return setError(VBOX_E_INVALID_VM_STATE,
7820 tr("The machine is not offline (state is %s)"),
7821 Global::stringifyMachineState(mData->mMachineState));
7822 break;
7823 }
7824 }
7825
7826 return S_OK;
7827}
7828
7829/**
7830 * Helper to initialize all associated child objects and allocate data
7831 * structures.
7832 *
7833 * This method must be called as a part of the object's initialization procedure
7834 * (usually done in the #init() method).
7835 *
7836 * @note Must be called only from #init() or from #registeredInit().
7837 */
7838HRESULT Machine::initDataAndChildObjects()
7839{
7840 AutoCaller autoCaller(this);
7841 AssertComRCReturnRC(autoCaller.rc());
7842 AssertComRCReturn(autoCaller.state() == InInit ||
7843 autoCaller.state() == Limited, E_FAIL);
7844
7845 AssertReturn(!mData->mAccessible, E_FAIL);
7846
7847 /* allocate data structures */
7848 mSSData.allocate();
7849 mUserData.allocate();
7850 mHWData.allocate();
7851 mMediaData.allocate();
7852 mStorageControllers.allocate();
7853 mUSBControllers.allocate();
7854
7855 /* initialize mOSTypeId */
7856 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
7857
7858 /* create associated BIOS settings object */
7859 unconst(mBIOSSettings).createObject();
7860 mBIOSSettings->init(this);
7861
7862 /* create an associated VRDE object (default is disabled) */
7863 unconst(mVRDEServer).createObject();
7864 mVRDEServer->init(this);
7865
7866 /* create associated serial port objects */
7867 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
7868 {
7869 unconst(mSerialPorts[slot]).createObject();
7870 mSerialPorts[slot]->init(this, slot);
7871 }
7872
7873 /* create associated parallel port objects */
7874 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
7875 {
7876 unconst(mParallelPorts[slot]).createObject();
7877 mParallelPorts[slot]->init(this, slot);
7878 }
7879
7880 /* create the audio adapter object (always present, default is disabled) */
7881 unconst(mAudioAdapter).createObject();
7882 mAudioAdapter->init(this);
7883
7884 /* create the USB device filters object (always present) */
7885 unconst(mUSBDeviceFilters).createObject();
7886 mUSBDeviceFilters->init(this);
7887
7888 /* create associated network adapter objects */
7889 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7890 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7891 {
7892 unconst(mNetworkAdapters[slot]).createObject();
7893 mNetworkAdapters[slot]->init(this, slot);
7894 }
7895
7896 /* create the bandwidth control */
7897 unconst(mBandwidthControl).createObject();
7898 mBandwidthControl->init(this);
7899
7900 return S_OK;
7901}
7902
7903/**
7904 * Helper to uninitialize all associated child objects and to free all data
7905 * structures.
7906 *
7907 * This method must be called as a part of the object's uninitialization
7908 * procedure (usually done in the #uninit() method).
7909 *
7910 * @note Must be called only from #uninit() or from #registeredInit().
7911 */
7912void Machine::uninitDataAndChildObjects()
7913{
7914 AutoCaller autoCaller(this);
7915 AssertComRCReturnVoid(autoCaller.rc());
7916 AssertComRCReturnVoid( autoCaller.state() == InUninit
7917 || autoCaller.state() == Limited);
7918
7919 /* tell all our other child objects we've been uninitialized */
7920 if (mBandwidthControl)
7921 {
7922 mBandwidthControl->uninit();
7923 unconst(mBandwidthControl).setNull();
7924 }
7925
7926 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7927 {
7928 if (mNetworkAdapters[slot])
7929 {
7930 mNetworkAdapters[slot]->uninit();
7931 unconst(mNetworkAdapters[slot]).setNull();
7932 }
7933 }
7934
7935 if (mUSBDeviceFilters)
7936 {
7937 mUSBDeviceFilters->uninit();
7938 unconst(mUSBDeviceFilters).setNull();
7939 }
7940
7941 if (mAudioAdapter)
7942 {
7943 mAudioAdapter->uninit();
7944 unconst(mAudioAdapter).setNull();
7945 }
7946
7947 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
7948 {
7949 if (mParallelPorts[slot])
7950 {
7951 mParallelPorts[slot]->uninit();
7952 unconst(mParallelPorts[slot]).setNull();
7953 }
7954 }
7955
7956 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
7957 {
7958 if (mSerialPorts[slot])
7959 {
7960 mSerialPorts[slot]->uninit();
7961 unconst(mSerialPorts[slot]).setNull();
7962 }
7963 }
7964
7965 if (mVRDEServer)
7966 {
7967 mVRDEServer->uninit();
7968 unconst(mVRDEServer).setNull();
7969 }
7970
7971 if (mBIOSSettings)
7972 {
7973 mBIOSSettings->uninit();
7974 unconst(mBIOSSettings).setNull();
7975 }
7976
7977 /* Deassociate media (only when a real Machine or a SnapshotMachine
7978 * instance is uninitialized; SessionMachine instances refer to real
7979 * Machine media). This is necessary for a clean re-initialization of
7980 * the VM after successfully re-checking the accessibility state. Note
7981 * that in case of normal Machine or SnapshotMachine uninitialization (as
7982 * a result of unregistering or deleting the snapshot), outdated media
7983 * attachments will already be uninitialized and deleted, so this
7984 * code will not affect them. */
7985 if ( !!mMediaData
7986 && (!i_isSessionMachine())
7987 )
7988 {
7989 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7990 it != mMediaData->mAttachments.end();
7991 ++it)
7992 {
7993 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
7994 if (pMedium.isNull())
7995 continue;
7996 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
7997 AssertComRC(rc);
7998 }
7999 }
8000
8001 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8002 {
8003 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8004 if (mData->mFirstSnapshot)
8005 {
8006 // snapshots tree is protected by machine write lock; strictly
8007 // this isn't necessary here since we're deleting the entire
8008 // machine, but otherwise we assert in Snapshot::uninit()
8009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8010 mData->mFirstSnapshot->uninit();
8011 mData->mFirstSnapshot.setNull();
8012 }
8013
8014 mData->mCurrentSnapshot.setNull();
8015 }
8016
8017 /* free data structures (the essential mData structure is not freed here
8018 * since it may be still in use) */
8019 mMediaData.free();
8020 mStorageControllers.free();
8021 mUSBControllers.free();
8022 mHWData.free();
8023 mUserData.free();
8024 mSSData.free();
8025}
8026
8027/**
8028 * Returns a pointer to the Machine object for this machine that acts like a
8029 * parent for complex machine data objects such as shared folders, etc.
8030 *
8031 * For primary Machine objects and for SnapshotMachine objects, returns this
8032 * object's pointer itself. For SessionMachine objects, returns the peer
8033 * (primary) machine pointer.
8034 */
8035Machine* Machine::i_getMachine()
8036{
8037 if (i_isSessionMachine())
8038 return (Machine*)mPeer;
8039 return this;
8040}
8041
8042/**
8043 * Makes sure that there are no machine state dependents. If necessary, waits
8044 * for the number of dependents to drop to zero.
8045 *
8046 * Make sure this method is called from under this object's write lock to
8047 * guarantee that no new dependents may be added when this method returns
8048 * control to the caller.
8049 *
8050 * @note Locks this object for writing. The lock will be released while waiting
8051 * (if necessary).
8052 *
8053 * @warning To be used only in methods that change the machine state!
8054 */
8055void Machine::i_ensureNoStateDependencies()
8056{
8057 AssertReturnVoid(isWriteLockOnCurrentThread());
8058
8059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8060
8061 /* Wait for all state dependents if necessary */
8062 if (mData->mMachineStateDeps != 0)
8063 {
8064 /* lazy semaphore creation */
8065 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8066 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8067
8068 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8069 mData->mMachineStateDeps));
8070
8071 ++mData->mMachineStateChangePending;
8072
8073 /* reset the semaphore before waiting, the last dependent will signal
8074 * it */
8075 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8076
8077 alock.release();
8078
8079 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8080
8081 alock.acquire();
8082
8083 -- mData->mMachineStateChangePending;
8084 }
8085}
8086
8087/**
8088 * Changes the machine state and informs callbacks.
8089 *
8090 * This method is not intended to fail so it either returns S_OK or asserts (and
8091 * returns a failure).
8092 *
8093 * @note Locks this object for writing.
8094 */
8095HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8096{
8097 LogFlowThisFuncEnter();
8098 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8099
8100 AutoCaller autoCaller(this);
8101 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8102
8103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8104
8105 /* wait for state dependents to drop to zero */
8106 i_ensureNoStateDependencies();
8107
8108 if (mData->mMachineState != aMachineState)
8109 {
8110 mData->mMachineState = aMachineState;
8111
8112 RTTimeNow(&mData->mLastStateChange);
8113
8114 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8115 }
8116
8117 LogFlowThisFuncLeave();
8118 return S_OK;
8119}
8120
8121/**
8122 * Searches for a shared folder with the given logical name
8123 * in the collection of shared folders.
8124 *
8125 * @param aName logical name of the shared folder
8126 * @param aSharedFolder where to return the found object
8127 * @param aSetError whether to set the error info if the folder is
8128 * not found
8129 * @return
8130 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8131 *
8132 * @note
8133 * must be called from under the object's lock!
8134 */
8135HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8136 ComObjPtr<SharedFolder> &aSharedFolder,
8137 bool aSetError /* = false */)
8138{
8139 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8140 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8141 it != mHWData->mSharedFolders.end();
8142 ++it)
8143 {
8144 SharedFolder *pSF = *it;
8145 AutoCaller autoCaller(pSF);
8146 if (pSF->i_getName() == aName)
8147 {
8148 aSharedFolder = pSF;
8149 rc = S_OK;
8150 break;
8151 }
8152 }
8153
8154 if (aSetError && FAILED(rc))
8155 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8156
8157 return rc;
8158}
8159
8160/**
8161 * Initializes all machine instance data from the given settings structures
8162 * from XML. The exception is the machine UUID which needs special handling
8163 * depending on the caller's use case, so the caller needs to set that herself.
8164 *
8165 * This gets called in several contexts during machine initialization:
8166 *
8167 * -- When machine XML exists on disk already and needs to be loaded into memory,
8168 * for example, from registeredInit() to load all registered machines on
8169 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8170 * attached to the machine should be part of some media registry already.
8171 *
8172 * -- During OVF import, when a machine config has been constructed from an
8173 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8174 * ensure that the media listed as attachments in the config (which have
8175 * been imported from the OVF) receive the correct registry ID.
8176 *
8177 * -- During VM cloning.
8178 *
8179 * @param config Machine settings from XML.
8180 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8181 * for each attached medium in the config.
8182 * @return
8183 */
8184HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8185 const Guid *puuidRegistry)
8186{
8187 // copy name, description, OS type, teleporter, UTC etc.
8188 mUserData->s = config.machineUserData;
8189
8190 // Decode the Icon overide data from config userdata and set onto Machine.
8191 #define DECODE_STR_MAX _1M
8192 const char* pszStr = config.machineUserData.ovIcon.c_str();
8193 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8194 if (cbOut > DECODE_STR_MAX)
8195 return setError(E_FAIL,
8196 tr("Icon Data too long.'%d' > '%d'"),
8197 cbOut,
8198 DECODE_STR_MAX);
8199 com::SafeArray<BYTE> iconByte(cbOut);
8200 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8201 if (FAILED(rc))
8202 return setError(E_FAIL,
8203 tr("Failure to Decode Icon Data. '%s' (%d)"),
8204 pszStr,
8205 rc);
8206 mUserData->mIcon.resize(iconByte.size());
8207 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8208
8209 // look up the object by Id to check it is valid
8210 ComPtr<IGuestOSType> guestOSType;
8211 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8212 guestOSType.asOutParam());
8213 if (FAILED(rc)) return rc;
8214
8215 // stateFile (optional)
8216 if (config.strStateFile.isEmpty())
8217 mSSData->strStateFilePath.setNull();
8218 else
8219 {
8220 Utf8Str stateFilePathFull(config.strStateFile);
8221 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8222 if (RT_FAILURE(vrc))
8223 return setError(E_FAIL,
8224 tr("Invalid saved state file path '%s' (%Rrc)"),
8225 config.strStateFile.c_str(),
8226 vrc);
8227 mSSData->strStateFilePath = stateFilePathFull;
8228 }
8229
8230 // snapshot folder needs special processing so set it again
8231 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8232 if (FAILED(rc)) return rc;
8233
8234 /* Copy the extra data items (Not in any case config is already the same as
8235 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8236 * make sure the extra data map is copied). */
8237 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8238
8239 /* currentStateModified (optional, default is true) */
8240 mData->mCurrentStateModified = config.fCurrentStateModified;
8241
8242 mData->mLastStateChange = config.timeLastStateChange;
8243
8244 /*
8245 * note: all mUserData members must be assigned prior this point because
8246 * we need to commit changes in order to let mUserData be shared by all
8247 * snapshot machine instances.
8248 */
8249 mUserData.commitCopy();
8250
8251 // machine registry, if present (must be loaded before snapshots)
8252 if (config.canHaveOwnMediaRegistry())
8253 {
8254 // determine machine folder
8255 Utf8Str strMachineFolder = i_getSettingsFileFull();
8256 strMachineFolder.stripFilename();
8257 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8258 config.mediaRegistry,
8259 strMachineFolder);
8260 if (FAILED(rc)) return rc;
8261 }
8262
8263 /* Snapshot node (optional) */
8264 size_t cRootSnapshots;
8265 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8266 {
8267 // there must be only one root snapshot
8268 Assert(cRootSnapshots == 1);
8269
8270 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8271
8272 rc = i_loadSnapshot(snap,
8273 config.uuidCurrentSnapshot,
8274 NULL); // no parent == first snapshot
8275 if (FAILED(rc)) return rc;
8276 }
8277
8278 // hardware data
8279 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8280 if (FAILED(rc)) return rc;
8281
8282 // load storage controllers
8283 rc = i_loadStorageControllers(config.storageMachine,
8284 puuidRegistry,
8285 NULL /* puuidSnapshot */);
8286 if (FAILED(rc)) return rc;
8287
8288 /*
8289 * NOTE: the assignment below must be the last thing to do,
8290 * otherwise it will be not possible to change the settings
8291 * somewhere in the code above because all setters will be
8292 * blocked by i_checkStateDependency(MutableStateDep).
8293 */
8294
8295 /* set the machine state to Aborted or Saved when appropriate */
8296 if (config.fAborted)
8297 {
8298 mSSData->strStateFilePath.setNull();
8299
8300 /* no need to use i_setMachineState() during init() */
8301 mData->mMachineState = MachineState_Aborted;
8302 }
8303 else if (!mSSData->strStateFilePath.isEmpty())
8304 {
8305 /* no need to use i_setMachineState() during init() */
8306 mData->mMachineState = MachineState_Saved;
8307 }
8308
8309 // after loading settings, we are no longer different from the XML on disk
8310 mData->flModifications = 0;
8311
8312 return S_OK;
8313}
8314
8315/**
8316 * Recursively loads all snapshots starting from the given.
8317 *
8318 * @param aNode <Snapshot> node.
8319 * @param aCurSnapshotId Current snapshot ID from the settings file.
8320 * @param aParentSnapshot Parent snapshot.
8321 */
8322HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8323 const Guid &aCurSnapshotId,
8324 Snapshot *aParentSnapshot)
8325{
8326 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8327 AssertReturn(!i_isSessionMachine(), E_FAIL);
8328
8329 HRESULT rc = S_OK;
8330
8331 Utf8Str strStateFile;
8332 if (!data.strStateFile.isEmpty())
8333 {
8334 /* optional */
8335 strStateFile = data.strStateFile;
8336 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8337 if (RT_FAILURE(vrc))
8338 return setError(E_FAIL,
8339 tr("Invalid saved state file path '%s' (%Rrc)"),
8340 strStateFile.c_str(),
8341 vrc);
8342 }
8343
8344 /* create a snapshot machine object */
8345 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8346 pSnapshotMachine.createObject();
8347 rc = pSnapshotMachine->initFromSettings(this,
8348 data.hardware,
8349 &data.debugging,
8350 &data.autostart,
8351 data.storage,
8352 data.uuid.ref(),
8353 strStateFile);
8354 if (FAILED(rc)) return rc;
8355
8356 /* create a snapshot object */
8357 ComObjPtr<Snapshot> pSnapshot;
8358 pSnapshot.createObject();
8359 /* initialize the snapshot */
8360 rc = pSnapshot->init(mParent, // VirtualBox object
8361 data.uuid,
8362 data.strName,
8363 data.strDescription,
8364 data.timestamp,
8365 pSnapshotMachine,
8366 aParentSnapshot);
8367 if (FAILED(rc)) return rc;
8368
8369 /* memorize the first snapshot if necessary */
8370 if (!mData->mFirstSnapshot)
8371 mData->mFirstSnapshot = pSnapshot;
8372
8373 /* memorize the current snapshot when appropriate */
8374 if ( !mData->mCurrentSnapshot
8375 && pSnapshot->i_getId() == aCurSnapshotId
8376 )
8377 mData->mCurrentSnapshot = pSnapshot;
8378
8379 // now create the children
8380 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8381 it != data.llChildSnapshots.end();
8382 ++it)
8383 {
8384 const settings::Snapshot &childData = *it;
8385 // recurse
8386 rc = i_loadSnapshot(childData,
8387 aCurSnapshotId,
8388 pSnapshot); // parent = the one we created above
8389 if (FAILED(rc)) return rc;
8390 }
8391
8392 return rc;
8393}
8394
8395/**
8396 * Loads settings into mHWData.
8397 *
8398 * @param data Reference to the hardware settings.
8399 * @param pDbg Pointer to the debugging settings.
8400 * @param pAutostart Pointer to the autostart settings.
8401 */
8402HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8403 const settings::Autostart *pAutostart)
8404{
8405 AssertReturn(!i_isSessionMachine(), E_FAIL);
8406
8407 HRESULT rc = S_OK;
8408
8409 try
8410 {
8411 /* The hardware version attribute (optional). */
8412 mHWData->mHWVersion = data.strVersion;
8413 mHWData->mHardwareUUID = data.uuid;
8414
8415 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8416 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8417 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8418 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8419 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8420 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8421 mHWData->mPAEEnabled = data.fPAE;
8422 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8423 mHWData->mLongMode = data.enmLongMode;
8424 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8425 mHWData->mCPUCount = data.cCPUs;
8426 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8427 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8428
8429 // cpu
8430 if (mHWData->mCPUHotPlugEnabled)
8431 {
8432 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8433 it != data.llCpus.end();
8434 ++it)
8435 {
8436 const settings::Cpu &cpu = *it;
8437
8438 mHWData->mCPUAttached[cpu.ulId] = true;
8439 }
8440 }
8441
8442 // cpuid leafs
8443 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8444 it != data.llCpuIdLeafs.end();
8445 ++it)
8446 {
8447 const settings::CpuIdLeaf &leaf = *it;
8448
8449 switch (leaf.ulId)
8450 {
8451 case 0x0:
8452 case 0x1:
8453 case 0x2:
8454 case 0x3:
8455 case 0x4:
8456 case 0x5:
8457 case 0x6:
8458 case 0x7:
8459 case 0x8:
8460 case 0x9:
8461 case 0xA:
8462 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8463 break;
8464
8465 case 0x80000000:
8466 case 0x80000001:
8467 case 0x80000002:
8468 case 0x80000003:
8469 case 0x80000004:
8470 case 0x80000005:
8471 case 0x80000006:
8472 case 0x80000007:
8473 case 0x80000008:
8474 case 0x80000009:
8475 case 0x8000000A:
8476 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8477 break;
8478
8479 default:
8480 /* just ignore */
8481 break;
8482 }
8483 }
8484
8485 mHWData->mMemorySize = data.ulMemorySizeMB;
8486 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8487
8488 // boot order
8489 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8490 {
8491 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8492 if (it == data.mapBootOrder.end())
8493 mHWData->mBootOrder[i] = DeviceType_Null;
8494 else
8495 mHWData->mBootOrder[i] = it->second;
8496 }
8497
8498 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8499 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8500 mHWData->mMonitorCount = data.cMonitors;
8501 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8502 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8503 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8504 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8505 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8506 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8507 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8508 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8509 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8510 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8511 if (!data.strVideoCaptureFile.isEmpty())
8512 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8513 else
8514 mHWData->mVideoCaptureFile.setNull();
8515 mHWData->mFirmwareType = data.firmwareType;
8516 mHWData->mPointingHIDType = data.pointingHIDType;
8517 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8518 mHWData->mChipsetType = data.chipsetType;
8519 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8520 mHWData->mHPETEnabled = data.fHPETEnabled;
8521
8522 /* VRDEServer */
8523 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8524 if (FAILED(rc)) return rc;
8525
8526 /* BIOS */
8527 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8528 if (FAILED(rc)) return rc;
8529
8530 // Bandwidth control (must come before network adapters)
8531 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8532 if (FAILED(rc)) return rc;
8533
8534 /* Shared folders */
8535 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8536 it != data.usbSettings.llUSBControllers.end();
8537 ++it)
8538 {
8539 const settings::USBController &settingsCtrl = *it;
8540 ComObjPtr<USBController> newCtrl;
8541
8542 newCtrl.createObject();
8543 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8544 mUSBControllers->push_back(newCtrl);
8545 }
8546
8547 /* USB device filters */
8548 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8549 if (FAILED(rc)) return rc;
8550
8551 // network adapters
8552 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8553 uint32_t oldCount = mNetworkAdapters.size();
8554 if (newCount > oldCount)
8555 {
8556 mNetworkAdapters.resize(newCount);
8557 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8558 {
8559 unconst(mNetworkAdapters[slot]).createObject();
8560 mNetworkAdapters[slot]->init(this, slot);
8561 }
8562 }
8563 else if (newCount < oldCount)
8564 mNetworkAdapters.resize(newCount);
8565 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8566 it != data.llNetworkAdapters.end();
8567 ++it)
8568 {
8569 const settings::NetworkAdapter &nic = *it;
8570
8571 /* slot unicity is guaranteed by XML Schema */
8572 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8573 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8574 if (FAILED(rc)) return rc;
8575 }
8576
8577 // serial ports
8578 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8579 it != data.llSerialPorts.end();
8580 ++it)
8581 {
8582 const settings::SerialPort &s = *it;
8583
8584 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8585 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8586 if (FAILED(rc)) return rc;
8587 }
8588
8589 // parallel ports (optional)
8590 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8591 it != data.llParallelPorts.end();
8592 ++it)
8593 {
8594 const settings::ParallelPort &p = *it;
8595
8596 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8597 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8598 if (FAILED(rc)) return rc;
8599 }
8600
8601 /* AudioAdapter */
8602 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8603 if (FAILED(rc)) return rc;
8604
8605 /* Shared folders */
8606 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8607 it != data.llSharedFolders.end();
8608 ++it)
8609 {
8610 const settings::SharedFolder &sf = *it;
8611
8612 ComObjPtr<SharedFolder> sharedFolder;
8613 /* Check for double entries. Not allowed! */
8614 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8615 if (SUCCEEDED(rc))
8616 return setError(VBOX_E_OBJECT_IN_USE,
8617 tr("Shared folder named '%s' already exists"),
8618 sf.strName.c_str());
8619
8620 /* Create the new shared folder. Don't break on error. This will be
8621 * reported when the machine starts. */
8622 sharedFolder.createObject();
8623 rc = sharedFolder->init(i_getMachine(),
8624 sf.strName,
8625 sf.strHostPath,
8626 RT_BOOL(sf.fWritable),
8627 RT_BOOL(sf.fAutoMount),
8628 false /* fFailOnError */);
8629 if (FAILED(rc)) return rc;
8630 mHWData->mSharedFolders.push_back(sharedFolder);
8631 }
8632
8633 // Clipboard
8634 mHWData->mClipboardMode = data.clipboardMode;
8635
8636 // drag'n'drop
8637 mHWData->mDnDMode = data.dndMode;
8638
8639 // guest settings
8640 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8641
8642 // IO settings
8643 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8644 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8645
8646 // Host PCI devices
8647 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8648 it != data.pciAttachments.end();
8649 ++it)
8650 {
8651 const settings::HostPCIDeviceAttachment &hpda = *it;
8652 ComObjPtr<PCIDeviceAttachment> pda;
8653
8654 pda.createObject();
8655 pda->i_loadSettings(this, hpda);
8656 mHWData->mPCIDeviceAssignments.push_back(pda);
8657 }
8658
8659 /*
8660 * (The following isn't really real hardware, but it lives in HWData
8661 * for reasons of convenience.)
8662 */
8663
8664#ifdef VBOX_WITH_GUEST_PROPS
8665 /* Guest properties (optional) */
8666 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8667 it != data.llGuestProperties.end();
8668 ++it)
8669 {
8670 const settings::GuestProperty &prop = *it;
8671 uint32_t fFlags = guestProp::NILFLAG;
8672 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8673 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8674 mHWData->mGuestProperties[prop.strName] = property;
8675 }
8676
8677 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8678#endif /* VBOX_WITH_GUEST_PROPS defined */
8679
8680 rc = i_loadDebugging(pDbg);
8681 if (FAILED(rc))
8682 return rc;
8683
8684 mHWData->mAutostart = *pAutostart;
8685
8686 /* default frontend */
8687 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8688 }
8689 catch(std::bad_alloc &)
8690 {
8691 return E_OUTOFMEMORY;
8692 }
8693
8694 AssertComRC(rc);
8695 return rc;
8696}
8697
8698/**
8699 * Called from Machine::loadHardware() to load the debugging settings of the
8700 * machine.
8701 *
8702 * @param pDbg Pointer to the settings.
8703 */
8704HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8705{
8706 mHWData->mDebugging = *pDbg;
8707 /* no more processing currently required, this will probably change. */
8708 return S_OK;
8709}
8710
8711/**
8712 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8713 *
8714 * @param data
8715 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8716 * @param puuidSnapshot
8717 * @return
8718 */
8719HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8720 const Guid *puuidRegistry,
8721 const Guid *puuidSnapshot)
8722{
8723 AssertReturn(!i_isSessionMachine(), E_FAIL);
8724
8725 HRESULT rc = S_OK;
8726
8727 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8728 it != data.llStorageControllers.end();
8729 ++it)
8730 {
8731 const settings::StorageController &ctlData = *it;
8732
8733 ComObjPtr<StorageController> pCtl;
8734 /* Try to find one with the name first. */
8735 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8736 if (SUCCEEDED(rc))
8737 return setError(VBOX_E_OBJECT_IN_USE,
8738 tr("Storage controller named '%s' already exists"),
8739 ctlData.strName.c_str());
8740
8741 pCtl.createObject();
8742 rc = pCtl->init(this,
8743 ctlData.strName,
8744 ctlData.storageBus,
8745 ctlData.ulInstance,
8746 ctlData.fBootable);
8747 if (FAILED(rc)) return rc;
8748
8749 mStorageControllers->push_back(pCtl);
8750
8751 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8752 if (FAILED(rc)) return rc;
8753
8754 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8755 if (FAILED(rc)) return rc;
8756
8757 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8758 if (FAILED(rc)) return rc;
8759
8760 /* Set IDE emulation settings (only for AHCI controller). */
8761 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8762 {
8763 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8764 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8765 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8766 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8767 )
8768 return rc;
8769 }
8770
8771 /* Load the attached devices now. */
8772 rc = i_loadStorageDevices(pCtl,
8773 ctlData,
8774 puuidRegistry,
8775 puuidSnapshot);
8776 if (FAILED(rc)) return rc;
8777 }
8778
8779 return S_OK;
8780}
8781
8782/**
8783 * Called from i_loadStorageControllers for a controller's devices.
8784 *
8785 * @param aStorageController
8786 * @param data
8787 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8788 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8789 * @return
8790 */
8791HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8792 const settings::StorageController &data,
8793 const Guid *puuidRegistry,
8794 const Guid *puuidSnapshot)
8795{
8796 HRESULT rc = S_OK;
8797
8798 /* paranoia: detect duplicate attachments */
8799 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8800 it != data.llAttachedDevices.end();
8801 ++it)
8802 {
8803 const settings::AttachedDevice &ad = *it;
8804
8805 for (settings::AttachedDevicesList::const_iterator it2 = it;
8806 it2 != data.llAttachedDevices.end();
8807 ++it2)
8808 {
8809 if (it == it2)
8810 continue;
8811
8812 const settings::AttachedDevice &ad2 = *it2;
8813
8814 if ( ad.lPort == ad2.lPort
8815 && ad.lDevice == ad2.lDevice)
8816 {
8817 return setError(E_FAIL,
8818 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8819 aStorageController->i_getName().c_str(),
8820 ad.lPort,
8821 ad.lDevice,
8822 mUserData->s.strName.c_str());
8823 }
8824 }
8825 }
8826
8827 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8828 it != data.llAttachedDevices.end();
8829 ++it)
8830 {
8831 const settings::AttachedDevice &dev = *it;
8832 ComObjPtr<Medium> medium;
8833
8834 switch (dev.deviceType)
8835 {
8836 case DeviceType_Floppy:
8837 case DeviceType_DVD:
8838 if (dev.strHostDriveSrc.isNotEmpty())
8839 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
8840 false /* fRefresh */, medium);
8841 else
8842 rc = mParent->i_findRemoveableMedium(dev.deviceType,
8843 dev.uuid,
8844 false /* fRefresh */,
8845 false /* aSetError */,
8846 medium);
8847 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8848 // This is not an error. The host drive or UUID might have vanished, so just go
8849 // ahead without this removeable medium attachment
8850 rc = S_OK;
8851 break;
8852
8853 case DeviceType_HardDisk:
8854 {
8855 /* find a hard disk by UUID */
8856 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8857 if (FAILED(rc))
8858 {
8859 if (i_isSnapshotMachine())
8860 {
8861 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8862 // so the user knows that the bad disk is in a snapshot somewhere
8863 com::ErrorInfo info;
8864 return setError(E_FAIL,
8865 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8866 puuidSnapshot->raw(),
8867 info.getText().raw());
8868 }
8869 else
8870 return rc;
8871 }
8872
8873 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8874
8875 if (medium->i_getType() == MediumType_Immutable)
8876 {
8877 if (i_isSnapshotMachine())
8878 return setError(E_FAIL,
8879 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8880 "of the virtual machine '%s' ('%s')"),
8881 medium->i_getLocationFull().c_str(),
8882 dev.uuid.raw(),
8883 puuidSnapshot->raw(),
8884 mUserData->s.strName.c_str(),
8885 mData->m_strConfigFileFull.c_str());
8886
8887 return setError(E_FAIL,
8888 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8889 medium->i_getLocationFull().c_str(),
8890 dev.uuid.raw(),
8891 mUserData->s.strName.c_str(),
8892 mData->m_strConfigFileFull.c_str());
8893 }
8894
8895 if (medium->i_getType() == MediumType_MultiAttach)
8896 {
8897 if (i_isSnapshotMachine())
8898 return setError(E_FAIL,
8899 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8900 "of the virtual machine '%s' ('%s')"),
8901 medium->i_getLocationFull().c_str(),
8902 dev.uuid.raw(),
8903 puuidSnapshot->raw(),
8904 mUserData->s.strName.c_str(),
8905 mData->m_strConfigFileFull.c_str());
8906
8907 return setError(E_FAIL,
8908 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8909 medium->i_getLocationFull().c_str(),
8910 dev.uuid.raw(),
8911 mUserData->s.strName.c_str(),
8912 mData->m_strConfigFileFull.c_str());
8913 }
8914
8915 if ( !i_isSnapshotMachine()
8916 && medium->i_getChildren().size() != 0
8917 )
8918 return setError(E_FAIL,
8919 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8920 "because it has %d differencing child hard disks"),
8921 medium->i_getLocationFull().c_str(),
8922 dev.uuid.raw(),
8923 mUserData->s.strName.c_str(),
8924 mData->m_strConfigFileFull.c_str(),
8925 medium->i_getChildren().size());
8926
8927 if (i_findAttachment(mMediaData->mAttachments,
8928 medium))
8929 return setError(E_FAIL,
8930 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8931 medium->i_getLocationFull().c_str(),
8932 dev.uuid.raw(),
8933 mUserData->s.strName.c_str(),
8934 mData->m_strConfigFileFull.c_str());
8935
8936 break;
8937 }
8938
8939 default:
8940 return setError(E_FAIL,
8941 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8942 medium->i_getLocationFull().c_str(),
8943 mUserData->s.strName.c_str(),
8944 mData->m_strConfigFileFull.c_str());
8945 }
8946
8947 if (FAILED(rc))
8948 break;
8949
8950 /* Bandwidth groups are loaded at this point. */
8951 ComObjPtr<BandwidthGroup> pBwGroup;
8952
8953 if (!dev.strBwGroup.isEmpty())
8954 {
8955 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8956 if (FAILED(rc))
8957 return setError(E_FAIL,
8958 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8959 medium->i_getLocationFull().c_str(),
8960 dev.strBwGroup.c_str(),
8961 mUserData->s.strName.c_str(),
8962 mData->m_strConfigFileFull.c_str());
8963 pBwGroup->i_reference();
8964 }
8965
8966 const Bstr controllerName = aStorageController->i_getName();
8967 ComObjPtr<MediumAttachment> pAttachment;
8968 pAttachment.createObject();
8969 rc = pAttachment->init(this,
8970 medium,
8971 controllerName,
8972 dev.lPort,
8973 dev.lDevice,
8974 dev.deviceType,
8975 false,
8976 dev.fPassThrough,
8977 dev.fTempEject,
8978 dev.fNonRotational,
8979 dev.fDiscard,
8980 dev.fHotPluggable,
8981 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
8982 if (FAILED(rc)) break;
8983
8984 /* associate the medium with this machine and snapshot */
8985 if (!medium.isNull())
8986 {
8987 AutoCaller medCaller(medium);
8988 if (FAILED(medCaller.rc())) return medCaller.rc();
8989 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8990
8991 if (i_isSnapshotMachine())
8992 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
8993 else
8994 rc = medium->i_addBackReference(mData->mUuid);
8995 /* If the medium->addBackReference fails it sets an appropriate
8996 * error message, so no need to do any guesswork here. */
8997
8998 if (puuidRegistry)
8999 // caller wants registry ID to be set on all attached media (OVF import case)
9000 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9001 }
9002
9003 if (FAILED(rc))
9004 break;
9005
9006 /* back up mMediaData to let registeredInit() properly rollback on failure
9007 * (= limited accessibility) */
9008 i_setModified(IsModified_Storage);
9009 mMediaData.backup();
9010 mMediaData->mAttachments.push_back(pAttachment);
9011 }
9012
9013 return rc;
9014}
9015
9016/**
9017 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9018 *
9019 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9020 * @param aSnapshot where to return the found snapshot
9021 * @param aSetError true to set extended error info on failure
9022 */
9023HRESULT Machine::i_findSnapshotById(const Guid &aId,
9024 ComObjPtr<Snapshot> &aSnapshot,
9025 bool aSetError /* = false */)
9026{
9027 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9028
9029 if (!mData->mFirstSnapshot)
9030 {
9031 if (aSetError)
9032 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9033 return E_FAIL;
9034 }
9035
9036 if (aId.isZero())
9037 aSnapshot = mData->mFirstSnapshot;
9038 else
9039 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9040
9041 if (!aSnapshot)
9042 {
9043 if (aSetError)
9044 return setError(E_FAIL,
9045 tr("Could not find a snapshot with UUID {%s}"),
9046 aId.toString().c_str());
9047 return E_FAIL;
9048 }
9049
9050 return S_OK;
9051}
9052
9053/**
9054 * Returns the snapshot with the given name or fails of no such snapshot.
9055 *
9056 * @param aName snapshot name to find
9057 * @param aSnapshot where to return the found snapshot
9058 * @param aSetError true to set extended error info on failure
9059 */
9060HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9061 ComObjPtr<Snapshot> &aSnapshot,
9062 bool aSetError /* = false */)
9063{
9064 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9065
9066 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9067
9068 if (!mData->mFirstSnapshot)
9069 {
9070 if (aSetError)
9071 return setError(VBOX_E_OBJECT_NOT_FOUND,
9072 tr("This machine does not have any snapshots"));
9073 return VBOX_E_OBJECT_NOT_FOUND;
9074 }
9075
9076 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9077
9078 if (!aSnapshot)
9079 {
9080 if (aSetError)
9081 return setError(VBOX_E_OBJECT_NOT_FOUND,
9082 tr("Could not find a snapshot named '%s'"), strName.c_str());
9083 return VBOX_E_OBJECT_NOT_FOUND;
9084 }
9085
9086 return S_OK;
9087}
9088
9089/**
9090 * Returns a storage controller object with the given name.
9091 *
9092 * @param aName storage controller name to find
9093 * @param aStorageController where to return the found storage controller
9094 * @param aSetError true to set extended error info on failure
9095 */
9096HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9097 ComObjPtr<StorageController> &aStorageController,
9098 bool aSetError /* = false */)
9099{
9100 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9101
9102 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9103 it != mStorageControllers->end();
9104 ++it)
9105 {
9106 if ((*it)->i_getName() == aName)
9107 {
9108 aStorageController = (*it);
9109 return S_OK;
9110 }
9111 }
9112
9113 if (aSetError)
9114 return setError(VBOX_E_OBJECT_NOT_FOUND,
9115 tr("Could not find a storage controller named '%s'"),
9116 aName.c_str());
9117 return VBOX_E_OBJECT_NOT_FOUND;
9118}
9119
9120/**
9121 * Returns a USB controller object with the given name.
9122 *
9123 * @param aName USB controller name to find
9124 * @param aUSBController where to return the found USB controller
9125 * @param aSetError true to set extended error info on failure
9126 */
9127HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9128 ComObjPtr<USBController> &aUSBController,
9129 bool aSetError /* = false */)
9130{
9131 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9132
9133 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9134 it != mUSBControllers->end();
9135 ++it)
9136 {
9137 if ((*it)->i_getName() == aName)
9138 {
9139 aUSBController = (*it);
9140 return S_OK;
9141 }
9142 }
9143
9144 if (aSetError)
9145 return setError(VBOX_E_OBJECT_NOT_FOUND,
9146 tr("Could not find a storage controller named '%s'"),
9147 aName.c_str());
9148 return VBOX_E_OBJECT_NOT_FOUND;
9149}
9150
9151/**
9152 * Returns the number of USB controller instance of the given type.
9153 *
9154 * @param enmType USB controller type.
9155 */
9156ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9157{
9158 ULONG cCtrls = 0;
9159
9160 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9161 it != mUSBControllers->end();
9162 ++it)
9163 {
9164 if ((*it)->i_getControllerType() == enmType)
9165 cCtrls++;
9166 }
9167
9168 return cCtrls;
9169}
9170
9171HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9172 MediaData::AttachmentList &atts)
9173{
9174 AutoCaller autoCaller(this);
9175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9176
9177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9178
9179 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9180 it != mMediaData->mAttachments.end();
9181 ++it)
9182 {
9183 const ComObjPtr<MediumAttachment> &pAtt = *it;
9184 // should never happen, but deal with NULL pointers in the list.
9185 AssertStmt(!pAtt.isNull(), continue);
9186
9187 // getControllerName() needs caller+read lock
9188 AutoCaller autoAttCaller(pAtt);
9189 if (FAILED(autoAttCaller.rc()))
9190 {
9191 atts.clear();
9192 return autoAttCaller.rc();
9193 }
9194 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9195
9196 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9197 atts.push_back(pAtt);
9198 }
9199
9200 return S_OK;
9201}
9202
9203
9204/**
9205 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9206 * file if the machine name was changed and about creating a new settings file
9207 * if this is a new machine.
9208 *
9209 * @note Must be never called directly but only from #saveSettings().
9210 */
9211HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9212{
9213 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9214
9215 HRESULT rc = S_OK;
9216
9217 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9218
9219 /// @todo need to handle primary group change, too
9220
9221 /* attempt to rename the settings file if machine name is changed */
9222 if ( mUserData->s.fNameSync
9223 && mUserData.isBackedUp()
9224 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9225 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9226 )
9227 {
9228 bool dirRenamed = false;
9229 bool fileRenamed = false;
9230
9231 Utf8Str configFile, newConfigFile;
9232 Utf8Str configFilePrev, newConfigFilePrev;
9233 Utf8Str configDir, newConfigDir;
9234
9235 do
9236 {
9237 int vrc = VINF_SUCCESS;
9238
9239 Utf8Str name = mUserData.backedUpData()->s.strName;
9240 Utf8Str newName = mUserData->s.strName;
9241 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9242 if (group == "/")
9243 group.setNull();
9244 Utf8Str newGroup = mUserData->s.llGroups.front();
9245 if (newGroup == "/")
9246 newGroup.setNull();
9247
9248 configFile = mData->m_strConfigFileFull;
9249
9250 /* first, rename the directory if it matches the group and machine name */
9251 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9252 group.c_str(), RTPATH_DELIMITER, name.c_str());
9253 /** @todo hack, make somehow use of ComposeMachineFilename */
9254 if (mUserData->s.fDirectoryIncludesUUID)
9255 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9256 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9257 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9258 /** @todo hack, make somehow use of ComposeMachineFilename */
9259 if (mUserData->s.fDirectoryIncludesUUID)
9260 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9261 configDir = configFile;
9262 configDir.stripFilename();
9263 newConfigDir = configDir;
9264 if ( configDir.length() >= groupPlusName.length()
9265 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9266 groupPlusName.c_str()))
9267 {
9268 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9269 Utf8Str newConfigBaseDir(newConfigDir);
9270 newConfigDir.append(newGroupPlusName);
9271 /* consistency: use \ if appropriate on the platform */
9272 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9273 /* new dir and old dir cannot be equal here because of 'if'
9274 * above and because name != newName */
9275 Assert(configDir != newConfigDir);
9276 if (!fSettingsFileIsNew)
9277 {
9278 /* perform real rename only if the machine is not new */
9279 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9280 if ( vrc == VERR_FILE_NOT_FOUND
9281 || vrc == VERR_PATH_NOT_FOUND)
9282 {
9283 /* create the parent directory, then retry renaming */
9284 Utf8Str parent(newConfigDir);
9285 parent.stripFilename();
9286 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9287 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9288 }
9289 if (RT_FAILURE(vrc))
9290 {
9291 rc = setError(E_FAIL,
9292 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9293 configDir.c_str(),
9294 newConfigDir.c_str(),
9295 vrc);
9296 break;
9297 }
9298 /* delete subdirectories which are no longer needed */
9299 Utf8Str dir(configDir);
9300 dir.stripFilename();
9301 while (dir != newConfigBaseDir && dir != ".")
9302 {
9303 vrc = RTDirRemove(dir.c_str());
9304 if (RT_FAILURE(vrc))
9305 break;
9306 dir.stripFilename();
9307 }
9308 dirRenamed = true;
9309 }
9310 }
9311
9312 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9313 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9314
9315 /* then try to rename the settings file itself */
9316 if (newConfigFile != configFile)
9317 {
9318 /* get the path to old settings file in renamed directory */
9319 configFile = Utf8StrFmt("%s%c%s",
9320 newConfigDir.c_str(),
9321 RTPATH_DELIMITER,
9322 RTPathFilename(configFile.c_str()));
9323 if (!fSettingsFileIsNew)
9324 {
9325 /* perform real rename only if the machine is not new */
9326 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9327 if (RT_FAILURE(vrc))
9328 {
9329 rc = setError(E_FAIL,
9330 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9331 configFile.c_str(),
9332 newConfigFile.c_str(),
9333 vrc);
9334 break;
9335 }
9336 fileRenamed = true;
9337 configFilePrev = configFile;
9338 configFilePrev += "-prev";
9339 newConfigFilePrev = newConfigFile;
9340 newConfigFilePrev += "-prev";
9341 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9342 }
9343 }
9344
9345 // update m_strConfigFileFull amd mConfigFile
9346 mData->m_strConfigFileFull = newConfigFile;
9347 // compute the relative path too
9348 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9349
9350 // store the old and new so that VirtualBox::i_saveSettings() can update
9351 // the media registry
9352 if ( mData->mRegistered
9353 && (configDir != newConfigDir || configFile != newConfigFile))
9354 {
9355 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9356
9357 if (pfNeedsGlobalSaveSettings)
9358 *pfNeedsGlobalSaveSettings = true;
9359 }
9360
9361 // in the saved state file path, replace the old directory with the new directory
9362 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9363 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9364
9365 // and do the same thing for the saved state file paths of all the online snapshots
9366 if (mData->mFirstSnapshot)
9367 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9368 newConfigDir.c_str());
9369 }
9370 while (0);
9371
9372 if (FAILED(rc))
9373 {
9374 /* silently try to rename everything back */
9375 if (fileRenamed)
9376 {
9377 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9378 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9379 }
9380 if (dirRenamed)
9381 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9382 }
9383
9384 if (FAILED(rc)) return rc;
9385 }
9386
9387 if (fSettingsFileIsNew)
9388 {
9389 /* create a virgin config file */
9390 int vrc = VINF_SUCCESS;
9391
9392 /* ensure the settings directory exists */
9393 Utf8Str path(mData->m_strConfigFileFull);
9394 path.stripFilename();
9395 if (!RTDirExists(path.c_str()))
9396 {
9397 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9398 if (RT_FAILURE(vrc))
9399 {
9400 return setError(E_FAIL,
9401 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9402 path.c_str(),
9403 vrc);
9404 }
9405 }
9406
9407 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9408 path = Utf8Str(mData->m_strConfigFileFull);
9409 RTFILE f = NIL_RTFILE;
9410 vrc = RTFileOpen(&f, path.c_str(),
9411 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9412 if (RT_FAILURE(vrc))
9413 return setError(E_FAIL,
9414 tr("Could not create the settings file '%s' (%Rrc)"),
9415 path.c_str(),
9416 vrc);
9417 RTFileClose(f);
9418 }
9419
9420 return rc;
9421}
9422
9423/**
9424 * Saves and commits machine data, user data and hardware data.
9425 *
9426 * Note that on failure, the data remains uncommitted.
9427 *
9428 * @a aFlags may combine the following flags:
9429 *
9430 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9431 * Used when saving settings after an operation that makes them 100%
9432 * correspond to the settings from the current snapshot.
9433 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9434 * #isReallyModified() returns false. This is necessary for cases when we
9435 * change machine data directly, not through the backup()/commit() mechanism.
9436 * - SaveS_Force: settings will be saved without doing a deep compare of the
9437 * settings structures. This is used when this is called because snapshots
9438 * have changed to avoid the overhead of the deep compare.
9439 *
9440 * @note Must be called from under this object's write lock. Locks children for
9441 * writing.
9442 *
9443 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9444 * initialized to false and that will be set to true by this function if
9445 * the caller must invoke VirtualBox::i_saveSettings() because the global
9446 * settings have changed. This will happen if a machine rename has been
9447 * saved and the global machine and media registries will therefore need
9448 * updating.
9449 */
9450HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9451 int aFlags /*= 0*/)
9452{
9453 LogFlowThisFuncEnter();
9454
9455 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9456
9457 /* make sure child objects are unable to modify the settings while we are
9458 * saving them */
9459 i_ensureNoStateDependencies();
9460
9461 AssertReturn(!i_isSnapshotMachine(),
9462 E_FAIL);
9463
9464 HRESULT rc = S_OK;
9465 bool fNeedsWrite = false;
9466
9467 /* First, prepare to save settings. It will care about renaming the
9468 * settings directory and file if the machine name was changed and about
9469 * creating a new settings file if this is a new machine. */
9470 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9471 if (FAILED(rc)) return rc;
9472
9473 // keep a pointer to the current settings structures
9474 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9475 settings::MachineConfigFile *pNewConfig = NULL;
9476
9477 try
9478 {
9479 // make a fresh one to have everyone write stuff into
9480 pNewConfig = new settings::MachineConfigFile(NULL);
9481 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9482
9483 // now go and copy all the settings data from COM to the settings structures
9484 // (this calles i_saveSettings() on all the COM objects in the machine)
9485 i_copyMachineDataToSettings(*pNewConfig);
9486
9487 if (aFlags & SaveS_ResetCurStateModified)
9488 {
9489 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9490 mData->mCurrentStateModified = FALSE;
9491 fNeedsWrite = true; // always, no need to compare
9492 }
9493 else if (aFlags & SaveS_Force)
9494 {
9495 fNeedsWrite = true; // always, no need to compare
9496 }
9497 else
9498 {
9499 if (!mData->mCurrentStateModified)
9500 {
9501 // do a deep compare of the settings that we just saved with the settings
9502 // previously stored in the config file; this invokes MachineConfigFile::operator==
9503 // which does a deep compare of all the settings, which is expensive but less expensive
9504 // than writing out XML in vain
9505 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9506
9507 // could still be modified if any settings changed
9508 mData->mCurrentStateModified = fAnySettingsChanged;
9509
9510 fNeedsWrite = fAnySettingsChanged;
9511 }
9512 else
9513 fNeedsWrite = true;
9514 }
9515
9516 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9517
9518 if (fNeedsWrite)
9519 // now spit it all out!
9520 pNewConfig->write(mData->m_strConfigFileFull);
9521
9522 mData->pMachineConfigFile = pNewConfig;
9523 delete pOldConfig;
9524 i_commit();
9525
9526 // after saving settings, we are no longer different from the XML on disk
9527 mData->flModifications = 0;
9528 }
9529 catch (HRESULT err)
9530 {
9531 // we assume that error info is set by the thrower
9532 rc = err;
9533
9534 // restore old config
9535 delete pNewConfig;
9536 mData->pMachineConfigFile = pOldConfig;
9537 }
9538 catch (...)
9539 {
9540 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9541 }
9542
9543 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9544 {
9545 /* Fire the data change event, even on failure (since we've already
9546 * committed all data). This is done only for SessionMachines because
9547 * mutable Machine instances are always not registered (i.e. private
9548 * to the client process that creates them) and thus don't need to
9549 * inform callbacks. */
9550 if (i_isSessionMachine())
9551 mParent->i_onMachineDataChange(mData->mUuid);
9552 }
9553
9554 LogFlowThisFunc(("rc=%08X\n", rc));
9555 LogFlowThisFuncLeave();
9556 return rc;
9557}
9558
9559/**
9560 * Implementation for saving the machine settings into the given
9561 * settings::MachineConfigFile instance. This copies machine extradata
9562 * from the previous machine config file in the instance data, if any.
9563 *
9564 * This gets called from two locations:
9565 *
9566 * -- Machine::i_saveSettings(), during the regular XML writing;
9567 *
9568 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9569 * exported to OVF and we write the VirtualBox proprietary XML
9570 * into a <vbox:Machine> tag.
9571 *
9572 * This routine fills all the fields in there, including snapshots, *except*
9573 * for the following:
9574 *
9575 * -- fCurrentStateModified. There is some special logic associated with that.
9576 *
9577 * The caller can then call MachineConfigFile::write() or do something else
9578 * with it.
9579 *
9580 * Caller must hold the machine lock!
9581 *
9582 * This throws XML errors and HRESULT, so the caller must have a catch block!
9583 */
9584void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9585{
9586 // deep copy extradata
9587 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9588
9589 config.uuid = mData->mUuid;
9590
9591 // copy name, description, OS type, teleport, UTC etc.
9592 config.machineUserData = mUserData->s;
9593
9594 // Encode the Icon Override data from Machine and store on config userdata.
9595 com::SafeArray<BYTE> iconByte;
9596 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9597 ssize_t cbData = iconByte.size();
9598 if (cbData > 0)
9599 {
9600 ssize_t cchOut = RTBase64EncodedLength(cbData);
9601 Utf8Str strIconData;
9602 strIconData.reserve(cchOut+1);
9603 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9604 strIconData.mutableRaw(), strIconData.capacity(),
9605 NULL);
9606 if (RT_FAILURE(vrc))
9607 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9608 strIconData.jolt();
9609 config.machineUserData.ovIcon = strIconData;
9610 }
9611 else
9612 config.machineUserData.ovIcon.setNull();
9613
9614 if ( mData->mMachineState == MachineState_Saved
9615 || mData->mMachineState == MachineState_Restoring
9616 // when deleting a snapshot we may or may not have a saved state in the current state,
9617 // so let's not assert here please
9618 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9619 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9620 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9621 && (!mSSData->strStateFilePath.isEmpty())
9622 )
9623 )
9624 {
9625 Assert(!mSSData->strStateFilePath.isEmpty());
9626 /* try to make the file name relative to the settings file dir */
9627 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9628 }
9629 else
9630 {
9631 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9632 config.strStateFile.setNull();
9633 }
9634
9635 if (mData->mCurrentSnapshot)
9636 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9637 else
9638 config.uuidCurrentSnapshot.clear();
9639
9640 config.timeLastStateChange = mData->mLastStateChange;
9641 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9642 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9643
9644 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9645 if (FAILED(rc)) throw rc;
9646
9647 rc = i_saveStorageControllers(config.storageMachine);
9648 if (FAILED(rc)) throw rc;
9649
9650 // save machine's media registry if this is VirtualBox 4.0 or later
9651 if (config.canHaveOwnMediaRegistry())
9652 {
9653 // determine machine folder
9654 Utf8Str strMachineFolder = i_getSettingsFileFull();
9655 strMachineFolder.stripFilename();
9656 mParent->i_saveMediaRegistry(config.mediaRegistry,
9657 i_getId(), // only media with registry ID == machine UUID
9658 strMachineFolder);
9659 // this throws HRESULT
9660 }
9661
9662 // save snapshots
9663 rc = i_saveAllSnapshots(config);
9664 if (FAILED(rc)) throw rc;
9665}
9666
9667/**
9668 * Saves all snapshots of the machine into the given machine config file. Called
9669 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9670 * @param config
9671 * @return
9672 */
9673HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9674{
9675 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9676
9677 HRESULT rc = S_OK;
9678
9679 try
9680 {
9681 config.llFirstSnapshot.clear();
9682
9683 if (mData->mFirstSnapshot)
9684 {
9685 settings::Snapshot snapNew;
9686 config.llFirstSnapshot.push_back(snapNew);
9687
9688 // get reference to the fresh copy of the snapshot on the list and
9689 // work on that copy directly to avoid excessive copying later
9690 settings::Snapshot &snap = config.llFirstSnapshot.front();
9691
9692 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9693 if (FAILED(rc)) throw rc;
9694 }
9695
9696// if (mType == IsSessionMachine)
9697// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9698
9699 }
9700 catch (HRESULT err)
9701 {
9702 /* we assume that error info is set by the thrower */
9703 rc = err;
9704 }
9705 catch (...)
9706 {
9707 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9708 }
9709
9710 return rc;
9711}
9712
9713/**
9714 * Saves the VM hardware configuration. It is assumed that the
9715 * given node is empty.
9716 *
9717 * @param data Reference to the settings object for the hardware config.
9718 * @param pDbg Pointer to the settings object for the debugging config
9719 * which happens to live in mHWData.
9720 * @param pAutostart Pointer to the settings object for the autostart config
9721 * which happens to live in mHWData.
9722 */
9723HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9724 settings::Autostart *pAutostart)
9725{
9726 HRESULT rc = S_OK;
9727
9728 try
9729 {
9730 /* The hardware version attribute (optional).
9731 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9732 if ( mHWData->mHWVersion == "1"
9733 && mSSData->strStateFilePath.isEmpty()
9734 )
9735 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9736 other point needs to be found where this can be done. */
9737
9738 data.strVersion = mHWData->mHWVersion;
9739 data.uuid = mHWData->mHardwareUUID;
9740
9741 // CPU
9742 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9743 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9744 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9745 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9746 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9747 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9748 data.fPAE = !!mHWData->mPAEEnabled;
9749 data.enmLongMode = mHWData->mLongMode;
9750 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9751 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9752
9753 /* Standard and Extended CPUID leafs. */
9754 data.llCpuIdLeafs.clear();
9755 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9756 {
9757 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9758 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9759 }
9760 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9761 {
9762 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9763 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9764 }
9765
9766 data.cCPUs = mHWData->mCPUCount;
9767 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9768 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9769
9770 data.llCpus.clear();
9771 if (data.fCpuHotPlug)
9772 {
9773 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9774 {
9775 if (mHWData->mCPUAttached[idx])
9776 {
9777 settings::Cpu cpu;
9778 cpu.ulId = idx;
9779 data.llCpus.push_back(cpu);
9780 }
9781 }
9782 }
9783
9784 // memory
9785 data.ulMemorySizeMB = mHWData->mMemorySize;
9786 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9787
9788 // firmware
9789 data.firmwareType = mHWData->mFirmwareType;
9790
9791 // HID
9792 data.pointingHIDType = mHWData->mPointingHIDType;
9793 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9794
9795 // chipset
9796 data.chipsetType = mHWData->mChipsetType;
9797
9798 // paravirt
9799 data.paravirtProvider = mHWData->mParavirtProvider;
9800
9801
9802 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9803
9804 // HPET
9805 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9806
9807 // boot order
9808 data.mapBootOrder.clear();
9809 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9810 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9811
9812 // display
9813 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9814 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9815 data.cMonitors = mHWData->mMonitorCount;
9816 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9817 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9818 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9819 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9820 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
9821 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
9822 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9823 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
9824 {
9825 if (mHWData->maVideoCaptureScreens[i])
9826 ASMBitSet(&data.u64VideoCaptureScreens, i);
9827 else
9828 ASMBitClear(&data.u64VideoCaptureScreens, i);
9829 }
9830 /* store relative video capture file if possible */
9831 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
9832
9833 /* VRDEServer settings (optional) */
9834 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
9835 if (FAILED(rc)) throw rc;
9836
9837 /* BIOS (required) */
9838 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
9839 if (FAILED(rc)) throw rc;
9840
9841 /* USB Controller (required) */
9842 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
9843 {
9844 ComObjPtr<USBController> ctrl = *it;
9845 settings::USBController settingsCtrl;
9846
9847 settingsCtrl.strName = ctrl->i_getName();
9848 settingsCtrl.enmType = ctrl->i_getControllerType();
9849
9850 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
9851 }
9852
9853 /* USB device filters (required) */
9854 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
9855 if (FAILED(rc)) throw rc;
9856
9857 /* Network adapters (required) */
9858 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9859 data.llNetworkAdapters.clear();
9860 /* Write out only the nominal number of network adapters for this
9861 * chipset type. Since Machine::commit() hasn't been called there
9862 * may be extra NIC settings in the vector. */
9863 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9864 {
9865 settings::NetworkAdapter nic;
9866 nic.ulSlot = slot;
9867 /* paranoia check... must not be NULL, but must not crash either. */
9868 if (mNetworkAdapters[slot])
9869 {
9870 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
9871 if (FAILED(rc)) throw rc;
9872
9873 data.llNetworkAdapters.push_back(nic);
9874 }
9875 }
9876
9877 /* Serial ports */
9878 data.llSerialPorts.clear();
9879 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
9880 {
9881 settings::SerialPort s;
9882 s.ulSlot = slot;
9883 rc = mSerialPorts[slot]->i_saveSettings(s);
9884 if (FAILED(rc)) return rc;
9885
9886 data.llSerialPorts.push_back(s);
9887 }
9888
9889 /* Parallel ports */
9890 data.llParallelPorts.clear();
9891 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
9892 {
9893 settings::ParallelPort p;
9894 p.ulSlot = slot;
9895 rc = mParallelPorts[slot]->i_saveSettings(p);
9896 if (FAILED(rc)) return rc;
9897
9898 data.llParallelPorts.push_back(p);
9899 }
9900
9901 /* Audio adapter */
9902 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
9903 if (FAILED(rc)) return rc;
9904
9905 /* Shared folders */
9906 data.llSharedFolders.clear();
9907 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9908 it != mHWData->mSharedFolders.end();
9909 ++it)
9910 {
9911 SharedFolder *pSF = *it;
9912 AutoCaller sfCaller(pSF);
9913 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9914 settings::SharedFolder sf;
9915 sf.strName = pSF->i_getName();
9916 sf.strHostPath = pSF->i_getHostPath();
9917 sf.fWritable = !!pSF->i_isWritable();
9918 sf.fAutoMount = !!pSF->i_isAutoMounted();
9919
9920 data.llSharedFolders.push_back(sf);
9921 }
9922
9923 // clipboard
9924 data.clipboardMode = mHWData->mClipboardMode;
9925
9926 // drag'n'drop
9927 data.dndMode = mHWData->mDnDMode;
9928
9929 /* Guest */
9930 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9931
9932 // IO settings
9933 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9934 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9935
9936 /* BandwidthControl (required) */
9937 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
9938 if (FAILED(rc)) throw rc;
9939
9940 /* Host PCI devices */
9941 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9942 it != mHWData->mPCIDeviceAssignments.end();
9943 ++it)
9944 {
9945 ComObjPtr<PCIDeviceAttachment> pda = *it;
9946 settings::HostPCIDeviceAttachment hpda;
9947
9948 rc = pda->i_saveSettings(hpda);
9949 if (FAILED(rc)) throw rc;
9950
9951 data.pciAttachments.push_back(hpda);
9952 }
9953
9954
9955 // guest properties
9956 data.llGuestProperties.clear();
9957#ifdef VBOX_WITH_GUEST_PROPS
9958 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
9959 it != mHWData->mGuestProperties.end();
9960 ++it)
9961 {
9962 HWData::GuestProperty property = it->second;
9963
9964 /* Remove transient guest properties at shutdown unless we
9965 * are saving state */
9966 if ( ( mData->mMachineState == MachineState_PoweredOff
9967 || mData->mMachineState == MachineState_Aborted
9968 || mData->mMachineState == MachineState_Teleported)
9969 && ( property.mFlags & guestProp::TRANSIENT
9970 || property.mFlags & guestProp::TRANSRESET))
9971 continue;
9972 settings::GuestProperty prop;
9973 prop.strName = it->first;
9974 prop.strValue = property.strValue;
9975 prop.timestamp = property.mTimestamp;
9976 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9977 guestProp::writeFlags(property.mFlags, szFlags);
9978 prop.strFlags = szFlags;
9979
9980 data.llGuestProperties.push_back(prop);
9981 }
9982
9983 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9984 /* I presume this doesn't require a backup(). */
9985 mData->mGuestPropertiesModified = FALSE;
9986#endif /* VBOX_WITH_GUEST_PROPS defined */
9987
9988 *pDbg = mHWData->mDebugging;
9989 *pAutostart = mHWData->mAutostart;
9990
9991 data.strDefaultFrontend = mHWData->mDefaultFrontend;
9992 }
9993 catch(std::bad_alloc &)
9994 {
9995 return E_OUTOFMEMORY;
9996 }
9997
9998 AssertComRC(rc);
9999 return rc;
10000}
10001
10002/**
10003 * Saves the storage controller configuration.
10004 *
10005 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10006 */
10007HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10008{
10009 data.llStorageControllers.clear();
10010
10011 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10012 it != mStorageControllers->end();
10013 ++it)
10014 {
10015 HRESULT rc;
10016 ComObjPtr<StorageController> pCtl = *it;
10017
10018 settings::StorageController ctl;
10019 ctl.strName = pCtl->i_getName();
10020 ctl.controllerType = pCtl->i_getControllerType();
10021 ctl.storageBus = pCtl->i_getStorageBus();
10022 ctl.ulInstance = pCtl->i_getInstance();
10023 ctl.fBootable = pCtl->i_getBootable();
10024
10025 /* Save the port count. */
10026 ULONG portCount;
10027 rc = pCtl->COMGETTER(PortCount)(&portCount);
10028 ComAssertComRCRet(rc, rc);
10029 ctl.ulPortCount = portCount;
10030
10031 /* Save fUseHostIOCache */
10032 BOOL fUseHostIOCache;
10033 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10034 ComAssertComRCRet(rc, rc);
10035 ctl.fUseHostIOCache = !!fUseHostIOCache;
10036
10037 /* Save IDE emulation settings. */
10038 if (ctl.controllerType == StorageControllerType_IntelAhci)
10039 {
10040 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10041 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10042 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10043 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10044 )
10045 ComAssertComRCRet(rc, rc);
10046 }
10047
10048 /* save the devices now. */
10049 rc = i_saveStorageDevices(pCtl, ctl);
10050 ComAssertComRCRet(rc, rc);
10051
10052 data.llStorageControllers.push_back(ctl);
10053 }
10054
10055 return S_OK;
10056}
10057
10058/**
10059 * Saves the hard disk configuration.
10060 */
10061HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10062 settings::StorageController &data)
10063{
10064 MediaData::AttachmentList atts;
10065
10066 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10067 if (FAILED(rc)) return rc;
10068
10069 data.llAttachedDevices.clear();
10070 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10071 it != atts.end();
10072 ++it)
10073 {
10074 settings::AttachedDevice dev;
10075 IMediumAttachment *iA = *it;
10076 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10077 Medium *pMedium = pAttach->i_getMedium();
10078
10079 dev.deviceType = pAttach->i_getType();
10080 dev.lPort = pAttach->i_getPort();
10081 dev.lDevice = pAttach->i_getDevice();
10082 dev.fPassThrough = pAttach->i_getPassthrough();
10083 dev.fHotPluggable = pAttach->i_getHotPluggable();
10084 if (pMedium)
10085 {
10086 if (pMedium->i_isHostDrive())
10087 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10088 else
10089 dev.uuid = pMedium->i_getId();
10090 dev.fTempEject = pAttach->i_getTempEject();
10091 dev.fNonRotational = pAttach->i_getNonRotational();
10092 dev.fDiscard = pAttach->i_getDiscard();
10093 }
10094
10095 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10096
10097 data.llAttachedDevices.push_back(dev);
10098 }
10099
10100 return S_OK;
10101}
10102
10103/**
10104 * Saves machine state settings as defined by aFlags
10105 * (SaveSTS_* values).
10106 *
10107 * @param aFlags Combination of SaveSTS_* flags.
10108 *
10109 * @note Locks objects for writing.
10110 */
10111HRESULT Machine::i_saveStateSettings(int aFlags)
10112{
10113 if (aFlags == 0)
10114 return S_OK;
10115
10116 AutoCaller autoCaller(this);
10117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10118
10119 /* This object's write lock is also necessary to serialize file access
10120 * (prevent concurrent reads and writes) */
10121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10122
10123 HRESULT rc = S_OK;
10124
10125 Assert(mData->pMachineConfigFile);
10126
10127 try
10128 {
10129 if (aFlags & SaveSTS_CurStateModified)
10130 mData->pMachineConfigFile->fCurrentStateModified = true;
10131
10132 if (aFlags & SaveSTS_StateFilePath)
10133 {
10134 if (!mSSData->strStateFilePath.isEmpty())
10135 /* try to make the file name relative to the settings file dir */
10136 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10137 else
10138 mData->pMachineConfigFile->strStateFile.setNull();
10139 }
10140
10141 if (aFlags & SaveSTS_StateTimeStamp)
10142 {
10143 Assert( mData->mMachineState != MachineState_Aborted
10144 || mSSData->strStateFilePath.isEmpty());
10145
10146 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10147
10148 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10149//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10150 }
10151
10152 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10153 }
10154 catch (...)
10155 {
10156 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10157 }
10158
10159 return rc;
10160}
10161
10162/**
10163 * Ensures that the given medium is added to a media registry. If this machine
10164 * was created with 4.0 or later, then the machine registry is used. Otherwise
10165 * the global VirtualBox media registry is used.
10166 *
10167 * Caller must NOT hold machine lock, media tree or any medium locks!
10168 *
10169 * @param pMedium
10170 */
10171void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10172{
10173 /* Paranoia checks: do not hold machine or media tree locks. */
10174 AssertReturnVoid(!isWriteLockOnCurrentThread());
10175 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10176
10177 ComObjPtr<Medium> pBase;
10178 {
10179 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10180 pBase = pMedium->i_getBase();
10181 }
10182
10183 /* Paranoia checks: do not hold medium locks. */
10184 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10185 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10186
10187 // decide which medium registry to use now that the medium is attached:
10188 Guid uuid;
10189 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10190 // machine XML is VirtualBox 4.0 or higher:
10191 uuid = i_getId(); // machine UUID
10192 else
10193 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10194
10195 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10196 mParent->i_markRegistryModified(uuid);
10197
10198 /* For more complex hard disk structures it can happen that the base
10199 * medium isn't yet associated with any medium registry. Do that now. */
10200 if (pMedium != pBase)
10201 {
10202 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10203 mParent->i_markRegistryModified(uuid);
10204 }
10205}
10206
10207/**
10208 * Creates differencing hard disks for all normal hard disks attached to this
10209 * machine and a new set of attachments to refer to created disks.
10210 *
10211 * Used when taking a snapshot or when deleting the current state. Gets called
10212 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10213 *
10214 * This method assumes that mMediaData contains the original hard disk attachments
10215 * it needs to create diffs for. On success, these attachments will be replaced
10216 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10217 * called to delete created diffs which will also rollback mMediaData and restore
10218 * whatever was backed up before calling this method.
10219 *
10220 * Attachments with non-normal hard disks are left as is.
10221 *
10222 * If @a aOnline is @c false then the original hard disks that require implicit
10223 * diffs will be locked for reading. Otherwise it is assumed that they are
10224 * already locked for writing (when the VM was started). Note that in the latter
10225 * case it is responsibility of the caller to lock the newly created diffs for
10226 * writing if this method succeeds.
10227 *
10228 * @param aProgress Progress object to run (must contain at least as
10229 * many operations left as the number of hard disks
10230 * attached).
10231 * @param aOnline Whether the VM was online prior to this operation.
10232 *
10233 * @note The progress object is not marked as completed, neither on success nor
10234 * on failure. This is a responsibility of the caller.
10235 *
10236 * @note Locks this object and the media tree for writing.
10237 */
10238HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10239 ULONG aWeight,
10240 bool aOnline)
10241{
10242 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10243
10244 AutoCaller autoCaller(this);
10245 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10246
10247 AutoMultiWriteLock2 alock(this->lockHandle(),
10248 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10249
10250 /* must be in a protective state because we release the lock below */
10251 AssertReturn( mData->mMachineState == MachineState_Saving
10252 || mData->mMachineState == MachineState_LiveSnapshotting
10253 || mData->mMachineState == MachineState_RestoringSnapshot
10254 || mData->mMachineState == MachineState_DeletingSnapshot
10255 , E_FAIL);
10256
10257 HRESULT rc = S_OK;
10258
10259 // use appropriate locked media map (online or offline)
10260 MediumLockListMap lockedMediaOffline;
10261 MediumLockListMap *lockedMediaMap;
10262 if (aOnline)
10263 lockedMediaMap = &mData->mSession.mLockedMedia;
10264 else
10265 lockedMediaMap = &lockedMediaOffline;
10266
10267 try
10268 {
10269 if (!aOnline)
10270 {
10271 /* lock all attached hard disks early to detect "in use"
10272 * situations before creating actual diffs */
10273 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10274 it != mMediaData->mAttachments.end();
10275 ++it)
10276 {
10277 MediumAttachment* pAtt = *it;
10278 if (pAtt->i_getType() == DeviceType_HardDisk)
10279 {
10280 Medium* pMedium = pAtt->i_getMedium();
10281 Assert(pMedium);
10282
10283 MediumLockList *pMediumLockList(new MediumLockList());
10284 alock.release();
10285 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10286 false /* fMediumLockWrite */,
10287 NULL,
10288 *pMediumLockList);
10289 alock.acquire();
10290 if (FAILED(rc))
10291 {
10292 delete pMediumLockList;
10293 throw rc;
10294 }
10295 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10296 if (FAILED(rc))
10297 {
10298 throw setError(rc,
10299 tr("Collecting locking information for all attached media failed"));
10300 }
10301 }
10302 }
10303
10304 /* Now lock all media. If this fails, nothing is locked. */
10305 alock.release();
10306 rc = lockedMediaMap->Lock();
10307 alock.acquire();
10308 if (FAILED(rc))
10309 {
10310 throw setError(rc,
10311 tr("Locking of attached media failed"));
10312 }
10313 }
10314
10315 /* remember the current list (note that we don't use backup() since
10316 * mMediaData may be already backed up) */
10317 MediaData::AttachmentList atts = mMediaData->mAttachments;
10318
10319 /* start from scratch */
10320 mMediaData->mAttachments.clear();
10321
10322 /* go through remembered attachments and create diffs for normal hard
10323 * disks and attach them */
10324 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10325 it != atts.end();
10326 ++it)
10327 {
10328 MediumAttachment* pAtt = *it;
10329
10330 DeviceType_T devType = pAtt->i_getType();
10331 Medium* pMedium = pAtt->i_getMedium();
10332
10333 if ( devType != DeviceType_HardDisk
10334 || pMedium == NULL
10335 || pMedium->i_getType() != MediumType_Normal)
10336 {
10337 /* copy the attachment as is */
10338
10339 /** @todo the progress object created in Console::TakeSnaphot
10340 * only expects operations for hard disks. Later other
10341 * device types need to show up in the progress as well. */
10342 if (devType == DeviceType_HardDisk)
10343 {
10344 if (pMedium == NULL)
10345 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10346 aWeight); // weight
10347 else
10348 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10349 pMedium->i_getBase()->i_getName().c_str()).raw(),
10350 aWeight); // weight
10351 }
10352
10353 mMediaData->mAttachments.push_back(pAtt);
10354 continue;
10355 }
10356
10357 /* need a diff */
10358 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10359 pMedium->i_getBase()->i_getName().c_str()).raw(),
10360 aWeight); // weight
10361
10362 Utf8Str strFullSnapshotFolder;
10363 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10364
10365 ComObjPtr<Medium> diff;
10366 diff.createObject();
10367 // store the diff in the same registry as the parent
10368 // (this cannot fail here because we can't create implicit diffs for
10369 // unregistered images)
10370 Guid uuidRegistryParent;
10371 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10372 Assert(fInRegistry); NOREF(fInRegistry);
10373 rc = diff->init(mParent,
10374 pMedium->i_getPreferredDiffFormat(),
10375 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10376 uuidRegistryParent);
10377 if (FAILED(rc)) throw rc;
10378
10379 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10380 * the push_back? Looks like we're going to release medium with the
10381 * wrong kind of lock (general issue with if we fail anywhere at all)
10382 * and an orphaned VDI in the snapshots folder. */
10383
10384 /* update the appropriate lock list */
10385 MediumLockList *pMediumLockList;
10386 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10387 AssertComRCThrowRC(rc);
10388 if (aOnline)
10389 {
10390 alock.release();
10391 /* The currently attached medium will be read-only, change
10392 * the lock type to read. */
10393 rc = pMediumLockList->Update(pMedium, false);
10394 alock.acquire();
10395 AssertComRCThrowRC(rc);
10396 }
10397
10398 /* release the locks before the potentially lengthy operation */
10399 alock.release();
10400 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10401 pMediumLockList,
10402 NULL /* aProgress */,
10403 true /* aWait */);
10404 alock.acquire();
10405 if (FAILED(rc)) throw rc;
10406
10407 /* actual lock list update is done in Medium::commitMedia */
10408
10409 rc = diff->i_addBackReference(mData->mUuid);
10410 AssertComRCThrowRC(rc);
10411
10412 /* add a new attachment */
10413 ComObjPtr<MediumAttachment> attachment;
10414 attachment.createObject();
10415 rc = attachment->init(this,
10416 diff,
10417 pAtt->i_getControllerName(),
10418 pAtt->i_getPort(),
10419 pAtt->i_getDevice(),
10420 DeviceType_HardDisk,
10421 true /* aImplicit */,
10422 false /* aPassthrough */,
10423 false /* aTempEject */,
10424 pAtt->i_getNonRotational(),
10425 pAtt->i_getDiscard(),
10426 pAtt->i_getHotPluggable(),
10427 pAtt->i_getBandwidthGroup());
10428 if (FAILED(rc)) throw rc;
10429
10430 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10431 AssertComRCThrowRC(rc);
10432 mMediaData->mAttachments.push_back(attachment);
10433 }
10434 }
10435 catch (HRESULT aRC) { rc = aRC; }
10436
10437 /* unlock all hard disks we locked when there is no VM */
10438 if (!aOnline)
10439 {
10440 ErrorInfoKeeper eik;
10441
10442 HRESULT rc1 = lockedMediaMap->Clear();
10443 AssertComRC(rc1);
10444 }
10445
10446 return rc;
10447}
10448
10449/**
10450 * Deletes implicit differencing hard disks created either by
10451 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10452 *
10453 * Note that to delete hard disks created by #AttachDevice() this method is
10454 * called from #fixupMedia() when the changes are rolled back.
10455 *
10456 * @note Locks this object and the media tree for writing.
10457 */
10458HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10459{
10460 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10461
10462 AutoCaller autoCaller(this);
10463 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10464
10465 AutoMultiWriteLock2 alock(this->lockHandle(),
10466 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10467
10468 /* We absolutely must have backed up state. */
10469 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10470
10471 /* Check if there are any implicitly created diff images. */
10472 bool fImplicitDiffs = false;
10473 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10474 it != mMediaData->mAttachments.end();
10475 ++it)
10476 {
10477 const ComObjPtr<MediumAttachment> &pAtt = *it;
10478 if (pAtt->i_isImplicit())
10479 {
10480 fImplicitDiffs = true;
10481 break;
10482 }
10483 }
10484 /* If there is nothing to do, leave early. This saves lots of image locking
10485 * effort. It also avoids a MachineStateChanged event without real reason.
10486 * This is important e.g. when loading a VM config, because there should be
10487 * no events. Otherwise API clients can become thoroughly confused for
10488 * inaccessible VMs (the code for loading VM configs uses this method for
10489 * cleanup if the config makes no sense), as they take such events as an
10490 * indication that the VM is alive, and they would force the VM config to
10491 * be reread, leading to an endless loop. */
10492 if (!fImplicitDiffs)
10493 return S_OK;
10494
10495 HRESULT rc = S_OK;
10496 MachineState_T oldState = mData->mMachineState;
10497
10498 /* will release the lock before the potentially lengthy operation,
10499 * so protect with the special state (unless already protected) */
10500 if ( oldState != MachineState_Saving
10501 && oldState != MachineState_LiveSnapshotting
10502 && oldState != MachineState_RestoringSnapshot
10503 && oldState != MachineState_DeletingSnapshot
10504 && oldState != MachineState_DeletingSnapshotOnline
10505 && oldState != MachineState_DeletingSnapshotPaused
10506 )
10507 i_setMachineState(MachineState_SettingUp);
10508
10509 // use appropriate locked media map (online or offline)
10510 MediumLockListMap lockedMediaOffline;
10511 MediumLockListMap *lockedMediaMap;
10512 if (aOnline)
10513 lockedMediaMap = &mData->mSession.mLockedMedia;
10514 else
10515 lockedMediaMap = &lockedMediaOffline;
10516
10517 try
10518 {
10519 if (!aOnline)
10520 {
10521 /* lock all attached hard disks early to detect "in use"
10522 * situations before deleting actual diffs */
10523 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10524 it != mMediaData->mAttachments.end();
10525 ++it)
10526 {
10527 MediumAttachment* pAtt = *it;
10528 if (pAtt->i_getType() == DeviceType_HardDisk)
10529 {
10530 Medium* pMedium = pAtt->i_getMedium();
10531 Assert(pMedium);
10532
10533 MediumLockList *pMediumLockList(new MediumLockList());
10534 alock.release();
10535 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10536 false /* fMediumLockWrite */,
10537 NULL,
10538 *pMediumLockList);
10539 alock.acquire();
10540
10541 if (FAILED(rc))
10542 {
10543 delete pMediumLockList;
10544 throw rc;
10545 }
10546
10547 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10548 if (FAILED(rc))
10549 throw rc;
10550 }
10551 }
10552
10553 if (FAILED(rc))
10554 throw rc;
10555 } // end of offline
10556
10557 /* Lock lists are now up to date and include implicitly created media */
10558
10559 /* Go through remembered attachments and delete all implicitly created
10560 * diffs and fix up the attachment information */
10561 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10562 MediaData::AttachmentList implicitAtts;
10563 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10564 it != mMediaData->mAttachments.end();
10565 ++it)
10566 {
10567 ComObjPtr<MediumAttachment> pAtt = *it;
10568 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10569 if (pMedium.isNull())
10570 continue;
10571
10572 // Implicit attachments go on the list for deletion and back references are removed.
10573 if (pAtt->i_isImplicit())
10574 {
10575 /* Deassociate and mark for deletion */
10576 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10577 rc = pMedium->i_removeBackReference(mData->mUuid);
10578 if (FAILED(rc))
10579 throw rc;
10580 implicitAtts.push_back(pAtt);
10581 continue;
10582 }
10583
10584 /* Was this medium attached before? */
10585 if (!i_findAttachment(oldAtts, pMedium))
10586 {
10587 /* no: de-associate */
10588 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10589 rc = pMedium->i_removeBackReference(mData->mUuid);
10590 if (FAILED(rc))
10591 throw rc;
10592 continue;
10593 }
10594 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10595 }
10596
10597 /* If there are implicit attachments to delete, throw away the lock
10598 * map contents (which will unlock all media) since the medium
10599 * attachments will be rolled back. Below we need to completely
10600 * recreate the lock map anyway since it is infinitely complex to
10601 * do this incrementally (would need reconstructing each attachment
10602 * change, which would be extremely hairy). */
10603 if (implicitAtts.size() != 0)
10604 {
10605 ErrorInfoKeeper eik;
10606
10607 HRESULT rc1 = lockedMediaMap->Clear();
10608 AssertComRC(rc1);
10609 }
10610
10611 /* rollback hard disk changes */
10612 mMediaData.rollback();
10613
10614 MultiResult mrc(S_OK);
10615
10616 // Delete unused implicit diffs.
10617 if (implicitAtts.size() != 0)
10618 {
10619 alock.release();
10620
10621 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10622 {
10623 // Remove medium associated with this attachment.
10624 ComObjPtr<MediumAttachment> pAtt = *it;
10625 Assert(pAtt);
10626 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10627 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10628 Assert(pMedium);
10629
10630 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10631 // continue on delete failure, just collect error messages
10632 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10633 pMedium->i_getLocationFull().c_str() ));
10634 mrc = rc;
10635 }
10636
10637 alock.acquire();
10638
10639 /* if there is a VM recreate media lock map as mentioned above,
10640 * otherwise it is a waste of time and we leave things unlocked */
10641 if (aOnline)
10642 {
10643 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10644 /* must never be NULL, but better safe than sorry */
10645 if (!pMachine.isNull())
10646 {
10647 alock.release();
10648 rc = mData->mSession.mMachine->lockMedia();
10649 alock.acquire();
10650 if (FAILED(rc))
10651 throw rc;
10652 }
10653 }
10654 }
10655 }
10656 catch (HRESULT aRC) {rc = aRC;}
10657
10658 if (mData->mMachineState == MachineState_SettingUp)
10659 i_setMachineState(oldState);
10660
10661 /* unlock all hard disks we locked when there is no VM */
10662 if (!aOnline)
10663 {
10664 ErrorInfoKeeper eik;
10665
10666 HRESULT rc1 = lockedMediaMap->Clear();
10667 AssertComRC(rc1);
10668 }
10669
10670 return rc;
10671}
10672
10673
10674/**
10675 * Looks through the given list of media attachments for one with the given parameters
10676 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10677 * can be searched as well if needed.
10678 *
10679 * @param list
10680 * @param aControllerName
10681 * @param aControllerPort
10682 * @param aDevice
10683 * @return
10684 */
10685MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10686 IN_BSTR aControllerName,
10687 LONG aControllerPort,
10688 LONG aDevice)
10689{
10690 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10691 {
10692 MediumAttachment *pAttach = *it;
10693 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10694 return pAttach;
10695 }
10696
10697 return NULL;
10698}
10699
10700/**
10701 * Looks through the given list of media attachments for one with the given parameters
10702 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10703 * can be searched as well if needed.
10704 *
10705 * @param list
10706 * @param aControllerName
10707 * @param aControllerPort
10708 * @param aDevice
10709 * @return
10710 */
10711MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10712 ComObjPtr<Medium> pMedium)
10713{
10714 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10715 {
10716 MediumAttachment *pAttach = *it;
10717 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10718 if (pMediumThis == pMedium)
10719 return pAttach;
10720 }
10721
10722 return NULL;
10723}
10724
10725/**
10726 * Looks through the given list of media attachments for one with the given parameters
10727 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10728 * can be searched as well if needed.
10729 *
10730 * @param list
10731 * @param aControllerName
10732 * @param aControllerPort
10733 * @param aDevice
10734 * @return
10735 */
10736MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10737 Guid &id)
10738{
10739 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10740 {
10741 MediumAttachment *pAttach = *it;
10742 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10743 if (pMediumThis->i_getId() == id)
10744 return pAttach;
10745 }
10746
10747 return NULL;
10748}
10749
10750/**
10751 * Main implementation for Machine::DetachDevice. This also gets called
10752 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10753 *
10754 * @param pAttach Medium attachment to detach.
10755 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10756 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10757 * SnapshotMachine, and this must be its snapshot.
10758 * @return
10759 */
10760HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10761 AutoWriteLock &writeLock,
10762 Snapshot *pSnapshot)
10763{
10764 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10765 DeviceType_T mediumType = pAttach->i_getType();
10766
10767 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10768
10769 if (pAttach->i_isImplicit())
10770 {
10771 /* attempt to implicitly delete the implicitly created diff */
10772
10773 /// @todo move the implicit flag from MediumAttachment to Medium
10774 /// and forbid any hard disk operation when it is implicit. Or maybe
10775 /// a special media state for it to make it even more simple.
10776
10777 Assert(mMediaData.isBackedUp());
10778
10779 /* will release the lock before the potentially lengthy operation, so
10780 * protect with the special state */
10781 MachineState_T oldState = mData->mMachineState;
10782 i_setMachineState(MachineState_SettingUp);
10783
10784 writeLock.release();
10785
10786 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
10787 true /*aWait*/);
10788
10789 writeLock.acquire();
10790
10791 i_setMachineState(oldState);
10792
10793 if (FAILED(rc)) return rc;
10794 }
10795
10796 i_setModified(IsModified_Storage);
10797 mMediaData.backup();
10798 mMediaData->mAttachments.remove(pAttach);
10799
10800 if (!oldmedium.isNull())
10801 {
10802 // if this is from a snapshot, do not defer detachment to commitMedia()
10803 if (pSnapshot)
10804 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
10805 // else if non-hard disk media, do not defer detachment to commitMedia() either
10806 else if (mediumType != DeviceType_HardDisk)
10807 oldmedium->i_removeBackReference(mData->mUuid);
10808 }
10809
10810 return S_OK;
10811}
10812
10813/**
10814 * Goes thru all media of the given list and
10815 *
10816 * 1) calls i_detachDevice() on each of them for this machine and
10817 * 2) adds all Medium objects found in the process to the given list,
10818 * depending on cleanupMode.
10819 *
10820 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10821 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10822 * media to the list.
10823 *
10824 * This gets called from Machine::Unregister, both for the actual Machine and
10825 * the SnapshotMachine objects that might be found in the snapshots.
10826 *
10827 * Requires caller and locking. The machine lock must be passed in because it
10828 * will be passed on to i_detachDevice which needs it for temporary unlocking.
10829 *
10830 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
10831 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10832 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
10833 * Full, then all media get added;
10834 * otherwise no media get added.
10835 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10836 * @return
10837 */
10838HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
10839 Snapshot *pSnapshot,
10840 CleanupMode_T cleanupMode,
10841 MediaList &llMedia)
10842{
10843 Assert(isWriteLockOnCurrentThread());
10844
10845 HRESULT rc;
10846
10847 // make a temporary list because i_detachDevice invalidates iterators into
10848 // mMediaData->mAttachments
10849 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10850
10851 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
10852 {
10853 ComObjPtr<MediumAttachment> &pAttach = *it;
10854 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
10855
10856 if (!pMedium.isNull())
10857 {
10858 AutoCaller mac(pMedium);
10859 if (FAILED(mac.rc())) return mac.rc();
10860 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10861 DeviceType_T devType = pMedium->i_getDeviceType();
10862 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10863 && devType == DeviceType_HardDisk)
10864 || (cleanupMode == CleanupMode_Full)
10865 )
10866 {
10867 llMedia.push_back(pMedium);
10868 ComObjPtr<Medium> pParent = pMedium->i_getParent();
10869 /*
10870 * Search for medias which are not attached to any machine, but
10871 * in the chain to an attached disk. Mediums are only consided
10872 * if they are:
10873 * - have only one child
10874 * - no references to any machines
10875 * - are of normal medium type
10876 */
10877 while (!pParent.isNull())
10878 {
10879 AutoCaller mac1(pParent);
10880 if (FAILED(mac1.rc())) return mac1.rc();
10881 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10882 if (pParent->i_getChildren().size() == 1)
10883 {
10884 if ( pParent->i_getMachineBackRefCount() == 0
10885 && pParent->i_getType() == MediumType_Normal
10886 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10887 llMedia.push_back(pParent);
10888 }
10889 else
10890 break;
10891 pParent = pParent->i_getParent();
10892 }
10893 }
10894 }
10895
10896 // real machine: then we need to use the proper method
10897 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
10898
10899 if (FAILED(rc))
10900 return rc;
10901 }
10902
10903 return S_OK;
10904}
10905
10906/**
10907 * Perform deferred hard disk detachments.
10908 *
10909 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10910 * backed up).
10911 *
10912 * If @a aOnline is @c true then this method will also unlock the old hard disks
10913 * for which the new implicit diffs were created and will lock these new diffs for
10914 * writing.
10915 *
10916 * @param aOnline Whether the VM was online prior to this operation.
10917 *
10918 * @note Locks this object for writing!
10919 */
10920void Machine::i_commitMedia(bool aOnline /*= false*/)
10921{
10922 AutoCaller autoCaller(this);
10923 AssertComRCReturnVoid(autoCaller.rc());
10924
10925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10926
10927 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10928
10929 HRESULT rc = S_OK;
10930
10931 /* no attach/detach operations -- nothing to do */
10932 if (!mMediaData.isBackedUp())
10933 return;
10934
10935 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10936 bool fMediaNeedsLocking = false;
10937
10938 /* enumerate new attachments */
10939 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10940 it != mMediaData->mAttachments.end();
10941 ++it)
10942 {
10943 MediumAttachment *pAttach = *it;
10944
10945 pAttach->i_commit();
10946
10947 Medium* pMedium = pAttach->i_getMedium();
10948 bool fImplicit = pAttach->i_isImplicit();
10949
10950 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10951 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
10952 fImplicit));
10953
10954 /** @todo convert all this Machine-based voodoo to MediumAttachment
10955 * based commit logic. */
10956 if (fImplicit)
10957 {
10958 /* convert implicit attachment to normal */
10959 pAttach->i_setImplicit(false);
10960
10961 if ( aOnline
10962 && pMedium
10963 && pAttach->i_getType() == DeviceType_HardDisk
10964 )
10965 {
10966 ComObjPtr<Medium> parent = pMedium->i_getParent();
10967 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10968
10969 /* update the appropriate lock list */
10970 MediumLockList *pMediumLockList;
10971 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10972 AssertComRC(rc);
10973 if (pMediumLockList)
10974 {
10975 /* unlock if there's a need to change the locking */
10976 if (!fMediaNeedsLocking)
10977 {
10978 rc = mData->mSession.mLockedMedia.Unlock();
10979 AssertComRC(rc);
10980 fMediaNeedsLocking = true;
10981 }
10982 rc = pMediumLockList->Update(parent, false);
10983 AssertComRC(rc);
10984 rc = pMediumLockList->Append(pMedium, true);
10985 AssertComRC(rc);
10986 }
10987 }
10988
10989 continue;
10990 }
10991
10992 if (pMedium)
10993 {
10994 /* was this medium attached before? */
10995 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
10996 {
10997 MediumAttachment *pOldAttach = *oldIt;
10998 if (pOldAttach->i_getMedium() == pMedium)
10999 {
11000 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11001
11002 /* yes: remove from old to avoid de-association */
11003 oldAtts.erase(oldIt);
11004 break;
11005 }
11006 }
11007 }
11008 }
11009
11010 /* enumerate remaining old attachments and de-associate from the
11011 * current machine state */
11012 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11013 {
11014 MediumAttachment *pAttach = *it;
11015 Medium* pMedium = pAttach->i_getMedium();
11016
11017 /* Detach only hard disks, since DVD/floppy media is detached
11018 * instantly in MountMedium. */
11019 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11020 {
11021 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11022
11023 /* now de-associate from the current machine state */
11024 rc = pMedium->i_removeBackReference(mData->mUuid);
11025 AssertComRC(rc);
11026
11027 if (aOnline)
11028 {
11029 /* unlock since medium is not used anymore */
11030 MediumLockList *pMediumLockList;
11031 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11032 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11033 {
11034 /* this happens for online snapshots, there the attachment
11035 * is changing, but only to a diff image created under
11036 * the old one, so there is no separate lock list */
11037 Assert(!pMediumLockList);
11038 }
11039 else
11040 {
11041 AssertComRC(rc);
11042 if (pMediumLockList)
11043 {
11044 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11045 AssertComRC(rc);
11046 }
11047 }
11048 }
11049 }
11050 }
11051
11052 /* take media locks again so that the locking state is consistent */
11053 if (fMediaNeedsLocking)
11054 {
11055 Assert(aOnline);
11056 rc = mData->mSession.mLockedMedia.Lock();
11057 AssertComRC(rc);
11058 }
11059
11060 /* commit the hard disk changes */
11061 mMediaData.commit();
11062
11063 if (i_isSessionMachine())
11064 {
11065 /*
11066 * Update the parent machine to point to the new owner.
11067 * This is necessary because the stored parent will point to the
11068 * session machine otherwise and cause crashes or errors later
11069 * when the session machine gets invalid.
11070 */
11071 /** @todo Change the MediumAttachment class to behave like any other
11072 * class in this regard by creating peer MediumAttachment
11073 * objects for session machines and share the data with the peer
11074 * machine.
11075 */
11076 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11077 it != mMediaData->mAttachments.end();
11078 ++it)
11079 (*it)->i_updateParentMachine(mPeer);
11080
11081 /* attach new data to the primary machine and reshare it */
11082 mPeer->mMediaData.attach(mMediaData);
11083 }
11084
11085 return;
11086}
11087
11088/**
11089 * Perform deferred deletion of implicitly created diffs.
11090 *
11091 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11092 * backed up).
11093 *
11094 * @note Locks this object for writing!
11095 */
11096void Machine::i_rollbackMedia()
11097{
11098 AutoCaller autoCaller(this);
11099 AssertComRCReturnVoid(autoCaller.rc());
11100
11101 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11102 LogFlowThisFunc(("Entering rollbackMedia\n"));
11103
11104 HRESULT rc = S_OK;
11105
11106 /* no attach/detach operations -- nothing to do */
11107 if (!mMediaData.isBackedUp())
11108 return;
11109
11110 /* enumerate new attachments */
11111 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11112 it != mMediaData->mAttachments.end();
11113 ++it)
11114 {
11115 MediumAttachment *pAttach = *it;
11116 /* Fix up the backrefs for DVD/floppy media. */
11117 if (pAttach->i_getType() != DeviceType_HardDisk)
11118 {
11119 Medium* pMedium = pAttach->i_getMedium();
11120 if (pMedium)
11121 {
11122 rc = pMedium->i_removeBackReference(mData->mUuid);
11123 AssertComRC(rc);
11124 }
11125 }
11126
11127 (*it)->i_rollback();
11128
11129 pAttach = *it;
11130 /* Fix up the backrefs for DVD/floppy media. */
11131 if (pAttach->i_getType() != DeviceType_HardDisk)
11132 {
11133 Medium* pMedium = pAttach->i_getMedium();
11134 if (pMedium)
11135 {
11136 rc = pMedium->i_addBackReference(mData->mUuid);
11137 AssertComRC(rc);
11138 }
11139 }
11140 }
11141
11142 /** @todo convert all this Machine-based voodoo to MediumAttachment
11143 * based rollback logic. */
11144 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11145
11146 return;
11147}
11148
11149/**
11150 * Returns true if the settings file is located in the directory named exactly
11151 * as the machine; this means, among other things, that the machine directory
11152 * should be auto-renamed.
11153 *
11154 * @param aSettingsDir if not NULL, the full machine settings file directory
11155 * name will be assigned there.
11156 *
11157 * @note Doesn't lock anything.
11158 * @note Not thread safe (must be called from this object's lock).
11159 */
11160bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11161{
11162 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11163 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11164 if (aSettingsDir)
11165 *aSettingsDir = strMachineDirName;
11166 strMachineDirName.stripPath(); // vmname
11167 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11168 strConfigFileOnly.stripPath() // vmname.vbox
11169 .stripSuffix(); // vmname
11170 /** @todo hack, make somehow use of ComposeMachineFilename */
11171 if (mUserData->s.fDirectoryIncludesUUID)
11172 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11173
11174 AssertReturn(!strMachineDirName.isEmpty(), false);
11175 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11176
11177 return strMachineDirName == strConfigFileOnly;
11178}
11179
11180/**
11181 * Discards all changes to machine settings.
11182 *
11183 * @param aNotify Whether to notify the direct session about changes or not.
11184 *
11185 * @note Locks objects for writing!
11186 */
11187void Machine::i_rollback(bool aNotify)
11188{
11189 AutoCaller autoCaller(this);
11190 AssertComRCReturn(autoCaller.rc(), (void)0);
11191
11192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11193
11194 if (!mStorageControllers.isNull())
11195 {
11196 if (mStorageControllers.isBackedUp())
11197 {
11198 /* unitialize all new devices (absent in the backed up list). */
11199 StorageControllerList::const_iterator it = mStorageControllers->begin();
11200 StorageControllerList *backedList = mStorageControllers.backedUpData();
11201 while (it != mStorageControllers->end())
11202 {
11203 if ( std::find(backedList->begin(), backedList->end(), *it)
11204 == backedList->end()
11205 )
11206 {
11207 (*it)->uninit();
11208 }
11209 ++it;
11210 }
11211
11212 /* restore the list */
11213 mStorageControllers.rollback();
11214 }
11215
11216 /* rollback any changes to devices after restoring the list */
11217 if (mData->flModifications & IsModified_Storage)
11218 {
11219 StorageControllerList::const_iterator it = mStorageControllers->begin();
11220 while (it != mStorageControllers->end())
11221 {
11222 (*it)->i_rollback();
11223 ++it;
11224 }
11225 }
11226 }
11227
11228 if (!mUSBControllers.isNull())
11229 {
11230 if (mUSBControllers.isBackedUp())
11231 {
11232 /* unitialize all new devices (absent in the backed up list). */
11233 USBControllerList::const_iterator it = mUSBControllers->begin();
11234 USBControllerList *backedList = mUSBControllers.backedUpData();
11235 while (it != mUSBControllers->end())
11236 {
11237 if ( std::find(backedList->begin(), backedList->end(), *it)
11238 == backedList->end()
11239 )
11240 {
11241 (*it)->uninit();
11242 }
11243 ++it;
11244 }
11245
11246 /* restore the list */
11247 mUSBControllers.rollback();
11248 }
11249
11250 /* rollback any changes to devices after restoring the list */
11251 if (mData->flModifications & IsModified_USB)
11252 {
11253 USBControllerList::const_iterator it = mUSBControllers->begin();
11254 while (it != mUSBControllers->end())
11255 {
11256 (*it)->i_rollback();
11257 ++it;
11258 }
11259 }
11260 }
11261
11262 mUserData.rollback();
11263
11264 mHWData.rollback();
11265
11266 if (mData->flModifications & IsModified_Storage)
11267 i_rollbackMedia();
11268
11269 if (mBIOSSettings)
11270 mBIOSSettings->i_rollback();
11271
11272 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11273 mVRDEServer->i_rollback();
11274
11275 if (mAudioAdapter)
11276 mAudioAdapter->i_rollback();
11277
11278 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11279 mUSBDeviceFilters->i_rollback();
11280
11281 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11282 mBandwidthControl->i_rollback();
11283
11284 if (!mHWData.isNull())
11285 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11286 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11287 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11288 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11289
11290 if (mData->flModifications & IsModified_NetworkAdapters)
11291 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11292 if ( mNetworkAdapters[slot]
11293 && mNetworkAdapters[slot]->i_isModified())
11294 {
11295 mNetworkAdapters[slot]->i_rollback();
11296 networkAdapters[slot] = mNetworkAdapters[slot];
11297 }
11298
11299 if (mData->flModifications & IsModified_SerialPorts)
11300 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11301 if ( mSerialPorts[slot]
11302 && mSerialPorts[slot]->i_isModified())
11303 {
11304 mSerialPorts[slot]->i_rollback();
11305 serialPorts[slot] = mSerialPorts[slot];
11306 }
11307
11308 if (mData->flModifications & IsModified_ParallelPorts)
11309 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11310 if ( mParallelPorts[slot]
11311 && mParallelPorts[slot]->i_isModified())
11312 {
11313 mParallelPorts[slot]->i_rollback();
11314 parallelPorts[slot] = mParallelPorts[slot];
11315 }
11316
11317 if (aNotify)
11318 {
11319 /* inform the direct session about changes */
11320
11321 ComObjPtr<Machine> that = this;
11322 uint32_t flModifications = mData->flModifications;
11323 alock.release();
11324
11325 if (flModifications & IsModified_SharedFolders)
11326 that->i_onSharedFolderChange();
11327
11328 if (flModifications & IsModified_VRDEServer)
11329 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11330 if (flModifications & IsModified_USB)
11331 that->i_onUSBControllerChange();
11332
11333 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11334 if (networkAdapters[slot])
11335 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11336 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11337 if (serialPorts[slot])
11338 that->i_onSerialPortChange(serialPorts[slot]);
11339 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11340 if (parallelPorts[slot])
11341 that->i_onParallelPortChange(parallelPorts[slot]);
11342
11343 if (flModifications & IsModified_Storage)
11344 that->i_onStorageControllerChange();
11345
11346#if 0
11347 if (flModifications & IsModified_BandwidthControl)
11348 that->onBandwidthControlChange();
11349#endif
11350 }
11351}
11352
11353/**
11354 * Commits all the changes to machine settings.
11355 *
11356 * Note that this operation is supposed to never fail.
11357 *
11358 * @note Locks this object and children for writing.
11359 */
11360void Machine::i_commit()
11361{
11362 AutoCaller autoCaller(this);
11363 AssertComRCReturnVoid(autoCaller.rc());
11364
11365 AutoCaller peerCaller(mPeer);
11366 AssertComRCReturnVoid(peerCaller.rc());
11367
11368 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11369
11370 /*
11371 * use safe commit to ensure Snapshot machines (that share mUserData)
11372 * will still refer to a valid memory location
11373 */
11374 mUserData.commitCopy();
11375
11376 mHWData.commit();
11377
11378 if (mMediaData.isBackedUp())
11379 i_commitMedia(Global::IsOnline(mData->mMachineState));
11380
11381 mBIOSSettings->i_commit();
11382 mVRDEServer->i_commit();
11383 mAudioAdapter->i_commit();
11384 mUSBDeviceFilters->i_commit();
11385 mBandwidthControl->i_commit();
11386
11387 /* Since mNetworkAdapters is a list which might have been changed (resized)
11388 * without using the Backupable<> template we need to handle the copying
11389 * of the list entries manually, including the creation of peers for the
11390 * new objects. */
11391 bool commitNetworkAdapters = false;
11392 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11393 if (mPeer)
11394 {
11395 /* commit everything, even the ones which will go away */
11396 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11397 mNetworkAdapters[slot]->i_commit();
11398 /* copy over the new entries, creating a peer and uninit the original */
11399 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11400 for (size_t slot = 0; slot < newSize; slot++)
11401 {
11402 /* look if this adapter has a peer device */
11403 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11404 if (!peer)
11405 {
11406 /* no peer means the adapter is a newly created one;
11407 * create a peer owning data this data share it with */
11408 peer.createObject();
11409 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11410 }
11411 mPeer->mNetworkAdapters[slot] = peer;
11412 }
11413 /* uninit any no longer needed network adapters */
11414 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11415 mNetworkAdapters[slot]->uninit();
11416 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11417 {
11418 if (mPeer->mNetworkAdapters[slot])
11419 mPeer->mNetworkAdapters[slot]->uninit();
11420 }
11421 /* Keep the original network adapter count until this point, so that
11422 * discarding a chipset type change will not lose settings. */
11423 mNetworkAdapters.resize(newSize);
11424 mPeer->mNetworkAdapters.resize(newSize);
11425 }
11426 else
11427 {
11428 /* we have no peer (our parent is the newly created machine);
11429 * just commit changes to the network adapters */
11430 commitNetworkAdapters = true;
11431 }
11432 if (commitNetworkAdapters)
11433 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11434 mNetworkAdapters[slot]->i_commit();
11435
11436 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11437 mSerialPorts[slot]->i_commit();
11438 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11439 mParallelPorts[slot]->i_commit();
11440
11441 bool commitStorageControllers = false;
11442
11443 if (mStorageControllers.isBackedUp())
11444 {
11445 mStorageControllers.commit();
11446
11447 if (mPeer)
11448 {
11449 /* Commit all changes to new controllers (this will reshare data with
11450 * peers for those who have peers) */
11451 StorageControllerList *newList = new StorageControllerList();
11452 StorageControllerList::const_iterator it = mStorageControllers->begin();
11453 while (it != mStorageControllers->end())
11454 {
11455 (*it)->i_commit();
11456
11457 /* look if this controller has a peer device */
11458 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11459 if (!peer)
11460 {
11461 /* no peer means the device is a newly created one;
11462 * create a peer owning data this device share it with */
11463 peer.createObject();
11464 peer->init(mPeer, *it, true /* aReshare */);
11465 }
11466 else
11467 {
11468 /* remove peer from the old list */
11469 mPeer->mStorageControllers->remove(peer);
11470 }
11471 /* and add it to the new list */
11472 newList->push_back(peer);
11473
11474 ++it;
11475 }
11476
11477 /* uninit old peer's controllers that are left */
11478 it = mPeer->mStorageControllers->begin();
11479 while (it != mPeer->mStorageControllers->end())
11480 {
11481 (*it)->uninit();
11482 ++it;
11483 }
11484
11485 /* attach new list of controllers to our peer */
11486 mPeer->mStorageControllers.attach(newList);
11487 }
11488 else
11489 {
11490 /* we have no peer (our parent is the newly created machine);
11491 * just commit changes to devices */
11492 commitStorageControllers = true;
11493 }
11494 }
11495 else
11496 {
11497 /* the list of controllers itself is not changed,
11498 * just commit changes to controllers themselves */
11499 commitStorageControllers = true;
11500 }
11501
11502 if (commitStorageControllers)
11503 {
11504 StorageControllerList::const_iterator it = mStorageControllers->begin();
11505 while (it != mStorageControllers->end())
11506 {
11507 (*it)->i_commit();
11508 ++it;
11509 }
11510 }
11511
11512 bool commitUSBControllers = false;
11513
11514 if (mUSBControllers.isBackedUp())
11515 {
11516 mUSBControllers.commit();
11517
11518 if (mPeer)
11519 {
11520 /* Commit all changes to new controllers (this will reshare data with
11521 * peers for those who have peers) */
11522 USBControllerList *newList = new USBControllerList();
11523 USBControllerList::const_iterator it = mUSBControllers->begin();
11524 while (it != mUSBControllers->end())
11525 {
11526 (*it)->i_commit();
11527
11528 /* look if this controller has a peer device */
11529 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11530 if (!peer)
11531 {
11532 /* no peer means the device is a newly created one;
11533 * create a peer owning data this device share it with */
11534 peer.createObject();
11535 peer->init(mPeer, *it, true /* aReshare */);
11536 }
11537 else
11538 {
11539 /* remove peer from the old list */
11540 mPeer->mUSBControllers->remove(peer);
11541 }
11542 /* and add it to the new list */
11543 newList->push_back(peer);
11544
11545 ++it;
11546 }
11547
11548 /* uninit old peer's controllers that are left */
11549 it = mPeer->mUSBControllers->begin();
11550 while (it != mPeer->mUSBControllers->end())
11551 {
11552 (*it)->uninit();
11553 ++it;
11554 }
11555
11556 /* attach new list of controllers to our peer */
11557 mPeer->mUSBControllers.attach(newList);
11558 }
11559 else
11560 {
11561 /* we have no peer (our parent is the newly created machine);
11562 * just commit changes to devices */
11563 commitUSBControllers = true;
11564 }
11565 }
11566 else
11567 {
11568 /* the list of controllers itself is not changed,
11569 * just commit changes to controllers themselves */
11570 commitUSBControllers = true;
11571 }
11572
11573 if (commitUSBControllers)
11574 {
11575 USBControllerList::const_iterator it = mUSBControllers->begin();
11576 while (it != mUSBControllers->end())
11577 {
11578 (*it)->i_commit();
11579 ++it;
11580 }
11581 }
11582
11583 if (i_isSessionMachine())
11584 {
11585 /* attach new data to the primary machine and reshare it */
11586 mPeer->mUserData.attach(mUserData);
11587 mPeer->mHWData.attach(mHWData);
11588 /* mMediaData is reshared by fixupMedia */
11589 // mPeer->mMediaData.attach(mMediaData);
11590 Assert(mPeer->mMediaData.data() == mMediaData.data());
11591 }
11592}
11593
11594/**
11595 * Copies all the hardware data from the given machine.
11596 *
11597 * Currently, only called when the VM is being restored from a snapshot. In
11598 * particular, this implies that the VM is not running during this method's
11599 * call.
11600 *
11601 * @note This method must be called from under this object's lock.
11602 *
11603 * @note This method doesn't call #commit(), so all data remains backed up and
11604 * unsaved.
11605 */
11606void Machine::i_copyFrom(Machine *aThat)
11607{
11608 AssertReturnVoid(!i_isSnapshotMachine());
11609 AssertReturnVoid(aThat->i_isSnapshotMachine());
11610
11611 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11612
11613 mHWData.assignCopy(aThat->mHWData);
11614
11615 // create copies of all shared folders (mHWData after attaching a copy
11616 // contains just references to original objects)
11617 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11618 it != mHWData->mSharedFolders.end();
11619 ++it)
11620 {
11621 ComObjPtr<SharedFolder> folder;
11622 folder.createObject();
11623 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11624 AssertComRC(rc);
11625 *it = folder;
11626 }
11627
11628 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11629 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11630 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11631 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11632 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11633
11634 /* create private copies of all controllers */
11635 mStorageControllers.backup();
11636 mStorageControllers->clear();
11637 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11638 it != aThat->mStorageControllers->end();
11639 ++it)
11640 {
11641 ComObjPtr<StorageController> ctrl;
11642 ctrl.createObject();
11643 ctrl->initCopy(this, *it);
11644 mStorageControllers->push_back(ctrl);
11645 }
11646
11647 /* create private copies of all USB controllers */
11648 mUSBControllers.backup();
11649 mUSBControllers->clear();
11650 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11651 it != aThat->mUSBControllers->end();
11652 ++it)
11653 {
11654 ComObjPtr<USBController> ctrl;
11655 ctrl.createObject();
11656 ctrl->initCopy(this, *it);
11657 mUSBControllers->push_back(ctrl);
11658 }
11659
11660 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11661 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11662 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11663 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11664 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11665 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11666 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11667}
11668
11669/**
11670 * Returns whether the given storage controller is hotplug capable.
11671 *
11672 * @returns true if the controller supports hotplugging
11673 * false otherwise.
11674 * @param enmCtrlType The controller type to check for.
11675 */
11676bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11677{
11678 ComPtr<ISystemProperties> systemProperties;
11679 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11680 if (FAILED(rc))
11681 return false;
11682
11683 BOOL aHotplugCapable = FALSE;
11684 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11685
11686 return RT_BOOL(aHotplugCapable);
11687}
11688
11689#ifdef VBOX_WITH_RESOURCE_USAGE_API
11690
11691void Machine::i_getDiskList(MediaList &list)
11692{
11693 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11694 it != mMediaData->mAttachments.end();
11695 ++it)
11696 {
11697 MediumAttachment* pAttach = *it;
11698 /* just in case */
11699 AssertStmt(pAttach, continue);
11700
11701 AutoCaller localAutoCallerA(pAttach);
11702 if (FAILED(localAutoCallerA.rc())) continue;
11703
11704 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11705
11706 if (pAttach->i_getType() == DeviceType_HardDisk)
11707 list.push_back(pAttach->i_getMedium());
11708 }
11709}
11710
11711void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11712{
11713 AssertReturnVoid(isWriteLockOnCurrentThread());
11714 AssertPtrReturnVoid(aCollector);
11715
11716 pm::CollectorHAL *hal = aCollector->getHAL();
11717 /* Create sub metrics */
11718 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11719 "Percentage of processor time spent in user mode by the VM process.");
11720 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11721 "Percentage of processor time spent in kernel mode by the VM process.");
11722 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11723 "Size of resident portion of VM process in memory.");
11724 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11725 "Actual size of all VM disks combined.");
11726 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11727 "Network receive rate.");
11728 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11729 "Network transmit rate.");
11730 /* Create and register base metrics */
11731 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11732 cpuLoadUser, cpuLoadKernel);
11733 aCollector->registerBaseMetric(cpuLoad);
11734 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11735 ramUsageUsed);
11736 aCollector->registerBaseMetric(ramUsage);
11737 MediaList disks;
11738 i_getDiskList(disks);
11739 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11740 diskUsageUsed);
11741 aCollector->registerBaseMetric(diskUsage);
11742
11743 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11744 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11745 new pm::AggregateAvg()));
11746 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11747 new pm::AggregateMin()));
11748 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11749 new pm::AggregateMax()));
11750 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11751 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11752 new pm::AggregateAvg()));
11753 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11754 new pm::AggregateMin()));
11755 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11756 new pm::AggregateMax()));
11757
11758 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11759 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11760 new pm::AggregateAvg()));
11761 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11762 new pm::AggregateMin()));
11763 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11764 new pm::AggregateMax()));
11765
11766 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11767 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11768 new pm::AggregateAvg()));
11769 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11770 new pm::AggregateMin()));
11771 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11772 new pm::AggregateMax()));
11773
11774
11775 /* Guest metrics collector */
11776 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11777 aCollector->registerGuest(mCollectorGuest);
11778 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11779 this, __PRETTY_FUNCTION__, mCollectorGuest));
11780
11781 /* Create sub metrics */
11782 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11783 "Percentage of processor time spent in user mode as seen by the guest.");
11784 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11785 "Percentage of processor time spent in kernel mode as seen by the guest.");
11786 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11787 "Percentage of processor time spent idling as seen by the guest.");
11788
11789 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11790 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11791 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11792 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11793 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11794 pm::SubMetric *guestMemCache = new pm::SubMetric(
11795 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11796
11797 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
11798 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11799
11800 /* Create and register base metrics */
11801 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11802 machineNetRx, machineNetTx);
11803 aCollector->registerBaseMetric(machineNetRate);
11804
11805 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11806 guestLoadUser, guestLoadKernel, guestLoadIdle);
11807 aCollector->registerBaseMetric(guestCpuLoad);
11808
11809 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11810 guestMemTotal, guestMemFree,
11811 guestMemBalloon, guestMemShared,
11812 guestMemCache, guestPagedTotal);
11813 aCollector->registerBaseMetric(guestCpuMem);
11814
11815 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11816 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11817 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11818 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11819
11820 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11821 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11822 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11823 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11824
11825 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11826 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11827 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11828 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11829
11830 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11831 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11832 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11833 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11834
11835 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11836 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11837 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11838 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11839
11840 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11841 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11842 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11843 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11844
11845 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11846 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11847 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11848 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11849
11850 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11851 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11852 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11853 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11854
11855 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11856 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11857 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11858 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11859
11860 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11861 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11862 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11863 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11864
11865 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11866 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11867 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11868 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11869}
11870
11871void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11872{
11873 AssertReturnVoid(isWriteLockOnCurrentThread());
11874
11875 if (aCollector)
11876 {
11877 aCollector->unregisterMetricsFor(aMachine);
11878 aCollector->unregisterBaseMetricsFor(aMachine);
11879 }
11880}
11881
11882#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11883
11884
11885////////////////////////////////////////////////////////////////////////////////
11886
11887DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11888
11889HRESULT SessionMachine::FinalConstruct()
11890{
11891 LogFlowThisFunc(("\n"));
11892
11893 mClientToken = NULL;
11894
11895 return BaseFinalConstruct();
11896}
11897
11898void SessionMachine::FinalRelease()
11899{
11900 LogFlowThisFunc(("\n"));
11901
11902 Assert(!mClientToken);
11903 /* paranoia, should not hang around any more */
11904 if (mClientToken)
11905 {
11906 delete mClientToken;
11907 mClientToken = NULL;
11908 }
11909
11910 uninit(Uninit::Unexpected);
11911
11912 BaseFinalRelease();
11913}
11914
11915/**
11916 * @note Must be called only by Machine::LockMachine() from its own write lock.
11917 */
11918HRESULT SessionMachine::init(Machine *aMachine)
11919{
11920 LogFlowThisFuncEnter();
11921 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11922
11923 AssertReturn(aMachine, E_INVALIDARG);
11924
11925 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11926
11927 /* Enclose the state transition NotReady->InInit->Ready */
11928 AutoInitSpan autoInitSpan(this);
11929 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11930
11931 HRESULT rc = S_OK;
11932
11933 /* create the machine client token */
11934 try
11935 {
11936 mClientToken = new ClientToken(aMachine, this);
11937 if (!mClientToken->isReady())
11938 {
11939 delete mClientToken;
11940 mClientToken = NULL;
11941 rc = E_FAIL;
11942 }
11943 }
11944 catch (std::bad_alloc &)
11945 {
11946 rc = E_OUTOFMEMORY;
11947 }
11948 if (FAILED(rc))
11949 return rc;
11950
11951 /* memorize the peer Machine */
11952 unconst(mPeer) = aMachine;
11953 /* share the parent pointer */
11954 unconst(mParent) = aMachine->mParent;
11955
11956 /* take the pointers to data to share */
11957 mData.share(aMachine->mData);
11958 mSSData.share(aMachine->mSSData);
11959
11960 mUserData.share(aMachine->mUserData);
11961 mHWData.share(aMachine->mHWData);
11962 mMediaData.share(aMachine->mMediaData);
11963
11964 mStorageControllers.allocate();
11965 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11966 it != aMachine->mStorageControllers->end();
11967 ++it)
11968 {
11969 ComObjPtr<StorageController> ctl;
11970 ctl.createObject();
11971 ctl->init(this, *it);
11972 mStorageControllers->push_back(ctl);
11973 }
11974
11975 mUSBControllers.allocate();
11976 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
11977 it != aMachine->mUSBControllers->end();
11978 ++it)
11979 {
11980 ComObjPtr<USBController> ctl;
11981 ctl.createObject();
11982 ctl->init(this, *it);
11983 mUSBControllers->push_back(ctl);
11984 }
11985
11986 unconst(mBIOSSettings).createObject();
11987 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11988 /* create another VRDEServer object that will be mutable */
11989 unconst(mVRDEServer).createObject();
11990 mVRDEServer->init(this, aMachine->mVRDEServer);
11991 /* create another audio adapter object that will be mutable */
11992 unconst(mAudioAdapter).createObject();
11993 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11994 /* create a list of serial ports that will be mutable */
11995 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11996 {
11997 unconst(mSerialPorts[slot]).createObject();
11998 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11999 }
12000 /* create a list of parallel ports that will be mutable */
12001 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12002 {
12003 unconst(mParallelPorts[slot]).createObject();
12004 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12005 }
12006
12007 /* create another USB device filters object that will be mutable */
12008 unconst(mUSBDeviceFilters).createObject();
12009 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12010
12011 /* create a list of network adapters that will be mutable */
12012 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12013 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12014 {
12015 unconst(mNetworkAdapters[slot]).createObject();
12016 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12017 }
12018
12019 /* create another bandwidth control object that will be mutable */
12020 unconst(mBandwidthControl).createObject();
12021 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12022
12023 /* default is to delete saved state on Saved -> PoweredOff transition */
12024 mRemoveSavedState = true;
12025
12026 /* Confirm a successful initialization when it's the case */
12027 autoInitSpan.setSucceeded();
12028
12029 miNATNetworksStarted = 0;
12030
12031 LogFlowThisFuncLeave();
12032 return rc;
12033}
12034
12035/**
12036 * Uninitializes this session object. If the reason is other than
12037 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12038 * or the client watcher code.
12039 *
12040 * @param aReason uninitialization reason
12041 *
12042 * @note Locks mParent + this object for writing.
12043 */
12044void SessionMachine::uninit(Uninit::Reason aReason)
12045{
12046 LogFlowThisFuncEnter();
12047 LogFlowThisFunc(("reason=%d\n", aReason));
12048
12049 /*
12050 * Strongly reference ourselves to prevent this object deletion after
12051 * mData->mSession.mMachine.setNull() below (which can release the last
12052 * reference and call the destructor). Important: this must be done before
12053 * accessing any members (and before AutoUninitSpan that does it as well).
12054 * This self reference will be released as the very last step on return.
12055 */
12056 ComObjPtr<SessionMachine> selfRef = this;
12057
12058 /* Enclose the state transition Ready->InUninit->NotReady */
12059 AutoUninitSpan autoUninitSpan(this);
12060 if (autoUninitSpan.uninitDone())
12061 {
12062 LogFlowThisFunc(("Already uninitialized\n"));
12063 LogFlowThisFuncLeave();
12064 return;
12065 }
12066
12067 if (autoUninitSpan.initFailed())
12068 {
12069 /* We've been called by init() because it's failed. It's not really
12070 * necessary (nor it's safe) to perform the regular uninit sequence
12071 * below, the following is enough.
12072 */
12073 LogFlowThisFunc(("Initialization failed.\n"));
12074 /* destroy the machine client token */
12075 if (mClientToken)
12076 {
12077 delete mClientToken;
12078 mClientToken = NULL;
12079 }
12080 uninitDataAndChildObjects();
12081 mData.free();
12082 unconst(mParent) = NULL;
12083 unconst(mPeer) = NULL;
12084 LogFlowThisFuncLeave();
12085 return;
12086 }
12087
12088 MachineState_T lastState;
12089 {
12090 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12091 lastState = mData->mMachineState;
12092 }
12093 NOREF(lastState);
12094
12095#ifdef VBOX_WITH_USB
12096 // release all captured USB devices, but do this before requesting the locks below
12097 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12098 {
12099 /* Console::captureUSBDevices() is called in the VM process only after
12100 * setting the machine state to Starting or Restoring.
12101 * Console::detachAllUSBDevices() will be called upon successful
12102 * termination. So, we need to release USB devices only if there was
12103 * an abnormal termination of a running VM.
12104 *
12105 * This is identical to SessionMachine::DetachAllUSBDevices except
12106 * for the aAbnormal argument. */
12107 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12108 AssertComRC(rc);
12109 NOREF(rc);
12110
12111 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12112 if (service)
12113 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12114 }
12115#endif /* VBOX_WITH_USB */
12116
12117 // we need to lock this object in uninit() because the lock is shared
12118 // with mPeer (as well as data we modify below). mParent lock is needed
12119 // by several calls to it, and USB needs host lock.
12120 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12121
12122#ifdef VBOX_WITH_RESOURCE_USAGE_API
12123 /*
12124 * It is safe to call Machine::i_unregisterMetrics() here because
12125 * PerformanceCollector::samplerCallback no longer accesses guest methods
12126 * holding the lock.
12127 */
12128 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12129 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12130 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12131 this, __PRETTY_FUNCTION__, mCollectorGuest));
12132 if (mCollectorGuest)
12133 {
12134 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12135 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12136 mCollectorGuest = NULL;
12137 }
12138#endif
12139
12140 if (aReason == Uninit::Abnormal)
12141 {
12142 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12143 Global::IsOnlineOrTransient(lastState)));
12144
12145 /* reset the state to Aborted */
12146 if (mData->mMachineState != MachineState_Aborted)
12147 i_setMachineState(MachineState_Aborted);
12148 }
12149
12150 // any machine settings modified?
12151 if (mData->flModifications)
12152 {
12153 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12154 i_rollback(false /* aNotify */);
12155 }
12156
12157 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12158 || !mConsoleTaskData.mSnapshot);
12159 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12160 {
12161 LogWarningThisFunc(("canceling failed save state request!\n"));
12162 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12163 }
12164 else if (!mConsoleTaskData.mSnapshot.isNull())
12165 {
12166 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12167
12168 /* delete all differencing hard disks created (this will also attach
12169 * their parents back by rolling back mMediaData) */
12170 i_rollbackMedia();
12171
12172 // delete the saved state file (it might have been already created)
12173 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12174 // think it's still in use
12175 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12176 mConsoleTaskData.mSnapshot->uninit();
12177 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12178 }
12179
12180 mData->mSession.mPID = NIL_RTPROCESS;
12181
12182 if (aReason == Uninit::Unexpected)
12183 {
12184 /* Uninitialization didn't come from #checkForDeath(), so tell the
12185 * client watcher thread to update the set of machines that have open
12186 * sessions. */
12187 mParent->i_updateClientWatcher();
12188 }
12189
12190 /* uninitialize all remote controls */
12191 if (mData->mSession.mRemoteControls.size())
12192 {
12193 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12194 mData->mSession.mRemoteControls.size()));
12195
12196 Data::Session::RemoteControlList::iterator it =
12197 mData->mSession.mRemoteControls.begin();
12198 while (it != mData->mSession.mRemoteControls.end())
12199 {
12200 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12201 HRESULT rc = (*it)->Uninitialize();
12202 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12203 if (FAILED(rc))
12204 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12205 ++it;
12206 }
12207 mData->mSession.mRemoteControls.clear();
12208 }
12209
12210 /* Remove all references to the NAT network service. The service will stop
12211 * if all references (also from other VMs) are removed. */
12212 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12213 {
12214 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12215 {
12216 NetworkAttachmentType_T type;
12217 HRESULT hrc;
12218
12219 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12220 if ( SUCCEEDED(hrc)
12221 && type == NetworkAttachmentType_NATNetwork)
12222 {
12223 Bstr name;
12224 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12225 if (SUCCEEDED(hrc))
12226 {
12227 multilock.release();
12228 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12229 mUserData->s.strName.c_str(), name.raw()));
12230 mParent->i_natNetworkRefDec(name.raw());
12231 multilock.acquire();
12232 }
12233 }
12234 }
12235 }
12236
12237 /*
12238 * An expected uninitialization can come only from #checkForDeath().
12239 * Otherwise it means that something's gone really wrong (for example,
12240 * the Session implementation has released the VirtualBox reference
12241 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12242 * etc). However, it's also possible, that the client releases the IPC
12243 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12244 * but the VirtualBox release event comes first to the server process.
12245 * This case is practically possible, so we should not assert on an
12246 * unexpected uninit, just log a warning.
12247 */
12248
12249 if ((aReason == Uninit::Unexpected))
12250 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12251
12252 if (aReason != Uninit::Normal)
12253 {
12254 mData->mSession.mDirectControl.setNull();
12255 }
12256 else
12257 {
12258 /* this must be null here (see #OnSessionEnd()) */
12259 Assert(mData->mSession.mDirectControl.isNull());
12260 Assert(mData->mSession.mState == SessionState_Unlocking);
12261 Assert(!mData->mSession.mProgress.isNull());
12262 }
12263 if (mData->mSession.mProgress)
12264 {
12265 if (aReason == Uninit::Normal)
12266 mData->mSession.mProgress->i_notifyComplete(S_OK);
12267 else
12268 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12269 COM_IIDOF(ISession),
12270 getComponentName(),
12271 tr("The VM session was aborted"));
12272 mData->mSession.mProgress.setNull();
12273 }
12274
12275 /* remove the association between the peer machine and this session machine */
12276 Assert( (SessionMachine*)mData->mSession.mMachine == this
12277 || aReason == Uninit::Unexpected);
12278
12279 /* reset the rest of session data */
12280 mData->mSession.mMachine.setNull();
12281 mData->mSession.mState = SessionState_Unlocked;
12282 mData->mSession.mType.setNull();
12283
12284 /* destroy the machine client token before leaving the exclusive lock */
12285 if (mClientToken)
12286 {
12287 delete mClientToken;
12288 mClientToken = NULL;
12289 }
12290
12291 /* fire an event */
12292 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12293
12294 uninitDataAndChildObjects();
12295
12296 /* free the essential data structure last */
12297 mData.free();
12298
12299 /* release the exclusive lock before setting the below two to NULL */
12300 multilock.release();
12301
12302 unconst(mParent) = NULL;
12303 unconst(mPeer) = NULL;
12304
12305 LogFlowThisFuncLeave();
12306}
12307
12308// util::Lockable interface
12309////////////////////////////////////////////////////////////////////////////////
12310
12311/**
12312 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12313 * with the primary Machine instance (mPeer).
12314 */
12315RWLockHandle *SessionMachine::lockHandle() const
12316{
12317 AssertReturn(mPeer != NULL, NULL);
12318 return mPeer->lockHandle();
12319}
12320
12321// IInternalMachineControl methods
12322////////////////////////////////////////////////////////////////////////////////
12323
12324/**
12325 * Passes collected guest statistics to performance collector object
12326 */
12327STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12328 ULONG aCpuKernel, ULONG aCpuIdle,
12329 ULONG aMemTotal, ULONG aMemFree,
12330 ULONG aMemBalloon, ULONG aMemShared,
12331 ULONG aMemCache, ULONG aPageTotal,
12332 ULONG aAllocVMM, ULONG aFreeVMM,
12333 ULONG aBalloonedVMM, ULONG aSharedVMM,
12334 ULONG aVmNetRx, ULONG aVmNetTx)
12335{
12336#ifdef VBOX_WITH_RESOURCE_USAGE_API
12337 if (mCollectorGuest)
12338 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12339 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12340 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12341 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12342
12343 return S_OK;
12344#else
12345 NOREF(aValidStats);
12346 NOREF(aCpuUser);
12347 NOREF(aCpuKernel);
12348 NOREF(aCpuIdle);
12349 NOREF(aMemTotal);
12350 NOREF(aMemFree);
12351 NOREF(aMemBalloon);
12352 NOREF(aMemShared);
12353 NOREF(aMemCache);
12354 NOREF(aPageTotal);
12355 NOREF(aAllocVMM);
12356 NOREF(aFreeVMM);
12357 NOREF(aBalloonedVMM);
12358 NOREF(aSharedVMM);
12359 NOREF(aVmNetRx);
12360 NOREF(aVmNetTx);
12361 return E_NOTIMPL;
12362#endif
12363}
12364
12365/**
12366 * @note Locks this object for writing.
12367 */
12368STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12369{
12370 AutoCaller autoCaller(this);
12371 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12372
12373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12374
12375 mRemoveSavedState = aRemove;
12376
12377 return S_OK;
12378}
12379
12380/**
12381 * @note Locks the same as #i_setMachineState() does.
12382 */
12383STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12384{
12385 return i_setMachineState(aMachineState);
12386}
12387
12388/**
12389 * @note Locks this object for writing.
12390 */
12391STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12392{
12393 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12394 AutoCaller autoCaller(this);
12395 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12396
12397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12398
12399 if (mData->mSession.mState != SessionState_Locked)
12400 return VBOX_E_INVALID_OBJECT_STATE;
12401
12402 if (!mData->mSession.mProgress.isNull())
12403 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12404
12405 /* If we didn't reference the NAT network service yet, add a reference to
12406 * force a start */
12407 if (miNATNetworksStarted < 1)
12408 {
12409 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12410 {
12411 NetworkAttachmentType_T type;
12412 HRESULT hrc;
12413 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12414 if ( SUCCEEDED(hrc)
12415 && type == NetworkAttachmentType_NATNetwork)
12416 {
12417 Bstr name;
12418 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12419 if (SUCCEEDED(hrc))
12420 {
12421 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12422 mUserData->s.strName.c_str(), name.raw()));
12423 mPeer->lockHandle()->unlockWrite();
12424 mParent->i_natNetworkRefInc(name.raw());
12425#ifdef RT_LOCK_STRICT
12426 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12427#else
12428 mPeer->lockHandle()->lockWrite();
12429#endif
12430 }
12431 }
12432 }
12433 miNATNetworksStarted++;
12434 }
12435
12436 LogFlowThisFunc(("returns S_OK.\n"));
12437 return S_OK;
12438}
12439
12440/**
12441 * @note Locks this object for writing.
12442 */
12443STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12444{
12445 AutoCaller autoCaller(this);
12446 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12447
12448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12449
12450 if (mData->mSession.mState != SessionState_Locked)
12451 return VBOX_E_INVALID_OBJECT_STATE;
12452
12453 /* Finalize the LaunchVMProcess progress object. */
12454 if (mData->mSession.mProgress)
12455 {
12456 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12457 mData->mSession.mProgress.setNull();
12458 }
12459
12460 if (SUCCEEDED((HRESULT)iResult))
12461 {
12462#ifdef VBOX_WITH_RESOURCE_USAGE_API
12463 /* The VM has been powered up successfully, so it makes sense
12464 * now to offer the performance metrics for a running machine
12465 * object. Doing it earlier wouldn't be safe. */
12466 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12467 mData->mSession.mPID);
12468#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12469 }
12470
12471 return S_OK;
12472}
12473
12474/**
12475 * @note Locks this object for writing.
12476 */
12477STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12478{
12479 LogFlowThisFuncEnter();
12480
12481 AutoCaller autoCaller(this);
12482 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12483
12484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12485
12486 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12487 E_FAIL);
12488
12489 /* create a progress object to track operation completion */
12490 ComObjPtr<Progress> pProgress;
12491 pProgress.createObject();
12492 pProgress->init(i_getVirtualBox(),
12493 static_cast<IMachine *>(this) /* aInitiator */,
12494 Bstr(tr("Stopping the virtual machine")).raw(),
12495 FALSE /* aCancelable */);
12496
12497 /* fill in the console task data */
12498 mConsoleTaskData.mLastState = mData->mMachineState;
12499 mConsoleTaskData.mProgress = pProgress;
12500
12501 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12502 i_setMachineState(MachineState_Stopping);
12503
12504 pProgress.queryInterfaceTo(aProgress);
12505
12506 return S_OK;
12507}
12508
12509/**
12510 * @note Locks this object for writing.
12511 */
12512STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12513{
12514 LogFlowThisFuncEnter();
12515
12516 AutoCaller autoCaller(this);
12517 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12518
12519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12520
12521 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12522 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12523 && mConsoleTaskData.mLastState != MachineState_Null,
12524 E_FAIL);
12525
12526 /*
12527 * On failure, set the state to the state we had when BeginPoweringDown()
12528 * was called (this is expected by Console::PowerDown() and the associated
12529 * task). On success the VM process already changed the state to
12530 * MachineState_PoweredOff, so no need to do anything.
12531 */
12532 if (FAILED(iResult))
12533 i_setMachineState(mConsoleTaskData.mLastState);
12534
12535 /* notify the progress object about operation completion */
12536 Assert(mConsoleTaskData.mProgress);
12537 if (SUCCEEDED(iResult))
12538 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12539 else
12540 {
12541 Utf8Str strErrMsg(aErrMsg);
12542 if (strErrMsg.length())
12543 mConsoleTaskData.mProgress->i_notifyComplete(iResult,
12544 COM_IIDOF(ISession),
12545 getComponentName(),
12546 strErrMsg.c_str());
12547 else
12548 mConsoleTaskData.mProgress->i_notifyComplete(iResult);
12549 }
12550
12551 /* clear out the temporary saved state data */
12552 mConsoleTaskData.mLastState = MachineState_Null;
12553 mConsoleTaskData.mProgress.setNull();
12554
12555 LogFlowThisFuncLeave();
12556 return S_OK;
12557}
12558
12559
12560/**
12561 * Goes through the USB filters of the given machine to see if the given
12562 * device matches any filter or not.
12563 *
12564 * @note Locks the same as USBController::hasMatchingFilter() does.
12565 */
12566STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12567 BOOL *aMatched,
12568 ULONG *aMaskedIfs)
12569{
12570 LogFlowThisFunc(("\n"));
12571
12572 AutoCaller autoCaller(this);
12573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12574
12575#ifdef VBOX_WITH_USB
12576 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
12577#else
12578 NOREF(aUSBDevice);
12579 NOREF(aMaskedIfs);
12580 *aMatched = FALSE;
12581#endif
12582
12583 return S_OK;
12584}
12585
12586/**
12587 * @note Locks the same as Host::captureUSBDevice() does.
12588 */
12589STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12590{
12591 LogFlowThisFunc(("\n"));
12592
12593 AutoCaller autoCaller(this);
12594 AssertComRCReturnRC(autoCaller.rc());
12595
12596#ifdef VBOX_WITH_USB
12597 /* if captureDeviceForVM() fails, it must have set extended error info */
12598 clearError();
12599 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12600 if (FAILED(rc)) return rc;
12601
12602 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12603 AssertReturn(service, E_FAIL);
12604 return service->captureDeviceForVM(this, Guid(aId).ref());
12605#else
12606 NOREF(aId);
12607 return E_NOTIMPL;
12608#endif
12609}
12610
12611/**
12612 * @note Locks the same as Host::detachUSBDevice() does.
12613 */
12614STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12615{
12616 LogFlowThisFunc(("\n"));
12617
12618 AutoCaller autoCaller(this);
12619 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12620
12621#ifdef VBOX_WITH_USB
12622 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12623 AssertReturn(service, E_FAIL);
12624 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12625#else
12626 NOREF(aId);
12627 NOREF(aDone);
12628 return E_NOTIMPL;
12629#endif
12630}
12631
12632/**
12633 * Inserts all machine filters to the USB proxy service and then calls
12634 * Host::autoCaptureUSBDevices().
12635 *
12636 * Called by Console from the VM process upon VM startup.
12637 *
12638 * @note Locks what called methods lock.
12639 */
12640STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12641{
12642 LogFlowThisFunc(("\n"));
12643
12644 AutoCaller autoCaller(this);
12645 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12646
12647#ifdef VBOX_WITH_USB
12648 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12649 AssertComRC(rc);
12650 NOREF(rc);
12651
12652 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12653 AssertReturn(service, E_FAIL);
12654 return service->autoCaptureDevicesForVM(this);
12655#else
12656 return S_OK;
12657#endif
12658}
12659
12660/**
12661 * Removes all machine filters from the USB proxy service and then calls
12662 * Host::detachAllUSBDevices().
12663 *
12664 * Called by Console from the VM process upon normal VM termination or by
12665 * SessionMachine::uninit() upon abnormal VM termination (from under the
12666 * Machine/SessionMachine lock).
12667 *
12668 * @note Locks what called methods lock.
12669 */
12670STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12671{
12672 LogFlowThisFunc(("\n"));
12673
12674 AutoCaller autoCaller(this);
12675 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12676
12677#ifdef VBOX_WITH_USB
12678 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12679 AssertComRC(rc);
12680 NOREF(rc);
12681
12682 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12683 AssertReturn(service, E_FAIL);
12684 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12685#else
12686 NOREF(aDone);
12687 return S_OK;
12688#endif
12689}
12690
12691/**
12692 * @note Locks this object for writing.
12693 */
12694STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12695 IProgress **aProgress)
12696{
12697 LogFlowThisFuncEnter();
12698
12699 AssertReturn(aSession, E_INVALIDARG);
12700 AssertReturn(aProgress, E_INVALIDARG);
12701
12702 AutoCaller autoCaller(this);
12703
12704 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12705 /*
12706 * We don't assert below because it might happen that a non-direct session
12707 * informs us it is closed right after we've been uninitialized -- it's ok.
12708 */
12709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12710
12711 /* get IInternalSessionControl interface */
12712 ComPtr<IInternalSessionControl> control(aSession);
12713
12714 ComAssertRet(!control.isNull(), E_INVALIDARG);
12715
12716 /* Creating a Progress object requires the VirtualBox lock, and
12717 * thus locking it here is required by the lock order rules. */
12718 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12719
12720 if (control == mData->mSession.mDirectControl)
12721 {
12722 ComAssertRet(aProgress, E_POINTER);
12723
12724 /* The direct session is being normally closed by the client process
12725 * ----------------------------------------------------------------- */
12726
12727 /* go to the closing state (essential for all open*Session() calls and
12728 * for #checkForDeath()) */
12729 Assert(mData->mSession.mState == SessionState_Locked);
12730 mData->mSession.mState = SessionState_Unlocking;
12731
12732 /* set direct control to NULL to release the remote instance */
12733 mData->mSession.mDirectControl.setNull();
12734 LogFlowThisFunc(("Direct control is set to NULL\n"));
12735
12736 if (mData->mSession.mProgress)
12737 {
12738 /* finalize the progress, someone might wait if a frontend
12739 * closes the session before powering on the VM. */
12740 mData->mSession.mProgress->notifyComplete(E_FAIL,
12741 COM_IIDOF(ISession),
12742 getComponentName(),
12743 tr("The VM session was closed before any attempt to power it on"));
12744 mData->mSession.mProgress.setNull();
12745 }
12746
12747 /* Create the progress object the client will use to wait until
12748 * #checkForDeath() is called to uninitialize this session object after
12749 * it releases the IPC semaphore.
12750 * Note! Because we're "reusing" mProgress here, this must be a proxy
12751 * object just like for LaunchVMProcess. */
12752 Assert(mData->mSession.mProgress.isNull());
12753 ComObjPtr<ProgressProxy> progress;
12754 progress.createObject();
12755 ComPtr<IUnknown> pPeer(mPeer);
12756 progress->init(mParent, pPeer,
12757 Bstr(tr("Closing session")).raw(),
12758 FALSE /* aCancelable */);
12759 progress.queryInterfaceTo(aProgress);
12760 mData->mSession.mProgress = progress;
12761 }
12762 else
12763 {
12764 /* the remote session is being normally closed */
12765 Data::Session::RemoteControlList::iterator it =
12766 mData->mSession.mRemoteControls.begin();
12767 while (it != mData->mSession.mRemoteControls.end())
12768 {
12769 if (control == *it)
12770 break;
12771 ++it;
12772 }
12773 BOOL found = it != mData->mSession.mRemoteControls.end();
12774 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12775 E_INVALIDARG);
12776 // This MUST be erase(it), not remove(*it) as the latter triggers a
12777 // very nasty use after free due to the place where the value "lives".
12778 mData->mSession.mRemoteControls.erase(it);
12779 }
12780
12781 /* signal the client watcher thread, because the client is going away */
12782 mParent->i_updateClientWatcher();
12783
12784 LogFlowThisFuncLeave();
12785 return S_OK;
12786}
12787
12788/**
12789 * @note Locks this object for writing.
12790 */
12791STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12792{
12793 LogFlowThisFuncEnter();
12794
12795 CheckComArgOutPointerValid(aProgress);
12796 CheckComArgOutPointerValid(aStateFilePath);
12797
12798 AutoCaller autoCaller(this);
12799 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12800
12801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12802
12803 AssertReturn( mData->mMachineState == MachineState_Paused
12804 && mConsoleTaskData.mLastState == MachineState_Null
12805 && mConsoleTaskData.strStateFilePath.isEmpty(),
12806 E_FAIL);
12807
12808 /* create a progress object to track operation completion */
12809 ComObjPtr<Progress> pProgress;
12810 pProgress.createObject();
12811 pProgress->init(i_getVirtualBox(),
12812 static_cast<IMachine *>(this) /* aInitiator */,
12813 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12814 FALSE /* aCancelable */);
12815
12816 Utf8Str strStateFilePath;
12817 /* stateFilePath is null when the machine is not running */
12818 if (mData->mMachineState == MachineState_Paused)
12819 i_composeSavedStateFilename(strStateFilePath);
12820
12821 /* fill in the console task data */
12822 mConsoleTaskData.mLastState = mData->mMachineState;
12823 mConsoleTaskData.strStateFilePath = strStateFilePath;
12824 mConsoleTaskData.mProgress = pProgress;
12825
12826 /* set the state to Saving (this is expected by Console::SaveState()) */
12827 i_setMachineState(MachineState_Saving);
12828
12829 strStateFilePath.cloneTo(aStateFilePath);
12830 pProgress.queryInterfaceTo(aProgress);
12831
12832 return S_OK;
12833}
12834
12835/**
12836 * @note Locks mParent + this object for writing.
12837 */
12838STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12839{
12840 LogFlowThisFunc(("\n"));
12841
12842 AutoCaller autoCaller(this);
12843 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12844
12845 /* endSavingState() need mParent lock */
12846 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12847
12848 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12849 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12850 && mConsoleTaskData.mLastState != MachineState_Null
12851 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12852 E_FAIL);
12853
12854 /*
12855 * On failure, set the state to the state we had when BeginSavingState()
12856 * was called (this is expected by Console::SaveState() and the associated
12857 * task). On success the VM process already changed the state to
12858 * MachineState_Saved, so no need to do anything.
12859 */
12860 if (FAILED(iResult))
12861 i_setMachineState(mConsoleTaskData.mLastState);
12862
12863 return endSavingState(iResult, aErrMsg);
12864}
12865
12866/**
12867 * @note Locks this object for writing.
12868 */
12869STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12870{
12871 LogFlowThisFunc(("\n"));
12872
12873 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12874
12875 AutoCaller autoCaller(this);
12876 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12877
12878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12879
12880 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12881 || mData->mMachineState == MachineState_Teleported
12882 || mData->mMachineState == MachineState_Aborted
12883 , E_FAIL); /** @todo setError. */
12884
12885 Utf8Str stateFilePathFull = aSavedStateFile;
12886 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
12887 if (RT_FAILURE(vrc))
12888 return setError(VBOX_E_FILE_ERROR,
12889 tr("Invalid saved state file path '%ls' (%Rrc)"),
12890 aSavedStateFile,
12891 vrc);
12892
12893 mSSData->strStateFilePath = stateFilePathFull;
12894
12895 /* The below i_setMachineState() will detect the state transition and will
12896 * update the settings file */
12897
12898 return i_setMachineState(MachineState_Saved);
12899}
12900
12901STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12902 ComSafeArrayOut(BSTR, aValues),
12903 ComSafeArrayOut(LONG64, aTimestamps),
12904 ComSafeArrayOut(BSTR, aFlags))
12905{
12906 LogFlowThisFunc(("\n"));
12907
12908#ifdef VBOX_WITH_GUEST_PROPS
12909 using namespace guestProp;
12910
12911 AutoCaller autoCaller(this);
12912 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12913
12914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12915
12916 CheckComArgOutSafeArrayPointerValid(aNames);
12917 CheckComArgOutSafeArrayPointerValid(aValues);
12918 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12919 CheckComArgOutSafeArrayPointerValid(aFlags);
12920
12921 size_t cEntries = mHWData->mGuestProperties.size();
12922 com::SafeArray<BSTR> names(cEntries);
12923 com::SafeArray<BSTR> values(cEntries);
12924 com::SafeArray<LONG64> timestamps(cEntries);
12925 com::SafeArray<BSTR> flags(cEntries);
12926 unsigned i = 0;
12927 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
12928 it != mHWData->mGuestProperties.end();
12929 ++it)
12930 {
12931 char szFlags[MAX_FLAGS_LEN + 1];
12932 it->first.cloneTo(&names[i]);
12933 it->second.strValue.cloneTo(&values[i]);
12934 timestamps[i] = it->second.mTimestamp;
12935 /* If it is NULL, keep it NULL. */
12936 if (it->second.mFlags)
12937 {
12938 writeFlags(it->second.mFlags, szFlags);
12939 Bstr(szFlags).cloneTo(&flags[i]);
12940 }
12941 else
12942 flags[i] = NULL;
12943 ++i;
12944 }
12945 names.detachTo(ComSafeArrayOutArg(aNames));
12946 values.detachTo(ComSafeArrayOutArg(aValues));
12947 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12948 flags.detachTo(ComSafeArrayOutArg(aFlags));
12949 return S_OK;
12950#else
12951 ReturnComNotImplemented();
12952#endif
12953}
12954
12955STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12956 IN_BSTR aValue,
12957 LONG64 aTimestamp,
12958 IN_BSTR aFlags)
12959{
12960 LogFlowThisFunc(("\n"));
12961
12962#ifdef VBOX_WITH_GUEST_PROPS
12963 using namespace guestProp;
12964
12965 CheckComArgStrNotEmptyOrNull(aName);
12966 CheckComArgNotNull(aValue);
12967 CheckComArgNotNull(aFlags);
12968
12969 try
12970 {
12971 /*
12972 * Convert input up front.
12973 */
12974 Utf8Str utf8Name(aName);
12975 uint32_t fFlags = NILFLAG;
12976 if (aFlags)
12977 {
12978 Utf8Str utf8Flags(aFlags);
12979 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12980 AssertRCReturn(vrc, E_INVALIDARG);
12981 }
12982
12983 /*
12984 * Now grab the object lock, validate the state and do the update.
12985 */
12986 AutoCaller autoCaller(this);
12987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12988
12989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12990
12991 switch (mData->mMachineState)
12992 {
12993 case MachineState_Paused:
12994 case MachineState_Running:
12995 case MachineState_Teleporting:
12996 case MachineState_TeleportingPausedVM:
12997 case MachineState_LiveSnapshotting:
12998 case MachineState_DeletingSnapshotOnline:
12999 case MachineState_DeletingSnapshotPaused:
13000 case MachineState_Saving:
13001 case MachineState_Stopping:
13002 break;
13003
13004 default:
13005 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13006 VBOX_E_INVALID_VM_STATE);
13007 }
13008
13009 i_setModified(IsModified_MachineData);
13010 mHWData.backup();
13011
13012 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13013 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13014 if (it != mHWData->mGuestProperties.end())
13015 {
13016 if (!fDelete)
13017 {
13018 it->second.strValue = aValue;
13019 it->second.mTimestamp = aTimestamp;
13020 it->second.mFlags = fFlags;
13021 }
13022 else
13023 mHWData->mGuestProperties.erase(it);
13024
13025 mData->mGuestPropertiesModified = TRUE;
13026 }
13027 else if (!fDelete)
13028 {
13029 HWData::GuestProperty prop;
13030 prop.strValue = aValue;
13031 prop.mTimestamp = aTimestamp;
13032 prop.mFlags = fFlags;
13033
13034 mHWData->mGuestProperties[utf8Name] = prop;
13035 mData->mGuestPropertiesModified = TRUE;
13036 }
13037
13038 /*
13039 * Send a callback notification if appropriate
13040 */
13041 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13042 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13043 RTSTR_MAX,
13044 utf8Name.c_str(),
13045 RTSTR_MAX, NULL)
13046 )
13047 {
13048 alock.release();
13049
13050 mParent->i_onGuestPropertyChange(mData->mUuid,
13051 aName,
13052 aValue,
13053 aFlags);
13054 }
13055 }
13056 catch (...)
13057 {
13058 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13059 }
13060 return S_OK;
13061#else
13062 ReturnComNotImplemented();
13063#endif
13064}
13065
13066STDMETHODIMP SessionMachine::LockMedia()
13067{
13068 AutoCaller autoCaller(this);
13069 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13070
13071 AutoMultiWriteLock2 alock(this->lockHandle(),
13072 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13073
13074 AssertReturn( mData->mMachineState == MachineState_Starting
13075 || mData->mMachineState == MachineState_Restoring
13076 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13077
13078 clearError();
13079 alock.release();
13080 return lockMedia();
13081}
13082
13083STDMETHODIMP SessionMachine::UnlockMedia()
13084{
13085 HRESULT hrc = unlockMedia();
13086 return hrc;
13087}
13088
13089STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13090 IMediumAttachment **aNewAttachment)
13091{
13092 CheckComArgNotNull(aAttachment);
13093 CheckComArgOutPointerValid(aNewAttachment);
13094
13095 AutoCaller autoCaller(this);
13096 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13097
13098 // request the host lock first, since might be calling Host methods for getting host drives;
13099 // next, protect the media tree all the while we're in here, as well as our member variables
13100 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13101 this->lockHandle(),
13102 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13103
13104 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13105
13106 Bstr ctrlName;
13107 LONG lPort;
13108 LONG lDevice;
13109 bool fTempEject;
13110 {
13111 AutoCaller autoAttachCaller(this);
13112 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13113
13114 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13115
13116 /* Need to query the details first, as the IMediumAttachment reference
13117 * might be to the original settings, which we are going to change. */
13118 ctrlName = pAttach->i_getControllerName();
13119 lPort = pAttach->i_getPort();
13120 lDevice = pAttach->i_getDevice();
13121 fTempEject = pAttach->i_getTempEject();
13122 }
13123
13124 if (!fTempEject)
13125 {
13126 /* Remember previously mounted medium. The medium before taking the
13127 * backup is not necessarily the same thing. */
13128 ComObjPtr<Medium> oldmedium;
13129 oldmedium = pAttach->i_getMedium();
13130
13131 i_setModified(IsModified_Storage);
13132 mMediaData.backup();
13133
13134 // The backup operation makes the pAttach reference point to the
13135 // old settings. Re-get the correct reference.
13136 pAttach = i_findAttachment(mMediaData->mAttachments,
13137 ctrlName.raw(),
13138 lPort,
13139 lDevice);
13140
13141 {
13142 AutoCaller autoAttachCaller(this);
13143 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13144
13145 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13146 if (!oldmedium.isNull())
13147 oldmedium->i_removeBackReference(mData->mUuid);
13148
13149 pAttach->i_updateMedium(NULL);
13150 pAttach->i_updateEjected();
13151 }
13152
13153 i_setModified(IsModified_Storage);
13154 }
13155 else
13156 {
13157 {
13158 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13159 pAttach->i_updateEjected();
13160 }
13161 }
13162
13163 pAttach.queryInterfaceTo(aNewAttachment);
13164
13165 return S_OK;
13166}
13167
13168// public methods only for internal purposes
13169/////////////////////////////////////////////////////////////////////////////
13170
13171#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13172/**
13173 * Called from the client watcher thread to check for expected or unexpected
13174 * death of the client process that has a direct session to this machine.
13175 *
13176 * On Win32 and on OS/2, this method is called only when we've got the
13177 * mutex (i.e. the client has either died or terminated normally) so it always
13178 * returns @c true (the client is terminated, the session machine is
13179 * uninitialized).
13180 *
13181 * On other platforms, the method returns @c true if the client process has
13182 * terminated normally or abnormally and the session machine was uninitialized,
13183 * and @c false if the client process is still alive.
13184 *
13185 * @note Locks this object for writing.
13186 */
13187bool SessionMachine::i_checkForDeath()
13188{
13189 Uninit::Reason reason;
13190 bool terminated = false;
13191
13192 /* Enclose autoCaller with a block because calling uninit() from under it
13193 * will deadlock. */
13194 {
13195 AutoCaller autoCaller(this);
13196 if (!autoCaller.isOk())
13197 {
13198 /* return true if not ready, to cause the client watcher to exclude
13199 * the corresponding session from watching */
13200 LogFlowThisFunc(("Already uninitialized!\n"));
13201 return true;
13202 }
13203
13204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13205
13206 /* Determine the reason of death: if the session state is Closing here,
13207 * everything is fine. Otherwise it means that the client did not call
13208 * OnSessionEnd() before it released the IPC semaphore. This may happen
13209 * either because the client process has abnormally terminated, or
13210 * because it simply forgot to call ISession::Close() before exiting. We
13211 * threat the latter also as an abnormal termination (see
13212 * Session::uninit() for details). */
13213 reason = mData->mSession.mState == SessionState_Unlocking ?
13214 Uninit::Normal :
13215 Uninit::Abnormal;
13216
13217 if (mClientToken)
13218 terminated = mClientToken->release();
13219 } /* AutoCaller block */
13220
13221 if (terminated)
13222 uninit(reason);
13223
13224 return terminated;
13225}
13226
13227void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13228{
13229 LogFlowThisFunc(("\n"));
13230
13231 strTokenId.setNull();
13232
13233 AutoCaller autoCaller(this);
13234 AssertComRCReturnVoid(autoCaller.rc());
13235
13236 Assert(mClientToken);
13237 if (mClientToken)
13238 mClientToken->getId(strTokenId);
13239}
13240#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13241IToken *SessionMachine::i_getToken()
13242{
13243 LogFlowThisFunc(("\n"));
13244
13245 AutoCaller autoCaller(this);
13246 AssertComRCReturn(autoCaller.rc(), NULL);
13247
13248 Assert(mClientToken);
13249 if (mClientToken)
13250 return mClientToken->getToken();
13251 else
13252 return NULL;
13253}
13254#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13255
13256Machine::ClientToken *SessionMachine::i_getClientToken()
13257{
13258 LogFlowThisFunc(("\n"));
13259
13260 AutoCaller autoCaller(this);
13261 AssertComRCReturn(autoCaller.rc(), NULL);
13262
13263 return mClientToken;
13264}
13265
13266
13267/**
13268 * @note Locks this object for reading.
13269 */
13270HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13271{
13272 LogFlowThisFunc(("\n"));
13273
13274 AutoCaller autoCaller(this);
13275 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13276
13277 ComPtr<IInternalSessionControl> directControl;
13278 {
13279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13280 directControl = mData->mSession.mDirectControl;
13281 }
13282
13283 /* ignore notifications sent after #OnSessionEnd() is called */
13284 if (!directControl)
13285 return S_OK;
13286
13287 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13288}
13289
13290/**
13291 * @note Locks this object for reading.
13292 */
13293HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13294 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13295 IN_BSTR aGuestIp, LONG aGuestPort)
13296{
13297 LogFlowThisFunc(("\n"));
13298
13299 AutoCaller autoCaller(this);
13300 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13301
13302 ComPtr<IInternalSessionControl> directControl;
13303 {
13304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13305 directControl = mData->mSession.mDirectControl;
13306 }
13307
13308 /* ignore notifications sent after #OnSessionEnd() is called */
13309 if (!directControl)
13310 return S_OK;
13311 /*
13312 * instead acting like callback we ask IVirtualBox deliver corresponding event
13313 */
13314
13315 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13316 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13317 return S_OK;
13318}
13319
13320/**
13321 * @note Locks this object for reading.
13322 */
13323HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13324{
13325 LogFlowThisFunc(("\n"));
13326
13327 AutoCaller autoCaller(this);
13328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13329
13330 ComPtr<IInternalSessionControl> directControl;
13331 {
13332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13333 directControl = mData->mSession.mDirectControl;
13334 }
13335
13336 /* ignore notifications sent after #OnSessionEnd() is called */
13337 if (!directControl)
13338 return S_OK;
13339
13340 return directControl->OnSerialPortChange(serialPort);
13341}
13342
13343/**
13344 * @note Locks this object for reading.
13345 */
13346HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13347{
13348 LogFlowThisFunc(("\n"));
13349
13350 AutoCaller autoCaller(this);
13351 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13352
13353 ComPtr<IInternalSessionControl> directControl;
13354 {
13355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13356 directControl = mData->mSession.mDirectControl;
13357 }
13358
13359 /* ignore notifications sent after #OnSessionEnd() is called */
13360 if (!directControl)
13361 return S_OK;
13362
13363 return directControl->OnParallelPortChange(parallelPort);
13364}
13365
13366/**
13367 * @note Locks this object for reading.
13368 */
13369HRESULT SessionMachine::i_onStorageControllerChange()
13370{
13371 LogFlowThisFunc(("\n"));
13372
13373 AutoCaller autoCaller(this);
13374 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13375
13376 ComPtr<IInternalSessionControl> directControl;
13377 {
13378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13379 directControl = mData->mSession.mDirectControl;
13380 }
13381
13382 /* ignore notifications sent after #OnSessionEnd() is called */
13383 if (!directControl)
13384 return S_OK;
13385
13386 return directControl->OnStorageControllerChange();
13387}
13388
13389/**
13390 * @note Locks this object for reading.
13391 */
13392HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13393{
13394 LogFlowThisFunc(("\n"));
13395
13396 AutoCaller autoCaller(this);
13397 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13398
13399 ComPtr<IInternalSessionControl> directControl;
13400 {
13401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13402 directControl = mData->mSession.mDirectControl;
13403 }
13404
13405 /* ignore notifications sent after #OnSessionEnd() is called */
13406 if (!directControl)
13407 return S_OK;
13408
13409 return directControl->OnMediumChange(aAttachment, aForce);
13410}
13411
13412/**
13413 * @note Locks this object for reading.
13414 */
13415HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13416{
13417 LogFlowThisFunc(("\n"));
13418
13419 AutoCaller autoCaller(this);
13420 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13421
13422 ComPtr<IInternalSessionControl> directControl;
13423 {
13424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13425 directControl = mData->mSession.mDirectControl;
13426 }
13427
13428 /* ignore notifications sent after #OnSessionEnd() is called */
13429 if (!directControl)
13430 return S_OK;
13431
13432 return directControl->OnCPUChange(aCPU, aRemove);
13433}
13434
13435HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13436{
13437 LogFlowThisFunc(("\n"));
13438
13439 AutoCaller autoCaller(this);
13440 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13441
13442 ComPtr<IInternalSessionControl> directControl;
13443 {
13444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13445 directControl = mData->mSession.mDirectControl;
13446 }
13447
13448 /* ignore notifications sent after #OnSessionEnd() is called */
13449 if (!directControl)
13450 return S_OK;
13451
13452 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13453}
13454
13455/**
13456 * @note Locks this object for reading.
13457 */
13458HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13459{
13460 LogFlowThisFunc(("\n"));
13461
13462 AutoCaller autoCaller(this);
13463 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13464
13465 ComPtr<IInternalSessionControl> directControl;
13466 {
13467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13468 directControl = mData->mSession.mDirectControl;
13469 }
13470
13471 /* ignore notifications sent after #OnSessionEnd() is called */
13472 if (!directControl)
13473 return S_OK;
13474
13475 return directControl->OnVRDEServerChange(aRestart);
13476}
13477
13478/**
13479 * @note Locks this object for reading.
13480 */
13481HRESULT SessionMachine::i_onVideoCaptureChange()
13482{
13483 LogFlowThisFunc(("\n"));
13484
13485 AutoCaller autoCaller(this);
13486 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13487
13488 ComPtr<IInternalSessionControl> directControl;
13489 {
13490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13491 directControl = mData->mSession.mDirectControl;
13492 }
13493
13494 /* ignore notifications sent after #OnSessionEnd() is called */
13495 if (!directControl)
13496 return S_OK;
13497
13498 return directControl->OnVideoCaptureChange();
13499}
13500
13501/**
13502 * @note Locks this object for reading.
13503 */
13504HRESULT SessionMachine::i_onUSBControllerChange()
13505{
13506 LogFlowThisFunc(("\n"));
13507
13508 AutoCaller autoCaller(this);
13509 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13510
13511 ComPtr<IInternalSessionControl> directControl;
13512 {
13513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13514 directControl = mData->mSession.mDirectControl;
13515 }
13516
13517 /* ignore notifications sent after #OnSessionEnd() is called */
13518 if (!directControl)
13519 return S_OK;
13520
13521 return directControl->OnUSBControllerChange();
13522}
13523
13524/**
13525 * @note Locks this object for reading.
13526 */
13527HRESULT SessionMachine::i_onSharedFolderChange()
13528{
13529 LogFlowThisFunc(("\n"));
13530
13531 AutoCaller autoCaller(this);
13532 AssertComRCReturnRC(autoCaller.rc());
13533
13534 ComPtr<IInternalSessionControl> directControl;
13535 {
13536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13537 directControl = mData->mSession.mDirectControl;
13538 }
13539
13540 /* ignore notifications sent after #OnSessionEnd() is called */
13541 if (!directControl)
13542 return S_OK;
13543
13544 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13545}
13546
13547/**
13548 * @note Locks this object for reading.
13549 */
13550HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13551{
13552 LogFlowThisFunc(("\n"));
13553
13554 AutoCaller autoCaller(this);
13555 AssertComRCReturnRC(autoCaller.rc());
13556
13557 ComPtr<IInternalSessionControl> directControl;
13558 {
13559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13560 directControl = mData->mSession.mDirectControl;
13561 }
13562
13563 /* ignore notifications sent after #OnSessionEnd() is called */
13564 if (!directControl)
13565 return S_OK;
13566
13567 return directControl->OnClipboardModeChange(aClipboardMode);
13568}
13569
13570/**
13571 * @note Locks this object for reading.
13572 */
13573HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13574{
13575 LogFlowThisFunc(("\n"));
13576
13577 AutoCaller autoCaller(this);
13578 AssertComRCReturnRC(autoCaller.rc());
13579
13580 ComPtr<IInternalSessionControl> directControl;
13581 {
13582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13583 directControl = mData->mSession.mDirectControl;
13584 }
13585
13586 /* ignore notifications sent after #OnSessionEnd() is called */
13587 if (!directControl)
13588 return S_OK;
13589
13590 return directControl->OnDnDModeChange(aDnDMode);
13591}
13592
13593/**
13594 * @note Locks this object for reading.
13595 */
13596HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13597{
13598 LogFlowThisFunc(("\n"));
13599
13600 AutoCaller autoCaller(this);
13601 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13602
13603 ComPtr<IInternalSessionControl> directControl;
13604 {
13605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13606 directControl = mData->mSession.mDirectControl;
13607 }
13608
13609 /* ignore notifications sent after #OnSessionEnd() is called */
13610 if (!directControl)
13611 return S_OK;
13612
13613 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13614}
13615
13616/**
13617 * @note Locks this object for reading.
13618 */
13619HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13620{
13621 LogFlowThisFunc(("\n"));
13622
13623 AutoCaller autoCaller(this);
13624 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13625
13626 ComPtr<IInternalSessionControl> directControl;
13627 {
13628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13629 directControl = mData->mSession.mDirectControl;
13630 }
13631
13632 /* ignore notifications sent after #OnSessionEnd() is called */
13633 if (!directControl)
13634 return S_OK;
13635
13636 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13637}
13638
13639/**
13640 * Returns @c true if this machine's USB controller reports it has a matching
13641 * filter for the given USB device and @c false otherwise.
13642 *
13643 * @note locks this object for reading.
13644 */
13645bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13646{
13647 AutoCaller autoCaller(this);
13648 /* silently return if not ready -- this method may be called after the
13649 * direct machine session has been called */
13650 if (!autoCaller.isOk())
13651 return false;
13652
13653#ifdef VBOX_WITH_USB
13654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13655
13656 switch (mData->mMachineState)
13657 {
13658 case MachineState_Starting:
13659 case MachineState_Restoring:
13660 case MachineState_TeleportingIn:
13661 case MachineState_Paused:
13662 case MachineState_Running:
13663 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13664 * elsewhere... */
13665 alock.release();
13666 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13667 default: break;
13668 }
13669#else
13670 NOREF(aDevice);
13671 NOREF(aMaskedIfs);
13672#endif
13673 return false;
13674}
13675
13676/**
13677 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13678 */
13679HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13680 IVirtualBoxErrorInfo *aError,
13681 ULONG aMaskedIfs)
13682{
13683 LogFlowThisFunc(("\n"));
13684
13685 AutoCaller autoCaller(this);
13686
13687 /* This notification may happen after the machine object has been
13688 * uninitialized (the session was closed), so don't assert. */
13689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13690
13691 ComPtr<IInternalSessionControl> directControl;
13692 {
13693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13694 directControl = mData->mSession.mDirectControl;
13695 }
13696
13697 /* fail on notifications sent after #OnSessionEnd() is called, it is
13698 * expected by the caller */
13699 if (!directControl)
13700 return E_FAIL;
13701
13702 /* No locks should be held at this point. */
13703 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13704 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13705
13706 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13707}
13708
13709/**
13710 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13711 */
13712HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13713 IVirtualBoxErrorInfo *aError)
13714{
13715 LogFlowThisFunc(("\n"));
13716
13717 AutoCaller autoCaller(this);
13718
13719 /* This notification may happen after the machine object has been
13720 * uninitialized (the session was closed), so don't assert. */
13721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13722
13723 ComPtr<IInternalSessionControl> directControl;
13724 {
13725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13726 directControl = mData->mSession.mDirectControl;
13727 }
13728
13729 /* fail on notifications sent after #OnSessionEnd() is called, it is
13730 * expected by the caller */
13731 if (!directControl)
13732 return E_FAIL;
13733
13734 /* No locks should be held at this point. */
13735 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13736 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13737
13738 return directControl->OnUSBDeviceDetach(aId, aError);
13739}
13740
13741// protected methods
13742/////////////////////////////////////////////////////////////////////////////
13743
13744/**
13745 * Helper method to finalize saving the state.
13746 *
13747 * @note Must be called from under this object's lock.
13748 *
13749 * @param aRc S_OK if the snapshot has been taken successfully
13750 * @param aErrMsg human readable error message for failure
13751 *
13752 * @note Locks mParent + this objects for writing.
13753 */
13754HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13755{
13756 LogFlowThisFuncEnter();
13757
13758 AutoCaller autoCaller(this);
13759 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13760
13761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13762
13763 HRESULT rc = S_OK;
13764
13765 if (SUCCEEDED(aRc))
13766 {
13767 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13768
13769 /* save all VM settings */
13770 rc = i_saveSettings(NULL);
13771 // no need to check whether VirtualBox.xml needs saving also since
13772 // we can't have a name change pending at this point
13773 }
13774 else
13775 {
13776 // delete the saved state file (it might have been already created);
13777 // we need not check whether this is shared with a snapshot here because
13778 // we certainly created this saved state file here anew
13779 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13780 }
13781
13782 /* notify the progress object about operation completion */
13783 Assert(mConsoleTaskData.mProgress);
13784 if (SUCCEEDED(aRc))
13785 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13786 else
13787 {
13788 if (aErrMsg.length())
13789 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13790 COM_IIDOF(ISession),
13791 getComponentName(),
13792 aErrMsg.c_str());
13793 else
13794 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13795 }
13796
13797 /* clear out the temporary saved state data */
13798 mConsoleTaskData.mLastState = MachineState_Null;
13799 mConsoleTaskData.strStateFilePath.setNull();
13800 mConsoleTaskData.mProgress.setNull();
13801
13802 LogFlowThisFuncLeave();
13803 return rc;
13804}
13805
13806/**
13807 * Deletes the given file if it is no longer in use by either the current machine state
13808 * (if the machine is "saved") or any of the machine's snapshots.
13809 *
13810 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13811 * but is different for each SnapshotMachine. When calling this, the order of calling this
13812 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13813 * is therefore critical. I know, it's all rather messy.
13814 *
13815 * @param strStateFile
13816 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13817 * the test for whether the saved state file is in use.
13818 */
13819void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13820 Snapshot *pSnapshotToIgnore)
13821{
13822 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13823 if ( (strStateFile.isNotEmpty())
13824 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13825 )
13826 // ... and it must also not be shared with other snapshots
13827 if ( !mData->mFirstSnapshot
13828 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13829 // this checks the SnapshotMachine's state file paths
13830 )
13831 RTFileDelete(strStateFile.c_str());
13832}
13833
13834/**
13835 * Locks the attached media.
13836 *
13837 * All attached hard disks are locked for writing and DVD/floppy are locked for
13838 * reading. Parents of attached hard disks (if any) are locked for reading.
13839 *
13840 * This method also performs accessibility check of all media it locks: if some
13841 * media is inaccessible, the method will return a failure and a bunch of
13842 * extended error info objects per each inaccessible medium.
13843 *
13844 * Note that this method is atomic: if it returns a success, all media are
13845 * locked as described above; on failure no media is locked at all (all
13846 * succeeded individual locks will be undone).
13847 *
13848 * The caller is responsible for doing the necessary state sanity checks.
13849 *
13850 * The locks made by this method must be undone by calling #unlockMedia() when
13851 * no more needed.
13852 */
13853HRESULT SessionMachine::lockMedia()
13854{
13855 AutoCaller autoCaller(this);
13856 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13857
13858 AutoMultiWriteLock2 alock(this->lockHandle(),
13859 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13860
13861 /* bail out if trying to lock things with already set up locking */
13862 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13863
13864 MultiResult mrc(S_OK);
13865
13866 /* Collect locking information for all medium objects attached to the VM. */
13867 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13868 it != mMediaData->mAttachments.end();
13869 ++it)
13870 {
13871 MediumAttachment* pAtt = *it;
13872 DeviceType_T devType = pAtt->i_getType();
13873 Medium *pMedium = pAtt->i_getMedium();
13874
13875 MediumLockList *pMediumLockList(new MediumLockList());
13876 // There can be attachments without a medium (floppy/dvd), and thus
13877 // it's impossible to create a medium lock list. It still makes sense
13878 // to have the empty medium lock list in the map in case a medium is
13879 // attached later.
13880 if (pMedium != NULL)
13881 {
13882 MediumType_T mediumType = pMedium->i_getType();
13883 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13884 || mediumType == MediumType_Shareable;
13885 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13886
13887 alock.release();
13888 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13889 !fIsReadOnlyLock /* fMediumLockWrite */,
13890 NULL,
13891 *pMediumLockList);
13892 alock.acquire();
13893 if (FAILED(mrc))
13894 {
13895 delete pMediumLockList;
13896 mData->mSession.mLockedMedia.Clear();
13897 break;
13898 }
13899 }
13900
13901 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13902 if (FAILED(rc))
13903 {
13904 mData->mSession.mLockedMedia.Clear();
13905 mrc = setError(rc,
13906 tr("Collecting locking information for all attached media failed"));
13907 break;
13908 }
13909 }
13910
13911 if (SUCCEEDED(mrc))
13912 {
13913 /* Now lock all media. If this fails, nothing is locked. */
13914 alock.release();
13915 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13916 alock.acquire();
13917 if (FAILED(rc))
13918 {
13919 mrc = setError(rc,
13920 tr("Locking of attached media failed"));
13921 }
13922 }
13923
13924 return mrc;
13925}
13926
13927/**
13928 * Undoes the locks made by by #lockMedia().
13929 */
13930HRESULT SessionMachine::unlockMedia()
13931{
13932 AutoCaller autoCaller(this);
13933 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
13934
13935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13936
13937 /* we may be holding important error info on the current thread;
13938 * preserve it */
13939 ErrorInfoKeeper eik;
13940
13941 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13942 AssertComRC(rc);
13943 return rc;
13944}
13945
13946/**
13947 * Helper to change the machine state (reimplementation).
13948 *
13949 * @note Locks this object for writing.
13950 * @note This method must not call i_saveSettings or SaveSettings, otherwise
13951 * it can cause crashes in random places due to unexpectedly committing
13952 * the current settings. The caller is responsible for that. The call
13953 * to saveStateSettings is fine, because this method does not commit.
13954 */
13955HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
13956{
13957 LogFlowThisFuncEnter();
13958 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13959
13960 AutoCaller autoCaller(this);
13961 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13962
13963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13964
13965 MachineState_T oldMachineState = mData->mMachineState;
13966
13967 AssertMsgReturn(oldMachineState != aMachineState,
13968 ("oldMachineState=%s, aMachineState=%s\n",
13969 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13970 E_FAIL);
13971
13972 HRESULT rc = S_OK;
13973
13974 int stsFlags = 0;
13975 bool deleteSavedState = false;
13976
13977 /* detect some state transitions */
13978
13979 if ( ( oldMachineState == MachineState_Saved
13980 && aMachineState == MachineState_Restoring)
13981 || ( ( oldMachineState == MachineState_PoweredOff
13982 || oldMachineState == MachineState_Teleported
13983 || oldMachineState == MachineState_Aborted
13984 )
13985 && ( aMachineState == MachineState_TeleportingIn
13986 || aMachineState == MachineState_Starting
13987 )
13988 )
13989 )
13990 {
13991 /* The EMT thread is about to start */
13992
13993 /* Nothing to do here for now... */
13994
13995 /// @todo NEWMEDIA don't let mDVDDrive and other children
13996 /// change anything when in the Starting/Restoring state
13997 }
13998 else if ( ( oldMachineState == MachineState_Running
13999 || oldMachineState == MachineState_Paused
14000 || oldMachineState == MachineState_Teleporting
14001 || oldMachineState == MachineState_LiveSnapshotting
14002 || oldMachineState == MachineState_Stuck
14003 || oldMachineState == MachineState_Starting
14004 || oldMachineState == MachineState_Stopping
14005 || oldMachineState == MachineState_Saving
14006 || oldMachineState == MachineState_Restoring
14007 || oldMachineState == MachineState_TeleportingPausedVM
14008 || oldMachineState == MachineState_TeleportingIn
14009 )
14010 && ( aMachineState == MachineState_PoweredOff
14011 || aMachineState == MachineState_Saved
14012 || aMachineState == MachineState_Teleported
14013 || aMachineState == MachineState_Aborted
14014 )
14015 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14016 * snapshot */
14017 && ( mConsoleTaskData.mSnapshot.isNull()
14018 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14019 )
14020 )
14021 {
14022 /* The EMT thread has just stopped, unlock attached media. Note that as
14023 * opposed to locking that is done from Console, we do unlocking here
14024 * because the VM process may have aborted before having a chance to
14025 * properly unlock all media it locked. */
14026
14027 unlockMedia();
14028 }
14029
14030 if (oldMachineState == MachineState_Restoring)
14031 {
14032 if (aMachineState != MachineState_Saved)
14033 {
14034 /*
14035 * delete the saved state file once the machine has finished
14036 * restoring from it (note that Console sets the state from
14037 * Restoring to Saved if the VM couldn't restore successfully,
14038 * to give the user an ability to fix an error and retry --
14039 * we keep the saved state file in this case)
14040 */
14041 deleteSavedState = true;
14042 }
14043 }
14044 else if ( oldMachineState == MachineState_Saved
14045 && ( aMachineState == MachineState_PoweredOff
14046 || aMachineState == MachineState_Aborted
14047 || aMachineState == MachineState_Teleported
14048 )
14049 )
14050 {
14051 /*
14052 * delete the saved state after Console::ForgetSavedState() is called
14053 * or if the VM process (owning a direct VM session) crashed while the
14054 * VM was Saved
14055 */
14056
14057 /// @todo (dmik)
14058 // Not sure that deleting the saved state file just because of the
14059 // client death before it attempted to restore the VM is a good
14060 // thing. But when it crashes we need to go to the Aborted state
14061 // which cannot have the saved state file associated... The only
14062 // way to fix this is to make the Aborted condition not a VM state
14063 // but a bool flag: i.e., when a crash occurs, set it to true and
14064 // change the state to PoweredOff or Saved depending on the
14065 // saved state presence.
14066
14067 deleteSavedState = true;
14068 mData->mCurrentStateModified = TRUE;
14069 stsFlags |= SaveSTS_CurStateModified;
14070 }
14071
14072 if ( aMachineState == MachineState_Starting
14073 || aMachineState == MachineState_Restoring
14074 || aMachineState == MachineState_TeleportingIn
14075 )
14076 {
14077 /* set the current state modified flag to indicate that the current
14078 * state is no more identical to the state in the
14079 * current snapshot */
14080 if (!mData->mCurrentSnapshot.isNull())
14081 {
14082 mData->mCurrentStateModified = TRUE;
14083 stsFlags |= SaveSTS_CurStateModified;
14084 }
14085 }
14086
14087 if (deleteSavedState)
14088 {
14089 if (mRemoveSavedState)
14090 {
14091 Assert(!mSSData->strStateFilePath.isEmpty());
14092
14093 // it is safe to delete the saved state file if ...
14094 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14095 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14096 // ... none of the snapshots share the saved state file
14097 )
14098 RTFileDelete(mSSData->strStateFilePath.c_str());
14099 }
14100
14101 mSSData->strStateFilePath.setNull();
14102 stsFlags |= SaveSTS_StateFilePath;
14103 }
14104
14105 /* redirect to the underlying peer machine */
14106 mPeer->i_setMachineState(aMachineState);
14107
14108 if ( aMachineState == MachineState_PoweredOff
14109 || aMachineState == MachineState_Teleported
14110 || aMachineState == MachineState_Aborted
14111 || aMachineState == MachineState_Saved)
14112 {
14113 /* the machine has stopped execution
14114 * (or the saved state file was adopted) */
14115 stsFlags |= SaveSTS_StateTimeStamp;
14116 }
14117
14118 if ( ( oldMachineState == MachineState_PoweredOff
14119 || oldMachineState == MachineState_Aborted
14120 || oldMachineState == MachineState_Teleported
14121 )
14122 && aMachineState == MachineState_Saved)
14123 {
14124 /* the saved state file was adopted */
14125 Assert(!mSSData->strStateFilePath.isEmpty());
14126 stsFlags |= SaveSTS_StateFilePath;
14127 }
14128
14129#ifdef VBOX_WITH_GUEST_PROPS
14130 if ( aMachineState == MachineState_PoweredOff
14131 || aMachineState == MachineState_Aborted
14132 || aMachineState == MachineState_Teleported)
14133 {
14134 /* Make sure any transient guest properties get removed from the
14135 * property store on shutdown. */
14136
14137 HWData::GuestPropertyMap::const_iterator it;
14138 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14139 if (!fNeedsSaving)
14140 for (it = mHWData->mGuestProperties.begin();
14141 it != mHWData->mGuestProperties.end(); ++it)
14142 if ( (it->second.mFlags & guestProp::TRANSIENT)
14143 || (it->second.mFlags & guestProp::TRANSRESET))
14144 {
14145 fNeedsSaving = true;
14146 break;
14147 }
14148 if (fNeedsSaving)
14149 {
14150 mData->mCurrentStateModified = TRUE;
14151 stsFlags |= SaveSTS_CurStateModified;
14152 }
14153 }
14154#endif
14155
14156 rc = i_saveStateSettings(stsFlags);
14157
14158 if ( ( oldMachineState != MachineState_PoweredOff
14159 && oldMachineState != MachineState_Aborted
14160 && oldMachineState != MachineState_Teleported
14161 )
14162 && ( aMachineState == MachineState_PoweredOff
14163 || aMachineState == MachineState_Aborted
14164 || aMachineState == MachineState_Teleported
14165 )
14166 )
14167 {
14168 /* we've been shut down for any reason */
14169 /* no special action so far */
14170 }
14171
14172 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14173 LogFlowThisFuncLeave();
14174 return rc;
14175}
14176
14177/**
14178 * Sends the current machine state value to the VM process.
14179 *
14180 * @note Locks this object for reading, then calls a client process.
14181 */
14182HRESULT SessionMachine::i_updateMachineStateOnClient()
14183{
14184 AutoCaller autoCaller(this);
14185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14186
14187 ComPtr<IInternalSessionControl> directControl;
14188 {
14189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14190 AssertReturn(!!mData, E_FAIL);
14191 directControl = mData->mSession.mDirectControl;
14192
14193 /* directControl may be already set to NULL here in #OnSessionEnd()
14194 * called too early by the direct session process while there is still
14195 * some operation (like deleting the snapshot) in progress. The client
14196 * process in this case is waiting inside Session::close() for the
14197 * "end session" process object to complete, while #uninit() called by
14198 * #checkForDeath() on the Watcher thread is waiting for the pending
14199 * operation to complete. For now, we accept this inconsistent behavior
14200 * and simply do nothing here. */
14201
14202 if (mData->mSession.mState == SessionState_Unlocking)
14203 return S_OK;
14204
14205 AssertReturn(!directControl.isNull(), E_FAIL);
14206 }
14207
14208 return directControl->UpdateMachineState(mData->mMachineState);
14209}
14210
14211HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14212{
14213 NOREF(aRemove);
14214 ReturnComNotImplemented();
14215}
14216
14217HRESULT Machine::updateState(MachineState_T aState)
14218{
14219 NOREF(aState);
14220 ReturnComNotImplemented();
14221}
14222
14223HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14224{
14225 NOREF(aProgress);
14226 ReturnComNotImplemented();
14227}
14228
14229HRESULT Machine::endPowerUp(LONG aResult)
14230{
14231 NOREF(aResult);
14232 ReturnComNotImplemented();
14233}
14234
14235HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14236{
14237 NOREF(aProgress);
14238 ReturnComNotImplemented();
14239}
14240
14241HRESULT Machine::endPoweringDown(LONG aResult,
14242 const com::Utf8Str &aErrMsg)
14243{
14244 NOREF(aResult);
14245 NOREF(aErrMsg);
14246 ReturnComNotImplemented();
14247}
14248
14249HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14250 BOOL *aMatched,
14251 ULONG *aMaskedInterfaces)
14252{
14253 NOREF(aDevice);
14254 NOREF(aMatched);
14255 NOREF(aMaskedInterfaces);
14256 ReturnComNotImplemented();
14257
14258}
14259
14260HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14261{
14262 NOREF(aId);
14263 ReturnComNotImplemented();
14264}
14265
14266HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14267 BOOL aDone)
14268{
14269 NOREF(aId);
14270 NOREF(aDone);
14271 ReturnComNotImplemented();
14272}
14273
14274HRESULT Machine::autoCaptureUSBDevices()
14275{
14276 ReturnComNotImplemented();
14277}
14278
14279HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14280{
14281 NOREF(aDone);
14282 ReturnComNotImplemented();
14283}
14284
14285HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14286 ComPtr<IProgress> &aProgress)
14287{
14288 NOREF(aSession);
14289 NOREF(aProgress);
14290 ReturnComNotImplemented();
14291}
14292
14293HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14294 com::Utf8Str &aStateFilePath)
14295{
14296 NOREF(aProgress);
14297 NOREF(aStateFilePath);
14298 ReturnComNotImplemented();
14299}
14300
14301HRESULT Machine::endSavingState(LONG aResult,
14302 const com::Utf8Str &aErrMsg)
14303{
14304 NOREF(aResult);
14305 NOREF(aErrMsg);
14306 ReturnComNotImplemented();
14307}
14308
14309HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14310{
14311 NOREF(aSavedStateFile);
14312 ReturnComNotImplemented();
14313}
14314
14315HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14316 const com::Utf8Str &aName,
14317 const com::Utf8Str &aDescription,
14318 const ComPtr<IProgress> &aConsoleProgress,
14319 BOOL aFTakingSnapshotOnline,
14320 com::Utf8Str &aStateFilePath)
14321{
14322 NOREF(aInitiator);
14323 NOREF(aName);
14324 NOREF(aDescription);
14325 NOREF(aConsoleProgress);
14326 NOREF(aFTakingSnapshotOnline);
14327 NOREF(aStateFilePath);
14328 ReturnComNotImplemented();
14329}
14330
14331HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14332{
14333 NOREF(aSuccess);
14334 ReturnComNotImplemented();
14335}
14336
14337HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14338 const com::Guid &aStartId,
14339 const com::Guid &aEndId,
14340 BOOL aDeleteAllChildren,
14341 MachineState_T *aMachineState,
14342 ComPtr<IProgress> &aProgress)
14343{
14344 NOREF(aInitiator);
14345 NOREF(aStartId);
14346 NOREF(aEndId);
14347 NOREF(aDeleteAllChildren);
14348 NOREF(aMachineState);
14349 NOREF(aProgress);
14350 ReturnComNotImplemented();
14351}
14352
14353HRESULT Machine::finishOnlineMergeMedium()
14354{
14355 ReturnComNotImplemented();
14356}
14357
14358HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14359 const ComPtr<ISnapshot> &aSnapshot,
14360 MachineState_T *aMachineState,
14361 ComPtr<IProgress> &aProgress)
14362{
14363 NOREF(aInitiator);
14364 NOREF(aSnapshot);
14365 NOREF(aMachineState);
14366 NOREF(aProgress);
14367 ReturnComNotImplemented();
14368}
14369
14370HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14371 std::vector<com::Utf8Str> &aValues,
14372 std::vector<LONG64> &aTimestamps,
14373 std::vector<com::Utf8Str> &aFlags)
14374{
14375 NOREF(aNames);
14376 NOREF(aValues);
14377 NOREF(aTimestamps);
14378 NOREF(aFlags);
14379 ReturnComNotImplemented();
14380}
14381
14382HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14383 const com::Utf8Str &aValue,
14384 LONG64 aTimestamp,
14385 const com::Utf8Str &aFlags)
14386{
14387 NOREF(aName);
14388 NOREF(aValue);
14389 NOREF(aTimestamp);
14390 NOREF(aFlags);
14391 ReturnComNotImplemented();
14392}
14393
14394HRESULT Machine::lockMedia()
14395{
14396 ReturnComNotImplemented();
14397}
14398
14399HRESULT Machine::unlockMedia()
14400{
14401 ReturnComNotImplemented();
14402}
14403
14404HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14405 ComPtr<IMediumAttachment> &aNewAttachment)
14406{
14407 NOREF(aAttachment);
14408 NOREF(aNewAttachment);
14409 ReturnComNotImplemented();
14410}
14411
14412HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14413 ULONG aCpuUser,
14414 ULONG aCpuKernel,
14415 ULONG aCpuIdle,
14416 ULONG aMemTotal,
14417 ULONG aMemFree,
14418 ULONG aMemBalloon,
14419 ULONG aMemShared,
14420 ULONG aMemCache,
14421 ULONG aPagedTotal,
14422 ULONG aMemAllocTotal,
14423 ULONG aMemFreeTotal,
14424 ULONG aMemBalloonTotal,
14425 ULONG aMemSharedTotal,
14426 ULONG aVmNetRx,
14427 ULONG aVmNetTx)
14428{
14429 NOREF(aValidStats);
14430 NOREF(aCpuUser);
14431 NOREF(aCpuKernel);
14432 NOREF(aCpuIdle);
14433 NOREF(aMemTotal);
14434 NOREF(aMemFree);
14435 NOREF(aMemBalloon);
14436 NOREF(aMemShared);
14437 NOREF(aMemCache);
14438 NOREF(aPagedTotal);
14439 NOREF(aMemAllocTotal);
14440 NOREF(aMemFreeTotal);
14441 NOREF(aMemBalloonTotal);
14442 NOREF(aMemSharedTotal);
14443 NOREF(aVmNetRx);
14444 NOREF(aVmNetTx);
14445 ReturnComNotImplemented();
14446}
Note: See TracBrowser for help on using the repository browser.

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