VirtualBox

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

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

SUP: child-process purification to avoid sysfer.dll.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 496.3 KB
Line 
1/* $Id: MachineImpl.cpp 52139 2014-07-22 20:19:29Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#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::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1243{
1244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1245
1246 *aParavirtProvider = mHWData->mParavirtProvider;
1247 switch (mHWData->mParavirtProvider)
1248 {
1249 case ParavirtProvider_None:
1250 case ParavirtProvider_HyperV:
1251 case ParavirtProvider_Minimal:
1252 break;
1253
1254 /* Resolve dynamic provider types to the effective types. */
1255 default:
1256 {
1257 ComPtr<IGuestOSType> ptrGuestOSType;
1258 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1259 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1260
1261 Bstr guestTypeFamilyId;
1262 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1263 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1264 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1265
1266 switch (mHWData->mParavirtProvider)
1267 {
1268 case ParavirtProvider_Legacy:
1269 {
1270 if (fOsXGuest)
1271 *aParavirtProvider = ParavirtProvider_Minimal;
1272 else
1273 *aParavirtProvider = ParavirtProvider_None;
1274 break;
1275 }
1276
1277 case ParavirtProvider_Default:
1278 {
1279 if (fOsXGuest)
1280 *aParavirtProvider = ParavirtProvider_Minimal;
1281#if 0 /* Activate this soon. */
1282 else if ( mUserData->s.strOsType == "Windows81"
1283 || mUserData->s.strOsType == "Windows81_64"
1284 || mUserData->s.strOsType == "Windows8"
1285 || mUserData->s.strOsType == "Windows8_64"
1286 || mUserData->s.strOsType == "Windows7"
1287 || mUserData->s.strOsType == "Windows7_64"
1288 || mUserData->s.strOsType == "WindowsVista"
1289 || mUserData->s.strOsType == "WindowsVista_64"
1290 || mUserData->s.strOsType == "Windows2012"
1291 || mUserData->s.strOsType == "Windows2012_64"
1292 || mUserData->s.strOsType == "Windows2008"
1293 || mUserData->s.strOsType == "Windows2008_64")
1294 {
1295 *aParavirtProvider = ParavirtProvider_HyperV;
1296 }
1297#endif
1298 else
1299 *aParavirtProvider = ParavirtProvider_None;
1300 break;
1301 }
1302 }
1303 break;
1304 }
1305 }
1306
1307 Assert( *aParavirtProvider == ParavirtProvider_None
1308 || *aParavirtProvider == ParavirtProvider_Minimal
1309 || *aParavirtProvider == ParavirtProvider_HyperV);
1310 return S_OK;
1311}
1312
1313HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1314{
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 aHardwareVersion = mHWData->mHWVersion;
1318
1319 return S_OK;
1320}
1321
1322HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1323{
1324 /* check known version */
1325 Utf8Str hwVersion = aHardwareVersion;
1326 if ( hwVersion.compare("1") != 0
1327 && hwVersion.compare("2") != 0)
1328 return setError(E_INVALIDARG,
1329 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1330
1331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 HRESULT rc = i_checkStateDependency(MutableStateDep);
1334 if (FAILED(rc)) return rc;
1335
1336 i_setModified(IsModified_MachineData);
1337 mHWData.backup();
1338 mHWData->mHWVersion = aHardwareVersion;
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346
1347 if (!mHWData->mHardwareUUID.isZero())
1348 aHardwareUUID = mHWData->mHardwareUUID;
1349 else
1350 aHardwareUUID = mData->mUuid;
1351
1352 return S_OK;
1353}
1354
1355HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1356{
1357 if (!aHardwareUUID.isValid())
1358 return E_INVALIDARG;
1359
1360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1361
1362 HRESULT rc = i_checkStateDependency(MutableStateDep);
1363 if (FAILED(rc)) return rc;
1364
1365 i_setModified(IsModified_MachineData);
1366 mHWData.backup();
1367 if (aHardwareUUID == mData->mUuid)
1368 mHWData->mHardwareUUID.clear();
1369 else
1370 mHWData->mHardwareUUID = aHardwareUUID;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1376{
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *aMemorySize = mHWData->mMemorySize;
1380
1381 return S_OK;
1382}
1383
1384HRESULT Machine::setMemorySize(ULONG aMemorySize)
1385{
1386 /* check RAM limits */
1387 if ( aMemorySize < MM_RAM_MIN_IN_MB
1388 || aMemorySize > MM_RAM_MAX_IN_MB
1389 )
1390 return setError(E_INVALIDARG,
1391 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1392 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mMemorySize = aMemorySize;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 *aCPUCount = mHWData->mCPUCount;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::setCPUCount(ULONG aCPUCount)
1416{
1417 /* check CPU limits */
1418 if ( aCPUCount < SchemaDefs::MinCPUCount
1419 || aCPUCount > SchemaDefs::MaxCPUCount
1420 )
1421 return setError(E_INVALIDARG,
1422 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1423 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1424
1425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1426
1427 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1428 if (mHWData->mCPUHotPlugEnabled)
1429 {
1430 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1431 {
1432 if (mHWData->mCPUAttached[idx])
1433 return setError(E_INVALIDARG,
1434 tr("There is still a CPU attached to socket %lu."
1435 "Detach the CPU before removing the socket"),
1436 aCPUCount, idx+1);
1437 }
1438 }
1439
1440 HRESULT rc = i_checkStateDependency(MutableStateDep);
1441 if (FAILED(rc)) return rc;
1442
1443 i_setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 mHWData->mCPUCount = aCPUCount;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1460{
1461 HRESULT rc = S_OK;
1462
1463 /* check throttle limits */
1464 if ( aCPUExecutionCap < 1
1465 || aCPUExecutionCap > 100
1466 )
1467 return setError(E_INVALIDARG,
1468 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1469 aCPUExecutionCap, 1, 100);
1470
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 alock.release();
1474 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1475 alock.acquire();
1476 if (FAILED(rc)) return rc;
1477
1478 i_setModified(IsModified_MachineData);
1479 mHWData.backup();
1480 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1481
1482 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1483 if (Global::IsOnline(mData->mMachineState))
1484 i_saveSettings(NULL);
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1490{
1491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1499{
1500 HRESULT rc = S_OK;
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 rc = i_checkStateDependency(MutableStateDep);
1505 if (FAILED(rc)) return rc;
1506
1507 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1508 {
1509 if (aCPUHotPlugEnabled)
1510 {
1511 i_setModified(IsModified_MachineData);
1512 mHWData.backup();
1513
1514 /* Add the amount of CPUs currently attached */
1515 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1516 mHWData->mCPUAttached[i] = true;
1517 }
1518 else
1519 {
1520 /*
1521 * We can disable hotplug only if the amount of maximum CPUs is equal
1522 * to the amount of attached CPUs
1523 */
1524 unsigned cCpusAttached = 0;
1525 unsigned iHighestId = 0;
1526
1527 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1528 {
1529 if (mHWData->mCPUAttached[i])
1530 {
1531 cCpusAttached++;
1532 iHighestId = i;
1533 }
1534 }
1535
1536 if ( (cCpusAttached != mHWData->mCPUCount)
1537 || (iHighestId >= mHWData->mCPUCount))
1538 return setError(E_INVALIDARG,
1539 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 }
1544 }
1545
1546 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1547
1548 return rc;
1549}
1550
1551HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1552{
1553#ifdef VBOX_WITH_USB_CARDREADER
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1557
1558 return S_OK;
1559#else
1560 NOREF(aEmulatedUSBCardReaderEnabled);
1561 return E_NOTIMPL;
1562#endif
1563}
1564
1565HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1566{
1567#ifdef VBOX_WITH_USB_CARDREADER
1568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 HRESULT rc = i_checkStateDependency(MutableStateDep);
1571 if (FAILED(rc)) return rc;
1572
1573 i_setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1576
1577 return S_OK;
1578#else
1579 NOREF(aEmulatedUSBCardReaderEnabled);
1580 return E_NOTIMPL;
1581#endif
1582}
1583
1584HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1585{
1586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 *aHPETEnabled = mHWData->mHPETEnabled;
1589
1590 return S_OK;
1591}
1592
1593HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1594{
1595 HRESULT rc = S_OK;
1596
1597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1598
1599 rc = i_checkStateDependency(MutableStateDep);
1600 if (FAILED(rc)) return rc;
1601
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604
1605 mHWData->mHPETEnabled = aHPETEnabled;
1606
1607 return rc;
1608}
1609
1610HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1611{
1612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1615 return S_OK;
1616}
1617
1618HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1619{
1620 HRESULT rc = S_OK;
1621
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 i_setModified(IsModified_MachineData);
1625 mHWData.backup();
1626 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1627
1628 alock.release();
1629 rc = i_onVideoCaptureChange();
1630 alock.acquire();
1631 if (FAILED(rc))
1632 {
1633 /*
1634 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1635 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1636 * determine if it should start or stop capturing. Therefore we need to manually
1637 * undo change.
1638 */
1639 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1640 return rc;
1641 }
1642
1643 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1644 if (Global::IsOnline(mData->mMachineState))
1645 i_saveSettings(NULL);
1646
1647 return rc;
1648}
1649
1650HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1651{
1652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1653 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1654 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1655 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1656 return S_OK;
1657}
1658
1659HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1660{
1661 SafeArray<BOOL> screens(aVideoCaptureScreens);
1662 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1663 bool fChanged = false;
1664
1665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1666
1667 for (unsigned i = 0; i < screens.size(); ++i)
1668 {
1669 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1670 {
1671 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1672 fChanged = true;
1673 }
1674 }
1675 if (fChanged)
1676 {
1677 alock.release();
1678 HRESULT rc = i_onVideoCaptureChange();
1679 alock.acquire();
1680 if (FAILED(rc)) return rc;
1681 i_setModified(IsModified_MachineData);
1682
1683 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1684 if (Global::IsOnline(mData->mMachineState))
1685 i_saveSettings(NULL);
1686 }
1687
1688 return S_OK;
1689}
1690
1691HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1692{
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694 if (mHWData->mVideoCaptureFile.isEmpty())
1695 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1696 else
1697 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1698 return S_OK;
1699}
1700
1701HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1702{
1703 Utf8Str strFile(aVideoCaptureFile);
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705
1706 if ( Global::IsOnline(mData->mMachineState)
1707 && mHWData->mVideoCaptureEnabled)
1708 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1709
1710 if (!RTPathStartsWithRoot(strFile.c_str()))
1711 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1712
1713 if (!strFile.isEmpty())
1714 {
1715 Utf8Str defaultFile;
1716 i_getDefaultVideoCaptureFile(defaultFile);
1717 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1718 strFile.setNull();
1719 }
1720
1721 i_setModified(IsModified_MachineData);
1722 mHWData.backup();
1723 mHWData->mVideoCaptureFile = strFile;
1724
1725 return S_OK;
1726}
1727
1728HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1729{
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1732 return S_OK;
1733}
1734
1735HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1736{
1737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 if ( Global::IsOnline(mData->mMachineState)
1740 && mHWData->mVideoCaptureEnabled)
1741 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1742
1743 i_setModified(IsModified_MachineData);
1744 mHWData.backup();
1745 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1746
1747 return S_OK;
1748}
1749
1750HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1751{
1752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1753 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1754 return S_OK;
1755}
1756
1757HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1758{
1759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1760
1761 if ( Global::IsOnline(mData->mMachineState)
1762 && mHWData->mVideoCaptureEnabled)
1763 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1764
1765 i_setModified(IsModified_MachineData);
1766 mHWData.backup();
1767 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1768
1769 return S_OK;
1770}
1771
1772HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1773{
1774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1775 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1776 return S_OK;
1777}
1778
1779HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1780{
1781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1782
1783 if ( Global::IsOnline(mData->mMachineState)
1784 && mHWData->mVideoCaptureEnabled)
1785 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1786
1787 i_setModified(IsModified_MachineData);
1788 mHWData.backup();
1789 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1790
1791 return S_OK;
1792}
1793
1794HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1795{
1796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1797 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1798 return S_OK;
1799}
1800
1801HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1802{
1803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1804
1805 if ( Global::IsOnline(mData->mMachineState)
1806 && mHWData->mVideoCaptureEnabled)
1807 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1808
1809 i_setModified(IsModified_MachineData);
1810 mHWData.backup();
1811 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1812
1813 return S_OK;
1814}
1815
1816HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1817{
1818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1821
1822 return S_OK;
1823}
1824
1825HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1826{
1827 switch (aGraphicsControllerType)
1828 {
1829 case GraphicsControllerType_Null:
1830 case GraphicsControllerType_VBoxVGA:
1831#ifdef VBOX_WITH_VMSVGA
1832 case GraphicsControllerType_VMSVGA:
1833#endif
1834 break;
1835 default:
1836 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1837 }
1838
1839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1840
1841 HRESULT rc = i_checkStateDependency(MutableStateDep);
1842 if (FAILED(rc)) return rc;
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854
1855 *aVRAMSize = mHWData->mVRAMSize;
1856
1857 return S_OK;
1858}
1859
1860HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1861{
1862 /* check VRAM limits */
1863 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1864 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1865 return setError(E_INVALIDARG,
1866 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1867 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1868
1869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 HRESULT rc = i_checkStateDependency(MutableStateDep);
1872 if (FAILED(rc)) return rc;
1873
1874 i_setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mVRAMSize = aVRAMSize;
1877
1878 return S_OK;
1879}
1880
1881/** @todo this method should not be public */
1882HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1887
1888 return S_OK;
1889}
1890
1891/**
1892 * Set the memory balloon size.
1893 *
1894 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1895 * we have to make sure that we never call IGuest from here.
1896 */
1897HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1898{
1899 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1900#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1901 /* check limits */
1902 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1903 return setError(E_INVALIDARG,
1904 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1905 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1906
1907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1912
1913 return S_OK;
1914#else
1915 NOREF(aMemoryBalloonSize);
1916 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1917#endif
1918}
1919
1920HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1921{
1922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1925 return S_OK;
1926}
1927
1928HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1929{
1930#ifdef VBOX_WITH_PAGE_SHARING
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1934 i_setModified(IsModified_MachineData);
1935 mHWData.backup();
1936 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1937 return S_OK;
1938#else
1939 NOREF(aPageFusionEnabled);
1940 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1941#endif
1942}
1943
1944HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1945{
1946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1947
1948 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1949
1950 return S_OK;
1951}
1952
1953HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1954{
1955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 HRESULT rc = i_checkStateDependency(MutableStateDep);
1958 if (FAILED(rc)) return rc;
1959
1960 /** @todo check validity! */
1961
1962 i_setModified(IsModified_MachineData);
1963 mHWData.backup();
1964 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1965
1966 return S_OK;
1967}
1968
1969
1970HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1971{
1972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1973
1974 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1975
1976 return S_OK;
1977}
1978
1979HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1980{
1981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1982
1983 HRESULT rc = i_checkStateDependency(MutableStateDep);
1984 if (FAILED(rc)) return rc;
1985
1986 /** @todo check validity! */
1987 i_setModified(IsModified_MachineData);
1988 mHWData.backup();
1989 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1990
1991 return S_OK;
1992}
1993
1994HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1995{
1996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1997
1998 *aMonitorCount = mHWData->mMonitorCount;
1999
2000 return S_OK;
2001}
2002
2003HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2004{
2005 /* make sure monitor count is a sensible number */
2006 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2007 return setError(E_INVALIDARG,
2008 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2009 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2010
2011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2012
2013 HRESULT rc = i_checkStateDependency(MutableStateDep);
2014 if (FAILED(rc)) return rc;
2015
2016 i_setModified(IsModified_MachineData);
2017 mHWData.backup();
2018 mHWData->mMonitorCount = aMonitorCount;
2019
2020 return S_OK;
2021}
2022
2023HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2024{
2025 /* mBIOSSettings is constant during life time, no need to lock */
2026 mBIOSSettings.queryInterfaceTo(aBIOSSettings.asOutParam());
2027
2028 return S_OK;
2029}
2030
2031HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2032{
2033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 switch (aProperty)
2036 {
2037 case CPUPropertyType_PAE:
2038 *aValue = mHWData->mPAEEnabled;
2039 break;
2040
2041 case CPUPropertyType_Synthetic:
2042 *aValue = mHWData->mSyntheticCpu;
2043 break;
2044
2045 case CPUPropertyType_LongMode:
2046 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2047 *aValue = TRUE;
2048 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2049 *aValue = FALSE;
2050#if HC_ARCH_BITS == 64
2051 else
2052 *aValue = TRUE;
2053#else
2054 else
2055 {
2056 *aValue = FALSE;
2057
2058 ComPtr<IGuestOSType> ptrGuestOSType;
2059 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2060 if (SUCCEEDED(hrc2))
2061 {
2062 BOOL fIs64Bit = FALSE;
2063 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2064 if (SUCCEEDED(hrc2) && fIs64Bit)
2065 {
2066 ComObjPtr<Host> ptrHost = mParent->i_host();
2067 alock.release();
2068
2069 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2070 if (FAILED(hrc2))
2071 *aValue = FALSE;
2072 }
2073 }
2074 }
2075#endif
2076 break;
2077
2078 case CPUPropertyType_TripleFaultReset:
2079 *aValue = mHWData->mTripleFaultReset;
2080 break;
2081
2082 default:
2083 return E_INVALIDARG;
2084 }
2085 return S_OK;
2086}
2087
2088HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2089{
2090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2091
2092 HRESULT rc = i_checkStateDependency(MutableStateDep);
2093 if (FAILED(rc)) return rc;
2094
2095 switch (aProperty)
2096 {
2097 case CPUPropertyType_PAE:
2098 i_setModified(IsModified_MachineData);
2099 mHWData.backup();
2100 mHWData->mPAEEnabled = !!aValue;
2101 break;
2102
2103 case CPUPropertyType_Synthetic:
2104 i_setModified(IsModified_MachineData);
2105 mHWData.backup();
2106 mHWData->mSyntheticCpu = !!aValue;
2107 break;
2108
2109 case CPUPropertyType_LongMode:
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2113 break;
2114
2115 case CPUPropertyType_TripleFaultReset:
2116 i_setModified(IsModified_MachineData);
2117 mHWData.backup();
2118 mHWData->mTripleFaultReset = !!aValue;
2119 break;
2120
2121 default:
2122 return E_INVALIDARG;
2123 }
2124 return S_OK;
2125}
2126
2127HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2128{
2129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2130
2131 switch(aId)
2132 {
2133 case 0x0:
2134 case 0x1:
2135 case 0x2:
2136 case 0x3:
2137 case 0x4:
2138 case 0x5:
2139 case 0x6:
2140 case 0x7:
2141 case 0x8:
2142 case 0x9:
2143 case 0xA:
2144 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2145 return E_INVALIDARG;
2146
2147 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2148 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2149 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2150 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2151 break;
2152
2153 case 0x80000000:
2154 case 0x80000001:
2155 case 0x80000002:
2156 case 0x80000003:
2157 case 0x80000004:
2158 case 0x80000005:
2159 case 0x80000006:
2160 case 0x80000007:
2161 case 0x80000008:
2162 case 0x80000009:
2163 case 0x8000000A:
2164 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2165 return E_INVALIDARG;
2166
2167 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2168 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2169 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2170 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2171 break;
2172
2173 default:
2174 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2175 }
2176 return S_OK;
2177}
2178
2179
2180HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2181{
2182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2183
2184 HRESULT rc = i_checkStateDependency(MutableStateDep);
2185 if (FAILED(rc)) return rc;
2186
2187 switch(aId)
2188 {
2189 case 0x0:
2190 case 0x1:
2191 case 0x2:
2192 case 0x3:
2193 case 0x4:
2194 case 0x5:
2195 case 0x6:
2196 case 0x7:
2197 case 0x8:
2198 case 0x9:
2199 case 0xA:
2200 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2201 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2202 i_setModified(IsModified_MachineData);
2203 mHWData.backup();
2204 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2205 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2206 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2207 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2208 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2209 break;
2210
2211 case 0x80000000:
2212 case 0x80000001:
2213 case 0x80000002:
2214 case 0x80000003:
2215 case 0x80000004:
2216 case 0x80000005:
2217 case 0x80000006:
2218 case 0x80000007:
2219 case 0x80000008:
2220 case 0x80000009:
2221 case 0x8000000A:
2222 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2223 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2224 i_setModified(IsModified_MachineData);
2225 mHWData.backup();
2226 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2227 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2228 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2229 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2230 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2231 break;
2232
2233 default:
2234 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2235 }
2236 return S_OK;
2237}
2238
2239HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2240{
2241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2242
2243 HRESULT rc = i_checkStateDependency(MutableStateDep);
2244 if (FAILED(rc)) return rc;
2245
2246 switch(aId)
2247 {
2248 case 0x0:
2249 case 0x1:
2250 case 0x2:
2251 case 0x3:
2252 case 0x4:
2253 case 0x5:
2254 case 0x6:
2255 case 0x7:
2256 case 0x8:
2257 case 0x9:
2258 case 0xA:
2259 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2260 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2261 i_setModified(IsModified_MachineData);
2262 mHWData.backup();
2263 /* Invalidate leaf. */
2264 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2265 break;
2266
2267 case 0x80000000:
2268 case 0x80000001:
2269 case 0x80000002:
2270 case 0x80000003:
2271 case 0x80000004:
2272 case 0x80000005:
2273 case 0x80000006:
2274 case 0x80000007:
2275 case 0x80000008:
2276 case 0x80000009:
2277 case 0x8000000A:
2278 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2279 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2280 i_setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 /* Invalidate leaf. */
2283 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2284 break;
2285
2286 default:
2287 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2288 }
2289 return S_OK;
2290}
2291
2292HRESULT Machine::removeAllCPUIDLeaves()
2293{
2294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 HRESULT rc = i_checkStateDependency(MutableStateDep);
2297 if (FAILED(rc)) return rc;
2298
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301
2302 /* Invalidate all standard leafs. */
2303 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2304 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2305
2306 /* Invalidate all extended leafs. */
2307 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2308 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2309
2310 return S_OK;
2311}
2312HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2313{
2314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2315
2316 switch(aProperty)
2317 {
2318 case HWVirtExPropertyType_Enabled:
2319 *aValue = mHWData->mHWVirtExEnabled;
2320 break;
2321
2322 case HWVirtExPropertyType_VPID:
2323 *aValue = mHWData->mHWVirtExVPIDEnabled;
2324 break;
2325
2326 case HWVirtExPropertyType_NestedPaging:
2327 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2328 break;
2329
2330 case HWVirtExPropertyType_UnrestrictedExecution:
2331 *aValue = mHWData->mHWVirtExUXEnabled;
2332 break;
2333
2334 case HWVirtExPropertyType_LargePages:
2335 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2336#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2337 *aValue = FALSE;
2338#endif
2339 break;
2340
2341 case HWVirtExPropertyType_Force:
2342 *aValue = mHWData->mHWVirtExForceEnabled;
2343 break;
2344
2345 default:
2346 return E_INVALIDARG;
2347 }
2348 return S_OK;
2349}
2350
2351HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2352{
2353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
2355 HRESULT rc = i_checkStateDependency(MutableStateDep);
2356 if (FAILED(rc)) return rc;
2357
2358 switch(aProperty)
2359 {
2360 case HWVirtExPropertyType_Enabled:
2361 i_setModified(IsModified_MachineData);
2362 mHWData.backup();
2363 mHWData->mHWVirtExEnabled = !!aValue;
2364 break;
2365
2366 case HWVirtExPropertyType_VPID:
2367 i_setModified(IsModified_MachineData);
2368 mHWData.backup();
2369 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2370 break;
2371
2372 case HWVirtExPropertyType_NestedPaging:
2373 i_setModified(IsModified_MachineData);
2374 mHWData.backup();
2375 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2376 break;
2377
2378 case HWVirtExPropertyType_UnrestrictedExecution:
2379 i_setModified(IsModified_MachineData);
2380 mHWData.backup();
2381 mHWData->mHWVirtExUXEnabled = !!aValue;
2382 break;
2383
2384 case HWVirtExPropertyType_LargePages:
2385 i_setModified(IsModified_MachineData);
2386 mHWData.backup();
2387 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2388 break;
2389
2390 case HWVirtExPropertyType_Force:
2391 i_setModified(IsModified_MachineData);
2392 mHWData.backup();
2393 mHWData->mHWVirtExForceEnabled = !!aValue;
2394 break;
2395
2396 default:
2397 return E_INVALIDARG;
2398 }
2399
2400 return S_OK;
2401}
2402
2403HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2404{
2405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2406
2407 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2408
2409 return S_OK;
2410}
2411
2412HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2413{
2414 /* @todo (r=dmik):
2415 * 1. Allow to change the name of the snapshot folder containing snapshots
2416 * 2. Rename the folder on disk instead of just changing the property
2417 * value (to be smart and not to leave garbage). Note that it cannot be
2418 * done here because the change may be rolled back. Thus, the right
2419 * place is #saveSettings().
2420 */
2421
2422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2423
2424 HRESULT rc = i_checkStateDependency(MutableStateDep);
2425 if (FAILED(rc)) return rc;
2426
2427 if (!mData->mCurrentSnapshot.isNull())
2428 return setError(E_FAIL,
2429 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2430
2431 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2432
2433 if (strSnapshotFolder.isEmpty())
2434 strSnapshotFolder = "Snapshots";
2435 int vrc = i_calculateFullPath(strSnapshotFolder,
2436 strSnapshotFolder);
2437 if (RT_FAILURE(vrc))
2438 return setError(E_FAIL,
2439 tr("Invalid snapshot folder '%s' (%Rrc)"),
2440 strSnapshotFolder.c_str(), vrc);
2441
2442 i_setModified(IsModified_MachineData);
2443 mUserData.backup();
2444
2445 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2446
2447 return S_OK;
2448}
2449
2450HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2451{
2452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 aMediumAttachments.resize(mMediaData->mAttachments.size());
2455 size_t i = 0;
2456 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2457 it != mMediaData->mAttachments.end(); ++it, ++i)
2458 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
2459
2460 return S_OK;
2461}
2462
2463HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2464{
2465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2466
2467 Assert(!!mVRDEServer);
2468
2469 mVRDEServer.queryInterfaceTo(aVRDEServer.asOutParam());
2470
2471 return S_OK;
2472}
2473
2474HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2475{
2476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 mAudioAdapter.queryInterfaceTo(aAudioAdapter.asOutParam());
2479
2480 return S_OK;
2481}
2482
2483HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2484{
2485#ifdef VBOX_WITH_VUSB
2486 clearError();
2487 MultiResult rc(S_OK);
2488
2489# ifdef VBOX_WITH_USB
2490 rc = mParent->i_host()->i_checkUSBProxyService();
2491 if (FAILED(rc)) return rc;
2492# endif
2493
2494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 USBControllerList data = *mUSBControllers.data();
2497 aUSBControllers.resize(data.size());
2498 size_t i = 0;
2499 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2500 (*it).queryInterfaceTo(aUSBControllers[i].asOutParam());
2501
2502 return S_OK;
2503#else
2504 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2505 * extended error info to indicate that USB is simply not available
2506 * (w/o treating it as a failure), for example, as in OSE */
2507 NOREF(aUSBControllers);
2508 ReturnComNotImplemented();
2509#endif /* VBOX_WITH_VUSB */
2510}
2511
2512HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2513{
2514#ifdef VBOX_WITH_VUSB
2515 clearError();
2516 MultiResult rc(S_OK);
2517
2518# ifdef VBOX_WITH_USB
2519 rc = mParent->i_host()->i_checkUSBProxyService();
2520 if (FAILED(rc)) return rc;
2521# endif
2522
2523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters.asOutParam());
2526#else
2527 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2528 * extended error info to indicate that USB is simply not available
2529 * (w/o treating it as a failure), for example, as in OSE */
2530 NOREF(aUSBDeviceFilters);
2531 ReturnComNotImplemented();
2532#endif /* VBOX_WITH_VUSB */
2533}
2534
2535HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2536{
2537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2538
2539 aSettingsFilePath = mData->m_strConfigFileFull;
2540
2541 return S_OK;
2542}
2543
2544HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2545{
2546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 HRESULT rc = i_checkStateDependency(MutableStateDep);
2549 if (FAILED(rc)) return rc;
2550
2551 if (!mData->pMachineConfigFile->fileExists())
2552 // this is a new machine, and no config file exists yet:
2553 *aSettingsModified = TRUE;
2554 else
2555 *aSettingsModified = (mData->flModifications != 0);
2556
2557 return S_OK;
2558}
2559
2560HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2561{
2562
2563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 *aSessionState = mData->mSession.mState;
2566
2567 return S_OK;
2568}
2569
2570HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2571{
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 aSessionType = mData->mSession.mType;
2575
2576 return S_OK;
2577}
2578
2579HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2580{
2581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 *aSessionPID = mData->mSession.mPID;
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getState(MachineState_T *aState)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 *aState = mData->mMachineState;
2593
2594 return S_OK;
2595}
2596
2597HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2598{
2599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2600
2601 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2602
2603 return S_OK;
2604}
2605
2606HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2607{
2608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2609
2610 aStateFilePath = mSSData->strStateFilePath;
2611
2612 return S_OK;
2613}
2614
2615HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2616{
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618
2619 i_getLogFolder(aLogFolder);
2620
2621 return S_OK;
2622}
2623
2624HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2625{
2626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2627
2628 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot.asOutParam());
2629
2630 return S_OK;
2631}
2632
2633HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2634{
2635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2636
2637 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2638 ? 0
2639 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2640
2641 return S_OK;
2642}
2643
2644HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2645{
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 /* Note: for machines with no snapshots, we always return FALSE
2649 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2650 * reasons :) */
2651
2652 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2653 ? FALSE
2654 : mData->mCurrentStateModified;
2655
2656 return S_OK;
2657}
2658
2659HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2660{
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 aSharedFolders.resize(mHWData->mSharedFolders.size());
2664 size_t i = 0;
2665 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2666 it != mHWData->mSharedFolders.end(); ++i, ++it)
2667 (*it).queryInterfaceTo(aSharedFolders[i].asOutParam());
2668
2669 return S_OK;
2670}
2671
2672HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2673{
2674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2675
2676 *aClipboardMode = mHWData->mClipboardMode;
2677
2678 return S_OK;
2679}
2680
2681HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2682{
2683 HRESULT rc = S_OK;
2684
2685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2686
2687 alock.release();
2688 rc = i_onClipboardModeChange(aClipboardMode);
2689 alock.acquire();
2690 if (FAILED(rc)) return rc;
2691
2692 i_setModified(IsModified_MachineData);
2693 mHWData.backup();
2694 mHWData->mClipboardMode = aClipboardMode;
2695
2696 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2697 if (Global::IsOnline(mData->mMachineState))
2698 i_saveSettings(NULL);
2699
2700 return S_OK;
2701}
2702
2703HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2704{
2705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
2707 *aDnDMode = mHWData->mDnDMode;
2708
2709 return S_OK;
2710}
2711
2712HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2713{
2714 HRESULT rc = S_OK;
2715
2716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 alock.release();
2719 rc = i_onDnDModeChange(aDnDMode);
2720
2721 alock.acquire();
2722 if (FAILED(rc)) return rc;
2723
2724 i_setModified(IsModified_MachineData);
2725 mHWData.backup();
2726 mHWData->mDnDMode = aDnDMode;
2727
2728 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2729 if (Global::IsOnline(mData->mMachineState))
2730 i_saveSettings(NULL);
2731
2732 return S_OK;
2733}
2734
2735HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2736{
2737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2738
2739 try
2740 {
2741 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2742 }
2743 catch (...)
2744 {
2745 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2746 }
2747
2748 return S_OK;
2749}
2750
2751HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
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 mHWData.backup();
2760 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2761 return rc;
2762}
2763
2764HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2765{
2766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2767 StorageControllerList data = *mStorageControllers.data();
2768 size_t i = 0;
2769 aStorageControllers.resize(data.size());
2770 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2771 (*it).queryInterfaceTo(aStorageControllers[i].asOutParam());
2772 return S_OK;
2773}
2774
2775HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 *aEnabled = mUserData->s.fTeleporterEnabled;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2785{
2786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 /* Only allow it to be set to true when PoweredOff or Aborted.
2789 (Clearing it is always permitted.) */
2790 if ( aTeleporterEnabled
2791 && mData->mRegistered
2792 && ( !i_isSessionMachine()
2793 || ( mData->mMachineState != MachineState_PoweredOff
2794 && mData->mMachineState != MachineState_Teleported
2795 && mData->mMachineState != MachineState_Aborted
2796 )
2797 )
2798 )
2799 return setError(VBOX_E_INVALID_VM_STATE,
2800 tr("The machine is not powered off (state is %s)"),
2801 Global::stringifyMachineState(mData->mMachineState));
2802
2803 i_setModified(IsModified_MachineData);
2804 mUserData.backup();
2805 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2806
2807 return S_OK;
2808}
2809
2810HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2811{
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2815
2816 return S_OK;
2817}
2818
2819HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2820{
2821 if (aTeleporterPort >= _64K)
2822 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2823
2824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2825
2826 HRESULT rc = i_checkStateDependency(MutableStateDep);
2827 if (FAILED(rc)) return rc;
2828
2829 i_setModified(IsModified_MachineData);
2830 mUserData.backup();
2831 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2832
2833 return S_OK;
2834}
2835
2836HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2837{
2838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2839
2840 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2841
2842 return S_OK;
2843}
2844
2845HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2846{
2847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 HRESULT rc = i_checkStateDependency(MutableStateDep);
2850 if (FAILED(rc)) return rc;
2851
2852 i_setModified(IsModified_MachineData);
2853 mUserData.backup();
2854 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2855
2856 return S_OK;
2857}
2858
2859HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2860{
2861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2862 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2863
2864 return S_OK;
2865}
2866
2867HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2868{
2869 /*
2870 * Hash the password first.
2871 */
2872 com::Utf8Str aT = aTeleporterPassword;
2873
2874 if (!aT.isEmpty())
2875 {
2876 if (VBoxIsPasswordHashed(&aT))
2877 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2878 VBoxHashPassword(&aT);
2879 }
2880
2881 /*
2882 * Do the update.
2883 */
2884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2885 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2886 if (SUCCEEDED(hrc))
2887 {
2888 i_setModified(IsModified_MachineData);
2889 mUserData.backup();
2890 mUserData->s.strTeleporterPassword = aT;
2891 }
2892
2893 return hrc;
2894}
2895
2896HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2897{
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2901 return S_OK;
2902}
2903
2904HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2905{
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 /* @todo deal with running state change. */
2909 HRESULT rc = i_checkStateDependency(MutableStateDep);
2910 if (FAILED(rc)) return rc;
2911
2912 i_setModified(IsModified_MachineData);
2913 mUserData.backup();
2914 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2915 return S_OK;
2916}
2917
2918HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2919{
2920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2923 return S_OK;
2924}
2925
2926HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2927{
2928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 /* @todo deal with running state change. */
2931 HRESULT rc = i_checkStateDependency(MutableStateDep);
2932 if (FAILED(rc)) return rc;
2933
2934 i_setModified(IsModified_MachineData);
2935 mUserData.backup();
2936 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2937 return S_OK;
2938}
2939
2940HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2941{
2942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2943
2944 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 /* @todo deal with running state change. */
2953 HRESULT rc = i_checkStateDependency(MutableStateDep);
2954 if (FAILED(rc)) return rc;
2955
2956 i_setModified(IsModified_MachineData);
2957 mUserData.backup();
2958 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2959 return S_OK;
2960}
2961
2962HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2963{
2964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2965
2966 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2972{
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 /* @todo deal with running state change. */
2976 HRESULT rc = i_checkStateDependency(MutableStateDep);
2977 if (FAILED(rc)) return rc;
2978
2979 i_setModified(IsModified_MachineData);
2980 mUserData.backup();
2981 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2982
2983 return S_OK;
2984}
2985
2986HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2987{
2988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2991 return S_OK;
2992}
2993
2994HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2995{
2996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 /* @todo deal with running state change. */
2999 HRESULT rc = i_checkStateDependency(MutableStateDep);
3000 if (FAILED(rc)) return rc;
3001
3002 i_setModified(IsModified_MachineData);
3003 mUserData.backup();
3004 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3005 return S_OK;
3006}
3007
3008HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3009{
3010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3013
3014 return S_OK;
3015}
3016
3017HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3018{
3019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3020
3021 /* Only allow it to be set to true when PoweredOff or Aborted.
3022 (Clearing it is always permitted.) */
3023 if ( aRTCUseUTC
3024 && mData->mRegistered
3025 && ( !i_isSessionMachine()
3026 || ( mData->mMachineState != MachineState_PoweredOff
3027 && mData->mMachineState != MachineState_Teleported
3028 && mData->mMachineState != MachineState_Aborted
3029 )
3030 )
3031 )
3032 return setError(VBOX_E_INVALID_VM_STATE,
3033 tr("The machine is not powered off (state is %s)"),
3034 Global::stringifyMachineState(mData->mMachineState));
3035
3036 i_setModified(IsModified_MachineData);
3037 mUserData.backup();
3038 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3039
3040 return S_OK;
3041}
3042
3043HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3044{
3045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3048
3049 return S_OK;
3050}
3051
3052HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3053{
3054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 HRESULT rc = i_checkStateDependency(MutableStateDep);
3057 if (FAILED(rc)) return rc;
3058
3059 i_setModified(IsModified_MachineData);
3060 mHWData.backup();
3061 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3062
3063 return S_OK;
3064}
3065
3066HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3067{
3068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3069
3070 *aIOCacheSize = mHWData->mIOCacheSize;
3071
3072 return S_OK;
3073}
3074
3075HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3076{
3077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3078
3079 HRESULT rc = i_checkStateDependency(MutableStateDep);
3080 if (FAILED(rc)) return rc;
3081
3082 i_setModified(IsModified_MachineData);
3083 mHWData.backup();
3084 mHWData->mIOCacheSize = aIOCacheSize;
3085
3086 return S_OK;
3087}
3088
3089
3090/**
3091 * @note Locks objects!
3092 */
3093HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3094 LockType_T aLockType)
3095
3096{
3097 /* check the session state */
3098 SessionState_T state;
3099 HRESULT rc = aSession->COMGETTER(State)(&state);
3100 if (FAILED(rc)) return rc;
3101
3102 if (state != SessionState_Unlocked)
3103 return setError(VBOX_E_INVALID_OBJECT_STATE,
3104 tr("The given session is busy"));
3105
3106 // get the client's IInternalSessionControl interface
3107 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3108 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3109 E_INVALIDARG);
3110
3111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3112
3113 if (!mData->mRegistered)
3114 return setError(E_UNEXPECTED,
3115 tr("The machine '%s' is not registered"),
3116 mUserData->s.strName.c_str());
3117
3118 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3119
3120 SessionState_T oldState = mData->mSession.mState;
3121 /* Hack: in case the session is closing and there is a progress object
3122 * which allows waiting for the session to be closed, take the opportunity
3123 * and do a limited wait (max. 1 second). This helps a lot when the system
3124 * is busy and thus session closing can take a little while. */
3125 if ( mData->mSession.mState == SessionState_Unlocking
3126 && mData->mSession.mProgress)
3127 {
3128 alock.release();
3129 mData->mSession.mProgress->WaitForCompletion(1000);
3130 alock.acquire();
3131 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3132 }
3133
3134 // try again now
3135 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3136 // (i.e. session machine exists)
3137 && (aLockType == LockType_Shared) // caller wants a shared link to the
3138 // existing session that holds the write lock:
3139 )
3140 {
3141 // OK, share the session... we are now dealing with three processes:
3142 // 1) VBoxSVC (where this code runs);
3143 // 2) process C: the caller's client process (who wants a shared session);
3144 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3145
3146 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3147 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3148 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3149 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3150 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3151
3152 /*
3153 * Release the lock before calling the client process. It's safe here
3154 * since the only thing to do after we get the lock again is to add
3155 * the remote control to the list (which doesn't directly influence
3156 * anything).
3157 */
3158 alock.release();
3159
3160 // get the console of the session holding the write lock (this is a remote call)
3161 ComPtr<IConsole> pConsoleW;
3162 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3163 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3164 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3165 if (FAILED(rc))
3166 // the failure may occur w/o any error info (from RPC), so provide one
3167 return setError(VBOX_E_VM_ERROR,
3168 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3169
3170 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3171
3172 // share the session machine and W's console with the caller's session
3173 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3174 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3175 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3176
3177 if (FAILED(rc))
3178 // the failure may occur w/o any error info (from RPC), so provide one
3179 return setError(VBOX_E_VM_ERROR,
3180 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3181 alock.acquire();
3182
3183 // need to revalidate the state after acquiring the lock again
3184 if (mData->mSession.mState != SessionState_Locked)
3185 {
3186 pSessionControl->Uninitialize();
3187 return setError(VBOX_E_INVALID_SESSION_STATE,
3188 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3189 mUserData->s.strName.c_str());
3190 }
3191
3192 // add the caller's session to the list
3193 mData->mSession.mRemoteControls.push_back(pSessionControl);
3194 }
3195 else if ( mData->mSession.mState == SessionState_Locked
3196 || mData->mSession.mState == SessionState_Unlocking
3197 )
3198 {
3199 // sharing not permitted, or machine still unlocking:
3200 return setError(VBOX_E_INVALID_OBJECT_STATE,
3201 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3202 mUserData->s.strName.c_str());
3203 }
3204 else
3205 {
3206 // machine is not locked: then write-lock the machine (create the session machine)
3207
3208 // must not be busy
3209 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3210
3211 // get the caller's session PID
3212 RTPROCESS pid = NIL_RTPROCESS;
3213 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3214 pSessionControl->GetPID((ULONG*)&pid);
3215 Assert(pid != NIL_RTPROCESS);
3216
3217 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3218
3219 if (fLaunchingVMProcess)
3220 {
3221 if (mData->mSession.mPID == NIL_RTPROCESS)
3222 {
3223 // two or more clients racing for a lock, the one which set the
3224 // session state to Spawning will win, the others will get an
3225 // error as we can't decide here if waiting a little would help
3226 // (only for shared locks this would avoid an error)
3227 return setError(VBOX_E_INVALID_OBJECT_STATE,
3228 tr("The machine '%s' already has a lock request pending"),
3229 mUserData->s.strName.c_str());
3230 }
3231
3232 // this machine is awaiting for a spawning session to be opened:
3233 // then the calling process must be the one that got started by
3234 // LaunchVMProcess()
3235
3236 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3237 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3238
3239#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3240 /* Hardened windows builds spawns three processes when a VM is
3241 launched, the 3rd one is the one that will end up here. */
3242 RTPROCESS ppid;
3243 int rc = RTProcQueryParent(pid, &ppid);
3244 if (RT_SUCCESS(rc))
3245 rc = RTProcQueryParent(ppid, &ppid);
3246 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3247 || rc == VERR_ACCESS_DENIED)
3248 {
3249 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3250 mData->mSession.mPID = pid;
3251 }
3252#endif
3253
3254 if (mData->mSession.mPID != pid)
3255 return setError(E_ACCESSDENIED,
3256 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3257 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3258 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3259 }
3260
3261 // create the mutable SessionMachine from the current machine
3262 ComObjPtr<SessionMachine> sessionMachine;
3263 sessionMachine.createObject();
3264 rc = sessionMachine->init(this);
3265 AssertComRC(rc);
3266
3267 /* NOTE: doing return from this function after this point but
3268 * before the end is forbidden since it may call SessionMachine::uninit()
3269 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3270 * lock while still holding the Machine lock in alock so that a deadlock
3271 * is possible due to the wrong lock order. */
3272
3273 if (SUCCEEDED(rc))
3274 {
3275 /*
3276 * Set the session state to Spawning to protect against subsequent
3277 * attempts to open a session and to unregister the machine after
3278 * we release the lock.
3279 */
3280 SessionState_T origState = mData->mSession.mState;
3281 mData->mSession.mState = SessionState_Spawning;
3282
3283#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3284 /* Get the client token ID to be passed to the client process */
3285 Utf8Str strTokenId;
3286 sessionMachine->i_getTokenId(strTokenId);
3287 Assert(!strTokenId.isEmpty());
3288#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3289 /* Get the client token to be passed to the client process */
3290 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3291 /* The token is now "owned" by pToken, fix refcount */
3292 if (!pToken.isNull())
3293 pToken->Release();
3294#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3295
3296 /*
3297 * Release the lock before calling the client process -- it will call
3298 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3299 * because the state is Spawning, so that LaunchVMProcess() and
3300 * LockMachine() calls will fail. This method, called before we
3301 * acquire the lock again, will fail because of the wrong PID.
3302 *
3303 * Note that mData->mSession.mRemoteControls accessed outside
3304 * the lock may not be modified when state is Spawning, so it's safe.
3305 */
3306 alock.release();
3307
3308 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3309#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3310 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3311#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3312 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3313 /* Now the token is owned by the client process. */
3314 pToken.setNull();
3315#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3316 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3317
3318 /* The failure may occur w/o any error info (from RPC), so provide one */
3319 if (FAILED(rc))
3320 setError(VBOX_E_VM_ERROR,
3321 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3322
3323 if ( SUCCEEDED(rc)
3324 && fLaunchingVMProcess
3325 )
3326 {
3327 /* complete the remote session initialization */
3328
3329 /* get the console from the direct session */
3330 ComPtr<IConsole> console;
3331 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3332 ComAssertComRC(rc);
3333
3334 if (SUCCEEDED(rc) && !console)
3335 {
3336 ComAssert(!!console);
3337 rc = E_FAIL;
3338 }
3339
3340 /* assign machine & console to the remote session */
3341 if (SUCCEEDED(rc))
3342 {
3343 /*
3344 * after LaunchVMProcess(), the first and the only
3345 * entry in remoteControls is that remote session
3346 */
3347 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3348 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3349 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3350
3351 /* The failure may occur w/o any error info (from RPC), so provide one */
3352 if (FAILED(rc))
3353 setError(VBOX_E_VM_ERROR,
3354 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3355 }
3356
3357 if (FAILED(rc))
3358 pSessionControl->Uninitialize();
3359 }
3360
3361 /* acquire the lock again */
3362 alock.acquire();
3363
3364 /* Restore the session state */
3365 mData->mSession.mState = origState;
3366 }
3367
3368 // finalize spawning anyway (this is why we don't return on errors above)
3369 if (fLaunchingVMProcess)
3370 {
3371 /* Note that the progress object is finalized later */
3372 /** @todo Consider checking mData->mSession.mProgress for cancellation
3373 * around here. */
3374
3375 /* We don't reset mSession.mPID here because it is necessary for
3376 * SessionMachine::uninit() to reap the child process later. */
3377
3378 if (FAILED(rc))
3379 {
3380 /* Close the remote session, remove the remote control from the list
3381 * and reset session state to Closed (@note keep the code in sync
3382 * with the relevant part in checkForSpawnFailure()). */
3383
3384 Assert(mData->mSession.mRemoteControls.size() == 1);
3385 if (mData->mSession.mRemoteControls.size() == 1)
3386 {
3387 ErrorInfoKeeper eik;
3388 mData->mSession.mRemoteControls.front()->Uninitialize();
3389 }
3390
3391 mData->mSession.mRemoteControls.clear();
3392 mData->mSession.mState = SessionState_Unlocked;
3393 }
3394 }
3395 else
3396 {
3397 /* memorize PID of the directly opened session */
3398 if (SUCCEEDED(rc))
3399 mData->mSession.mPID = pid;
3400 }
3401
3402 if (SUCCEEDED(rc))
3403 {
3404 /* memorize the direct session control and cache IUnknown for it */
3405 mData->mSession.mDirectControl = pSessionControl;
3406 mData->mSession.mState = SessionState_Locked;
3407 /* associate the SessionMachine with this Machine */
3408 mData->mSession.mMachine = sessionMachine;
3409
3410 /* request an IUnknown pointer early from the remote party for later
3411 * identity checks (it will be internally cached within mDirectControl
3412 * at least on XPCOM) */
3413 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3414 NOREF(unk);
3415 }
3416
3417 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3418 * would break the lock order */
3419 alock.release();
3420
3421 /* uninitialize the created session machine on failure */
3422 if (FAILED(rc))
3423 sessionMachine->uninit();
3424
3425 }
3426
3427 if (SUCCEEDED(rc))
3428 {
3429 /*
3430 * tell the client watcher thread to update the set of
3431 * machines that have open sessions
3432 */
3433 mParent->i_updateClientWatcher();
3434
3435 if (oldState != SessionState_Locked)
3436 /* fire an event */
3437 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3438 }
3439
3440 return rc;
3441}
3442
3443/**
3444 * @note Locks objects!
3445 */
3446HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3447 const com::Utf8Str &aType,
3448 const com::Utf8Str &aEnvironment,
3449 ComPtr<IProgress> &aProgress)
3450{
3451 Utf8Str strFrontend(aType);
3452 Utf8Str strEnvironment(aEnvironment);
3453 /* "emergencystop" doesn't need the session, so skip the checks/interface
3454 * retrieval. This code doesn't quite fit in here, but introducing a
3455 * special API method would be even more effort, and would require explicit
3456 * support by every API client. It's better to hide the feature a bit. */
3457 if (strFrontend != "emergencystop")
3458 CheckComArgNotNull(aSession);
3459
3460 HRESULT rc = S_OK;
3461 if (strFrontend.isEmpty())
3462 {
3463 Bstr bstrFrontend;
3464 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3465 if (FAILED(rc))
3466 return rc;
3467 strFrontend = bstrFrontend;
3468 if (strFrontend.isEmpty())
3469 {
3470 ComPtr<ISystemProperties> systemProperties;
3471 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3472 if (FAILED(rc))
3473 return rc;
3474 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3475 if (FAILED(rc))
3476 return rc;
3477 strFrontend = bstrFrontend;
3478 }
3479 /* paranoia - emergencystop is not a valid default */
3480 if (strFrontend == "emergencystop")
3481 strFrontend = Utf8Str::Empty;
3482 }
3483 /* default frontend: Qt GUI */
3484 if (strFrontend.isEmpty())
3485 strFrontend = "GUI/Qt";
3486
3487 if (strFrontend != "emergencystop")
3488 {
3489 /* check the session state */
3490 SessionState_T state;
3491 rc = aSession->COMGETTER(State)(&state);
3492 if (FAILED(rc))
3493 return rc;
3494
3495 if (state != SessionState_Unlocked)
3496 return setError(VBOX_E_INVALID_OBJECT_STATE,
3497 tr("The given session is busy"));
3498
3499 /* get the IInternalSessionControl interface */
3500 ComPtr<IInternalSessionControl> control(aSession);
3501 ComAssertMsgRet(!control.isNull(),
3502 ("No IInternalSessionControl interface"),
3503 E_INVALIDARG);
3504
3505 /* get the teleporter enable state for the progress object init. */
3506 BOOL fTeleporterEnabled;
3507 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3508 if (FAILED(rc))
3509 return rc;
3510
3511 /* create a progress object */
3512 ComObjPtr<ProgressProxy> progress;
3513 progress.createObject();
3514 rc = progress->init(mParent,
3515 static_cast<IMachine*>(this),
3516 Bstr(tr("Starting VM")).raw(),
3517 TRUE /* aCancelable */,
3518 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3519 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3520 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3521 2 /* uFirstOperationWeight */,
3522 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3523
3524 if (SUCCEEDED(rc))
3525 {
3526 rc = i_launchVMProcess(control, strFrontend, strEnvironment, progress);
3527 if (SUCCEEDED(rc))
3528 {
3529 progress.queryInterfaceTo(aProgress.asOutParam());
3530
3531 /* signal the client watcher thread */
3532 mParent->i_updateClientWatcher();
3533
3534 /* fire an event */
3535 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3536 }
3537 }
3538 }
3539 else
3540 {
3541 /* no progress object - either instant success or failure */
3542 aProgress = NULL;
3543
3544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3545
3546 if (mData->mSession.mState != SessionState_Locked)
3547 return setError(VBOX_E_INVALID_OBJECT_STATE,
3548 tr("The machine '%s' is not locked by a session"),
3549 mUserData->s.strName.c_str());
3550
3551 /* must have a VM process associated - do not kill normal API clients
3552 * with an open session */
3553 if (!Global::IsOnline(mData->mMachineState))
3554 return setError(VBOX_E_INVALID_OBJECT_STATE,
3555 tr("The machine '%s' does not have a VM process"),
3556 mUserData->s.strName.c_str());
3557
3558 /* forcibly terminate the VM process */
3559 if (mData->mSession.mPID != NIL_RTPROCESS)
3560 RTProcTerminate(mData->mSession.mPID);
3561
3562 /* signal the client watcher thread, as most likely the client has
3563 * been terminated */
3564 mParent->i_updateClientWatcher();
3565 }
3566
3567 return rc;
3568}
3569
3570HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3571{
3572 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3573 return setError(E_INVALIDARG,
3574 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3575 aPosition, SchemaDefs::MaxBootPosition);
3576
3577 if (aDevice == DeviceType_USB)
3578 return setError(E_NOTIMPL,
3579 tr("Booting from USB device is currently not supported"));
3580
3581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3582
3583 HRESULT rc = i_checkStateDependency(MutableStateDep);
3584 if (FAILED(rc)) return rc;
3585
3586 i_setModified(IsModified_MachineData);
3587 mHWData.backup();
3588 mHWData->mBootOrder[aPosition - 1] = aDevice;
3589
3590 return S_OK;
3591}
3592
3593HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3594{
3595 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3596 return setError(E_INVALIDARG,
3597 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3598 aPosition, SchemaDefs::MaxBootPosition);
3599
3600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3601
3602 *aDevice = mHWData->mBootOrder[aPosition - 1];
3603
3604 return S_OK;
3605}
3606
3607HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3608 LONG aControllerPort,
3609 LONG aDevice,
3610 DeviceType_T aType,
3611 const ComPtr<IMedium> &aMedium)
3612{
3613 IMedium *aM = aMedium;
3614 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3615 aName.c_str(), aControllerPort, aDevice, aType, aM));
3616
3617 // request the host lock first, since might be calling Host methods for getting host drives;
3618 // next, protect the media tree all the while we're in here, as well as our member variables
3619 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3620 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3621
3622 HRESULT rc = i_checkStateDependency(MutableStateDep);
3623 if (FAILED(rc)) return rc;
3624
3625 /// @todo NEWMEDIA implicit machine registration
3626 if (!mData->mRegistered)
3627 return setError(VBOX_E_INVALID_OBJECT_STATE,
3628 tr("Cannot attach storage devices to an unregistered machine"));
3629
3630 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3631
3632 /* Check for an existing controller. */
3633 ComObjPtr<StorageController> ctl;
3634 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3635 if (FAILED(rc)) return rc;
3636
3637 StorageControllerType_T ctrlType;
3638 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3639 if (FAILED(rc))
3640 return setError(E_FAIL,
3641 tr("Could not get type of controller '%s'"),
3642 aName.c_str());
3643
3644 bool fSilent = false;
3645 Utf8Str strReconfig;
3646
3647 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3648 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3649 if ( mData->mMachineState == MachineState_Paused
3650 && strReconfig == "1")
3651 fSilent = true;
3652
3653 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3654 bool fHotplug = false;
3655 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3656 fHotplug = true;
3657
3658 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3659 return setError(VBOX_E_INVALID_VM_STATE,
3660 tr("Controller '%s' does not support hotplugging"),
3661 aName.c_str());
3662
3663 // check that the port and device are not out of range
3664 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3665 if (FAILED(rc)) return rc;
3666
3667 /* check if the device slot is already busy */
3668 MediumAttachment *pAttachTemp;
3669 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3670 Bstr(aName).raw(),
3671 aControllerPort,
3672 aDevice)))
3673 {
3674 Medium *pMedium = pAttachTemp->i_getMedium();
3675 if (pMedium)
3676 {
3677 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3678 return setError(VBOX_E_OBJECT_IN_USE,
3679 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3680 pMedium->i_getLocationFull().c_str(),
3681 aControllerPort,
3682 aDevice,
3683 aName.c_str());
3684 }
3685 else
3686 return setError(VBOX_E_OBJECT_IN_USE,
3687 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3688 aControllerPort, aDevice, aName.c_str());
3689 }
3690
3691 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3692 if (aMedium && medium.isNull())
3693 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3694
3695 AutoCaller mediumCaller(medium);
3696 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3697
3698 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3699
3700 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3701 && !medium.isNull()
3702 )
3703 return setError(VBOX_E_OBJECT_IN_USE,
3704 tr("Medium '%s' is already attached to this virtual machine"),
3705 medium->i_getLocationFull().c_str());
3706
3707 if (!medium.isNull())
3708 {
3709 MediumType_T mtype = medium->i_getType();
3710 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3711 // For DVDs it's not written to the config file, so needs no global config
3712 // version bump. For floppies it's a new attribute "type", which is ignored
3713 // by older VirtualBox version, so needs no global config version bump either.
3714 // For hard disks this type is not accepted.
3715 if (mtype == MediumType_MultiAttach)
3716 {
3717 // This type is new with VirtualBox 4.0 and therefore requires settings
3718 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3719 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3720 // two reasons: The medium type is a property of the media registry tree, which
3721 // can reside in the global config file (for pre-4.0 media); we would therefore
3722 // possibly need to bump the global config version. We don't want to do that though
3723 // because that might make downgrading to pre-4.0 impossible.
3724 // As a result, we can only use these two new types if the medium is NOT in the
3725 // global registry:
3726 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3727 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3728 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3729 )
3730 return setError(VBOX_E_INVALID_OBJECT_STATE,
3731 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3732 "to machines that were created with VirtualBox 4.0 or later"),
3733 medium->i_getLocationFull().c_str());
3734 }
3735 }
3736
3737 bool fIndirect = false;
3738 if (!medium.isNull())
3739 fIndirect = medium->i_isReadOnly();
3740 bool associate = true;
3741
3742 do
3743 {
3744 if ( aType == DeviceType_HardDisk
3745 && mMediaData.isBackedUp())
3746 {
3747 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3748
3749 /* check if the medium was attached to the VM before we started
3750 * changing attachments in which case the attachment just needs to
3751 * be restored */
3752 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3753 {
3754 AssertReturn(!fIndirect, E_FAIL);
3755
3756 /* see if it's the same bus/channel/device */
3757 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3758 {
3759 /* the simplest case: restore the whole attachment
3760 * and return, nothing else to do */
3761 mMediaData->mAttachments.push_back(pAttachTemp);
3762
3763 /* Reattach the medium to the VM. */
3764 if (fHotplug || fSilent)
3765 {
3766 mediumLock.release();
3767 treeLock.release();
3768 alock.release();
3769
3770 MediumLockList *pMediumLockList(new MediumLockList());
3771
3772 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3773 true /* fMediumLockWrite */,
3774 NULL,
3775 *pMediumLockList);
3776 alock.acquire();
3777 if (FAILED(rc))
3778 delete pMediumLockList;
3779 else
3780 {
3781 mData->mSession.mLockedMedia.Unlock();
3782 alock.release();
3783 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3784 mData->mSession.mLockedMedia.Lock();
3785 alock.acquire();
3786 }
3787 alock.release();
3788
3789 if (SUCCEEDED(rc))
3790 {
3791 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3792 /* Remove lock list in case of error. */
3793 if (FAILED(rc))
3794 {
3795 mData->mSession.mLockedMedia.Unlock();
3796 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3797 mData->mSession.mLockedMedia.Lock();
3798 }
3799 }
3800 }
3801
3802 return S_OK;
3803 }
3804
3805 /* bus/channel/device differ; we need a new attachment object,
3806 * but don't try to associate it again */
3807 associate = false;
3808 break;
3809 }
3810 }
3811
3812 /* go further only if the attachment is to be indirect */
3813 if (!fIndirect)
3814 break;
3815
3816 /* perform the so called smart attachment logic for indirect
3817 * attachments. Note that smart attachment is only applicable to base
3818 * hard disks. */
3819
3820 if (medium->i_getParent().isNull())
3821 {
3822 /* first, investigate the backup copy of the current hard disk
3823 * attachments to make it possible to re-attach existing diffs to
3824 * another device slot w/o losing their contents */
3825 if (mMediaData.isBackedUp())
3826 {
3827 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3828
3829 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3830 uint32_t foundLevel = 0;
3831
3832 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3833 {
3834 uint32_t level = 0;
3835 MediumAttachment *pAttach = *it;
3836 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3837 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3838 if (pMedium.isNull())
3839 continue;
3840
3841 if (pMedium->i_getBase(&level) == medium)
3842 {
3843 /* skip the hard disk if its currently attached (we
3844 * cannot attach the same hard disk twice) */
3845 if (i_findAttachment(mMediaData->mAttachments,
3846 pMedium))
3847 continue;
3848
3849 /* matched device, channel and bus (i.e. attached to the
3850 * same place) will win and immediately stop the search;
3851 * otherwise the attachment that has the youngest
3852 * descendant of medium will be used
3853 */
3854 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3855 {
3856 /* the simplest case: restore the whole attachment
3857 * and return, nothing else to do */
3858 mMediaData->mAttachments.push_back(*it);
3859
3860 /* Reattach the medium to the VM. */
3861 if (fHotplug || fSilent)
3862 {
3863 mediumLock.release();
3864 treeLock.release();
3865 alock.release();
3866
3867 MediumLockList *pMediumLockList(new MediumLockList());
3868
3869 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3870 true /* fMediumLockWrite */,
3871 NULL,
3872 *pMediumLockList);
3873 alock.acquire();
3874 if (FAILED(rc))
3875 delete pMediumLockList;
3876 else
3877 {
3878 mData->mSession.mLockedMedia.Unlock();
3879 alock.release();
3880 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3881 mData->mSession.mLockedMedia.Lock();
3882 alock.acquire();
3883 }
3884 alock.release();
3885
3886 if (SUCCEEDED(rc))
3887 {
3888 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3889 /* Remove lock list in case of error. */
3890 if (FAILED(rc))
3891 {
3892 mData->mSession.mLockedMedia.Unlock();
3893 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3894 mData->mSession.mLockedMedia.Lock();
3895 }
3896 }
3897 }
3898
3899 return S_OK;
3900 }
3901 else if ( foundIt == oldAtts.end()
3902 || level > foundLevel /* prefer younger */
3903 )
3904 {
3905 foundIt = it;
3906 foundLevel = level;
3907 }
3908 }
3909 }
3910
3911 if (foundIt != oldAtts.end())
3912 {
3913 /* use the previously attached hard disk */
3914 medium = (*foundIt)->i_getMedium();
3915 mediumCaller.attach(medium);
3916 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3917 mediumLock.attach(medium);
3918 /* not implicit, doesn't require association with this VM */
3919 fIndirect = false;
3920 associate = false;
3921 /* go right to the MediumAttachment creation */
3922 break;
3923 }
3924 }
3925
3926 /* must give up the medium lock and medium tree lock as below we
3927 * go over snapshots, which needs a lock with higher lock order. */
3928 mediumLock.release();
3929 treeLock.release();
3930
3931 /* then, search through snapshots for the best diff in the given
3932 * hard disk's chain to base the new diff on */
3933
3934 ComObjPtr<Medium> base;
3935 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3936 while (snap)
3937 {
3938 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3939
3940 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
3941
3942 MediumAttachment *pAttachFound = NULL;
3943 uint32_t foundLevel = 0;
3944
3945 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
3946 {
3947 MediumAttachment *pAttach = *it;
3948 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3949 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3950 if (pMedium.isNull())
3951 continue;
3952
3953 uint32_t level = 0;
3954 if (pMedium->i_getBase(&level) == medium)
3955 {
3956 /* matched device, channel and bus (i.e. attached to the
3957 * same place) will win and immediately stop the search;
3958 * otherwise the attachment that has the youngest
3959 * descendant of medium will be used
3960 */
3961 if ( pAttach->i_getDevice() == aDevice
3962 && pAttach->i_getPort() == aControllerPort
3963 && pAttach->i_getControllerName() == aName
3964 )
3965 {
3966 pAttachFound = pAttach;
3967 break;
3968 }
3969 else if ( !pAttachFound
3970 || level > foundLevel /* prefer younger */
3971 )
3972 {
3973 pAttachFound = pAttach;
3974 foundLevel = level;
3975 }
3976 }
3977 }
3978
3979 if (pAttachFound)
3980 {
3981 base = pAttachFound->i_getMedium();
3982 break;
3983 }
3984
3985 snap = snap->i_getParent();
3986 }
3987
3988 /* re-lock medium tree and the medium, as we need it below */
3989 treeLock.acquire();
3990 mediumLock.acquire();
3991
3992 /* found a suitable diff, use it as a base */
3993 if (!base.isNull())
3994 {
3995 medium = base;
3996 mediumCaller.attach(medium);
3997 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3998 mediumLock.attach(medium);
3999 }
4000 }
4001
4002 Utf8Str strFullSnapshotFolder;
4003 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4004
4005 ComObjPtr<Medium> diff;
4006 diff.createObject();
4007 // store this diff in the same registry as the parent
4008 Guid uuidRegistryParent;
4009 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4010 {
4011 // parent image has no registry: this can happen if we're attaching a new immutable
4012 // image that has not yet been attached (medium then points to the base and we're
4013 // creating the diff image for the immutable, and the parent is not yet registered);
4014 // put the parent in the machine registry then
4015 mediumLock.release();
4016 treeLock.release();
4017 alock.release();
4018 i_addMediumToRegistry(medium);
4019 alock.acquire();
4020 treeLock.acquire();
4021 mediumLock.acquire();
4022 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4023 }
4024 rc = diff->init(mParent,
4025 medium->i_getPreferredDiffFormat(),
4026 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4027 uuidRegistryParent);
4028 if (FAILED(rc)) return rc;
4029
4030 /* Apply the normal locking logic to the entire chain. */
4031 MediumLockList *pMediumLockList(new MediumLockList());
4032 mediumLock.release();
4033 treeLock.release();
4034 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4035 true /* fMediumLockWrite */,
4036 medium,
4037 *pMediumLockList);
4038 treeLock.acquire();
4039 mediumLock.acquire();
4040 if (SUCCEEDED(rc))
4041 {
4042 mediumLock.release();
4043 treeLock.release();
4044 rc = pMediumLockList->Lock();
4045 treeLock.acquire();
4046 mediumLock.acquire();
4047 if (FAILED(rc))
4048 setError(rc,
4049 tr("Could not lock medium when creating diff '%s'"),
4050 diff->i_getLocationFull().c_str());
4051 else
4052 {
4053 /* will release the lock before the potentially lengthy
4054 * operation, so protect with the special state */
4055 MachineState_T oldState = mData->mMachineState;
4056 i_setMachineState(MachineState_SettingUp);
4057
4058 mediumLock.release();
4059 treeLock.release();
4060 alock.release();
4061
4062 rc = medium->i_createDiffStorage(diff,
4063 MediumVariant_Standard,
4064 pMediumLockList,
4065 NULL /* aProgress */,
4066 true /* aWait */);
4067
4068 alock.acquire();
4069 treeLock.acquire();
4070 mediumLock.acquire();
4071
4072 i_setMachineState(oldState);
4073 }
4074 }
4075
4076 /* Unlock the media and free the associated memory. */
4077 delete pMediumLockList;
4078
4079 if (FAILED(rc)) return rc;
4080
4081 /* use the created diff for the actual attachment */
4082 medium = diff;
4083 mediumCaller.attach(medium);
4084 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4085 mediumLock.attach(medium);
4086 }
4087 while (0);
4088
4089 ComObjPtr<MediumAttachment> attachment;
4090 attachment.createObject();
4091 rc = attachment->init(this,
4092 medium,
4093 aName,
4094 aControllerPort,
4095 aDevice,
4096 aType,
4097 fIndirect,
4098 false /* fPassthrough */,
4099 false /* fTempEject */,
4100 false /* fNonRotational */,
4101 false /* fDiscard */,
4102 fHotplug /* fHotPluggable */,
4103 Utf8Str::Empty);
4104 if (FAILED(rc)) return rc;
4105
4106 if (associate && !medium.isNull())
4107 {
4108 // as the last step, associate the medium to the VM
4109 rc = medium->i_addBackReference(mData->mUuid);
4110 // here we can fail because of Deleting, or being in process of creating a Diff
4111 if (FAILED(rc)) return rc;
4112
4113 mediumLock.release();
4114 treeLock.release();
4115 alock.release();
4116 i_addMediumToRegistry(medium);
4117 alock.acquire();
4118 treeLock.acquire();
4119 mediumLock.acquire();
4120 }
4121
4122 /* success: finally remember the attachment */
4123 i_setModified(IsModified_Storage);
4124 mMediaData.backup();
4125 mMediaData->mAttachments.push_back(attachment);
4126
4127 mediumLock.release();
4128 treeLock.release();
4129 alock.release();
4130
4131 if (fHotplug || fSilent)
4132 {
4133 if (!medium.isNull())
4134 {
4135 MediumLockList *pMediumLockList(new MediumLockList());
4136
4137 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4138 true /* fMediumLockWrite */,
4139 NULL,
4140 *pMediumLockList);
4141 alock.acquire();
4142 if (FAILED(rc))
4143 delete pMediumLockList;
4144 else
4145 {
4146 mData->mSession.mLockedMedia.Unlock();
4147 alock.release();
4148 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4149 mData->mSession.mLockedMedia.Lock();
4150 alock.acquire();
4151 }
4152 alock.release();
4153 }
4154
4155 if (SUCCEEDED(rc))
4156 {
4157 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4158 /* Remove lock list in case of error. */
4159 if (FAILED(rc))
4160 {
4161 mData->mSession.mLockedMedia.Unlock();
4162 mData->mSession.mLockedMedia.Remove(attachment);
4163 mData->mSession.mLockedMedia.Lock();
4164 }
4165 }
4166 }
4167
4168 mParent->i_saveModifiedRegistries();
4169
4170 return rc;
4171}
4172
4173HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4174 LONG aDevice)
4175{
4176 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4177 aName.c_str(), aControllerPort, aDevice));
4178
4179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4180
4181 HRESULT rc = i_checkStateDependency(MutableStateDep);
4182 if (FAILED(rc)) return rc;
4183
4184 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4185
4186 /* Check for an existing controller. */
4187 ComObjPtr<StorageController> ctl;
4188 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4189 if (FAILED(rc)) return rc;
4190
4191 StorageControllerType_T ctrlType;
4192 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4193 if (FAILED(rc))
4194 return setError(E_FAIL,
4195 tr("Could not get type of controller '%s'"),
4196 aName.c_str());
4197
4198 bool fSilent = false;
4199 Utf8Str strReconfig;
4200
4201 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4202 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4203 if ( mData->mMachineState == MachineState_Paused
4204 && strReconfig == "1")
4205 fSilent = true;
4206
4207 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4208 bool fHotplug = false;
4209 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4210 fHotplug = true;
4211
4212 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4213 return setError(VBOX_E_INVALID_VM_STATE,
4214 tr("Controller '%s' does not support hotplugging"),
4215 aName.c_str());
4216
4217 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4218 Bstr(aName).raw(),
4219 aControllerPort,
4220 aDevice);
4221 if (!pAttach)
4222 return setError(VBOX_E_OBJECT_NOT_FOUND,
4223 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4224 aDevice, aControllerPort, aName.c_str());
4225
4226 if (fHotplug && !pAttach->i_getHotPluggable())
4227 return setError(VBOX_E_NOT_SUPPORTED,
4228 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4229 aDevice, aControllerPort, aName.c_str());
4230
4231 /*
4232 * The VM has to detach the device before we delete any implicit diffs.
4233 * If this fails we can roll back without loosing data.
4234 */
4235 if (fHotplug || fSilent)
4236 {
4237 alock.release();
4238 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4239 alock.acquire();
4240 }
4241 if (FAILED(rc)) return rc;
4242
4243 /* If we are here everything went well and we can delete the implicit now. */
4244 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4245
4246 alock.release();
4247
4248 mParent->i_saveModifiedRegistries();
4249
4250 return rc;
4251}
4252
4253HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4254 LONG aDevice, BOOL aPassthrough)
4255{
4256 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4257 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4258
4259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4260
4261 HRESULT rc = i_checkStateDependency(MutableStateDep);
4262 if (FAILED(rc)) return rc;
4263
4264 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4265
4266 if (Global::IsOnlineOrTransient(mData->mMachineState))
4267 return setError(VBOX_E_INVALID_VM_STATE,
4268 tr("Invalid machine state: %s"),
4269 Global::stringifyMachineState(mData->mMachineState));
4270
4271 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4272 Bstr(aName).raw(),
4273 aControllerPort,
4274 aDevice);
4275 if (!pAttach)
4276 return setError(VBOX_E_OBJECT_NOT_FOUND,
4277 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4278 aDevice, aControllerPort, aName.c_str());
4279
4280
4281 i_setModified(IsModified_Storage);
4282 mMediaData.backup();
4283
4284 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4285
4286 if (pAttach->i_getType() != DeviceType_DVD)
4287 return setError(E_INVALIDARG,
4288 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4289 aDevice, aControllerPort, aName.c_str());
4290 pAttach->i_updatePassthrough(!!aPassthrough);
4291
4292 return S_OK;
4293}
4294
4295HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4296 LONG aDevice, BOOL aTemporaryEject)
4297{
4298
4299 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4300 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4301
4302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4303
4304 HRESULT rc = i_checkStateDependency(MutableStateDep);
4305 if (FAILED(rc)) return rc;
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_DVD)
4323 return setError(E_INVALIDARG,
4324 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4325 aDevice, aControllerPort, aName.c_str());
4326 pAttach->i_updateTempEject(!!aTemporaryEject);
4327
4328 return S_OK;
4329}
4330
4331HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4332 LONG aDevice, BOOL aNonRotational)
4333{
4334
4335 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4336 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4337
4338 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4339
4340 HRESULT rc = i_checkStateDependency(MutableStateDep);
4341 if (FAILED(rc)) return rc;
4342
4343 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4344
4345 if (Global::IsOnlineOrTransient(mData->mMachineState))
4346 return setError(VBOX_E_INVALID_VM_STATE,
4347 tr("Invalid machine state: %s"),
4348 Global::stringifyMachineState(mData->mMachineState));
4349
4350 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4351 Bstr(aName).raw(),
4352 aControllerPort,
4353 aDevice);
4354 if (!pAttach)
4355 return setError(VBOX_E_OBJECT_NOT_FOUND,
4356 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4357 aDevice, aControllerPort, aName.c_str());
4358
4359
4360 i_setModified(IsModified_Storage);
4361 mMediaData.backup();
4362
4363 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4364
4365 if (pAttach->i_getType() != DeviceType_HardDisk)
4366 return setError(E_INVALIDARG,
4367 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"),
4368 aDevice, aControllerPort, aName.c_str());
4369 pAttach->i_updateNonRotational(!!aNonRotational);
4370
4371 return S_OK;
4372}
4373
4374HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4375 LONG aDevice, BOOL aDiscard)
4376{
4377
4378 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4379 aName.c_str(), aControllerPort, aDevice, aDiscard));
4380
4381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4382
4383 HRESULT rc = i_checkStateDependency(MutableStateDep);
4384 if (FAILED(rc)) return rc;
4385
4386 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4387
4388 if (Global::IsOnlineOrTransient(mData->mMachineState))
4389 return setError(VBOX_E_INVALID_VM_STATE,
4390 tr("Invalid machine state: %s"),
4391 Global::stringifyMachineState(mData->mMachineState));
4392
4393 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4394 Bstr(aName).raw(),
4395 aControllerPort,
4396 aDevice);
4397 if (!pAttach)
4398 return setError(VBOX_E_OBJECT_NOT_FOUND,
4399 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4400 aDevice, aControllerPort, aName.c_str());
4401
4402
4403 i_setModified(IsModified_Storage);
4404 mMediaData.backup();
4405
4406 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4407
4408 if (pAttach->i_getType() != DeviceType_HardDisk)
4409 return setError(E_INVALIDARG,
4410 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"),
4411 aDevice, aControllerPort, aName.c_str());
4412 pAttach->i_updateDiscard(!!aDiscard);
4413
4414 return S_OK;
4415}
4416
4417HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4418 LONG aDevice, BOOL aHotPluggable)
4419{
4420 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4421 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4422
4423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4424
4425 HRESULT rc = i_checkStateDependency(MutableStateDep);
4426 if (FAILED(rc)) return rc;
4427
4428 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4429
4430 if (Global::IsOnlineOrTransient(mData->mMachineState))
4431 return setError(VBOX_E_INVALID_VM_STATE,
4432 tr("Invalid machine state: %s"),
4433 Global::stringifyMachineState(mData->mMachineState));
4434
4435 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4436 Bstr(aName).raw(),
4437 aControllerPort,
4438 aDevice);
4439 if (!pAttach)
4440 return setError(VBOX_E_OBJECT_NOT_FOUND,
4441 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4442 aDevice, aControllerPort, aName.c_str());
4443
4444 /* Check for an existing controller. */
4445 ComObjPtr<StorageController> ctl;
4446 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4447 if (FAILED(rc)) return rc;
4448
4449 StorageControllerType_T ctrlType;
4450 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4451 if (FAILED(rc))
4452 return setError(E_FAIL,
4453 tr("Could not get type of controller '%s'"),
4454 aName.c_str());
4455
4456 if (!i_isControllerHotplugCapable(ctrlType))
4457 return setError(VBOX_E_NOT_SUPPORTED,
4458 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4459 aName.c_str());
4460
4461 i_setModified(IsModified_Storage);
4462 mMediaData.backup();
4463
4464 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4465
4466 if (pAttach->i_getType() == DeviceType_Floppy)
4467 return setError(E_INVALIDARG,
4468 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"),
4469 aDevice, aControllerPort, aName.c_str());
4470 pAttach->i_updateHotPluggable(!!aHotPluggable);
4471
4472 return S_OK;
4473}
4474
4475HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4476 LONG aDevice)
4477{
4478 int rc = S_OK;
4479 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4480 aName.c_str(), aControllerPort, aDevice));
4481
4482 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4483
4484 return rc;
4485}
4486
4487HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4488 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4489{
4490 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4491 aName.c_str(), aControllerPort, aDevice));
4492
4493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4494
4495 HRESULT rc = i_checkStateDependency(MutableStateDep);
4496 if (FAILED(rc)) return rc;
4497
4498 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4499
4500 if (Global::IsOnlineOrTransient(mData->mMachineState))
4501 return setError(VBOX_E_INVALID_VM_STATE,
4502 tr("Invalid machine state: %s"),
4503 Global::stringifyMachineState(mData->mMachineState));
4504
4505 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4506 Bstr(aName).raw(),
4507 aControllerPort,
4508 aDevice);
4509 if (!pAttach)
4510 return setError(VBOX_E_OBJECT_NOT_FOUND,
4511 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4512 aDevice, aControllerPort, aName.c_str());
4513
4514
4515 i_setModified(IsModified_Storage);
4516 mMediaData.backup();
4517
4518 IBandwidthGroup *iB = aBandwidthGroup;
4519 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4520 if (aBandwidthGroup && group.isNull())
4521 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4522
4523 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4524
4525 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4526 if (strBandwidthGroupOld.isNotEmpty())
4527 {
4528 /* Get the bandwidth group object and release it - this must not fail. */
4529 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4530 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4531 Assert(SUCCEEDED(rc));
4532
4533 pBandwidthGroupOld->i_release();
4534 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4535 }
4536
4537 if (!group.isNull())
4538 {
4539 group->i_reference();
4540 pAttach->i_updateBandwidthGroup(group->i_getName());
4541 }
4542
4543 return S_OK;
4544}
4545
4546HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4547 LONG aControllerPort,
4548 LONG aDevice,
4549 DeviceType_T aType)
4550{
4551 HRESULT rc = S_OK;
4552
4553 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4554 aName.c_str(), aControllerPort, aDevice, aType));
4555
4556 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4557
4558 return rc;
4559}
4560
4561
4562HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4563 LONG aControllerPort,
4564 LONG aDevice,
4565 BOOL aForce)
4566{
4567 int rc = S_OK;
4568 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4569 aName.c_str(), aControllerPort, aForce));
4570
4571 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4572
4573 return rc;
4574}
4575
4576HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4577 LONG aControllerPort,
4578 LONG aDevice,
4579 const ComPtr<IMedium> &aMedium,
4580 BOOL aForce)
4581{
4582 int rc = S_OK;
4583 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4584 aName.c_str(), aControllerPort, aDevice, aForce));
4585
4586 // request the host lock first, since might be calling Host methods for getting host drives;
4587 // next, protect the media tree all the while we're in here, as well as our member variables
4588 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4589 this->lockHandle(),
4590 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4591
4592 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4593 Bstr(aName).raw(),
4594 aControllerPort,
4595 aDevice);
4596 if (pAttach.isNull())
4597 return setError(VBOX_E_OBJECT_NOT_FOUND,
4598 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4599 aDevice, aControllerPort, aName.c_str());
4600
4601 /* Remember previously mounted medium. The medium before taking the
4602 * backup is not necessarily the same thing. */
4603 ComObjPtr<Medium> oldmedium;
4604 oldmedium = pAttach->i_getMedium();
4605
4606 IMedium *iM = aMedium;
4607 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4608 if (aMedium && pMedium.isNull())
4609 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4610
4611 AutoCaller mediumCaller(pMedium);
4612 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4613
4614 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4615 if (pMedium)
4616 {
4617 DeviceType_T mediumType = pAttach->i_getType();
4618 switch (mediumType)
4619 {
4620 case DeviceType_DVD:
4621 case DeviceType_Floppy:
4622 break;
4623
4624 default:
4625 return setError(VBOX_E_INVALID_OBJECT_STATE,
4626 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4627 aControllerPort,
4628 aDevice,
4629 aName.c_str());
4630 }
4631 }
4632
4633 i_setModified(IsModified_Storage);
4634 mMediaData.backup();
4635
4636 {
4637 // The backup operation makes the pAttach reference point to the
4638 // old settings. Re-get the correct reference.
4639 pAttach = i_findAttachment(mMediaData->mAttachments,
4640 Bstr(aName).raw(),
4641 aControllerPort,
4642 aDevice);
4643 if (!oldmedium.isNull())
4644 oldmedium->i_removeBackReference(mData->mUuid);
4645 if (!pMedium.isNull())
4646 {
4647 pMedium->i_addBackReference(mData->mUuid);
4648
4649 mediumLock.release();
4650 multiLock.release();
4651 i_addMediumToRegistry(pMedium);
4652 multiLock.acquire();
4653 mediumLock.acquire();
4654 }
4655
4656 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4657 pAttach->i_updateMedium(pMedium);
4658 }
4659
4660 i_setModified(IsModified_Storage);
4661
4662 mediumLock.release();
4663 multiLock.release();
4664 rc = i_onMediumChange(pAttach, aForce);
4665 multiLock.acquire();
4666 mediumLock.acquire();
4667
4668 /* On error roll back this change only. */
4669 if (FAILED(rc))
4670 {
4671 if (!pMedium.isNull())
4672 pMedium->i_removeBackReference(mData->mUuid);
4673 pAttach = i_findAttachment(mMediaData->mAttachments,
4674 Bstr(aName).raw(),
4675 aControllerPort,
4676 aDevice);
4677 /* If the attachment is gone in the meantime, bail out. */
4678 if (pAttach.isNull())
4679 return rc;
4680 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4681 if (!oldmedium.isNull())
4682 oldmedium->i_addBackReference(mData->mUuid);
4683 pAttach->i_updateMedium(oldmedium);
4684 }
4685
4686 mediumLock.release();
4687 multiLock.release();
4688
4689 mParent->i_saveModifiedRegistries();
4690
4691 return rc;
4692}
4693HRESULT Machine::getMedium(const com::Utf8Str &aName,
4694 LONG aControllerPort,
4695 LONG aDevice,
4696 ComPtr<IMedium> &aMedium)
4697{
4698 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4699 aName.c_str(), aControllerPort, aDevice));
4700
4701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4702
4703 aMedium = NULL;
4704
4705 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4706 Bstr(aName).raw(),
4707 aControllerPort,
4708 aDevice);
4709 if (pAttach.isNull())
4710 return setError(VBOX_E_OBJECT_NOT_FOUND,
4711 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4712 aDevice, aControllerPort, aName.c_str());
4713
4714 pAttach->i_getMedium().queryInterfaceTo(aMedium.asOutParam());
4715
4716 return S_OK;
4717}
4718
4719HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4720{
4721
4722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4725
4726 return S_OK;
4727}
4728
4729HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4730{
4731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4732
4733 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4734
4735 return S_OK;
4736}
4737
4738HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4739{
4740 /* Do not assert if slot is out of range, just return the advertised
4741 status. testdriver/vbox.py triggers this in logVmInfo. */
4742 if (aSlot >= mNetworkAdapters.size())
4743 return setError(E_INVALIDARG,
4744 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4745 aSlot, mNetworkAdapters.size());
4746
4747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4748
4749 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4750
4751 return S_OK;
4752}
4753
4754HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4755{
4756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4757
4758 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4759 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4760 size_t i = 0;
4761 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4762 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4763 ++it, ++i)
4764 aKeys[i] = it->first;
4765
4766 return S_OK;
4767}
4768
4769 /**
4770 * @note Locks this object for reading.
4771 */
4772HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4773 com::Utf8Str &aValue)
4774{
4775 /* start with nothing found */
4776 aValue = "";
4777
4778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4779
4780 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4781 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4782 // found:
4783 aValue = it->second; // source is a Utf8Str
4784
4785 /* return the result to caller (may be empty) */
4786 return S_OK;
4787}
4788
4789 /**
4790 * @note Locks mParent for writing + this object for writing.
4791 */
4792HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4793{
4794 Utf8Str strOldValue; // empty
4795
4796 // locking note: we only hold the read lock briefly to look up the old value,
4797 // then release it and call the onExtraCanChange callbacks. There is a small
4798 // chance of a race insofar as the callback might be called twice if two callers
4799 // change the same key at the same time, but that's a much better solution
4800 // than the deadlock we had here before. The actual changing of the extradata
4801 // is then performed under the write lock and race-free.
4802
4803 // look up the old value first; if nothing has changed then we need not do anything
4804 {
4805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4806 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4807 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4808 strOldValue = it->second;
4809 }
4810
4811 bool fChanged;
4812 if ((fChanged = (strOldValue != aValue)))
4813 {
4814 // ask for permission from all listeners outside the locks;
4815 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4816 // lock to copy the list of callbacks to invoke
4817 Bstr error;
4818 Bstr bstrValue(aValue);
4819
4820 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4821 {
4822 const char *sep = error.isEmpty() ? "" : ": ";
4823 CBSTR err = error.raw();
4824 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4825 sep, err));
4826 return setError(E_ACCESSDENIED,
4827 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4828 aKey.c_str(),
4829 aValue.c_str(),
4830 sep,
4831 err);
4832 }
4833
4834 // data is changing and change not vetoed: then write it out under the lock
4835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4836
4837 if (i_isSnapshotMachine())
4838 {
4839 HRESULT rc = i_checkStateDependency(MutableStateDep);
4840 if (FAILED(rc)) return rc;
4841 }
4842
4843 if (aValue.isEmpty())
4844 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4845 else
4846 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4847 // creates a new key if needed
4848
4849 bool fNeedsGlobalSaveSettings = false;
4850 i_saveSettings(&fNeedsGlobalSaveSettings);
4851
4852 if (fNeedsGlobalSaveSettings)
4853 {
4854 // save the global settings; for that we should hold only the VirtualBox lock
4855 alock.release();
4856 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4857 mParent->i_saveSettings();
4858 }
4859 }
4860
4861 // fire notification outside the lock
4862 if (fChanged)
4863 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4864
4865 return S_OK;
4866}
4867
4868HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4869{
4870 aProgress = NULL;
4871 NOREF(aSettingsFilePath);
4872 ReturnComNotImplemented();
4873}
4874
4875HRESULT Machine::saveSettings()
4876{
4877 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4878
4879 /* when there was auto-conversion, we want to save the file even if
4880 * the VM is saved */
4881 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4882 if (FAILED(rc)) return rc;
4883
4884 /* the settings file path may never be null */
4885 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4886
4887 /* save all VM data excluding snapshots */
4888 bool fNeedsGlobalSaveSettings = false;
4889 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4890 mlock.release();
4891
4892 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4893 {
4894 // save the global settings; for that we should hold only the VirtualBox lock
4895 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4896 rc = mParent->i_saveSettings();
4897 }
4898
4899 return rc;
4900}
4901
4902
4903HRESULT Machine::discardSettings()
4904{
4905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4906
4907 HRESULT rc = i_checkStateDependency(MutableStateDep);
4908 if (FAILED(rc)) return rc;
4909
4910 /*
4911 * during this rollback, the session will be notified if data has
4912 * been actually changed
4913 */
4914 i_rollback(true /* aNotify */);
4915
4916 return S_OK;
4917}
4918
4919/** @note Locks objects! */
4920HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4921 std::vector<ComPtr<IMedium> > &aMedia)
4922{
4923 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4924 AutoLimitedCaller autoCaller(this);
4925 AssertComRCReturnRC(autoCaller.rc());
4926
4927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4928
4929 Guid id(i_getId());
4930
4931 if (mData->mSession.mState != SessionState_Unlocked)
4932 return setError(VBOX_E_INVALID_OBJECT_STATE,
4933 tr("Cannot unregister the machine '%s' while it is locked"),
4934 mUserData->s.strName.c_str());
4935
4936 // wait for state dependents to drop to zero
4937 i_ensureNoStateDependencies();
4938
4939 if (!mData->mAccessible)
4940 {
4941 // inaccessible maschines can only be unregistered; uninitialize ourselves
4942 // here because currently there may be no unregistered that are inaccessible
4943 // (this state combination is not supported). Note releasing the caller and
4944 // leaving the lock before calling uninit()
4945 alock.release();
4946 autoCaller.release();
4947
4948 uninit();
4949
4950 mParent->i_unregisterMachine(this, id);
4951 // calls VirtualBox::i_saveSettings()
4952
4953 return S_OK;
4954 }
4955
4956 HRESULT rc = S_OK;
4957
4958 // discard saved state
4959 if (mData->mMachineState == MachineState_Saved)
4960 {
4961 // add the saved state file to the list of files the caller should delete
4962 Assert(!mSSData->strStateFilePath.isEmpty());
4963 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4964
4965 mSSData->strStateFilePath.setNull();
4966
4967 // unconditionally set the machine state to powered off, we now
4968 // know no session has locked the machine
4969 mData->mMachineState = MachineState_PoweredOff;
4970 }
4971
4972 size_t cSnapshots = 0;
4973 if (mData->mFirstSnapshot)
4974 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
4975 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
4976 // fail now before we start detaching media
4977 return setError(VBOX_E_INVALID_OBJECT_STATE,
4978 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4979 mUserData->s.strName.c_str(), cSnapshots);
4980
4981 // This list collects the medium objects from all medium attachments
4982 // which we will detach from the machine and its snapshots, in a specific
4983 // order which allows for closing all media without getting "media in use"
4984 // errors, simply by going through the list from the front to the back:
4985 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4986 // and must be closed before the parent media from the snapshots, or closing the parents
4987 // will fail because they still have children);
4988 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4989 // the root ("first") snapshot of the machine.
4990 MediaList llMedia;
4991
4992 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4993 && mMediaData->mAttachments.size()
4994 )
4995 {
4996 // we have media attachments: detach them all and add the Medium objects to our list
4997 if (aCleanupMode != CleanupMode_UnregisterOnly)
4998 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4999 else
5000 return setError(VBOX_E_INVALID_OBJECT_STATE,
5001 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5002 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5003 }
5004
5005 if (cSnapshots)
5006 {
5007 // autoCleanup must be true here, or we would have failed above
5008
5009 // add the media from the medium attachments of the snapshots to llMedia
5010 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5011 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5012 // into the children first
5013
5014 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5015 MachineState_T oldState = mData->mMachineState;
5016 mData->mMachineState = MachineState_DeletingSnapshot;
5017
5018 // make a copy of the first snapshot so the refcount does not drop to 0
5019 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5020 // because of the AutoCaller voodoo)
5021 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5022
5023 // GO!
5024 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5025
5026 mData->mMachineState = oldState;
5027 }
5028
5029 if (FAILED(rc))
5030 {
5031 i_rollbackMedia();
5032 return rc;
5033 }
5034
5035 // commit all the media changes made above
5036 i_commitMedia();
5037
5038 mData->mRegistered = false;
5039
5040 // machine lock no longer needed
5041 alock.release();
5042
5043 // return media to caller
5044 size_t i = 0;
5045 aMedia.resize(llMedia.size());
5046 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5047 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5048
5049 mParent->i_unregisterMachine(this, id);
5050 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5051
5052 return S_OK;
5053}
5054
5055struct Machine::DeleteTask
5056{
5057 ComObjPtr<Machine> pMachine;
5058 RTCList<ComPtr<IMedium> > llMediums;
5059 StringsList llFilesToDelete;
5060 ComObjPtr<Progress> pProgress;
5061};
5062
5063HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5064{
5065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5066
5067 HRESULT rc = i_checkStateDependency(MutableStateDep);
5068 if (FAILED(rc)) return rc;
5069
5070 if (mData->mRegistered)
5071 return setError(VBOX_E_INVALID_VM_STATE,
5072 tr("Cannot delete settings of a registered machine"));
5073
5074 DeleteTask *pTask = new DeleteTask;
5075 pTask->pMachine = this;
5076
5077 // collect files to delete
5078 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5079
5080 for (size_t i = 0; i < aMedia.size(); ++i)
5081 {
5082 IMedium *pIMedium(aMedia[i]);
5083 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5084 if (pMedium.isNull())
5085 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5086 SafeArray<BSTR> ids;
5087 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5088 if (FAILED(rc)) return rc;
5089 /* At this point the medium should not have any back references
5090 * anymore. If it has it is attached to another VM and *must* not
5091 * deleted. */
5092 if (ids.size() < 1)
5093 pTask->llMediums.append(pMedium);
5094 }
5095 if (mData->pMachineConfigFile->fileExists())
5096 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5097
5098 pTask->pProgress.createObject();
5099 pTask->pProgress->init(i_getVirtualBox(),
5100 static_cast<IMachine*>(this) /* aInitiator */,
5101 Bstr(tr("Deleting files")).raw(),
5102 true /* fCancellable */,
5103 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5104 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5105
5106 int vrc = RTThreadCreate(NULL,
5107 Machine::deleteThread,
5108 (void*)pTask,
5109 0,
5110 RTTHREADTYPE_MAIN_WORKER,
5111 0,
5112 "MachineDelete");
5113
5114 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5115
5116 if (RT_FAILURE(vrc))
5117 {
5118 delete pTask;
5119 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5120 }
5121
5122 LogFlowFuncLeave();
5123
5124 return S_OK;
5125}
5126
5127/**
5128 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5129 * calls Machine::deleteTaskWorker() on the actual machine object.
5130 * @param Thread
5131 * @param pvUser
5132 * @return
5133 */
5134/*static*/
5135DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5136{
5137 LogFlowFuncEnter();
5138
5139 DeleteTask *pTask = (DeleteTask*)pvUser;
5140 Assert(pTask);
5141 Assert(pTask->pMachine);
5142 Assert(pTask->pProgress);
5143
5144 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5145 pTask->pProgress->i_notifyComplete(rc);
5146
5147 delete pTask;
5148
5149 LogFlowFuncLeave();
5150
5151 NOREF(Thread);
5152
5153 return VINF_SUCCESS;
5154}
5155
5156/**
5157 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5158 * @param task
5159 * @return
5160 */
5161HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5162{
5163 AutoCaller autoCaller(this);
5164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5165
5166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5167
5168 HRESULT rc = S_OK;
5169
5170 try
5171 {
5172 ULONG uLogHistoryCount = 3;
5173 ComPtr<ISystemProperties> systemProperties;
5174 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5175 if (FAILED(rc)) throw rc;
5176
5177 if (!systemProperties.isNull())
5178 {
5179 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5180 if (FAILED(rc)) throw rc;
5181 }
5182
5183 MachineState_T oldState = mData->mMachineState;
5184 i_setMachineState(MachineState_SettingUp);
5185 alock.release();
5186 for (size_t i = 0; i < task.llMediums.size(); ++i)
5187 {
5188 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5189 {
5190 AutoCaller mac(pMedium);
5191 if (FAILED(mac.rc())) throw mac.rc();
5192 Utf8Str strLocation = pMedium->i_getLocationFull();
5193 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5194 if (FAILED(rc)) throw rc;
5195 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5196 }
5197 ComPtr<IProgress> pProgress2;
5198 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5199 if (FAILED(rc)) throw rc;
5200 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5201 if (FAILED(rc)) throw rc;
5202 /* Check the result of the asynchronous process. */
5203 LONG iRc;
5204 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5205 if (FAILED(rc)) throw rc;
5206 /* If the thread of the progress object has an error, then
5207 * retrieve the error info from there, or it'll be lost. */
5208 if (FAILED(iRc))
5209 throw setError(ProgressErrorInfo(pProgress2));
5210
5211 /* Close the medium, deliberately without checking the return
5212- * code, and without leaving any trace in the error info, as
5213- * a failure here is a very minor issue, which shouldn't happen
5214- * as above we even managed to delete the medium. */
5215 {
5216 ErrorInfoKeeper eik;
5217 pMedium->Close();
5218 }
5219 }
5220 i_setMachineState(oldState);
5221 alock.acquire();
5222
5223 // delete the files pushed on the task list by Machine::Delete()
5224 // (this includes saved states of the machine and snapshots and
5225 // medium storage files from the IMedium list passed in, and the
5226 // machine XML file)
5227 StringsList::const_iterator it = task.llFilesToDelete.begin();
5228 while (it != task.llFilesToDelete.end())
5229 {
5230 const Utf8Str &strFile = *it;
5231 LogFunc(("Deleting file %s\n", strFile.c_str()));
5232 int vrc = RTFileDelete(strFile.c_str());
5233 if (RT_FAILURE(vrc))
5234 throw setError(VBOX_E_IPRT_ERROR,
5235 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5236
5237 ++it;
5238 if (it == task.llFilesToDelete.end())
5239 {
5240 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5241 if (FAILED(rc)) throw rc;
5242 break;
5243 }
5244
5245 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5246 if (FAILED(rc)) throw rc;
5247 }
5248
5249 /* delete the settings only when the file actually exists */
5250 if (mData->pMachineConfigFile->fileExists())
5251 {
5252 /* Delete any backup or uncommitted XML files. Ignore failures.
5253 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5254 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5255 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5256 RTFileDelete(otherXml.c_str());
5257 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5258 RTFileDelete(otherXml.c_str());
5259
5260 /* delete the Logs folder, nothing important should be left
5261 * there (we don't check for errors because the user might have
5262 * some private files there that we don't want to delete) */
5263 Utf8Str logFolder;
5264 getLogFolder(logFolder);
5265 Assert(logFolder.length());
5266 if (RTDirExists(logFolder.c_str()))
5267 {
5268 /* Delete all VBox.log[.N] files from the Logs folder
5269 * (this must be in sync with the rotation logic in
5270 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5271 * files that may have been created by the GUI. */
5272 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5273 logFolder.c_str(), RTPATH_DELIMITER);
5274 RTFileDelete(log.c_str());
5275 log = Utf8StrFmt("%s%cVBox.png",
5276 logFolder.c_str(), RTPATH_DELIMITER);
5277 RTFileDelete(log.c_str());
5278 for (int i = uLogHistoryCount; i > 0; i--)
5279 {
5280 log = Utf8StrFmt("%s%cVBox.log.%d",
5281 logFolder.c_str(), RTPATH_DELIMITER, i);
5282 RTFileDelete(log.c_str());
5283 log = Utf8StrFmt("%s%cVBox.png.%d",
5284 logFolder.c_str(), RTPATH_DELIMITER, i);
5285 RTFileDelete(log.c_str());
5286 }
5287
5288 RTDirRemove(logFolder.c_str());
5289 }
5290
5291 /* delete the Snapshots folder, nothing important should be left
5292 * there (we don't check for errors because the user might have
5293 * some private files there that we don't want to delete) */
5294 Utf8Str strFullSnapshotFolder;
5295 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5296 Assert(!strFullSnapshotFolder.isEmpty());
5297 if (RTDirExists(strFullSnapshotFolder.c_str()))
5298 RTDirRemove(strFullSnapshotFolder.c_str());
5299
5300 // delete the directory that contains the settings file, but only
5301 // if it matches the VM name
5302 Utf8Str settingsDir;
5303 if (i_isInOwnDir(&settingsDir))
5304 RTDirRemove(settingsDir.c_str());
5305 }
5306
5307 alock.release();
5308
5309 mParent->i_saveModifiedRegistries();
5310 }
5311 catch (HRESULT aRC) { rc = aRC; }
5312
5313 return rc;
5314}
5315
5316HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5317{
5318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5319
5320 ComObjPtr<Snapshot> pSnapshot;
5321 HRESULT rc;
5322
5323 if (aNameOrId.isEmpty())
5324 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5325 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5326 else
5327 {
5328 Guid uuid(aNameOrId);
5329 if (uuid.isValid())
5330 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5331 else
5332 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5333 }
5334 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5335
5336 return rc;
5337}
5338
5339HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5340{
5341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5342
5343 HRESULT rc = i_checkStateDependency(MutableStateDep);
5344 if (FAILED(rc)) return rc;
5345
5346 ComObjPtr<SharedFolder> sharedFolder;
5347 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5348 if (SUCCEEDED(rc))
5349 return setError(VBOX_E_OBJECT_IN_USE,
5350 tr("Shared folder named '%s' already exists"),
5351 aName.c_str());
5352
5353 sharedFolder.createObject();
5354 rc = sharedFolder->init(i_getMachine(),
5355 aName,
5356 aHostPath,
5357 !!aWritable,
5358 !!aAutomount,
5359 true /* fFailOnError */);
5360 if (FAILED(rc)) return rc;
5361
5362 i_setModified(IsModified_SharedFolders);
5363 mHWData.backup();
5364 mHWData->mSharedFolders.push_back(sharedFolder);
5365
5366 /* inform the direct session if any */
5367 alock.release();
5368 i_onSharedFolderChange();
5369
5370 return S_OK;
5371}
5372
5373HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5374{
5375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5376
5377 HRESULT rc = i_checkStateDependency(MutableStateDep);
5378 if (FAILED(rc)) return rc;
5379
5380 ComObjPtr<SharedFolder> sharedFolder;
5381 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5382 if (FAILED(rc)) return rc;
5383
5384 i_setModified(IsModified_SharedFolders);
5385 mHWData.backup();
5386 mHWData->mSharedFolders.remove(sharedFolder);
5387
5388 /* inform the direct session if any */
5389 alock.release();
5390 i_onSharedFolderChange();
5391
5392 return S_OK;
5393}
5394
5395HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5396{
5397 /* start with No */
5398 *aCanShow = FALSE;
5399
5400 ComPtr<IInternalSessionControl> directControl;
5401 {
5402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5403
5404 if (mData->mSession.mState != SessionState_Locked)
5405 return setError(VBOX_E_INVALID_VM_STATE,
5406 tr("Machine is not locked for session (session state: %s)"),
5407 Global::stringifySessionState(mData->mSession.mState));
5408
5409 directControl = mData->mSession.mDirectControl;
5410 }
5411
5412 /* ignore calls made after #OnSessionEnd() is called */
5413 if (!directControl)
5414 return S_OK;
5415
5416 LONG64 dummy;
5417 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5418}
5419
5420HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5421{
5422 ComPtr<IInternalSessionControl> directControl;
5423 {
5424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5425
5426 if (mData->mSession.mState != SessionState_Locked)
5427 return setError(E_FAIL,
5428 tr("Machine is not locked for session (session state: %s)"),
5429 Global::stringifySessionState(mData->mSession.mState));
5430
5431 directControl = mData->mSession.mDirectControl;
5432 }
5433
5434 /* ignore calls made after #OnSessionEnd() is called */
5435 if (!directControl)
5436 return S_OK;
5437
5438 BOOL dummy;
5439 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5440}
5441
5442#ifdef VBOX_WITH_GUEST_PROPS
5443/**
5444 * Look up a guest property in VBoxSVC's internal structures.
5445 */
5446HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5447 com::Utf8Str &aValue,
5448 LONG64 *aTimestamp,
5449 com::Utf8Str &aFlags) const
5450{
5451 using namespace guestProp;
5452
5453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5454 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5455
5456 if (it != mHWData->mGuestProperties.end())
5457 {
5458 char szFlags[MAX_FLAGS_LEN + 1];
5459 aValue = it->second.strValue;
5460 *aTimestamp = it->second.mTimestamp;
5461 writeFlags(it->second.mFlags, szFlags);
5462 aFlags = Utf8Str(szFlags);
5463 }
5464
5465 return S_OK;
5466}
5467
5468/**
5469 * Query the VM that a guest property belongs to for the property.
5470 * @returns E_ACCESSDENIED if the VM process is not available or not
5471 * currently handling queries and the lookup should then be done in
5472 * VBoxSVC.
5473 */
5474HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5475 com::Utf8Str &aValue,
5476 LONG64 *aTimestamp,
5477 com::Utf8Str &aFlags) const
5478{
5479 HRESULT rc = S_OK;
5480 BSTR bValue = NULL;
5481 BSTR bFlags = NULL;
5482
5483 ComPtr<IInternalSessionControl> directControl;
5484 directControl = mData->mSession.mDirectControl;
5485
5486 /* fail if we were called after #OnSessionEnd() is called. This is a
5487 * silly race condition. */
5488
5489 /** @todo This code is bothering API clients (like python script clients) with
5490 * the AccessGuestProperty call, creating unncessary IPC. Need to
5491 * have a way of figuring out which kind of direct session it is... */
5492 if (!directControl)
5493 rc = E_ACCESSDENIED;
5494 else
5495 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), NULL, NULL,
5496 false /* isSetter */,
5497 &bValue, aTimestamp, &bFlags);
5498
5499 aValue = bValue;
5500 aFlags = bFlags;
5501
5502 return rc;
5503}
5504#endif // VBOX_WITH_GUEST_PROPS
5505
5506HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5507 com::Utf8Str &aValue,
5508 LONG64 *aTimestamp,
5509 com::Utf8Str &aFlags)
5510{
5511#ifndef VBOX_WITH_GUEST_PROPS
5512 ReturnComNotImplemented();
5513#else // VBOX_WITH_GUEST_PROPS
5514
5515 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5516
5517 if (rc == E_ACCESSDENIED)
5518 /* The VM is not running or the service is not (yet) accessible */
5519 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5520 return rc;
5521#endif // VBOX_WITH_GUEST_PROPS
5522}
5523
5524HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5525{
5526 LONG64 dummyTimestamp;
5527 com::Utf8Str dummyFlags;
5528 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5529 return rc;
5530
5531}
5532HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5533{
5534 com::Utf8Str dummyFlags;
5535 com::Utf8Str dummyValue;
5536 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5537 return rc;
5538}
5539
5540#ifdef VBOX_WITH_GUEST_PROPS
5541/**
5542 * Set a guest property in VBoxSVC's internal structures.
5543 */
5544HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5545 const com::Utf8Str &aFlags)
5546{
5547 using namespace guestProp;
5548
5549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5550 HRESULT rc = S_OK;
5551
5552 rc = i_checkStateDependency(MutableStateDep);
5553 if (FAILED(rc)) return rc;
5554
5555 try
5556 {
5557 uint32_t fFlags = NILFLAG;
5558 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5559 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5560
5561 bool fDelete = aValue.isEmpty();
5562 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5563 if (it == mHWData->mGuestProperties.end())
5564 {
5565 if (!fDelete)
5566 {
5567 i_setModified(IsModified_MachineData);
5568 mHWData.backupEx();
5569
5570 RTTIMESPEC time;
5571 HWData::GuestProperty prop;
5572 prop.strValue = Bstr(aValue).raw();
5573 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5574 prop.mFlags = fFlags;
5575 mHWData->mGuestProperties[aName] = prop;
5576 }
5577 }
5578 else
5579 {
5580 if (it->second.mFlags & (RDONLYHOST))
5581 {
5582 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5583 }
5584 else
5585 {
5586 i_setModified(IsModified_MachineData);
5587 mHWData.backupEx();
5588
5589 /* The backupEx() operation invalidates our iterator,
5590 * so get a new one. */
5591 it = mHWData->mGuestProperties.find(aName);
5592 Assert(it != mHWData->mGuestProperties.end());
5593
5594 if (!fDelete)
5595 {
5596 RTTIMESPEC time;
5597 it->second.strValue = aValue;
5598 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5599 it->second.mFlags = fFlags;
5600 }
5601 else
5602 mHWData->mGuestProperties.erase(it);
5603 }
5604 }
5605
5606 if ( SUCCEEDED(rc)
5607 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5608 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5609 RTSTR_MAX,
5610 aName.c_str(),
5611 RTSTR_MAX,
5612 NULL)
5613 )
5614 )
5615 {
5616 alock.release();
5617
5618 mParent->i_onGuestPropertyChange(mData->mUuid,
5619 Bstr(aName).raw(),
5620 Bstr(aValue).raw(),
5621 Bstr(aFlags).raw());
5622 }
5623 }
5624 catch (std::bad_alloc &)
5625 {
5626 rc = E_OUTOFMEMORY;
5627 }
5628
5629 return rc;
5630}
5631
5632/**
5633 * Set a property on the VM that that property belongs to.
5634 * @returns E_ACCESSDENIED if the VM process is not available or not
5635 * currently handling queries and the setting should then be done in
5636 * VBoxSVC.
5637 */
5638HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5639 const com::Utf8Str &aFlags)
5640{
5641 HRESULT rc;
5642
5643 try
5644 {
5645 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5646
5647 BSTR dummy = NULL; /* will not be changed (setter) */
5648 LONG64 dummy64;
5649 if (!directControl)
5650 rc = E_ACCESSDENIED;
5651 else
5652 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5653 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5654 true /* isSetter */,
5655 &dummy, &dummy64, &dummy);
5656 }
5657 catch (std::bad_alloc &)
5658 {
5659 rc = E_OUTOFMEMORY;
5660 }
5661
5662 return rc;
5663}
5664#endif // VBOX_WITH_GUEST_PROPS
5665
5666HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5667 const com::Utf8Str &aFlags)
5668{
5669#ifndef VBOX_WITH_GUEST_PROPS
5670 ReturnComNotImplemented();
5671#else // VBOX_WITH_GUEST_PROPS
5672 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags);
5673 if (rc == E_ACCESSDENIED)
5674 /* The VM is not running or the service is not (yet) accessible */
5675 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags);
5676 return rc;
5677#endif // VBOX_WITH_GUEST_PROPS
5678}
5679
5680HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5681{
5682 return setGuestProperty(aProperty, aValue, "");
5683}
5684
5685HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5686{
5687 return setGuestProperty(aName, "", "");
5688}
5689
5690#ifdef VBOX_WITH_GUEST_PROPS
5691/**
5692 * Enumerate the guest properties in VBoxSVC's internal structures.
5693 */
5694HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5695 std::vector<com::Utf8Str> &aNames,
5696 std::vector<com::Utf8Str> &aValues,
5697 std::vector<LONG64> &aTimestamps,
5698 std::vector<com::Utf8Str> &aFlags)
5699{
5700 using namespace guestProp;
5701
5702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5703 Utf8Str strPatterns(aPatterns);
5704
5705 HWData::GuestPropertyMap propMap;
5706
5707 /*
5708 * Look for matching patterns and build up a list.
5709 */
5710 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5711 while (it != mHWData->mGuestProperties.end())
5712 {
5713 if ( strPatterns.isEmpty()
5714 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5715 RTSTR_MAX,
5716 it->first.c_str(),
5717 RTSTR_MAX,
5718 NULL)
5719 )
5720 propMap.insert(*it);
5721 it++;
5722 }
5723
5724 alock.release();
5725
5726 /*
5727 * And build up the arrays for returning the property information.
5728 */
5729 size_t cEntries = propMap.size();
5730
5731 aNames.resize(cEntries);
5732 aValues.resize(cEntries);
5733 aTimestamps.resize(cEntries);
5734 aFlags.resize(cEntries);
5735
5736 char szFlags[MAX_FLAGS_LEN + 1];
5737 size_t i= 0;
5738 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5739 {
5740 aNames[i] = it->first;
5741 aValues[i] = it->second.strValue;
5742 aTimestamps[i] = it->second.mTimestamp;
5743 writeFlags(it->second.mFlags, szFlags);
5744 aFlags[i] = Utf8Str(szFlags);
5745 }
5746
5747 return S_OK;
5748}
5749
5750/**
5751 * Enumerate the properties managed by a VM.
5752 * @returns E_ACCESSDENIED if the VM process is not available or not
5753 * currently handling queries and the setting should then be done in
5754 * VBoxSVC.
5755 */
5756HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5757 std::vector<com::Utf8Str> &aNames,
5758 std::vector<com::Utf8Str> &aValues,
5759 std::vector<LONG64> &aTimestamps,
5760 std::vector<com::Utf8Str> &aFlags)
5761{
5762 HRESULT rc;
5763 ComPtr<IInternalSessionControl> directControl;
5764 directControl = mData->mSession.mDirectControl;
5765
5766
5767 com::SafeArray<BSTR> bNames;
5768 com::SafeArray<BSTR> bValues;
5769 com::SafeArray<LONG64> bTimestamps;
5770 com::SafeArray<BSTR> bFlags;
5771
5772 if (!directControl)
5773 rc = E_ACCESSDENIED;
5774 else
5775 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5776 ComSafeArrayAsOutParam(bNames),
5777 ComSafeArrayAsOutParam(bValues),
5778 ComSafeArrayAsOutParam(bTimestamps),
5779 ComSafeArrayAsOutParam(bFlags));
5780 size_t i;
5781 aNames.resize(bNames.size());
5782 for (i = 0; i < bNames.size(); ++i)
5783 aNames[i] = Utf8Str(bNames[i]);
5784 aValues.resize(bValues.size());
5785 for (i = 0; i < bValues.size(); ++i)
5786 aValues[i] = Utf8Str(bValues[i]);
5787 aTimestamps.resize(bTimestamps.size());
5788 for (i = 0; i < bTimestamps.size(); ++i)
5789 aTimestamps[i] = bTimestamps[i];
5790 aFlags.resize(bFlags.size());
5791 for (i = 0; i < bFlags.size(); ++i)
5792 aFlags[i] = Utf8Str(bFlags[i]);
5793
5794 return rc;
5795}
5796#endif // VBOX_WITH_GUEST_PROPS
5797HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5798 std::vector<com::Utf8Str> &aNames,
5799 std::vector<com::Utf8Str> &aValues,
5800 std::vector<LONG64> &aTimestamps,
5801 std::vector<com::Utf8Str> &aFlags)
5802{
5803#ifndef VBOX_WITH_GUEST_PROPS
5804 ReturnComNotImplemented();
5805#else // VBOX_WITH_GUEST_PROPS
5806
5807 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5808
5809 if (rc == E_ACCESSDENIED)
5810 /* The VM is not running or the service is not (yet) accessible */
5811 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5812 return rc;
5813#endif // VBOX_WITH_GUEST_PROPS
5814}
5815
5816HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5817 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5818{
5819 MediaData::AttachmentList atts;
5820
5821 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5822 if (FAILED(rc)) return rc;
5823
5824 size_t i = 0;
5825 aMediumAttachments.resize(atts.size());
5826 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5827 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5828
5829 return S_OK;
5830}
5831
5832HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5833 LONG aControllerPort,
5834 LONG aDevice,
5835 ComPtr<IMediumAttachment> &aAttachment)
5836{
5837 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5838 aName.c_str(), aControllerPort, aDevice));
5839
5840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5841
5842 aAttachment = NULL;
5843
5844 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5845 Bstr(aName).raw(),
5846 aControllerPort,
5847 aDevice);
5848 if (pAttach.isNull())
5849 return setError(VBOX_E_OBJECT_NOT_FOUND,
5850 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5851 aDevice, aControllerPort, aName.c_str());
5852
5853 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5854
5855 return S_OK;
5856}
5857
5858
5859HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5860 StorageBus_T aConnectionType,
5861 ComPtr<IStorageController> &aController)
5862{
5863 if ( (aConnectionType <= StorageBus_Null)
5864 || (aConnectionType > StorageBus_USB))
5865 return setError(E_INVALIDARG,
5866 tr("Invalid connection type: %d"),
5867 aConnectionType);
5868
5869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5870
5871 HRESULT rc = i_checkStateDependency(MutableStateDep);
5872 if (FAILED(rc)) return rc;
5873
5874 /* try to find one with the name first. */
5875 ComObjPtr<StorageController> ctrl;
5876
5877 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5878 if (SUCCEEDED(rc))
5879 return setError(VBOX_E_OBJECT_IN_USE,
5880 tr("Storage controller named '%s' already exists"),
5881 aName.c_str());
5882
5883 ctrl.createObject();
5884
5885 /* get a new instance number for the storage controller */
5886 ULONG ulInstance = 0;
5887 bool fBootable = true;
5888 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5889 it != mStorageControllers->end();
5890 ++it)
5891 {
5892 if ((*it)->i_getStorageBus() == aConnectionType)
5893 {
5894 ULONG ulCurInst = (*it)->i_getInstance();
5895
5896 if (ulCurInst >= ulInstance)
5897 ulInstance = ulCurInst + 1;
5898
5899 /* Only one controller of each type can be marked as bootable. */
5900 if ((*it)->i_getBootable())
5901 fBootable = false;
5902 }
5903 }
5904
5905 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5906 if (FAILED(rc)) return rc;
5907
5908 i_setModified(IsModified_Storage);
5909 mStorageControllers.backup();
5910 mStorageControllers->push_back(ctrl);
5911
5912 ctrl.queryInterfaceTo(aController.asOutParam());
5913
5914 /* inform the direct session if any */
5915 alock.release();
5916 i_onStorageControllerChange();
5917
5918 return S_OK;
5919}
5920
5921HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5922 ComPtr<IStorageController> &aStorageController)
5923{
5924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5925
5926 ComObjPtr<StorageController> ctrl;
5927
5928 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5929 if (SUCCEEDED(rc))
5930 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5931
5932 return rc;
5933}
5934
5935HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
5936 ComPtr<IStorageController> &aStorageController)
5937{
5938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5939
5940 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5941 it != mStorageControllers->end();
5942 ++it)
5943 {
5944 if ((*it)->i_getInstance() == aInstance)
5945 {
5946 (*it).queryInterfaceTo(aStorageController.asOutParam());
5947 return S_OK;
5948 }
5949 }
5950
5951 return setError(VBOX_E_OBJECT_NOT_FOUND,
5952 tr("Could not find a storage controller with instance number '%lu'"),
5953 aInstance);
5954}
5955
5956HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5957{
5958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5959
5960 HRESULT rc = i_checkStateDependency(MutableStateDep);
5961 if (FAILED(rc)) return rc;
5962
5963 ComObjPtr<StorageController> ctrl;
5964
5965 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5966 if (SUCCEEDED(rc))
5967 {
5968 /* Ensure that only one controller of each type is marked as bootable. */
5969 if (aBootable == TRUE)
5970 {
5971 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5972 it != mStorageControllers->end();
5973 ++it)
5974 {
5975 ComObjPtr<StorageController> aCtrl = (*it);
5976
5977 if ( (aCtrl->i_getName() != aName)
5978 && aCtrl->i_getBootable() == TRUE
5979 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5980 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5981 {
5982 aCtrl->i_setBootable(FALSE);
5983 break;
5984 }
5985 }
5986 }
5987
5988 if (SUCCEEDED(rc))
5989 {
5990 ctrl->i_setBootable(aBootable);
5991 i_setModified(IsModified_Storage);
5992 }
5993 }
5994
5995 if (SUCCEEDED(rc))
5996 {
5997 /* inform the direct session if any */
5998 alock.release();
5999 i_onStorageControllerChange();
6000 }
6001
6002 return rc;
6003}
6004
6005HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6006{
6007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6008
6009 HRESULT rc = i_checkStateDependency(MutableStateDep);
6010 if (FAILED(rc)) return rc;
6011
6012 ComObjPtr<StorageController> ctrl;
6013 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6014 if (FAILED(rc)) return rc;
6015
6016 {
6017 /* find all attached devices to the appropriate storage controller and detach them all */
6018 // make a temporary list because detachDevice invalidates iterators into
6019 // mMediaData->mAttachments
6020 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6021
6022 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6023 it != llAttachments2.end();
6024 ++it)
6025 {
6026 MediumAttachment *pAttachTemp = *it;
6027
6028 AutoCaller localAutoCaller(pAttachTemp);
6029 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6030
6031 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6032
6033 if (pAttachTemp->i_getControllerName() == aName)
6034 {
6035 rc = i_detachDevice(pAttachTemp, alock, NULL);
6036 if (FAILED(rc)) return rc;
6037 }
6038 }
6039 }
6040
6041 /* We can remove it now. */
6042 i_setModified(IsModified_Storage);
6043 mStorageControllers.backup();
6044
6045 ctrl->i_unshare();
6046
6047 mStorageControllers->remove(ctrl);
6048
6049 /* inform the direct session if any */
6050 alock.release();
6051 i_onStorageControllerChange();
6052
6053 return S_OK;
6054}
6055
6056HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6057 ComPtr<IUSBController> &aController)
6058{
6059 if ( (aType <= USBControllerType_Null)
6060 || (aType >= USBControllerType_Last))
6061 return setError(E_INVALIDARG,
6062 tr("Invalid USB controller type: %d"),
6063 aType);
6064
6065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6066
6067 HRESULT rc = i_checkStateDependency(MutableStateDep);
6068 if (FAILED(rc)) return rc;
6069
6070 /* try to find one with the same type first. */
6071 ComObjPtr<USBController> ctrl;
6072
6073 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6074 if (SUCCEEDED(rc))
6075 return setError(VBOX_E_OBJECT_IN_USE,
6076 tr("USB controller named '%s' already exists"),
6077 aName.c_str());
6078
6079 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6080 ULONG maxInstances;
6081 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6082 if (FAILED(rc))
6083 return rc;
6084
6085 ULONG cInstances = i_getUSBControllerCountByType(aType);
6086 if (cInstances >= maxInstances)
6087 return setError(E_INVALIDARG,
6088 tr("Too many USB controllers of this type"));
6089
6090 ctrl.createObject();
6091
6092 rc = ctrl->init(this, aName, aType);
6093 if (FAILED(rc)) return rc;
6094
6095 i_setModified(IsModified_USB);
6096 mUSBControllers.backup();
6097 mUSBControllers->push_back(ctrl);
6098
6099 ctrl.queryInterfaceTo(aController.asOutParam());
6100
6101 /* inform the direct session if any */
6102 alock.release();
6103 i_onUSBControllerChange();
6104
6105 return S_OK;
6106}
6107
6108HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6109{
6110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6111
6112 ComObjPtr<USBController> ctrl;
6113
6114 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6115 if (SUCCEEDED(rc))
6116 ctrl.queryInterfaceTo(aController.asOutParam());
6117
6118 return rc;
6119}
6120
6121HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6122 ULONG *aControllers)
6123{
6124 if ( (aType <= USBControllerType_Null)
6125 || (aType >= USBControllerType_Last))
6126 return setError(E_INVALIDARG,
6127 tr("Invalid USB controller type: %d"),
6128 aType);
6129
6130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6131
6132 ComObjPtr<USBController> ctrl;
6133
6134 *aControllers = i_getUSBControllerCountByType(aType);
6135
6136 return S_OK;
6137}
6138
6139HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6140{
6141
6142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6143
6144 HRESULT rc = i_checkStateDependency(MutableStateDep);
6145 if (FAILED(rc)) return rc;
6146
6147 ComObjPtr<USBController> ctrl;
6148 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6149 if (FAILED(rc)) return rc;
6150
6151 i_setModified(IsModified_USB);
6152 mUSBControllers.backup();
6153
6154 ctrl->i_unshare();
6155
6156 mUSBControllers->remove(ctrl);
6157
6158 /* inform the direct session if any */
6159 alock.release();
6160 i_onUSBControllerChange();
6161
6162 return S_OK;
6163}
6164
6165HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6166 ULONG *aOriginX,
6167 ULONG *aOriginY,
6168 ULONG *aWidth,
6169 ULONG *aHeight,
6170 BOOL *aEnabled)
6171{
6172 uint32_t u32OriginX= 0;
6173 uint32_t u32OriginY= 0;
6174 uint32_t u32Width = 0;
6175 uint32_t u32Height = 0;
6176 uint16_t u16Flags = 0;
6177
6178 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6179 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6180 if (RT_FAILURE(vrc))
6181 {
6182#ifdef RT_OS_WINDOWS
6183 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6184 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6185 * So just assign fEnable to TRUE again.
6186 * The right fix would be to change GUI API wrappers to make sure that parameters
6187 * are changed only if API succeeds.
6188 */
6189 *aEnabled = TRUE;
6190#endif
6191 return setError(VBOX_E_IPRT_ERROR,
6192 tr("Saved guest size is not available (%Rrc)"),
6193 vrc);
6194 }
6195
6196 *aOriginX = u32OriginX;
6197 *aOriginY = u32OriginY;
6198 *aWidth = u32Width;
6199 *aHeight = u32Height;
6200 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6201
6202 return S_OK;
6203}
6204
6205HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6206{
6207 if (aScreenId != 0)
6208 return E_NOTIMPL;
6209
6210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6211
6212 uint8_t *pu8Data = NULL;
6213 uint32_t cbData = 0;
6214 uint32_t u32Width = 0;
6215 uint32_t u32Height = 0;
6216
6217 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6218
6219 if (RT_FAILURE(vrc))
6220 return setError(VBOX_E_IPRT_ERROR,
6221 tr("Saved screenshot data is not available (%Rrc)"),
6222 vrc);
6223
6224 *aSize = cbData;
6225 *aWidth = u32Width;
6226 *aHeight = u32Height;
6227
6228 freeSavedDisplayScreenshot(pu8Data);
6229
6230 return S_OK;
6231}
6232
6233
6234HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6235{
6236 if (aScreenId != 0)
6237 return E_NOTIMPL;
6238
6239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6240
6241 uint8_t *pu8Data = NULL;
6242 uint32_t cbData = 0;
6243 uint32_t u32Width = 0;
6244 uint32_t u32Height = 0;
6245
6246 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6247
6248 if (RT_FAILURE(vrc))
6249 return setError(VBOX_E_IPRT_ERROR,
6250 tr("Saved screenshot data is not available (%Rrc)"),
6251 vrc);
6252
6253 *aWidth = u32Width;
6254 *aHeight = u32Height;
6255
6256 com::SafeArray<BYTE> bitmap(cbData);
6257 /* Convert pixels to format expected by the API caller. */
6258 if (aBGR)
6259 {
6260 /* [0] B, [1] G, [2] R, [3] A. */
6261 for (unsigned i = 0; i < cbData; i += 4)
6262 {
6263 bitmap[i] = pu8Data[i];
6264 bitmap[i + 1] = pu8Data[i + 1];
6265 bitmap[i + 2] = pu8Data[i + 2];
6266 bitmap[i + 3] = 0xff;
6267 }
6268 }
6269 else
6270 {
6271 /* [0] R, [1] G, [2] B, [3] A. */
6272 for (unsigned i = 0; i < cbData; i += 4)
6273 {
6274 bitmap[i] = pu8Data[i + 2];
6275 bitmap[i + 1] = pu8Data[i + 1];
6276 bitmap[i + 2] = pu8Data[i];
6277 bitmap[i + 3] = 0xff;
6278 }
6279 }
6280 aData.resize(bitmap.size());
6281 for (size_t i = 0; i < bitmap.size(); ++i)
6282 aData[i] = bitmap[i];
6283
6284 freeSavedDisplayScreenshot(pu8Data);
6285
6286 return S_OK;
6287}
6288
6289HRESULT Machine::readSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6290{
6291 if (aScreenId != 0)
6292 return E_NOTIMPL;
6293
6294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6295
6296 uint8_t *pu8Data = NULL;
6297 uint32_t cbData = 0;
6298 uint32_t u32Width = 0;
6299 uint32_t u32Height = 0;
6300
6301 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6302
6303 if (RT_FAILURE(vrc))
6304 return setError(VBOX_E_IPRT_ERROR,
6305 tr("Saved screenshot data is not available (%Rrc)"),
6306 vrc);
6307
6308 *aWidth = u32Width;
6309 *aHeight = u32Height;
6310
6311 HRESULT rc = S_OK;
6312 uint8_t *pu8PNG = NULL;
6313 uint32_t cbPNG = 0;
6314 uint32_t cxPNG = 0;
6315 uint32_t cyPNG = 0;
6316
6317 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6318
6319 if (RT_SUCCESS(vrc))
6320 {
6321 com::SafeArray<BYTE> screenData(cbPNG);
6322 screenData.initFrom(pu8PNG, cbPNG);
6323 if (pu8PNG)
6324 RTMemFree(pu8PNG);
6325 aData.resize(screenData.size());
6326 for (size_t i = 0; i < screenData.size(); ++i)
6327 aData[i] = screenData[i];
6328 }
6329 else
6330 {
6331 if (pu8PNG)
6332 RTMemFree(pu8PNG);
6333 return setError(VBOX_E_IPRT_ERROR,
6334 tr("Could not convert screenshot to PNG (%Rrc)"),
6335 vrc);
6336 }
6337
6338 freeSavedDisplayScreenshot(pu8Data);
6339
6340 return rc;
6341}
6342
6343HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6344{
6345 if (aScreenId != 0)
6346 return E_NOTIMPL;
6347
6348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6349
6350 uint8_t *pu8Data = NULL;
6351 uint32_t cbData = 0;
6352 uint32_t u32Width = 0;
6353 uint32_t u32Height = 0;
6354
6355 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6356
6357 if (RT_FAILURE(vrc))
6358 return setError(VBOX_E_IPRT_ERROR,
6359 tr("Saved screenshot data is not available (%Rrc)"),
6360 vrc);
6361
6362 *aSize = cbData;
6363 *aWidth = u32Width;
6364 *aHeight = u32Height;
6365
6366 freeSavedDisplayScreenshot(pu8Data);
6367
6368 return S_OK;
6369}
6370
6371HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6372{
6373 if (aScreenId != 0)
6374 return E_NOTIMPL;
6375
6376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6377
6378 uint8_t *pu8Data = NULL;
6379 uint32_t cbData = 0;
6380 uint32_t u32Width = 0;
6381 uint32_t u32Height = 0;
6382
6383 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6384
6385 if (RT_FAILURE(vrc))
6386 return setError(VBOX_E_IPRT_ERROR,
6387 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6388 vrc);
6389
6390 *aWidth = u32Width;
6391 *aHeight = u32Height;
6392
6393 com::SafeArray<BYTE> png(cbData);
6394 png.initFrom(pu8Data, cbData);
6395 aData.resize(png.size());
6396 for (size_t i = 0; i < png.size(); ++i)
6397 aData[i] = png[i];
6398
6399 freeSavedDisplayScreenshot(pu8Data);
6400
6401 return S_OK;
6402}
6403
6404HRESULT Machine::hotPlugCPU(ULONG aCpu)
6405{
6406 HRESULT rc = S_OK;
6407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6408
6409 if (!mHWData->mCPUHotPlugEnabled)
6410 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6411
6412 if (aCpu >= mHWData->mCPUCount)
6413 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6414
6415 if (mHWData->mCPUAttached[aCpu])
6416 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6417
6418 alock.release();
6419 rc = i_onCPUChange(aCpu, false);
6420 alock.acquire();
6421 if (FAILED(rc)) return rc;
6422
6423 i_setModified(IsModified_MachineData);
6424 mHWData.backup();
6425 mHWData->mCPUAttached[aCpu] = true;
6426
6427 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6428 if (Global::IsOnline(mData->mMachineState))
6429 i_saveSettings(NULL);
6430
6431 return S_OK;
6432}
6433
6434HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6435{
6436 HRESULT rc = S_OK;
6437
6438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6439
6440 if (!mHWData->mCPUHotPlugEnabled)
6441 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6442
6443 if (aCpu >= SchemaDefs::MaxCPUCount)
6444 return setError(E_INVALIDARG,
6445 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6446 SchemaDefs::MaxCPUCount);
6447
6448 if (!mHWData->mCPUAttached[aCpu])
6449 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6450
6451 /* CPU 0 can't be detached */
6452 if (aCpu == 0)
6453 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6454
6455 alock.release();
6456 rc = i_onCPUChange(aCpu, true);
6457 alock.acquire();
6458 if (FAILED(rc)) return rc;
6459
6460 i_setModified(IsModified_MachineData);
6461 mHWData.backup();
6462 mHWData->mCPUAttached[aCpu] = false;
6463
6464 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6465 if (Global::IsOnline(mData->mMachineState))
6466 i_saveSettings(NULL);
6467
6468 return S_OK;
6469}
6470
6471HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6472{
6473 *aAttached = false;
6474
6475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6476
6477 /* If hotplug is enabled the CPU is always enabled. */
6478 if (!mHWData->mCPUHotPlugEnabled)
6479 {
6480 if (aCpu < mHWData->mCPUCount)
6481 *aAttached = true;
6482 }
6483 else
6484 {
6485 if (aCpu < SchemaDefs::MaxCPUCount)
6486 *aAttached = mHWData->mCPUAttached[aCpu];
6487 }
6488
6489 return S_OK;
6490}
6491
6492HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6493{
6494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6495
6496 Utf8Str log = i_queryLogFilename(aIdx);
6497 if (!RTFileExists(log.c_str()))
6498 log.setNull();
6499 aFilename = log;
6500
6501 return S_OK;
6502}
6503
6504HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6505{
6506 if (aSize < 0)
6507 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6508
6509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6510
6511 HRESULT rc = S_OK;
6512 Utf8Str log = i_queryLogFilename(aIdx);
6513
6514 /* do not unnecessarily hold the lock while doing something which does
6515 * not need the lock and potentially takes a long time. */
6516 alock.release();
6517
6518 /* Limit the chunk size to 32K for now, as that gives better performance
6519 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6520 * One byte expands to approx. 25 bytes of breathtaking XML. */
6521 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6522 com::SafeArray<BYTE> logData(cbData);
6523
6524 RTFILE LogFile;
6525 int vrc = RTFileOpen(&LogFile, log.c_str(),
6526 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6527 if (RT_SUCCESS(vrc))
6528 {
6529 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6530 if (RT_SUCCESS(vrc))
6531 logData.resize(cbData);
6532 else
6533 rc = setError(VBOX_E_IPRT_ERROR,
6534 tr("Could not read log file '%s' (%Rrc)"),
6535 log.c_str(), vrc);
6536 RTFileClose(LogFile);
6537 }
6538 else
6539 rc = setError(VBOX_E_IPRT_ERROR,
6540 tr("Could not open log file '%s' (%Rrc)"),
6541 log.c_str(), vrc);
6542
6543 if (FAILED(rc))
6544 logData.resize(0);
6545
6546 aData.resize(logData.size());
6547 for (size_t i = 0; i < logData.size(); ++i)
6548 aData[i] = logData[i];
6549
6550 return rc;
6551}
6552
6553
6554/**
6555 * Currently this method doesn't attach device to the running VM,
6556 * just makes sure it's plugged on next VM start.
6557 */
6558HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6559{
6560 // lock scope
6561 {
6562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6563
6564 HRESULT rc = i_checkStateDependency(MutableStateDep);
6565 if (FAILED(rc)) return rc;
6566
6567 ChipsetType_T aChipset = ChipsetType_PIIX3;
6568 COMGETTER(ChipsetType)(&aChipset);
6569
6570 if (aChipset != ChipsetType_ICH9)
6571 {
6572 return setError(E_INVALIDARG,
6573 tr("Host PCI attachment only supported with ICH9 chipset"));
6574 }
6575
6576 // check if device with this host PCI address already attached
6577 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6578 it != mHWData->mPCIDeviceAssignments.end();
6579 ++it)
6580 {
6581 LONG iHostAddress = -1;
6582 ComPtr<PCIDeviceAttachment> pAttach;
6583 pAttach = *it;
6584 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6585 if (iHostAddress == aHostAddress)
6586 return setError(E_INVALIDARG,
6587 tr("Device with host PCI address already attached to this VM"));
6588 }
6589
6590 ComObjPtr<PCIDeviceAttachment> pda;
6591 char name[32];
6592
6593 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6594 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6595 Bstr bname(name);
6596 pda.createObject();
6597 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6598 i_setModified(IsModified_MachineData);
6599 mHWData.backup();
6600 mHWData->mPCIDeviceAssignments.push_back(pda);
6601 }
6602
6603 return S_OK;
6604}
6605
6606/**
6607 * Currently this method doesn't detach device from the running VM,
6608 * just makes sure it's not plugged on next VM start.
6609 */
6610HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6611{
6612 ComObjPtr<PCIDeviceAttachment> pAttach;
6613 bool fRemoved = false;
6614 HRESULT rc;
6615
6616 // lock scope
6617 {
6618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6619
6620 rc = i_checkStateDependency(MutableStateDep);
6621 if (FAILED(rc)) return rc;
6622
6623 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6624 it != mHWData->mPCIDeviceAssignments.end();
6625 ++it)
6626 {
6627 LONG iHostAddress = -1;
6628 pAttach = *it;
6629 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6630 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6631 {
6632 i_setModified(IsModified_MachineData);
6633 mHWData.backup();
6634 mHWData->mPCIDeviceAssignments.remove(pAttach);
6635 fRemoved = true;
6636 break;
6637 }
6638 }
6639 }
6640
6641
6642 /* Fire event outside of the lock */
6643 if (fRemoved)
6644 {
6645 Assert(!pAttach.isNull());
6646 ComPtr<IEventSource> es;
6647 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6648 Assert(SUCCEEDED(rc));
6649 Bstr mid;
6650 rc = this->COMGETTER(Id)(mid.asOutParam());
6651 Assert(SUCCEEDED(rc));
6652 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6653 }
6654
6655 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6656 tr("No host PCI device %08x attached"),
6657 aHostAddress
6658 );
6659}
6660
6661HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6662{
6663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6664
6665 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6666
6667 size_t i = 0;
6668 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6669 it != mHWData->mPCIDeviceAssignments.end();
6670 ++i, ++it)
6671 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6672
6673 return S_OK;
6674}
6675
6676HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6677{
6678 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6679
6680 return S_OK;
6681}
6682
6683HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6684{
6685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6686
6687 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6688
6689 return S_OK;
6690}
6691
6692HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6693{
6694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6695 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6696 if (SUCCEEDED(hrc))
6697 {
6698 hrc = mHWData.backupEx();
6699 if (SUCCEEDED(hrc))
6700 {
6701 i_setModified(IsModified_MachineData);
6702 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6703 }
6704 }
6705 return hrc;
6706}
6707
6708HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6709{
6710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6711 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6712 return S_OK;
6713}
6714
6715HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6716{
6717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6718 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6719 if (SUCCEEDED(hrc))
6720 {
6721 hrc = mHWData.backupEx();
6722 if (SUCCEEDED(hrc))
6723 {
6724 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6725 if (SUCCEEDED(hrc))
6726 i_setModified(IsModified_MachineData);
6727 }
6728 }
6729 return hrc;
6730}
6731
6732HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6733{
6734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6735
6736 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6737
6738 return S_OK;
6739}
6740
6741HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6742{
6743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6744 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6745 if (SUCCEEDED(hrc))
6746 {
6747 hrc = mHWData.backupEx();
6748 if (SUCCEEDED(hrc))
6749 {
6750 i_setModified(IsModified_MachineData);
6751 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6752 }
6753 }
6754 return hrc;
6755}
6756
6757HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6758{
6759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6760
6761 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6762
6763 return S_OK;
6764}
6765
6766HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6767{
6768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6769
6770 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6771 if ( SUCCEEDED(hrc)
6772 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6773 {
6774 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6775 int vrc;
6776
6777 if (aAutostartEnabled)
6778 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6779 else
6780 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6781
6782 if (RT_SUCCESS(vrc))
6783 {
6784 hrc = mHWData.backupEx();
6785 if (SUCCEEDED(hrc))
6786 {
6787 i_setModified(IsModified_MachineData);
6788 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6789 }
6790 }
6791 else if (vrc == VERR_NOT_SUPPORTED)
6792 hrc = setError(VBOX_E_NOT_SUPPORTED,
6793 tr("The VM autostart feature is not supported on this platform"));
6794 else if (vrc == VERR_PATH_NOT_FOUND)
6795 hrc = setError(E_FAIL,
6796 tr("The path to the autostart database is not set"));
6797 else
6798 hrc = setError(E_UNEXPECTED,
6799 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6800 aAutostartEnabled ? "Adding" : "Removing",
6801 mUserData->s.strName.c_str(), vrc);
6802 }
6803 return hrc;
6804}
6805
6806HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6807{
6808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6809
6810 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6811
6812 return S_OK;
6813}
6814
6815HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6816{
6817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6818 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6819 if (SUCCEEDED(hrc))
6820 {
6821 hrc = mHWData.backupEx();
6822 if (SUCCEEDED(hrc))
6823 {
6824 i_setModified(IsModified_MachineData);
6825 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6826 }
6827 }
6828 return hrc;
6829}
6830
6831HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6832{
6833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6834
6835 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6836
6837 return S_OK;
6838}
6839
6840HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6841{
6842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6843 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6844 if ( SUCCEEDED(hrc)
6845 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6846 {
6847 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6848 int vrc;
6849
6850 if (aAutostopType != AutostopType_Disabled)
6851 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6852 else
6853 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6854
6855 if (RT_SUCCESS(vrc))
6856 {
6857 hrc = mHWData.backupEx();
6858 if (SUCCEEDED(hrc))
6859 {
6860 i_setModified(IsModified_MachineData);
6861 mHWData->mAutostart.enmAutostopType = aAutostopType;
6862 }
6863 }
6864 else if (vrc == VERR_NOT_SUPPORTED)
6865 hrc = setError(VBOX_E_NOT_SUPPORTED,
6866 tr("The VM autostop feature is not supported on this platform"));
6867 else if (vrc == VERR_PATH_NOT_FOUND)
6868 hrc = setError(E_FAIL,
6869 tr("The path to the autostart database is not set"));
6870 else
6871 hrc = setError(E_UNEXPECTED,
6872 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6873 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6874 mUserData->s.strName.c_str(), vrc);
6875 }
6876 return hrc;
6877}
6878
6879HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6880{
6881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6882
6883 aDefaultFrontend = mHWData->mDefaultFrontend;
6884
6885 return S_OK;
6886}
6887
6888HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6889{
6890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6891 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6892 if (SUCCEEDED(hrc))
6893 {
6894 hrc = mHWData.backupEx();
6895 if (SUCCEEDED(hrc))
6896 {
6897 i_setModified(IsModified_MachineData);
6898 mHWData->mDefaultFrontend = aDefaultFrontend;
6899 }
6900 }
6901 return hrc;
6902}
6903
6904HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6905{
6906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6907 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
6908 aIcon.resize(mUserData->mIcon.size());
6909 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
6910 aIcon.resize(icon.size());
6911 for (size_t i = 0; i < icon.size(); ++i)
6912 aIcon[i] = icon[i];
6913 return S_OK;
6914}
6915
6916HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6917{
6918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6919 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6920 if (SUCCEEDED(hrc))
6921 {
6922 i_setModified(IsModified_MachineData);
6923 mUserData.backup();
6924 com::SafeArray<BYTE> icon(aIcon);
6925 mUserData->mIcon.resize(aIcon.size());
6926 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
6927 }
6928 return hrc;
6929}
6930
6931HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6932{
6933
6934#ifdef VBOX_WITH_USB
6935 *aUSBProxyAvailable = true;
6936#else
6937 *aUSBProxyAvailable = false;
6938#endif
6939 return S_OK;
6940}
6941
6942HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6943 ComPtr<IProgress> &aProgress)
6944{
6945 ComObjPtr<Progress> pP;
6946 Progress *ppP = pP;
6947 IProgress *iP = static_cast<IProgress *>(ppP);
6948 IProgress **pProgress = &iP;
6949
6950 IMachine *pTarget = aTarget;
6951
6952 /* Convert the options. */
6953 RTCList<CloneOptions_T> optList;
6954 if (aOptions.size())
6955 for (size_t i = 0; i < aOptions.size(); ++i)
6956 optList.append(aOptions[i]);
6957
6958 if (optList.contains(CloneOptions_Link))
6959 {
6960 if (!i_isSnapshotMachine())
6961 return setError(E_INVALIDARG,
6962 tr("Linked clone can only be created from a snapshot"));
6963 if (aMode != CloneMode_MachineState)
6964 return setError(E_INVALIDARG,
6965 tr("Linked clone can only be created for a single machine state"));
6966 }
6967 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6968
6969 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6970
6971 HRESULT rc = pWorker->start(pProgress);
6972
6973 pP = static_cast<Progress *>(*pProgress);
6974 pP.queryInterfaceTo(aProgress.asOutParam());
6975
6976 return rc;
6977
6978}
6979
6980// public methods for internal purposes
6981/////////////////////////////////////////////////////////////////////////////
6982
6983/**
6984 * Adds the given IsModified_* flag to the dirty flags of the machine.
6985 * This must be called either during i_loadSettings or under the machine write lock.
6986 * @param fl
6987 */
6988void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6989{
6990 mData->flModifications |= fl;
6991 if (fAllowStateModification && i_isStateModificationAllowed())
6992 mData->mCurrentStateModified = true;
6993}
6994
6995/**
6996 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6997 * care of the write locking.
6998 *
6999 * @param fModifications The flag to add.
7000 */
7001void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7002{
7003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7004 i_setModified(fModification, fAllowStateModification);
7005}
7006
7007/**
7008 * Saves the registry entry of this machine to the given configuration node.
7009 *
7010 * @param aEntryNode Node to save the registry entry to.
7011 *
7012 * @note locks this object for reading.
7013 */
7014HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7015{
7016 AutoLimitedCaller autoCaller(this);
7017 AssertComRCReturnRC(autoCaller.rc());
7018
7019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7020
7021 data.uuid = mData->mUuid;
7022 data.strSettingsFile = mData->m_strConfigFile;
7023
7024 return S_OK;
7025}
7026
7027/**
7028 * Calculates the absolute path of the given path taking the directory of the
7029 * machine settings file as the current directory.
7030 *
7031 * @param aPath Path to calculate the absolute path for.
7032 * @param aResult Where to put the result (used only on success, can be the
7033 * same Utf8Str instance as passed in @a aPath).
7034 * @return IPRT result.
7035 *
7036 * @note Locks this object for reading.
7037 */
7038int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7039{
7040 AutoCaller autoCaller(this);
7041 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7042
7043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7044
7045 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7046
7047 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7048
7049 strSettingsDir.stripFilename();
7050 char folder[RTPATH_MAX];
7051 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7052 if (RT_SUCCESS(vrc))
7053 aResult = folder;
7054
7055 return vrc;
7056}
7057
7058/**
7059 * Copies strSource to strTarget, making it relative to the machine folder
7060 * if it is a subdirectory thereof, or simply copying it otherwise.
7061 *
7062 * @param strSource Path to evaluate and copy.
7063 * @param strTarget Buffer to receive target path.
7064 *
7065 * @note Locks this object for reading.
7066 */
7067void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7068 Utf8Str &strTarget)
7069{
7070 AutoCaller autoCaller(this);
7071 AssertComRCReturn(autoCaller.rc(), (void)0);
7072
7073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7074
7075 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7076 // use strTarget as a temporary buffer to hold the machine settings dir
7077 strTarget = mData->m_strConfigFileFull;
7078 strTarget.stripFilename();
7079 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7080 {
7081 // is relative: then append what's left
7082 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7083 // for empty paths (only possible for subdirs) use "." to avoid
7084 // triggering default settings for not present config attributes.
7085 if (strTarget.isEmpty())
7086 strTarget = ".";
7087 }
7088 else
7089 // is not relative: then overwrite
7090 strTarget = strSource;
7091}
7092
7093/**
7094 * Returns the full path to the machine's log folder in the
7095 * \a aLogFolder argument.
7096 */
7097void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7098{
7099 AutoCaller autoCaller(this);
7100 AssertComRCReturnVoid(autoCaller.rc());
7101
7102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7103
7104 char szTmp[RTPATH_MAX];
7105 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7106 if (RT_SUCCESS(vrc))
7107 {
7108 if (szTmp[0] && !mUserData.isNull())
7109 {
7110 char szTmp2[RTPATH_MAX];
7111 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7112 if (RT_SUCCESS(vrc))
7113 aLogFolder = BstrFmt("%s%c%s",
7114 szTmp2,
7115 RTPATH_DELIMITER,
7116 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7117 }
7118 else
7119 vrc = VERR_PATH_IS_RELATIVE;
7120 }
7121
7122 if (RT_FAILURE(vrc))
7123 {
7124 // fallback if VBOX_USER_LOGHOME is not set or invalid
7125 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7126 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7127 aLogFolder.append(RTPATH_DELIMITER);
7128 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7129 }
7130}
7131
7132/**
7133 * Returns the full path to the machine's log file for an given index.
7134 */
7135Utf8Str Machine::i_queryLogFilename(ULONG idx)
7136{
7137 Utf8Str logFolder;
7138 getLogFolder(logFolder);
7139 Assert(logFolder.length());
7140 Utf8Str log;
7141 if (idx == 0)
7142 log = Utf8StrFmt("%s%cVBox.log",
7143 logFolder.c_str(), RTPATH_DELIMITER);
7144 else
7145 log = Utf8StrFmt("%s%cVBox.log.%d",
7146 logFolder.c_str(), RTPATH_DELIMITER, idx);
7147 return log;
7148}
7149
7150/**
7151 * Composes a unique saved state filename based on the current system time. The filename is
7152 * granular to the second so this will work so long as no more than one snapshot is taken on
7153 * a machine per second.
7154 *
7155 * Before version 4.1, we used this formula for saved state files:
7156 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7157 * which no longer works because saved state files can now be shared between the saved state of the
7158 * "saved" machine and an online snapshot, and the following would cause problems:
7159 * 1) save machine
7160 * 2) create online snapshot from that machine state --> reusing saved state file
7161 * 3) save machine again --> filename would be reused, breaking the online snapshot
7162 *
7163 * So instead we now use a timestamp.
7164 *
7165 * @param str
7166 */
7167
7168void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7169{
7170 AutoCaller autoCaller(this);
7171 AssertComRCReturnVoid(autoCaller.rc());
7172
7173 {
7174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7175 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7176 }
7177
7178 RTTIMESPEC ts;
7179 RTTimeNow(&ts);
7180 RTTIME time;
7181 RTTimeExplode(&time, &ts);
7182
7183 strStateFilePath += RTPATH_DELIMITER;
7184 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7185 time.i32Year, time.u8Month, time.u8MonthDay,
7186 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7187}
7188
7189/**
7190 * Returns the full path to the default video capture file.
7191 */
7192void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7193{
7194 AutoCaller autoCaller(this);
7195 AssertComRCReturnVoid(autoCaller.rc());
7196
7197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7198
7199 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7200 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7201 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7202}
7203
7204/**
7205 * Returns whether at least one USB controller is present for the VM.
7206 */
7207bool Machine::i_isUSBControllerPresent()
7208{
7209 AutoCaller autoCaller(this);
7210 AssertComRCReturn(autoCaller.rc(), false);
7211
7212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7213
7214 return (mUSBControllers->size() > 0);
7215}
7216
7217/**
7218 * @note Locks this object for writing, calls the client process
7219 * (inside the lock).
7220 */
7221HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7222 const Utf8Str &strFrontend,
7223 const Utf8Str &strEnvironment,
7224 ProgressProxy *aProgress)
7225{
7226 LogFlowThisFuncEnter();
7227
7228 AssertReturn(aControl, E_FAIL);
7229 AssertReturn(aProgress, E_FAIL);
7230 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7231
7232 AutoCaller autoCaller(this);
7233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7234
7235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7236
7237 if (!mData->mRegistered)
7238 return setError(E_UNEXPECTED,
7239 tr("The machine '%s' is not registered"),
7240 mUserData->s.strName.c_str());
7241
7242 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7243
7244 if ( mData->mSession.mState == SessionState_Locked
7245 || mData->mSession.mState == SessionState_Spawning
7246 || mData->mSession.mState == SessionState_Unlocking)
7247 return setError(VBOX_E_INVALID_OBJECT_STATE,
7248 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7249 mUserData->s.strName.c_str());
7250
7251 /* may not be busy */
7252 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7253
7254 /* get the path to the executable */
7255 char szPath[RTPATH_MAX];
7256 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7257 size_t sz = strlen(szPath);
7258 szPath[sz++] = RTPATH_DELIMITER;
7259 szPath[sz] = 0;
7260 char *cmd = szPath + sz;
7261 sz = sizeof(szPath) - sz;
7262
7263 int vrc = VINF_SUCCESS;
7264 RTPROCESS pid = NIL_RTPROCESS;
7265
7266 RTENV env = RTENV_DEFAULT;
7267
7268 if (!strEnvironment.isEmpty())
7269 {
7270 char *newEnvStr = NULL;
7271
7272 do
7273 {
7274 /* clone the current environment */
7275 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7276 AssertRCBreakStmt(vrc2, vrc = vrc2);
7277
7278 newEnvStr = RTStrDup(strEnvironment.c_str());
7279 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7280
7281 /* put new variables to the environment
7282 * (ignore empty variable names here since RTEnv API
7283 * intentionally doesn't do that) */
7284 char *var = newEnvStr;
7285 for (char *p = newEnvStr; *p; ++p)
7286 {
7287 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7288 {
7289 *p = '\0';
7290 if (*var)
7291 {
7292 char *val = strchr(var, '=');
7293 if (val)
7294 {
7295 *val++ = '\0';
7296 vrc2 = RTEnvSetEx(env, var, val);
7297 }
7298 else
7299 vrc2 = RTEnvUnsetEx(env, var);
7300 if (RT_FAILURE(vrc2))
7301 break;
7302 }
7303 var = p + 1;
7304 }
7305 }
7306 if (RT_SUCCESS(vrc2) && *var)
7307 vrc2 = RTEnvPutEx(env, var);
7308
7309 AssertRCBreakStmt(vrc2, vrc = vrc2);
7310 }
7311 while (0);
7312
7313 if (newEnvStr != NULL)
7314 RTStrFree(newEnvStr);
7315 }
7316
7317#ifdef VBOX_WITH_QTGUI
7318 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7319 {
7320# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7321 /* Modify the base path so that we don't need to use ".." below. */
7322 RTPathStripTrailingSlash(szPath);
7323 RTPathStripFilename(szPath);
7324 sz = strlen(szPath);
7325 cmd = szPath + sz;
7326 sz = sizeof(szPath) - sz;
7327
7328#define OSX_APP_NAME "VirtualBoxVM"
7329#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7330
7331 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7332 if ( strAppOverride.contains(".")
7333 || strAppOverride.contains("/")
7334 || strAppOverride.contains("\\")
7335 || strAppOverride.contains(":"))
7336 strAppOverride.setNull();
7337 Utf8Str strAppPath;
7338 if (!strAppOverride.isEmpty())
7339 {
7340 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7341 Utf8Str strFullPath(szPath);
7342 strFullPath.append(strAppPath);
7343 /* there is a race, but people using this deserve the failure */
7344 if (!RTFileExists(strFullPath.c_str()))
7345 strAppOverride.setNull();
7346 }
7347 if (strAppOverride.isEmpty())
7348 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7349 const char *VirtualBox_exe = strAppPath.c_str();
7350 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
7351# else
7352 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7353 Assert(sz >= sizeof(VirtualBox_exe));
7354# endif
7355 strcpy(cmd, VirtualBox_exe);
7356
7357 Utf8Str idStr = mData->mUuid.toString();
7358 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7359 "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7360 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7361 }
7362#else /* !VBOX_WITH_QTGUI */
7363 if (0)
7364 ;
7365#endif /* VBOX_WITH_QTGUI */
7366
7367 else
7368
7369#ifdef VBOX_WITH_VBOXSDL
7370 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7371 {
7372 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7373 Assert(sz >= sizeof(VBoxSDL_exe));
7374 strcpy(cmd, VBoxSDL_exe);
7375
7376 Utf8Str idStr = mData->mUuid.toString();
7377 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7378 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7379 }
7380#else /* !VBOX_WITH_VBOXSDL */
7381 if (0)
7382 ;
7383#endif /* !VBOX_WITH_VBOXSDL */
7384
7385 else
7386
7387#ifdef VBOX_WITH_HEADLESS
7388 if ( strFrontend == "headless"
7389 || strFrontend == "capture"
7390 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7391 )
7392 {
7393 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7394 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7395 * and a VM works even if the server has not been installed.
7396 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7397 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7398 * differently in 4.0 and 3.x.
7399 */
7400 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7401 Assert(sz >= sizeof(VBoxHeadless_exe));
7402 strcpy(cmd, VBoxHeadless_exe);
7403
7404 Utf8Str idStr = mData->mUuid.toString();
7405 /* Leave space for "--capture" arg. */
7406 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7407 "--startvm", idStr.c_str(),
7408 "--vrde", "config",
7409 0, /* For "--capture". */
7410 0 };
7411 if (strFrontend == "capture")
7412 {
7413 unsigned pos = RT_ELEMENTS(args) - 2;
7414 args[pos] = "--capture";
7415 }
7416 vrc = RTProcCreate(szPath, args, env,
7417#ifdef RT_OS_WINDOWS
7418 RTPROC_FLAGS_NO_WINDOW
7419#else
7420 0
7421#endif
7422 , &pid);
7423 }
7424#else /* !VBOX_WITH_HEADLESS */
7425 if (0)
7426 ;
7427#endif /* !VBOX_WITH_HEADLESS */
7428 else
7429 {
7430 RTEnvDestroy(env);
7431 return setError(E_INVALIDARG,
7432 tr("Invalid frontend name: '%s'"),
7433 strFrontend.c_str());
7434 }
7435
7436 RTEnvDestroy(env);
7437
7438 if (RT_FAILURE(vrc))
7439 return setError(VBOX_E_IPRT_ERROR,
7440 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7441 mUserData->s.strName.c_str(), vrc);
7442
7443 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7444
7445 /*
7446 * Note that we don't release the lock here before calling the client,
7447 * because it doesn't need to call us back if called with a NULL argument.
7448 * Releasing the lock here is dangerous because we didn't prepare the
7449 * launch data yet, but the client we've just started may happen to be
7450 * too fast and call LockMachine() that will fail (because of PID, etc.),
7451 * so that the Machine will never get out of the Spawning session state.
7452 */
7453
7454 /* inform the session that it will be a remote one */
7455 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7456#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7457 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7458#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7459 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7460#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7461 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7462
7463 if (FAILED(rc))
7464 {
7465 /* restore the session state */
7466 mData->mSession.mState = SessionState_Unlocked;
7467 alock.release();
7468 mParent->i_addProcessToReap(pid);
7469 /* The failure may occur w/o any error info (from RPC), so provide one */
7470 return setError(VBOX_E_VM_ERROR,
7471 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7472 }
7473
7474 /* attach launch data to the machine */
7475 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7476 mData->mSession.mRemoteControls.push_back(aControl);
7477 mData->mSession.mProgress = aProgress;
7478 mData->mSession.mPID = pid;
7479 mData->mSession.mState = SessionState_Spawning;
7480 mData->mSession.mType = strFrontend;
7481
7482 alock.release();
7483 mParent->i_addProcessToReap(pid);
7484
7485 LogFlowThisFuncLeave();
7486 return S_OK;
7487}
7488
7489/**
7490 * Returns @c true if the given session machine instance has an open direct
7491 * session (and optionally also for direct sessions which are closing) and
7492 * returns the session control machine instance if so.
7493 *
7494 * Note that when the method returns @c false, the arguments remain unchanged.
7495 *
7496 * @param aMachine Session machine object.
7497 * @param aControl Direct session control object (optional).
7498 * @param aAllowClosing If true then additionally a session which is currently
7499 * being closed will also be allowed.
7500 *
7501 * @note locks this object for reading.
7502 */
7503bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7504 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7505 bool aAllowClosing /*= false*/)
7506{
7507 AutoLimitedCaller autoCaller(this);
7508 AssertComRCReturn(autoCaller.rc(), false);
7509
7510 /* just return false for inaccessible machines */
7511 if (getObjectState().getState() != ObjectState::Ready)
7512 return false;
7513
7514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7515
7516 if ( mData->mSession.mState == SessionState_Locked
7517 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7518 )
7519 {
7520 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7521
7522 aMachine = mData->mSession.mMachine;
7523
7524 if (aControl != NULL)
7525 *aControl = mData->mSession.mDirectControl;
7526
7527 return true;
7528 }
7529
7530 return false;
7531}
7532
7533/**
7534 * Returns @c true if the given machine has an spawning direct session.
7535 *
7536 * @note locks this object for reading.
7537 */
7538bool Machine::i_isSessionSpawning()
7539{
7540 AutoLimitedCaller autoCaller(this);
7541 AssertComRCReturn(autoCaller.rc(), false);
7542
7543 /* just return false for inaccessible machines */
7544 if (getObjectState().getState() != ObjectState::Ready)
7545 return false;
7546
7547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7548
7549 if (mData->mSession.mState == SessionState_Spawning)
7550 return true;
7551
7552 return false;
7553}
7554
7555/**
7556 * Called from the client watcher thread to check for unexpected client process
7557 * death during Session_Spawning state (e.g. before it successfully opened a
7558 * direct session).
7559 *
7560 * On Win32 and on OS/2, this method is called only when we've got the
7561 * direct client's process termination notification, so it always returns @c
7562 * true.
7563 *
7564 * On other platforms, this method returns @c true if the client process is
7565 * terminated and @c false if it's still alive.
7566 *
7567 * @note Locks this object for writing.
7568 */
7569bool Machine::i_checkForSpawnFailure()
7570{
7571 AutoCaller autoCaller(this);
7572 if (!autoCaller.isOk())
7573 {
7574 /* nothing to do */
7575 LogFlowThisFunc(("Already uninitialized!\n"));
7576 return true;
7577 }
7578
7579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7580
7581 if (mData->mSession.mState != SessionState_Spawning)
7582 {
7583 /* nothing to do */
7584 LogFlowThisFunc(("Not spawning any more!\n"));
7585 return true;
7586 }
7587
7588 HRESULT rc = S_OK;
7589
7590 /* PID not yet initialized, skip check. */
7591 if (mData->mSession.mPID == NIL_RTPROCESS)
7592 return false;
7593
7594 RTPROCSTATUS status;
7595 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7596
7597 if (vrc != VERR_PROCESS_RUNNING)
7598 {
7599 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7600 rc = setError(E_FAIL,
7601 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7602 i_getName().c_str(), status.iStatus);
7603 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7604 rc = setError(E_FAIL,
7605 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7606 i_getName().c_str(), status.iStatus);
7607 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7608 rc = setError(E_FAIL,
7609 tr("The virtual machine '%s' has terminated abnormally"),
7610 i_getName().c_str(), status.iStatus);
7611 else
7612 rc = setError(E_FAIL,
7613 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7614 i_getName().c_str(), vrc);
7615 }
7616
7617 if (FAILED(rc))
7618 {
7619 /* Close the remote session, remove the remote control from the list
7620 * and reset session state to Closed (@note keep the code in sync with
7621 * the relevant part in LockMachine()). */
7622
7623 Assert(mData->mSession.mRemoteControls.size() == 1);
7624 if (mData->mSession.mRemoteControls.size() == 1)
7625 {
7626 ErrorInfoKeeper eik;
7627 mData->mSession.mRemoteControls.front()->Uninitialize();
7628 }
7629
7630 mData->mSession.mRemoteControls.clear();
7631 mData->mSession.mState = SessionState_Unlocked;
7632
7633 /* finalize the progress after setting the state */
7634 if (!mData->mSession.mProgress.isNull())
7635 {
7636 mData->mSession.mProgress->notifyComplete(rc);
7637 mData->mSession.mProgress.setNull();
7638 }
7639
7640 mData->mSession.mPID = NIL_RTPROCESS;
7641
7642 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7643 return true;
7644 }
7645
7646 return false;
7647}
7648
7649/**
7650 * Checks whether the machine can be registered. If so, commits and saves
7651 * all settings.
7652 *
7653 * @note Must be called from mParent's write lock. Locks this object and
7654 * children for writing.
7655 */
7656HRESULT Machine::i_prepareRegister()
7657{
7658 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7659
7660 AutoLimitedCaller autoCaller(this);
7661 AssertComRCReturnRC(autoCaller.rc());
7662
7663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7664
7665 /* wait for state dependents to drop to zero */
7666 i_ensureNoStateDependencies();
7667
7668 if (!mData->mAccessible)
7669 return setError(VBOX_E_INVALID_OBJECT_STATE,
7670 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7671 mUserData->s.strName.c_str(),
7672 mData->mUuid.toString().c_str());
7673
7674 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7675
7676 if (mData->mRegistered)
7677 return setError(VBOX_E_INVALID_OBJECT_STATE,
7678 tr("The machine '%s' with UUID {%s} is already registered"),
7679 mUserData->s.strName.c_str(),
7680 mData->mUuid.toString().c_str());
7681
7682 HRESULT rc = S_OK;
7683
7684 // Ensure the settings are saved. If we are going to be registered and
7685 // no config file exists yet, create it by calling i_saveSettings() too.
7686 if ( (mData->flModifications)
7687 || (!mData->pMachineConfigFile->fileExists())
7688 )
7689 {
7690 rc = i_saveSettings(NULL);
7691 // no need to check whether VirtualBox.xml needs saving too since
7692 // we can't have a machine XML file rename pending
7693 if (FAILED(rc)) return rc;
7694 }
7695
7696 /* more config checking goes here */
7697
7698 if (SUCCEEDED(rc))
7699 {
7700 /* we may have had implicit modifications we want to fix on success */
7701 i_commit();
7702
7703 mData->mRegistered = true;
7704 }
7705 else
7706 {
7707 /* we may have had implicit modifications we want to cancel on failure*/
7708 i_rollback(false /* aNotify */);
7709 }
7710
7711 return rc;
7712}
7713
7714/**
7715 * Increases the number of objects dependent on the machine state or on the
7716 * registered state. Guarantees that these two states will not change at least
7717 * until #releaseStateDependency() is called.
7718 *
7719 * Depending on the @a aDepType value, additional state checks may be made.
7720 * These checks will set extended error info on failure. See
7721 * #checkStateDependency() for more info.
7722 *
7723 * If this method returns a failure, the dependency is not added and the caller
7724 * is not allowed to rely on any particular machine state or registration state
7725 * value and may return the failed result code to the upper level.
7726 *
7727 * @param aDepType Dependency type to add.
7728 * @param aState Current machine state (NULL if not interested).
7729 * @param aRegistered Current registered state (NULL if not interested).
7730 *
7731 * @note Locks this object for writing.
7732 */
7733HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7734 MachineState_T *aState /* = NULL */,
7735 BOOL *aRegistered /* = NULL */)
7736{
7737 AutoCaller autoCaller(this);
7738 AssertComRCReturnRC(autoCaller.rc());
7739
7740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7741
7742 HRESULT rc = i_checkStateDependency(aDepType);
7743 if (FAILED(rc)) return rc;
7744
7745 {
7746 if (mData->mMachineStateChangePending != 0)
7747 {
7748 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7749 * drop to zero so don't add more. It may make sense to wait a bit
7750 * and retry before reporting an error (since the pending state
7751 * transition should be really quick) but let's just assert for
7752 * now to see if it ever happens on practice. */
7753
7754 AssertFailed();
7755
7756 return setError(E_ACCESSDENIED,
7757 tr("Machine state change is in progress. Please retry the operation later."));
7758 }
7759
7760 ++mData->mMachineStateDeps;
7761 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7762 }
7763
7764 if (aState)
7765 *aState = mData->mMachineState;
7766 if (aRegistered)
7767 *aRegistered = mData->mRegistered;
7768
7769 return S_OK;
7770}
7771
7772/**
7773 * Decreases the number of objects dependent on the machine state.
7774 * Must always complete the #addStateDependency() call after the state
7775 * dependency is no more necessary.
7776 */
7777void Machine::i_releaseStateDependency()
7778{
7779 AutoCaller autoCaller(this);
7780 AssertComRCReturnVoid(autoCaller.rc());
7781
7782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7783
7784 /* releaseStateDependency() w/o addStateDependency()? */
7785 AssertReturnVoid(mData->mMachineStateDeps != 0);
7786 -- mData->mMachineStateDeps;
7787
7788 if (mData->mMachineStateDeps == 0)
7789 {
7790 /* inform i_ensureNoStateDependencies() that there are no more deps */
7791 if (mData->mMachineStateChangePending != 0)
7792 {
7793 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7794 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7795 }
7796 }
7797}
7798
7799Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7800{
7801 /* start with nothing found */
7802 Utf8Str strResult("");
7803
7804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7805
7806 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7807 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7808 // found:
7809 strResult = it->second; // source is a Utf8Str
7810
7811 return strResult;
7812}
7813
7814// protected methods
7815/////////////////////////////////////////////////////////////////////////////
7816
7817/**
7818 * Performs machine state checks based on the @a aDepType value. If a check
7819 * fails, this method will set extended error info, otherwise it will return
7820 * S_OK. It is supposed, that on failure, the caller will immediately return
7821 * the return value of this method to the upper level.
7822 *
7823 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7824 *
7825 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7826 * current state of this machine object allows to change settings of the
7827 * machine (i.e. the machine is not registered, or registered but not running
7828 * and not saved). It is useful to call this method from Machine setters
7829 * before performing any change.
7830 *
7831 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7832 * as for MutableStateDep except that if the machine is saved, S_OK is also
7833 * returned. This is useful in setters which allow changing machine
7834 * properties when it is in the saved state.
7835 *
7836 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7837 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7838 * Aborted).
7839 *
7840 * @param aDepType Dependency type to check.
7841 *
7842 * @note Non Machine based classes should use #addStateDependency() and
7843 * #releaseStateDependency() methods or the smart AutoStateDependency
7844 * template.
7845 *
7846 * @note This method must be called from under this object's read or write
7847 * lock.
7848 */
7849HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7850{
7851 switch (aDepType)
7852 {
7853 case AnyStateDep:
7854 {
7855 break;
7856 }
7857 case MutableStateDep:
7858 {
7859 if ( mData->mRegistered
7860 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7861 Paused should actually be included here... (Live Migration) */
7862 || ( mData->mMachineState != MachineState_Paused
7863 && mData->mMachineState != MachineState_Running
7864 && mData->mMachineState != MachineState_Aborted
7865 && mData->mMachineState != MachineState_Teleported
7866 && mData->mMachineState != MachineState_PoweredOff
7867 )
7868 )
7869 )
7870 return setError(VBOX_E_INVALID_VM_STATE,
7871 tr("The machine is not mutable (state is %s)"),
7872 Global::stringifyMachineState(mData->mMachineState));
7873 break;
7874 }
7875 case MutableOrSavedStateDep:
7876 {
7877 if ( mData->mRegistered
7878 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7879 Paused should actually be included here... (Live Migration) */
7880 || ( mData->mMachineState != MachineState_Paused
7881 && mData->mMachineState != MachineState_Running
7882 && mData->mMachineState != MachineState_Aborted
7883 && mData->mMachineState != MachineState_Teleported
7884 && mData->mMachineState != MachineState_Saved
7885 && mData->mMachineState != MachineState_PoweredOff
7886 )
7887 )
7888 )
7889 return setError(VBOX_E_INVALID_VM_STATE,
7890 tr("The machine is not mutable (state is %s)"),
7891 Global::stringifyMachineState(mData->mMachineState));
7892 break;
7893 }
7894 case OfflineStateDep:
7895 {
7896 if ( mData->mRegistered
7897 && ( !i_isSessionMachine()
7898 || ( mData->mMachineState != MachineState_PoweredOff
7899 && mData->mMachineState != MachineState_Saved
7900 && mData->mMachineState != MachineState_Aborted
7901 && mData->mMachineState != MachineState_Teleported
7902 )
7903 )
7904 )
7905 return setError(VBOX_E_INVALID_VM_STATE,
7906 tr("The machine is not offline (state is %s)"),
7907 Global::stringifyMachineState(mData->mMachineState));
7908 break;
7909 }
7910 }
7911
7912 return S_OK;
7913}
7914
7915/**
7916 * Helper to initialize all associated child objects and allocate data
7917 * structures.
7918 *
7919 * This method must be called as a part of the object's initialization procedure
7920 * (usually done in the #init() method).
7921 *
7922 * @note Must be called only from #init() or from #registeredInit().
7923 */
7924HRESULT Machine::initDataAndChildObjects()
7925{
7926 AutoCaller autoCaller(this);
7927 AssertComRCReturnRC(autoCaller.rc());
7928 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
7929 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7930
7931 AssertReturn(!mData->mAccessible, E_FAIL);
7932
7933 /* allocate data structures */
7934 mSSData.allocate();
7935 mUserData.allocate();
7936 mHWData.allocate();
7937 mMediaData.allocate();
7938 mStorageControllers.allocate();
7939 mUSBControllers.allocate();
7940
7941 /* initialize mOSTypeId */
7942 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
7943
7944 /* create associated BIOS settings object */
7945 unconst(mBIOSSettings).createObject();
7946 mBIOSSettings->init(this);
7947
7948 /* create an associated VRDE object (default is disabled) */
7949 unconst(mVRDEServer).createObject();
7950 mVRDEServer->init(this);
7951
7952 /* create associated serial port objects */
7953 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
7954 {
7955 unconst(mSerialPorts[slot]).createObject();
7956 mSerialPorts[slot]->init(this, slot);
7957 }
7958
7959 /* create associated parallel port objects */
7960 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
7961 {
7962 unconst(mParallelPorts[slot]).createObject();
7963 mParallelPorts[slot]->init(this, slot);
7964 }
7965
7966 /* create the audio adapter object (always present, default is disabled) */
7967 unconst(mAudioAdapter).createObject();
7968 mAudioAdapter->init(this);
7969
7970 /* create the USB device filters object (always present) */
7971 unconst(mUSBDeviceFilters).createObject();
7972 mUSBDeviceFilters->init(this);
7973
7974 /* create associated network adapter objects */
7975 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7976 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7977 {
7978 unconst(mNetworkAdapters[slot]).createObject();
7979 mNetworkAdapters[slot]->init(this, slot);
7980 }
7981
7982 /* create the bandwidth control */
7983 unconst(mBandwidthControl).createObject();
7984 mBandwidthControl->init(this);
7985
7986 return S_OK;
7987}
7988
7989/**
7990 * Helper to uninitialize all associated child objects and to free all data
7991 * structures.
7992 *
7993 * This method must be called as a part of the object's uninitialization
7994 * procedure (usually done in the #uninit() method).
7995 *
7996 * @note Must be called only from #uninit() or from #registeredInit().
7997 */
7998void Machine::uninitDataAndChildObjects()
7999{
8000 AutoCaller autoCaller(this);
8001 AssertComRCReturnVoid(autoCaller.rc());
8002 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8003 || getObjectState().getState() == ObjectState::Limited);
8004
8005 /* tell all our other child objects we've been uninitialized */
8006 if (mBandwidthControl)
8007 {
8008 mBandwidthControl->uninit();
8009 unconst(mBandwidthControl).setNull();
8010 }
8011
8012 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8013 {
8014 if (mNetworkAdapters[slot])
8015 {
8016 mNetworkAdapters[slot]->uninit();
8017 unconst(mNetworkAdapters[slot]).setNull();
8018 }
8019 }
8020
8021 if (mUSBDeviceFilters)
8022 {
8023 mUSBDeviceFilters->uninit();
8024 unconst(mUSBDeviceFilters).setNull();
8025 }
8026
8027 if (mAudioAdapter)
8028 {
8029 mAudioAdapter->uninit();
8030 unconst(mAudioAdapter).setNull();
8031 }
8032
8033 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8034 {
8035 if (mParallelPorts[slot])
8036 {
8037 mParallelPorts[slot]->uninit();
8038 unconst(mParallelPorts[slot]).setNull();
8039 }
8040 }
8041
8042 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8043 {
8044 if (mSerialPorts[slot])
8045 {
8046 mSerialPorts[slot]->uninit();
8047 unconst(mSerialPorts[slot]).setNull();
8048 }
8049 }
8050
8051 if (mVRDEServer)
8052 {
8053 mVRDEServer->uninit();
8054 unconst(mVRDEServer).setNull();
8055 }
8056
8057 if (mBIOSSettings)
8058 {
8059 mBIOSSettings->uninit();
8060 unconst(mBIOSSettings).setNull();
8061 }
8062
8063 /* Deassociate media (only when a real Machine or a SnapshotMachine
8064 * instance is uninitialized; SessionMachine instances refer to real
8065 * Machine media). This is necessary for a clean re-initialization of
8066 * the VM after successfully re-checking the accessibility state. Note
8067 * that in case of normal Machine or SnapshotMachine uninitialization (as
8068 * a result of unregistering or deleting the snapshot), outdated media
8069 * attachments will already be uninitialized and deleted, so this
8070 * code will not affect them. */
8071 if ( !!mMediaData
8072 && (!i_isSessionMachine())
8073 )
8074 {
8075 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8076 it != mMediaData->mAttachments.end();
8077 ++it)
8078 {
8079 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8080 if (pMedium.isNull())
8081 continue;
8082 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8083 AssertComRC(rc);
8084 }
8085 }
8086
8087 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8088 {
8089 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8090 if (mData->mFirstSnapshot)
8091 {
8092 // snapshots tree is protected by machine write lock; strictly
8093 // this isn't necessary here since we're deleting the entire
8094 // machine, but otherwise we assert in Snapshot::uninit()
8095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8096 mData->mFirstSnapshot->uninit();
8097 mData->mFirstSnapshot.setNull();
8098 }
8099
8100 mData->mCurrentSnapshot.setNull();
8101 }
8102
8103 /* free data structures (the essential mData structure is not freed here
8104 * since it may be still in use) */
8105 mMediaData.free();
8106 mStorageControllers.free();
8107 mUSBControllers.free();
8108 mHWData.free();
8109 mUserData.free();
8110 mSSData.free();
8111}
8112
8113/**
8114 * Returns a pointer to the Machine object for this machine that acts like a
8115 * parent for complex machine data objects such as shared folders, etc.
8116 *
8117 * For primary Machine objects and for SnapshotMachine objects, returns this
8118 * object's pointer itself. For SessionMachine objects, returns the peer
8119 * (primary) machine pointer.
8120 */
8121Machine* Machine::i_getMachine()
8122{
8123 if (i_isSessionMachine())
8124 return (Machine*)mPeer;
8125 return this;
8126}
8127
8128/**
8129 * Makes sure that there are no machine state dependents. If necessary, waits
8130 * for the number of dependents to drop to zero.
8131 *
8132 * Make sure this method is called from under this object's write lock to
8133 * guarantee that no new dependents may be added when this method returns
8134 * control to the caller.
8135 *
8136 * @note Locks this object for writing. The lock will be released while waiting
8137 * (if necessary).
8138 *
8139 * @warning To be used only in methods that change the machine state!
8140 */
8141void Machine::i_ensureNoStateDependencies()
8142{
8143 AssertReturnVoid(isWriteLockOnCurrentThread());
8144
8145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8146
8147 /* Wait for all state dependents if necessary */
8148 if (mData->mMachineStateDeps != 0)
8149 {
8150 /* lazy semaphore creation */
8151 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8152 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8153
8154 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8155 mData->mMachineStateDeps));
8156
8157 ++mData->mMachineStateChangePending;
8158
8159 /* reset the semaphore before waiting, the last dependent will signal
8160 * it */
8161 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8162
8163 alock.release();
8164
8165 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8166
8167 alock.acquire();
8168
8169 -- mData->mMachineStateChangePending;
8170 }
8171}
8172
8173/**
8174 * Changes the machine state and informs callbacks.
8175 *
8176 * This method is not intended to fail so it either returns S_OK or asserts (and
8177 * returns a failure).
8178 *
8179 * @note Locks this object for writing.
8180 */
8181HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8182{
8183 LogFlowThisFuncEnter();
8184 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8185
8186 AutoCaller autoCaller(this);
8187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8188
8189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8190
8191 /* wait for state dependents to drop to zero */
8192 i_ensureNoStateDependencies();
8193
8194 if (mData->mMachineState != aMachineState)
8195 {
8196 mData->mMachineState = aMachineState;
8197
8198 RTTimeNow(&mData->mLastStateChange);
8199
8200 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8201 }
8202
8203 LogFlowThisFuncLeave();
8204 return S_OK;
8205}
8206
8207/**
8208 * Searches for a shared folder with the given logical name
8209 * in the collection of shared folders.
8210 *
8211 * @param aName logical name of the shared folder
8212 * @param aSharedFolder where to return the found object
8213 * @param aSetError whether to set the error info if the folder is
8214 * not found
8215 * @return
8216 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8217 *
8218 * @note
8219 * must be called from under the object's lock!
8220 */
8221HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8222 ComObjPtr<SharedFolder> &aSharedFolder,
8223 bool aSetError /* = false */)
8224{
8225 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8226 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8227 it != mHWData->mSharedFolders.end();
8228 ++it)
8229 {
8230 SharedFolder *pSF = *it;
8231 AutoCaller autoCaller(pSF);
8232 if (pSF->i_getName() == aName)
8233 {
8234 aSharedFolder = pSF;
8235 rc = S_OK;
8236 break;
8237 }
8238 }
8239
8240 if (aSetError && FAILED(rc))
8241 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8242
8243 return rc;
8244}
8245
8246/**
8247 * Initializes all machine instance data from the given settings structures
8248 * from XML. The exception is the machine UUID which needs special handling
8249 * depending on the caller's use case, so the caller needs to set that herself.
8250 *
8251 * This gets called in several contexts during machine initialization:
8252 *
8253 * -- When machine XML exists on disk already and needs to be loaded into memory,
8254 * for example, from registeredInit() to load all registered machines on
8255 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8256 * attached to the machine should be part of some media registry already.
8257 *
8258 * -- During OVF import, when a machine config has been constructed from an
8259 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8260 * ensure that the media listed as attachments in the config (which have
8261 * been imported from the OVF) receive the correct registry ID.
8262 *
8263 * -- During VM cloning.
8264 *
8265 * @param config Machine settings from XML.
8266 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8267 * for each attached medium in the config.
8268 * @return
8269 */
8270HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8271 const Guid *puuidRegistry)
8272{
8273 // copy name, description, OS type, teleporter, UTC etc.
8274 mUserData->s = config.machineUserData;
8275
8276 // Decode the Icon overide data from config userdata and set onto Machine.
8277 #define DECODE_STR_MAX _1M
8278 const char* pszStr = config.machineUserData.ovIcon.c_str();
8279 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8280 if (cbOut > DECODE_STR_MAX)
8281 return setError(E_FAIL,
8282 tr("Icon Data too long.'%d' > '%d'"),
8283 cbOut,
8284 DECODE_STR_MAX);
8285 com::SafeArray<BYTE> iconByte(cbOut);
8286 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8287 if (FAILED(rc))
8288 return setError(E_FAIL,
8289 tr("Failure to Decode Icon Data. '%s' (%d)"),
8290 pszStr,
8291 rc);
8292 mUserData->mIcon.resize(iconByte.size());
8293 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8294
8295 // look up the object by Id to check it is valid
8296 ComPtr<IGuestOSType> guestOSType;
8297 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8298 guestOSType.asOutParam());
8299 if (FAILED(rc)) return rc;
8300
8301 // stateFile (optional)
8302 if (config.strStateFile.isEmpty())
8303 mSSData->strStateFilePath.setNull();
8304 else
8305 {
8306 Utf8Str stateFilePathFull(config.strStateFile);
8307 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8308 if (RT_FAILURE(vrc))
8309 return setError(E_FAIL,
8310 tr("Invalid saved state file path '%s' (%Rrc)"),
8311 config.strStateFile.c_str(),
8312 vrc);
8313 mSSData->strStateFilePath = stateFilePathFull;
8314 }
8315
8316 // snapshot folder needs special processing so set it again
8317 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8318 if (FAILED(rc)) return rc;
8319
8320 /* Copy the extra data items (Not in any case config is already the same as
8321 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8322 * make sure the extra data map is copied). */
8323 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8324
8325 /* currentStateModified (optional, default is true) */
8326 mData->mCurrentStateModified = config.fCurrentStateModified;
8327
8328 mData->mLastStateChange = config.timeLastStateChange;
8329
8330 /*
8331 * note: all mUserData members must be assigned prior this point because
8332 * we need to commit changes in order to let mUserData be shared by all
8333 * snapshot machine instances.
8334 */
8335 mUserData.commitCopy();
8336
8337 // machine registry, if present (must be loaded before snapshots)
8338 if (config.canHaveOwnMediaRegistry())
8339 {
8340 // determine machine folder
8341 Utf8Str strMachineFolder = i_getSettingsFileFull();
8342 strMachineFolder.stripFilename();
8343 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8344 config.mediaRegistry,
8345 strMachineFolder);
8346 if (FAILED(rc)) return rc;
8347 }
8348
8349 /* Snapshot node (optional) */
8350 size_t cRootSnapshots;
8351 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8352 {
8353 // there must be only one root snapshot
8354 Assert(cRootSnapshots == 1);
8355
8356 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8357
8358 rc = i_loadSnapshot(snap,
8359 config.uuidCurrentSnapshot,
8360 NULL); // no parent == first snapshot
8361 if (FAILED(rc)) return rc;
8362 }
8363
8364 // hardware data
8365 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8366 if (FAILED(rc)) return rc;
8367
8368 // load storage controllers
8369 rc = i_loadStorageControllers(config.storageMachine,
8370 puuidRegistry,
8371 NULL /* puuidSnapshot */);
8372 if (FAILED(rc)) return rc;
8373
8374 /*
8375 * NOTE: the assignment below must be the last thing to do,
8376 * otherwise it will be not possible to change the settings
8377 * somewhere in the code above because all setters will be
8378 * blocked by i_checkStateDependency(MutableStateDep).
8379 */
8380
8381 /* set the machine state to Aborted or Saved when appropriate */
8382 if (config.fAborted)
8383 {
8384 mSSData->strStateFilePath.setNull();
8385
8386 /* no need to use i_setMachineState() during init() */
8387 mData->mMachineState = MachineState_Aborted;
8388 }
8389 else if (!mSSData->strStateFilePath.isEmpty())
8390 {
8391 /* no need to use i_setMachineState() during init() */
8392 mData->mMachineState = MachineState_Saved;
8393 }
8394
8395 // after loading settings, we are no longer different from the XML on disk
8396 mData->flModifications = 0;
8397
8398 return S_OK;
8399}
8400
8401/**
8402 * Recursively loads all snapshots starting from the given.
8403 *
8404 * @param aNode <Snapshot> node.
8405 * @param aCurSnapshotId Current snapshot ID from the settings file.
8406 * @param aParentSnapshot Parent snapshot.
8407 */
8408HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8409 const Guid &aCurSnapshotId,
8410 Snapshot *aParentSnapshot)
8411{
8412 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8413 AssertReturn(!i_isSessionMachine(), E_FAIL);
8414
8415 HRESULT rc = S_OK;
8416
8417 Utf8Str strStateFile;
8418 if (!data.strStateFile.isEmpty())
8419 {
8420 /* optional */
8421 strStateFile = data.strStateFile;
8422 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8423 if (RT_FAILURE(vrc))
8424 return setError(E_FAIL,
8425 tr("Invalid saved state file path '%s' (%Rrc)"),
8426 strStateFile.c_str(),
8427 vrc);
8428 }
8429
8430 /* create a snapshot machine object */
8431 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8432 pSnapshotMachine.createObject();
8433 rc = pSnapshotMachine->initFromSettings(this,
8434 data.hardware,
8435 &data.debugging,
8436 &data.autostart,
8437 data.storage,
8438 data.uuid.ref(),
8439 strStateFile);
8440 if (FAILED(rc)) return rc;
8441
8442 /* create a snapshot object */
8443 ComObjPtr<Snapshot> pSnapshot;
8444 pSnapshot.createObject();
8445 /* initialize the snapshot */
8446 rc = pSnapshot->init(mParent, // VirtualBox object
8447 data.uuid,
8448 data.strName,
8449 data.strDescription,
8450 data.timestamp,
8451 pSnapshotMachine,
8452 aParentSnapshot);
8453 if (FAILED(rc)) return rc;
8454
8455 /* memorize the first snapshot if necessary */
8456 if (!mData->mFirstSnapshot)
8457 mData->mFirstSnapshot = pSnapshot;
8458
8459 /* memorize the current snapshot when appropriate */
8460 if ( !mData->mCurrentSnapshot
8461 && pSnapshot->i_getId() == aCurSnapshotId
8462 )
8463 mData->mCurrentSnapshot = pSnapshot;
8464
8465 // now create the children
8466 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8467 it != data.llChildSnapshots.end();
8468 ++it)
8469 {
8470 const settings::Snapshot &childData = *it;
8471 // recurse
8472 rc = i_loadSnapshot(childData,
8473 aCurSnapshotId,
8474 pSnapshot); // parent = the one we created above
8475 if (FAILED(rc)) return rc;
8476 }
8477
8478 return rc;
8479}
8480
8481/**
8482 * Loads settings into mHWData.
8483 *
8484 * @param data Reference to the hardware settings.
8485 * @param pDbg Pointer to the debugging settings.
8486 * @param pAutostart Pointer to the autostart settings.
8487 */
8488HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8489 const settings::Autostart *pAutostart)
8490{
8491 AssertReturn(!i_isSessionMachine(), E_FAIL);
8492
8493 HRESULT rc = S_OK;
8494
8495 try
8496 {
8497 /* The hardware version attribute (optional). */
8498 mHWData->mHWVersion = data.strVersion;
8499 mHWData->mHardwareUUID = data.uuid;
8500
8501 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8502 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8503 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8504 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8505 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8506 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8507 mHWData->mPAEEnabled = data.fPAE;
8508 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8509 mHWData->mLongMode = data.enmLongMode;
8510 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8511 mHWData->mCPUCount = data.cCPUs;
8512 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8513 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8514
8515 // cpu
8516 if (mHWData->mCPUHotPlugEnabled)
8517 {
8518 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8519 it != data.llCpus.end();
8520 ++it)
8521 {
8522 const settings::Cpu &cpu = *it;
8523
8524 mHWData->mCPUAttached[cpu.ulId] = true;
8525 }
8526 }
8527
8528 // cpuid leafs
8529 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8530 it != data.llCpuIdLeafs.end();
8531 ++it)
8532 {
8533 const settings::CpuIdLeaf &leaf = *it;
8534
8535 switch (leaf.ulId)
8536 {
8537 case 0x0:
8538 case 0x1:
8539 case 0x2:
8540 case 0x3:
8541 case 0x4:
8542 case 0x5:
8543 case 0x6:
8544 case 0x7:
8545 case 0x8:
8546 case 0x9:
8547 case 0xA:
8548 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8549 break;
8550
8551 case 0x80000000:
8552 case 0x80000001:
8553 case 0x80000002:
8554 case 0x80000003:
8555 case 0x80000004:
8556 case 0x80000005:
8557 case 0x80000006:
8558 case 0x80000007:
8559 case 0x80000008:
8560 case 0x80000009:
8561 case 0x8000000A:
8562 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8563 break;
8564
8565 default:
8566 /* just ignore */
8567 break;
8568 }
8569 }
8570
8571 mHWData->mMemorySize = data.ulMemorySizeMB;
8572 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8573
8574 // boot order
8575 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8576 {
8577 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8578 if (it == data.mapBootOrder.end())
8579 mHWData->mBootOrder[i] = DeviceType_Null;
8580 else
8581 mHWData->mBootOrder[i] = it->second;
8582 }
8583
8584 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8585 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8586 mHWData->mMonitorCount = data.cMonitors;
8587 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8588 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8589 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8590 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8591 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8592 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8593 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8594 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8595 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8596 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8597 if (!data.strVideoCaptureFile.isEmpty())
8598 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8599 else
8600 mHWData->mVideoCaptureFile.setNull();
8601 mHWData->mFirmwareType = data.firmwareType;
8602 mHWData->mPointingHIDType = data.pointingHIDType;
8603 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8604 mHWData->mChipsetType = data.chipsetType;
8605 mHWData->mParavirtProvider = data.paravirtProvider;
8606 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8607 mHWData->mHPETEnabled = data.fHPETEnabled;
8608
8609 /* VRDEServer */
8610 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8611 if (FAILED(rc)) return rc;
8612
8613 /* BIOS */
8614 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8615 if (FAILED(rc)) return rc;
8616
8617 // Bandwidth control (must come before network adapters)
8618 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8619 if (FAILED(rc)) return rc;
8620
8621 /* Shared folders */
8622 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8623 it != data.usbSettings.llUSBControllers.end();
8624 ++it)
8625 {
8626 const settings::USBController &settingsCtrl = *it;
8627 ComObjPtr<USBController> newCtrl;
8628
8629 newCtrl.createObject();
8630 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8631 mUSBControllers->push_back(newCtrl);
8632 }
8633
8634 /* USB device filters */
8635 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8636 if (FAILED(rc)) return rc;
8637
8638 // network adapters
8639 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8640 uint32_t oldCount = mNetworkAdapters.size();
8641 if (newCount > oldCount)
8642 {
8643 mNetworkAdapters.resize(newCount);
8644 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8645 {
8646 unconst(mNetworkAdapters[slot]).createObject();
8647 mNetworkAdapters[slot]->init(this, slot);
8648 }
8649 }
8650 else if (newCount < oldCount)
8651 mNetworkAdapters.resize(newCount);
8652 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8653 it != data.llNetworkAdapters.end();
8654 ++it)
8655 {
8656 const settings::NetworkAdapter &nic = *it;
8657
8658 /* slot unicity is guaranteed by XML Schema */
8659 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8660 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8661 if (FAILED(rc)) return rc;
8662 }
8663
8664 // serial ports
8665 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8666 it != data.llSerialPorts.end();
8667 ++it)
8668 {
8669 const settings::SerialPort &s = *it;
8670
8671 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8672 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8673 if (FAILED(rc)) return rc;
8674 }
8675
8676 // parallel ports (optional)
8677 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8678 it != data.llParallelPorts.end();
8679 ++it)
8680 {
8681 const settings::ParallelPort &p = *it;
8682
8683 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8684 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8685 if (FAILED(rc)) return rc;
8686 }
8687
8688 /* AudioAdapter */
8689 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8690 if (FAILED(rc)) return rc;
8691
8692 /* Shared folders */
8693 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8694 it != data.llSharedFolders.end();
8695 ++it)
8696 {
8697 const settings::SharedFolder &sf = *it;
8698
8699 ComObjPtr<SharedFolder> sharedFolder;
8700 /* Check for double entries. Not allowed! */
8701 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8702 if (SUCCEEDED(rc))
8703 return setError(VBOX_E_OBJECT_IN_USE,
8704 tr("Shared folder named '%s' already exists"),
8705 sf.strName.c_str());
8706
8707 /* Create the new shared folder. Don't break on error. This will be
8708 * reported when the machine starts. */
8709 sharedFolder.createObject();
8710 rc = sharedFolder->init(i_getMachine(),
8711 sf.strName,
8712 sf.strHostPath,
8713 RT_BOOL(sf.fWritable),
8714 RT_BOOL(sf.fAutoMount),
8715 false /* fFailOnError */);
8716 if (FAILED(rc)) return rc;
8717 mHWData->mSharedFolders.push_back(sharedFolder);
8718 }
8719
8720 // Clipboard
8721 mHWData->mClipboardMode = data.clipboardMode;
8722
8723 // drag'n'drop
8724 mHWData->mDnDMode = data.dndMode;
8725
8726 // guest settings
8727 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8728
8729 // IO settings
8730 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8731 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8732
8733 // Host PCI devices
8734 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8735 it != data.pciAttachments.end();
8736 ++it)
8737 {
8738 const settings::HostPCIDeviceAttachment &hpda = *it;
8739 ComObjPtr<PCIDeviceAttachment> pda;
8740
8741 pda.createObject();
8742 pda->i_loadSettings(this, hpda);
8743 mHWData->mPCIDeviceAssignments.push_back(pda);
8744 }
8745
8746 /*
8747 * (The following isn't really real hardware, but it lives in HWData
8748 * for reasons of convenience.)
8749 */
8750
8751#ifdef VBOX_WITH_GUEST_PROPS
8752 /* Guest properties (optional) */
8753 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8754 it != data.llGuestProperties.end();
8755 ++it)
8756 {
8757 const settings::GuestProperty &prop = *it;
8758 uint32_t fFlags = guestProp::NILFLAG;
8759 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8760 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8761 mHWData->mGuestProperties[prop.strName] = property;
8762 }
8763
8764 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8765#endif /* VBOX_WITH_GUEST_PROPS defined */
8766
8767 rc = i_loadDebugging(pDbg);
8768 if (FAILED(rc))
8769 return rc;
8770
8771 mHWData->mAutostart = *pAutostart;
8772
8773 /* default frontend */
8774 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8775 }
8776 catch(std::bad_alloc &)
8777 {
8778 return E_OUTOFMEMORY;
8779 }
8780
8781 AssertComRC(rc);
8782 return rc;
8783}
8784
8785/**
8786 * Called from Machine::loadHardware() to load the debugging settings of the
8787 * machine.
8788 *
8789 * @param pDbg Pointer to the settings.
8790 */
8791HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8792{
8793 mHWData->mDebugging = *pDbg;
8794 /* no more processing currently required, this will probably change. */
8795 return S_OK;
8796}
8797
8798/**
8799 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8800 *
8801 * @param data
8802 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8803 * @param puuidSnapshot
8804 * @return
8805 */
8806HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8807 const Guid *puuidRegistry,
8808 const Guid *puuidSnapshot)
8809{
8810 AssertReturn(!i_isSessionMachine(), E_FAIL);
8811
8812 HRESULT rc = S_OK;
8813
8814 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8815 it != data.llStorageControllers.end();
8816 ++it)
8817 {
8818 const settings::StorageController &ctlData = *it;
8819
8820 ComObjPtr<StorageController> pCtl;
8821 /* Try to find one with the name first. */
8822 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8823 if (SUCCEEDED(rc))
8824 return setError(VBOX_E_OBJECT_IN_USE,
8825 tr("Storage controller named '%s' already exists"),
8826 ctlData.strName.c_str());
8827
8828 pCtl.createObject();
8829 rc = pCtl->init(this,
8830 ctlData.strName,
8831 ctlData.storageBus,
8832 ctlData.ulInstance,
8833 ctlData.fBootable);
8834 if (FAILED(rc)) return rc;
8835
8836 mStorageControllers->push_back(pCtl);
8837
8838 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8839 if (FAILED(rc)) return rc;
8840
8841 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8842 if (FAILED(rc)) return rc;
8843
8844 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8845 if (FAILED(rc)) return rc;
8846
8847 /* Set IDE emulation settings (only for AHCI controller). */
8848 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8849 {
8850 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8851 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8852 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8853 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8854 )
8855 return rc;
8856 }
8857
8858 /* Load the attached devices now. */
8859 rc = i_loadStorageDevices(pCtl,
8860 ctlData,
8861 puuidRegistry,
8862 puuidSnapshot);
8863 if (FAILED(rc)) return rc;
8864 }
8865
8866 return S_OK;
8867}
8868
8869/**
8870 * Called from i_loadStorageControllers for a controller's devices.
8871 *
8872 * @param aStorageController
8873 * @param data
8874 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8875 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8876 * @return
8877 */
8878HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8879 const settings::StorageController &data,
8880 const Guid *puuidRegistry,
8881 const Guid *puuidSnapshot)
8882{
8883 HRESULT rc = S_OK;
8884
8885 /* paranoia: detect duplicate attachments */
8886 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8887 it != data.llAttachedDevices.end();
8888 ++it)
8889 {
8890 const settings::AttachedDevice &ad = *it;
8891
8892 for (settings::AttachedDevicesList::const_iterator it2 = it;
8893 it2 != data.llAttachedDevices.end();
8894 ++it2)
8895 {
8896 if (it == it2)
8897 continue;
8898
8899 const settings::AttachedDevice &ad2 = *it2;
8900
8901 if ( ad.lPort == ad2.lPort
8902 && ad.lDevice == ad2.lDevice)
8903 {
8904 return setError(E_FAIL,
8905 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8906 aStorageController->i_getName().c_str(),
8907 ad.lPort,
8908 ad.lDevice,
8909 mUserData->s.strName.c_str());
8910 }
8911 }
8912 }
8913
8914 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8915 it != data.llAttachedDevices.end();
8916 ++it)
8917 {
8918 const settings::AttachedDevice &dev = *it;
8919 ComObjPtr<Medium> medium;
8920
8921 switch (dev.deviceType)
8922 {
8923 case DeviceType_Floppy:
8924 case DeviceType_DVD:
8925 if (dev.strHostDriveSrc.isNotEmpty())
8926 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
8927 false /* fRefresh */, medium);
8928 else
8929 rc = mParent->i_findRemoveableMedium(dev.deviceType,
8930 dev.uuid,
8931 false /* fRefresh */,
8932 false /* aSetError */,
8933 medium);
8934 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8935 // This is not an error. The host drive or UUID might have vanished, so just go
8936 // ahead without this removeable medium attachment
8937 rc = S_OK;
8938 break;
8939
8940 case DeviceType_HardDisk:
8941 {
8942 /* find a hard disk by UUID */
8943 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8944 if (FAILED(rc))
8945 {
8946 if (i_isSnapshotMachine())
8947 {
8948 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8949 // so the user knows that the bad disk is in a snapshot somewhere
8950 com::ErrorInfo info;
8951 return setError(E_FAIL,
8952 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8953 puuidSnapshot->raw(),
8954 info.getText().raw());
8955 }
8956 else
8957 return rc;
8958 }
8959
8960 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8961
8962 if (medium->i_getType() == MediumType_Immutable)
8963 {
8964 if (i_isSnapshotMachine())
8965 return setError(E_FAIL,
8966 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8967 "of the virtual machine '%s' ('%s')"),
8968 medium->i_getLocationFull().c_str(),
8969 dev.uuid.raw(),
8970 puuidSnapshot->raw(),
8971 mUserData->s.strName.c_str(),
8972 mData->m_strConfigFileFull.c_str());
8973
8974 return setError(E_FAIL,
8975 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8976 medium->i_getLocationFull().c_str(),
8977 dev.uuid.raw(),
8978 mUserData->s.strName.c_str(),
8979 mData->m_strConfigFileFull.c_str());
8980 }
8981
8982 if (medium->i_getType() == MediumType_MultiAttach)
8983 {
8984 if (i_isSnapshotMachine())
8985 return setError(E_FAIL,
8986 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8987 "of the virtual machine '%s' ('%s')"),
8988 medium->i_getLocationFull().c_str(),
8989 dev.uuid.raw(),
8990 puuidSnapshot->raw(),
8991 mUserData->s.strName.c_str(),
8992 mData->m_strConfigFileFull.c_str());
8993
8994 return setError(E_FAIL,
8995 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8996 medium->i_getLocationFull().c_str(),
8997 dev.uuid.raw(),
8998 mUserData->s.strName.c_str(),
8999 mData->m_strConfigFileFull.c_str());
9000 }
9001
9002 if ( !i_isSnapshotMachine()
9003 && medium->i_getChildren().size() != 0
9004 )
9005 return setError(E_FAIL,
9006 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9007 "because it has %d differencing child hard disks"),
9008 medium->i_getLocationFull().c_str(),
9009 dev.uuid.raw(),
9010 mUserData->s.strName.c_str(),
9011 mData->m_strConfigFileFull.c_str(),
9012 medium->i_getChildren().size());
9013
9014 if (i_findAttachment(mMediaData->mAttachments,
9015 medium))
9016 return setError(E_FAIL,
9017 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9018 medium->i_getLocationFull().c_str(),
9019 dev.uuid.raw(),
9020 mUserData->s.strName.c_str(),
9021 mData->m_strConfigFileFull.c_str());
9022
9023 break;
9024 }
9025
9026 default:
9027 return setError(E_FAIL,
9028 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9029 medium->i_getLocationFull().c_str(),
9030 mUserData->s.strName.c_str(),
9031 mData->m_strConfigFileFull.c_str());
9032 }
9033
9034 if (FAILED(rc))
9035 break;
9036
9037 /* Bandwidth groups are loaded at this point. */
9038 ComObjPtr<BandwidthGroup> pBwGroup;
9039
9040 if (!dev.strBwGroup.isEmpty())
9041 {
9042 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9043 if (FAILED(rc))
9044 return setError(E_FAIL,
9045 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9046 medium->i_getLocationFull().c_str(),
9047 dev.strBwGroup.c_str(),
9048 mUserData->s.strName.c_str(),
9049 mData->m_strConfigFileFull.c_str());
9050 pBwGroup->i_reference();
9051 }
9052
9053 const Bstr controllerName = aStorageController->i_getName();
9054 ComObjPtr<MediumAttachment> pAttachment;
9055 pAttachment.createObject();
9056 rc = pAttachment->init(this,
9057 medium,
9058 controllerName,
9059 dev.lPort,
9060 dev.lDevice,
9061 dev.deviceType,
9062 false,
9063 dev.fPassThrough,
9064 dev.fTempEject,
9065 dev.fNonRotational,
9066 dev.fDiscard,
9067 dev.fHotPluggable,
9068 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9069 if (FAILED(rc)) break;
9070
9071 /* associate the medium with this machine and snapshot */
9072 if (!medium.isNull())
9073 {
9074 AutoCaller medCaller(medium);
9075 if (FAILED(medCaller.rc())) return medCaller.rc();
9076 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9077
9078 if (i_isSnapshotMachine())
9079 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9080 else
9081 rc = medium->i_addBackReference(mData->mUuid);
9082 /* If the medium->addBackReference fails it sets an appropriate
9083 * error message, so no need to do any guesswork here. */
9084
9085 if (puuidRegistry)
9086 // caller wants registry ID to be set on all attached media (OVF import case)
9087 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9088 }
9089
9090 if (FAILED(rc))
9091 break;
9092
9093 /* back up mMediaData to let registeredInit() properly rollback on failure
9094 * (= limited accessibility) */
9095 i_setModified(IsModified_Storage);
9096 mMediaData.backup();
9097 mMediaData->mAttachments.push_back(pAttachment);
9098 }
9099
9100 return rc;
9101}
9102
9103/**
9104 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9105 *
9106 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9107 * @param aSnapshot where to return the found snapshot
9108 * @param aSetError true to set extended error info on failure
9109 */
9110HRESULT Machine::i_findSnapshotById(const Guid &aId,
9111 ComObjPtr<Snapshot> &aSnapshot,
9112 bool aSetError /* = false */)
9113{
9114 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9115
9116 if (!mData->mFirstSnapshot)
9117 {
9118 if (aSetError)
9119 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9120 return E_FAIL;
9121 }
9122
9123 if (aId.isZero())
9124 aSnapshot = mData->mFirstSnapshot;
9125 else
9126 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9127
9128 if (!aSnapshot)
9129 {
9130 if (aSetError)
9131 return setError(E_FAIL,
9132 tr("Could not find a snapshot with UUID {%s}"),
9133 aId.toString().c_str());
9134 return E_FAIL;
9135 }
9136
9137 return S_OK;
9138}
9139
9140/**
9141 * Returns the snapshot with the given name or fails of no such snapshot.
9142 *
9143 * @param aName snapshot name to find
9144 * @param aSnapshot where to return the found snapshot
9145 * @param aSetError true to set extended error info on failure
9146 */
9147HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9148 ComObjPtr<Snapshot> &aSnapshot,
9149 bool aSetError /* = false */)
9150{
9151 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9152
9153 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9154
9155 if (!mData->mFirstSnapshot)
9156 {
9157 if (aSetError)
9158 return setError(VBOX_E_OBJECT_NOT_FOUND,
9159 tr("This machine does not have any snapshots"));
9160 return VBOX_E_OBJECT_NOT_FOUND;
9161 }
9162
9163 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9164
9165 if (!aSnapshot)
9166 {
9167 if (aSetError)
9168 return setError(VBOX_E_OBJECT_NOT_FOUND,
9169 tr("Could not find a snapshot named '%s'"), strName.c_str());
9170 return VBOX_E_OBJECT_NOT_FOUND;
9171 }
9172
9173 return S_OK;
9174}
9175
9176/**
9177 * Returns a storage controller object with the given name.
9178 *
9179 * @param aName storage controller name to find
9180 * @param aStorageController where to return the found storage controller
9181 * @param aSetError true to set extended error info on failure
9182 */
9183HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9184 ComObjPtr<StorageController> &aStorageController,
9185 bool aSetError /* = false */)
9186{
9187 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9188
9189 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9190 it != mStorageControllers->end();
9191 ++it)
9192 {
9193 if ((*it)->i_getName() == aName)
9194 {
9195 aStorageController = (*it);
9196 return S_OK;
9197 }
9198 }
9199
9200 if (aSetError)
9201 return setError(VBOX_E_OBJECT_NOT_FOUND,
9202 tr("Could not find a storage controller named '%s'"),
9203 aName.c_str());
9204 return VBOX_E_OBJECT_NOT_FOUND;
9205}
9206
9207/**
9208 * Returns a USB controller object with the given name.
9209 *
9210 * @param aName USB controller name to find
9211 * @param aUSBController where to return the found USB controller
9212 * @param aSetError true to set extended error info on failure
9213 */
9214HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9215 ComObjPtr<USBController> &aUSBController,
9216 bool aSetError /* = false */)
9217{
9218 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9219
9220 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9221 it != mUSBControllers->end();
9222 ++it)
9223 {
9224 if ((*it)->i_getName() == aName)
9225 {
9226 aUSBController = (*it);
9227 return S_OK;
9228 }
9229 }
9230
9231 if (aSetError)
9232 return setError(VBOX_E_OBJECT_NOT_FOUND,
9233 tr("Could not find a storage controller named '%s'"),
9234 aName.c_str());
9235 return VBOX_E_OBJECT_NOT_FOUND;
9236}
9237
9238/**
9239 * Returns the number of USB controller instance of the given type.
9240 *
9241 * @param enmType USB controller type.
9242 */
9243ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9244{
9245 ULONG cCtrls = 0;
9246
9247 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9248 it != mUSBControllers->end();
9249 ++it)
9250 {
9251 if ((*it)->i_getControllerType() == enmType)
9252 cCtrls++;
9253 }
9254
9255 return cCtrls;
9256}
9257
9258HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9259 MediaData::AttachmentList &atts)
9260{
9261 AutoCaller autoCaller(this);
9262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9263
9264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9265
9266 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9267 it != mMediaData->mAttachments.end();
9268 ++it)
9269 {
9270 const ComObjPtr<MediumAttachment> &pAtt = *it;
9271 // should never happen, but deal with NULL pointers in the list.
9272 AssertStmt(!pAtt.isNull(), continue);
9273
9274 // getControllerName() needs caller+read lock
9275 AutoCaller autoAttCaller(pAtt);
9276 if (FAILED(autoAttCaller.rc()))
9277 {
9278 atts.clear();
9279 return autoAttCaller.rc();
9280 }
9281 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9282
9283 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9284 atts.push_back(pAtt);
9285 }
9286
9287 return S_OK;
9288}
9289
9290
9291/**
9292 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9293 * file if the machine name was changed and about creating a new settings file
9294 * if this is a new machine.
9295 *
9296 * @note Must be never called directly but only from #saveSettings().
9297 */
9298HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9299{
9300 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9301
9302 HRESULT rc = S_OK;
9303
9304 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9305
9306 /// @todo need to handle primary group change, too
9307
9308 /* attempt to rename the settings file if machine name is changed */
9309 if ( mUserData->s.fNameSync
9310 && mUserData.isBackedUp()
9311 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9312 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9313 )
9314 {
9315 bool dirRenamed = false;
9316 bool fileRenamed = false;
9317
9318 Utf8Str configFile, newConfigFile;
9319 Utf8Str configFilePrev, newConfigFilePrev;
9320 Utf8Str configDir, newConfigDir;
9321
9322 do
9323 {
9324 int vrc = VINF_SUCCESS;
9325
9326 Utf8Str name = mUserData.backedUpData()->s.strName;
9327 Utf8Str newName = mUserData->s.strName;
9328 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9329 if (group == "/")
9330 group.setNull();
9331 Utf8Str newGroup = mUserData->s.llGroups.front();
9332 if (newGroup == "/")
9333 newGroup.setNull();
9334
9335 configFile = mData->m_strConfigFileFull;
9336
9337 /* first, rename the directory if it matches the group and machine name */
9338 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9339 group.c_str(), RTPATH_DELIMITER, name.c_str());
9340 /** @todo hack, make somehow use of ComposeMachineFilename */
9341 if (mUserData->s.fDirectoryIncludesUUID)
9342 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9343 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9344 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9345 /** @todo hack, make somehow use of ComposeMachineFilename */
9346 if (mUserData->s.fDirectoryIncludesUUID)
9347 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9348 configDir = configFile;
9349 configDir.stripFilename();
9350 newConfigDir = configDir;
9351 if ( configDir.length() >= groupPlusName.length()
9352 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9353 groupPlusName.c_str()))
9354 {
9355 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9356 Utf8Str newConfigBaseDir(newConfigDir);
9357 newConfigDir.append(newGroupPlusName);
9358 /* consistency: use \ if appropriate on the platform */
9359 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9360 /* new dir and old dir cannot be equal here because of 'if'
9361 * above and because name != newName */
9362 Assert(configDir != newConfigDir);
9363 if (!fSettingsFileIsNew)
9364 {
9365 /* perform real rename only if the machine is not new */
9366 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9367 if ( vrc == VERR_FILE_NOT_FOUND
9368 || vrc == VERR_PATH_NOT_FOUND)
9369 {
9370 /* create the parent directory, then retry renaming */
9371 Utf8Str parent(newConfigDir);
9372 parent.stripFilename();
9373 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9374 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9375 }
9376 if (RT_FAILURE(vrc))
9377 {
9378 rc = setError(E_FAIL,
9379 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9380 configDir.c_str(),
9381 newConfigDir.c_str(),
9382 vrc);
9383 break;
9384 }
9385 /* delete subdirectories which are no longer needed */
9386 Utf8Str dir(configDir);
9387 dir.stripFilename();
9388 while (dir != newConfigBaseDir && dir != ".")
9389 {
9390 vrc = RTDirRemove(dir.c_str());
9391 if (RT_FAILURE(vrc))
9392 break;
9393 dir.stripFilename();
9394 }
9395 dirRenamed = true;
9396 }
9397 }
9398
9399 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9400 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9401
9402 /* then try to rename the settings file itself */
9403 if (newConfigFile != configFile)
9404 {
9405 /* get the path to old settings file in renamed directory */
9406 configFile = Utf8StrFmt("%s%c%s",
9407 newConfigDir.c_str(),
9408 RTPATH_DELIMITER,
9409 RTPathFilename(configFile.c_str()));
9410 if (!fSettingsFileIsNew)
9411 {
9412 /* perform real rename only if the machine is not new */
9413 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9414 if (RT_FAILURE(vrc))
9415 {
9416 rc = setError(E_FAIL,
9417 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9418 configFile.c_str(),
9419 newConfigFile.c_str(),
9420 vrc);
9421 break;
9422 }
9423 fileRenamed = true;
9424 configFilePrev = configFile;
9425 configFilePrev += "-prev";
9426 newConfigFilePrev = newConfigFile;
9427 newConfigFilePrev += "-prev";
9428 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9429 }
9430 }
9431
9432 // update m_strConfigFileFull amd mConfigFile
9433 mData->m_strConfigFileFull = newConfigFile;
9434 // compute the relative path too
9435 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9436
9437 // store the old and new so that VirtualBox::i_saveSettings() can update
9438 // the media registry
9439 if ( mData->mRegistered
9440 && (configDir != newConfigDir || configFile != newConfigFile))
9441 {
9442 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9443
9444 if (pfNeedsGlobalSaveSettings)
9445 *pfNeedsGlobalSaveSettings = true;
9446 }
9447
9448 // in the saved state file path, replace the old directory with the new directory
9449 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9450 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9451
9452 // and do the same thing for the saved state file paths of all the online snapshots
9453 if (mData->mFirstSnapshot)
9454 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9455 newConfigDir.c_str());
9456 }
9457 while (0);
9458
9459 if (FAILED(rc))
9460 {
9461 /* silently try to rename everything back */
9462 if (fileRenamed)
9463 {
9464 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9465 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9466 }
9467 if (dirRenamed)
9468 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9469 }
9470
9471 if (FAILED(rc)) return rc;
9472 }
9473
9474 if (fSettingsFileIsNew)
9475 {
9476 /* create a virgin config file */
9477 int vrc = VINF_SUCCESS;
9478
9479 /* ensure the settings directory exists */
9480 Utf8Str path(mData->m_strConfigFileFull);
9481 path.stripFilename();
9482 if (!RTDirExists(path.c_str()))
9483 {
9484 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9485 if (RT_FAILURE(vrc))
9486 {
9487 return setError(E_FAIL,
9488 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9489 path.c_str(),
9490 vrc);
9491 }
9492 }
9493
9494 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9495 path = Utf8Str(mData->m_strConfigFileFull);
9496 RTFILE f = NIL_RTFILE;
9497 vrc = RTFileOpen(&f, path.c_str(),
9498 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9499 if (RT_FAILURE(vrc))
9500 return setError(E_FAIL,
9501 tr("Could not create the settings file '%s' (%Rrc)"),
9502 path.c_str(),
9503 vrc);
9504 RTFileClose(f);
9505 }
9506
9507 return rc;
9508}
9509
9510/**
9511 * Saves and commits machine data, user data and hardware data.
9512 *
9513 * Note that on failure, the data remains uncommitted.
9514 *
9515 * @a aFlags may combine the following flags:
9516 *
9517 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9518 * Used when saving settings after an operation that makes them 100%
9519 * correspond to the settings from the current snapshot.
9520 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9521 * #isReallyModified() returns false. This is necessary for cases when we
9522 * change machine data directly, not through the backup()/commit() mechanism.
9523 * - SaveS_Force: settings will be saved without doing a deep compare of the
9524 * settings structures. This is used when this is called because snapshots
9525 * have changed to avoid the overhead of the deep compare.
9526 *
9527 * @note Must be called from under this object's write lock. Locks children for
9528 * writing.
9529 *
9530 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9531 * initialized to false and that will be set to true by this function if
9532 * the caller must invoke VirtualBox::i_saveSettings() because the global
9533 * settings have changed. This will happen if a machine rename has been
9534 * saved and the global machine and media registries will therefore need
9535 * updating.
9536 */
9537HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9538 int aFlags /*= 0*/)
9539{
9540 LogFlowThisFuncEnter();
9541
9542 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9543
9544 /* make sure child objects are unable to modify the settings while we are
9545 * saving them */
9546 i_ensureNoStateDependencies();
9547
9548 AssertReturn(!i_isSnapshotMachine(),
9549 E_FAIL);
9550
9551 HRESULT rc = S_OK;
9552 bool fNeedsWrite = false;
9553
9554 /* First, prepare to save settings. It will care about renaming the
9555 * settings directory and file if the machine name was changed and about
9556 * creating a new settings file if this is a new machine. */
9557 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9558 if (FAILED(rc)) return rc;
9559
9560 // keep a pointer to the current settings structures
9561 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9562 settings::MachineConfigFile *pNewConfig = NULL;
9563
9564 try
9565 {
9566 // make a fresh one to have everyone write stuff into
9567 pNewConfig = new settings::MachineConfigFile(NULL);
9568 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9569
9570 // now go and copy all the settings data from COM to the settings structures
9571 // (this calles i_saveSettings() on all the COM objects in the machine)
9572 i_copyMachineDataToSettings(*pNewConfig);
9573
9574 if (aFlags & SaveS_ResetCurStateModified)
9575 {
9576 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9577 mData->mCurrentStateModified = FALSE;
9578 fNeedsWrite = true; // always, no need to compare
9579 }
9580 else if (aFlags & SaveS_Force)
9581 {
9582 fNeedsWrite = true; // always, no need to compare
9583 }
9584 else
9585 {
9586 if (!mData->mCurrentStateModified)
9587 {
9588 // do a deep compare of the settings that we just saved with the settings
9589 // previously stored in the config file; this invokes MachineConfigFile::operator==
9590 // which does a deep compare of all the settings, which is expensive but less expensive
9591 // than writing out XML in vain
9592 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9593
9594 // could still be modified if any settings changed
9595 mData->mCurrentStateModified = fAnySettingsChanged;
9596
9597 fNeedsWrite = fAnySettingsChanged;
9598 }
9599 else
9600 fNeedsWrite = true;
9601 }
9602
9603 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9604
9605 if (fNeedsWrite)
9606 // now spit it all out!
9607 pNewConfig->write(mData->m_strConfigFileFull);
9608
9609 mData->pMachineConfigFile = pNewConfig;
9610 delete pOldConfig;
9611 i_commit();
9612
9613 // after saving settings, we are no longer different from the XML on disk
9614 mData->flModifications = 0;
9615 }
9616 catch (HRESULT err)
9617 {
9618 // we assume that error info is set by the thrower
9619 rc = err;
9620
9621 // restore old config
9622 delete pNewConfig;
9623 mData->pMachineConfigFile = pOldConfig;
9624 }
9625 catch (...)
9626 {
9627 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9628 }
9629
9630 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9631 {
9632 /* Fire the data change event, even on failure (since we've already
9633 * committed all data). This is done only for SessionMachines because
9634 * mutable Machine instances are always not registered (i.e. private
9635 * to the client process that creates them) and thus don't need to
9636 * inform callbacks. */
9637 if (i_isSessionMachine())
9638 mParent->i_onMachineDataChange(mData->mUuid);
9639 }
9640
9641 LogFlowThisFunc(("rc=%08X\n", rc));
9642 LogFlowThisFuncLeave();
9643 return rc;
9644}
9645
9646/**
9647 * Implementation for saving the machine settings into the given
9648 * settings::MachineConfigFile instance. This copies machine extradata
9649 * from the previous machine config file in the instance data, if any.
9650 *
9651 * This gets called from two locations:
9652 *
9653 * -- Machine::i_saveSettings(), during the regular XML writing;
9654 *
9655 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9656 * exported to OVF and we write the VirtualBox proprietary XML
9657 * into a <vbox:Machine> tag.
9658 *
9659 * This routine fills all the fields in there, including snapshots, *except*
9660 * for the following:
9661 *
9662 * -- fCurrentStateModified. There is some special logic associated with that.
9663 *
9664 * The caller can then call MachineConfigFile::write() or do something else
9665 * with it.
9666 *
9667 * Caller must hold the machine lock!
9668 *
9669 * This throws XML errors and HRESULT, so the caller must have a catch block!
9670 */
9671void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9672{
9673 // deep copy extradata
9674 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9675
9676 config.uuid = mData->mUuid;
9677
9678 // copy name, description, OS type, teleport, UTC etc.
9679 config.machineUserData = mUserData->s;
9680
9681 // Encode the Icon Override data from Machine and store on config userdata.
9682 com::SafeArray<BYTE> iconByte;
9683 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9684 ssize_t cbData = iconByte.size();
9685 if (cbData > 0)
9686 {
9687 ssize_t cchOut = RTBase64EncodedLength(cbData);
9688 Utf8Str strIconData;
9689 strIconData.reserve(cchOut+1);
9690 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9691 strIconData.mutableRaw(), strIconData.capacity(),
9692 NULL);
9693 if (RT_FAILURE(vrc))
9694 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9695 strIconData.jolt();
9696 config.machineUserData.ovIcon = strIconData;
9697 }
9698 else
9699 config.machineUserData.ovIcon.setNull();
9700
9701 if ( mData->mMachineState == MachineState_Saved
9702 || mData->mMachineState == MachineState_Restoring
9703 // when deleting a snapshot we may or may not have a saved state in the current state,
9704 // so let's not assert here please
9705 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9706 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9707 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9708 && (!mSSData->strStateFilePath.isEmpty())
9709 )
9710 )
9711 {
9712 Assert(!mSSData->strStateFilePath.isEmpty());
9713 /* try to make the file name relative to the settings file dir */
9714 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9715 }
9716 else
9717 {
9718 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9719 config.strStateFile.setNull();
9720 }
9721
9722 if (mData->mCurrentSnapshot)
9723 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9724 else
9725 config.uuidCurrentSnapshot.clear();
9726
9727 config.timeLastStateChange = mData->mLastStateChange;
9728 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9729 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9730
9731 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9732 if (FAILED(rc)) throw rc;
9733
9734 rc = i_saveStorageControllers(config.storageMachine);
9735 if (FAILED(rc)) throw rc;
9736
9737 // save machine's media registry if this is VirtualBox 4.0 or later
9738 if (config.canHaveOwnMediaRegistry())
9739 {
9740 // determine machine folder
9741 Utf8Str strMachineFolder = i_getSettingsFileFull();
9742 strMachineFolder.stripFilename();
9743 mParent->i_saveMediaRegistry(config.mediaRegistry,
9744 i_getId(), // only media with registry ID == machine UUID
9745 strMachineFolder);
9746 // this throws HRESULT
9747 }
9748
9749 // save snapshots
9750 rc = i_saveAllSnapshots(config);
9751 if (FAILED(rc)) throw rc;
9752}
9753
9754/**
9755 * Saves all snapshots of the machine into the given machine config file. Called
9756 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9757 * @param config
9758 * @return
9759 */
9760HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9761{
9762 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9763
9764 HRESULT rc = S_OK;
9765
9766 try
9767 {
9768 config.llFirstSnapshot.clear();
9769
9770 if (mData->mFirstSnapshot)
9771 {
9772 settings::Snapshot snapNew;
9773 config.llFirstSnapshot.push_back(snapNew);
9774
9775 // get reference to the fresh copy of the snapshot on the list and
9776 // work on that copy directly to avoid excessive copying later
9777 settings::Snapshot &snap = config.llFirstSnapshot.front();
9778
9779 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9780 if (FAILED(rc)) throw rc;
9781 }
9782
9783// if (mType == IsSessionMachine)
9784// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9785
9786 }
9787 catch (HRESULT err)
9788 {
9789 /* we assume that error info is set by the thrower */
9790 rc = err;
9791 }
9792 catch (...)
9793 {
9794 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9795 }
9796
9797 return rc;
9798}
9799
9800/**
9801 * Saves the VM hardware configuration. It is assumed that the
9802 * given node is empty.
9803 *
9804 * @param data Reference to the settings object for the hardware config.
9805 * @param pDbg Pointer to the settings object for the debugging config
9806 * which happens to live in mHWData.
9807 * @param pAutostart Pointer to the settings object for the autostart config
9808 * which happens to live in mHWData.
9809 */
9810HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9811 settings::Autostart *pAutostart)
9812{
9813 HRESULT rc = S_OK;
9814
9815 try
9816 {
9817 /* The hardware version attribute (optional).
9818 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9819 if ( mHWData->mHWVersion == "1"
9820 && mSSData->strStateFilePath.isEmpty()
9821 )
9822 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9823 other point needs to be found where this can be done. */
9824
9825 data.strVersion = mHWData->mHWVersion;
9826 data.uuid = mHWData->mHardwareUUID;
9827
9828 // CPU
9829 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9830 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9831 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9832 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9833 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9834 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9835 data.fPAE = !!mHWData->mPAEEnabled;
9836 data.enmLongMode = mHWData->mLongMode;
9837 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9838 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9839
9840 /* Standard and Extended CPUID leafs. */
9841 data.llCpuIdLeafs.clear();
9842 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9843 {
9844 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9845 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9846 }
9847 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9848 {
9849 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9850 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9851 }
9852
9853 data.cCPUs = mHWData->mCPUCount;
9854 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9855 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9856
9857 data.llCpus.clear();
9858 if (data.fCpuHotPlug)
9859 {
9860 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9861 {
9862 if (mHWData->mCPUAttached[idx])
9863 {
9864 settings::Cpu cpu;
9865 cpu.ulId = idx;
9866 data.llCpus.push_back(cpu);
9867 }
9868 }
9869 }
9870
9871 // memory
9872 data.ulMemorySizeMB = mHWData->mMemorySize;
9873 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9874
9875 // firmware
9876 data.firmwareType = mHWData->mFirmwareType;
9877
9878 // HID
9879 data.pointingHIDType = mHWData->mPointingHIDType;
9880 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9881
9882 // chipset
9883 data.chipsetType = mHWData->mChipsetType;
9884
9885 // paravirt
9886 data.paravirtProvider = mHWData->mParavirtProvider;
9887
9888
9889 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9890
9891 // HPET
9892 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9893
9894 // boot order
9895 data.mapBootOrder.clear();
9896 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9897 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9898
9899 // display
9900 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9901 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9902 data.cMonitors = mHWData->mMonitorCount;
9903 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9904 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9905 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9906 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9907 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
9908 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
9909 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9910 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
9911 {
9912 if (mHWData->maVideoCaptureScreens[i])
9913 ASMBitSet(&data.u64VideoCaptureScreens, i);
9914 else
9915 ASMBitClear(&data.u64VideoCaptureScreens, i);
9916 }
9917 /* store relative video capture file if possible */
9918 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
9919
9920 /* VRDEServer settings (optional) */
9921 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
9922 if (FAILED(rc)) throw rc;
9923
9924 /* BIOS (required) */
9925 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
9926 if (FAILED(rc)) throw rc;
9927
9928 /* USB Controller (required) */
9929 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
9930 {
9931 ComObjPtr<USBController> ctrl = *it;
9932 settings::USBController settingsCtrl;
9933
9934 settingsCtrl.strName = ctrl->i_getName();
9935 settingsCtrl.enmType = ctrl->i_getControllerType();
9936
9937 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
9938 }
9939
9940 /* USB device filters (required) */
9941 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
9942 if (FAILED(rc)) throw rc;
9943
9944 /* Network adapters (required) */
9945 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9946 data.llNetworkAdapters.clear();
9947 /* Write out only the nominal number of network adapters for this
9948 * chipset type. Since Machine::commit() hasn't been called there
9949 * may be extra NIC settings in the vector. */
9950 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9951 {
9952 settings::NetworkAdapter nic;
9953 nic.ulSlot = slot;
9954 /* paranoia check... must not be NULL, but must not crash either. */
9955 if (mNetworkAdapters[slot])
9956 {
9957 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
9958 if (FAILED(rc)) throw rc;
9959
9960 data.llNetworkAdapters.push_back(nic);
9961 }
9962 }
9963
9964 /* Serial ports */
9965 data.llSerialPorts.clear();
9966 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
9967 {
9968 settings::SerialPort s;
9969 s.ulSlot = slot;
9970 rc = mSerialPorts[slot]->i_saveSettings(s);
9971 if (FAILED(rc)) return rc;
9972
9973 data.llSerialPorts.push_back(s);
9974 }
9975
9976 /* Parallel ports */
9977 data.llParallelPorts.clear();
9978 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
9979 {
9980 settings::ParallelPort p;
9981 p.ulSlot = slot;
9982 rc = mParallelPorts[slot]->i_saveSettings(p);
9983 if (FAILED(rc)) return rc;
9984
9985 data.llParallelPorts.push_back(p);
9986 }
9987
9988 /* Audio adapter */
9989 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
9990 if (FAILED(rc)) return rc;
9991
9992 /* Shared folders */
9993 data.llSharedFolders.clear();
9994 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9995 it != mHWData->mSharedFolders.end();
9996 ++it)
9997 {
9998 SharedFolder *pSF = *it;
9999 AutoCaller sfCaller(pSF);
10000 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10001 settings::SharedFolder sf;
10002 sf.strName = pSF->i_getName();
10003 sf.strHostPath = pSF->i_getHostPath();
10004 sf.fWritable = !!pSF->i_isWritable();
10005 sf.fAutoMount = !!pSF->i_isAutoMounted();
10006
10007 data.llSharedFolders.push_back(sf);
10008 }
10009
10010 // clipboard
10011 data.clipboardMode = mHWData->mClipboardMode;
10012
10013 // drag'n'drop
10014 data.dndMode = mHWData->mDnDMode;
10015
10016 /* Guest */
10017 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10018
10019 // IO settings
10020 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10021 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10022
10023 /* BandwidthControl (required) */
10024 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10025 if (FAILED(rc)) throw rc;
10026
10027 /* Host PCI devices */
10028 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10029 it != mHWData->mPCIDeviceAssignments.end();
10030 ++it)
10031 {
10032 ComObjPtr<PCIDeviceAttachment> pda = *it;
10033 settings::HostPCIDeviceAttachment hpda;
10034
10035 rc = pda->i_saveSettings(hpda);
10036 if (FAILED(rc)) throw rc;
10037
10038 data.pciAttachments.push_back(hpda);
10039 }
10040
10041
10042 // guest properties
10043 data.llGuestProperties.clear();
10044#ifdef VBOX_WITH_GUEST_PROPS
10045 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10046 it != mHWData->mGuestProperties.end();
10047 ++it)
10048 {
10049 HWData::GuestProperty property = it->second;
10050
10051 /* Remove transient guest properties at shutdown unless we
10052 * are saving state */
10053 if ( ( mData->mMachineState == MachineState_PoweredOff
10054 || mData->mMachineState == MachineState_Aborted
10055 || mData->mMachineState == MachineState_Teleported)
10056 && ( property.mFlags & guestProp::TRANSIENT
10057 || property.mFlags & guestProp::TRANSRESET))
10058 continue;
10059 settings::GuestProperty prop;
10060 prop.strName = it->first;
10061 prop.strValue = property.strValue;
10062 prop.timestamp = property.mTimestamp;
10063 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10064 guestProp::writeFlags(property.mFlags, szFlags);
10065 prop.strFlags = szFlags;
10066
10067 data.llGuestProperties.push_back(prop);
10068 }
10069
10070 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10071 /* I presume this doesn't require a backup(). */
10072 mData->mGuestPropertiesModified = FALSE;
10073#endif /* VBOX_WITH_GUEST_PROPS defined */
10074
10075 *pDbg = mHWData->mDebugging;
10076 *pAutostart = mHWData->mAutostart;
10077
10078 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10079 }
10080 catch(std::bad_alloc &)
10081 {
10082 return E_OUTOFMEMORY;
10083 }
10084
10085 AssertComRC(rc);
10086 return rc;
10087}
10088
10089/**
10090 * Saves the storage controller configuration.
10091 *
10092 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10093 */
10094HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10095{
10096 data.llStorageControllers.clear();
10097
10098 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10099 it != mStorageControllers->end();
10100 ++it)
10101 {
10102 HRESULT rc;
10103 ComObjPtr<StorageController> pCtl = *it;
10104
10105 settings::StorageController ctl;
10106 ctl.strName = pCtl->i_getName();
10107 ctl.controllerType = pCtl->i_getControllerType();
10108 ctl.storageBus = pCtl->i_getStorageBus();
10109 ctl.ulInstance = pCtl->i_getInstance();
10110 ctl.fBootable = pCtl->i_getBootable();
10111
10112 /* Save the port count. */
10113 ULONG portCount;
10114 rc = pCtl->COMGETTER(PortCount)(&portCount);
10115 ComAssertComRCRet(rc, rc);
10116 ctl.ulPortCount = portCount;
10117
10118 /* Save fUseHostIOCache */
10119 BOOL fUseHostIOCache;
10120 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10121 ComAssertComRCRet(rc, rc);
10122 ctl.fUseHostIOCache = !!fUseHostIOCache;
10123
10124 /* Save IDE emulation settings. */
10125 if (ctl.controllerType == StorageControllerType_IntelAhci)
10126 {
10127 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10128 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10129 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10130 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10131 )
10132 ComAssertComRCRet(rc, rc);
10133 }
10134
10135 /* save the devices now. */
10136 rc = i_saveStorageDevices(pCtl, ctl);
10137 ComAssertComRCRet(rc, rc);
10138
10139 data.llStorageControllers.push_back(ctl);
10140 }
10141
10142 return S_OK;
10143}
10144
10145/**
10146 * Saves the hard disk configuration.
10147 */
10148HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10149 settings::StorageController &data)
10150{
10151 MediaData::AttachmentList atts;
10152
10153 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10154 if (FAILED(rc)) return rc;
10155
10156 data.llAttachedDevices.clear();
10157 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10158 it != atts.end();
10159 ++it)
10160 {
10161 settings::AttachedDevice dev;
10162 IMediumAttachment *iA = *it;
10163 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10164 Medium *pMedium = pAttach->i_getMedium();
10165
10166 dev.deviceType = pAttach->i_getType();
10167 dev.lPort = pAttach->i_getPort();
10168 dev.lDevice = pAttach->i_getDevice();
10169 dev.fPassThrough = pAttach->i_getPassthrough();
10170 dev.fHotPluggable = pAttach->i_getHotPluggable();
10171 if (pMedium)
10172 {
10173 if (pMedium->i_isHostDrive())
10174 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10175 else
10176 dev.uuid = pMedium->i_getId();
10177 dev.fTempEject = pAttach->i_getTempEject();
10178 dev.fNonRotational = pAttach->i_getNonRotational();
10179 dev.fDiscard = pAttach->i_getDiscard();
10180 }
10181
10182 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10183
10184 data.llAttachedDevices.push_back(dev);
10185 }
10186
10187 return S_OK;
10188}
10189
10190/**
10191 * Saves machine state settings as defined by aFlags
10192 * (SaveSTS_* values).
10193 *
10194 * @param aFlags Combination of SaveSTS_* flags.
10195 *
10196 * @note Locks objects for writing.
10197 */
10198HRESULT Machine::i_saveStateSettings(int aFlags)
10199{
10200 if (aFlags == 0)
10201 return S_OK;
10202
10203 AutoCaller autoCaller(this);
10204 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10205
10206 /* This object's write lock is also necessary to serialize file access
10207 * (prevent concurrent reads and writes) */
10208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10209
10210 HRESULT rc = S_OK;
10211
10212 Assert(mData->pMachineConfigFile);
10213
10214 try
10215 {
10216 if (aFlags & SaveSTS_CurStateModified)
10217 mData->pMachineConfigFile->fCurrentStateModified = true;
10218
10219 if (aFlags & SaveSTS_StateFilePath)
10220 {
10221 if (!mSSData->strStateFilePath.isEmpty())
10222 /* try to make the file name relative to the settings file dir */
10223 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10224 else
10225 mData->pMachineConfigFile->strStateFile.setNull();
10226 }
10227
10228 if (aFlags & SaveSTS_StateTimeStamp)
10229 {
10230 Assert( mData->mMachineState != MachineState_Aborted
10231 || mSSData->strStateFilePath.isEmpty());
10232
10233 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10234
10235 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10236//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10237 }
10238
10239 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10240 }
10241 catch (...)
10242 {
10243 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10244 }
10245
10246 return rc;
10247}
10248
10249/**
10250 * Ensures that the given medium is added to a media registry. If this machine
10251 * was created with 4.0 or later, then the machine registry is used. Otherwise
10252 * the global VirtualBox media registry is used.
10253 *
10254 * Caller must NOT hold machine lock, media tree or any medium locks!
10255 *
10256 * @param pMedium
10257 */
10258void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10259{
10260 /* Paranoia checks: do not hold machine or media tree locks. */
10261 AssertReturnVoid(!isWriteLockOnCurrentThread());
10262 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10263
10264 ComObjPtr<Medium> pBase;
10265 {
10266 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10267 pBase = pMedium->i_getBase();
10268 }
10269
10270 /* Paranoia checks: do not hold medium locks. */
10271 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10272 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10273
10274 // decide which medium registry to use now that the medium is attached:
10275 Guid uuid;
10276 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10277 // machine XML is VirtualBox 4.0 or higher:
10278 uuid = i_getId(); // machine UUID
10279 else
10280 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10281
10282 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10283 mParent->i_markRegistryModified(uuid);
10284
10285 /* For more complex hard disk structures it can happen that the base
10286 * medium isn't yet associated with any medium registry. Do that now. */
10287 if (pMedium != pBase)
10288 {
10289 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10290 mParent->i_markRegistryModified(uuid);
10291 }
10292}
10293
10294/**
10295 * Creates differencing hard disks for all normal hard disks attached to this
10296 * machine and a new set of attachments to refer to created disks.
10297 *
10298 * Used when taking a snapshot or when deleting the current state. Gets called
10299 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10300 *
10301 * This method assumes that mMediaData contains the original hard disk attachments
10302 * it needs to create diffs for. On success, these attachments will be replaced
10303 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10304 * called to delete created diffs which will also rollback mMediaData and restore
10305 * whatever was backed up before calling this method.
10306 *
10307 * Attachments with non-normal hard disks are left as is.
10308 *
10309 * If @a aOnline is @c false then the original hard disks that require implicit
10310 * diffs will be locked for reading. Otherwise it is assumed that they are
10311 * already locked for writing (when the VM was started). Note that in the latter
10312 * case it is responsibility of the caller to lock the newly created diffs for
10313 * writing if this method succeeds.
10314 *
10315 * @param aProgress Progress object to run (must contain at least as
10316 * many operations left as the number of hard disks
10317 * attached).
10318 * @param aOnline Whether the VM was online prior to this operation.
10319 *
10320 * @note The progress object is not marked as completed, neither on success nor
10321 * on failure. This is a responsibility of the caller.
10322 *
10323 * @note Locks this object and the media tree for writing.
10324 */
10325HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10326 ULONG aWeight,
10327 bool aOnline)
10328{
10329 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10330
10331 AutoCaller autoCaller(this);
10332 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10333
10334 AutoMultiWriteLock2 alock(this->lockHandle(),
10335 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10336
10337 /* must be in a protective state because we release the lock below */
10338 AssertReturn( mData->mMachineState == MachineState_Saving
10339 || mData->mMachineState == MachineState_LiveSnapshotting
10340 || mData->mMachineState == MachineState_RestoringSnapshot
10341 || mData->mMachineState == MachineState_DeletingSnapshot
10342 , E_FAIL);
10343
10344 HRESULT rc = S_OK;
10345
10346 // use appropriate locked media map (online or offline)
10347 MediumLockListMap lockedMediaOffline;
10348 MediumLockListMap *lockedMediaMap;
10349 if (aOnline)
10350 lockedMediaMap = &mData->mSession.mLockedMedia;
10351 else
10352 lockedMediaMap = &lockedMediaOffline;
10353
10354 try
10355 {
10356 if (!aOnline)
10357 {
10358 /* lock all attached hard disks early to detect "in use"
10359 * situations before creating actual diffs */
10360 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10361 it != mMediaData->mAttachments.end();
10362 ++it)
10363 {
10364 MediumAttachment* pAtt = *it;
10365 if (pAtt->i_getType() == DeviceType_HardDisk)
10366 {
10367 Medium* pMedium = pAtt->i_getMedium();
10368 Assert(pMedium);
10369
10370 MediumLockList *pMediumLockList(new MediumLockList());
10371 alock.release();
10372 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10373 false /* fMediumLockWrite */,
10374 NULL,
10375 *pMediumLockList);
10376 alock.acquire();
10377 if (FAILED(rc))
10378 {
10379 delete pMediumLockList;
10380 throw rc;
10381 }
10382 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10383 if (FAILED(rc))
10384 {
10385 throw setError(rc,
10386 tr("Collecting locking information for all attached media failed"));
10387 }
10388 }
10389 }
10390
10391 /* Now lock all media. If this fails, nothing is locked. */
10392 alock.release();
10393 rc = lockedMediaMap->Lock();
10394 alock.acquire();
10395 if (FAILED(rc))
10396 {
10397 throw setError(rc,
10398 tr("Locking of attached media failed"));
10399 }
10400 }
10401
10402 /* remember the current list (note that we don't use backup() since
10403 * mMediaData may be already backed up) */
10404 MediaData::AttachmentList atts = mMediaData->mAttachments;
10405
10406 /* start from scratch */
10407 mMediaData->mAttachments.clear();
10408
10409 /* go through remembered attachments and create diffs for normal hard
10410 * disks and attach them */
10411 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10412 it != atts.end();
10413 ++it)
10414 {
10415 MediumAttachment* pAtt = *it;
10416
10417 DeviceType_T devType = pAtt->i_getType();
10418 Medium* pMedium = pAtt->i_getMedium();
10419
10420 if ( devType != DeviceType_HardDisk
10421 || pMedium == NULL
10422 || pMedium->i_getType() != MediumType_Normal)
10423 {
10424 /* copy the attachment as is */
10425
10426 /** @todo the progress object created in Console::TakeSnaphot
10427 * only expects operations for hard disks. Later other
10428 * device types need to show up in the progress as well. */
10429 if (devType == DeviceType_HardDisk)
10430 {
10431 if (pMedium == NULL)
10432 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10433 aWeight); // weight
10434 else
10435 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10436 pMedium->i_getBase()->i_getName().c_str()).raw(),
10437 aWeight); // weight
10438 }
10439
10440 mMediaData->mAttachments.push_back(pAtt);
10441 continue;
10442 }
10443
10444 /* need a diff */
10445 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10446 pMedium->i_getBase()->i_getName().c_str()).raw(),
10447 aWeight); // weight
10448
10449 Utf8Str strFullSnapshotFolder;
10450 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10451
10452 ComObjPtr<Medium> diff;
10453 diff.createObject();
10454 // store the diff in the same registry as the parent
10455 // (this cannot fail here because we can't create implicit diffs for
10456 // unregistered images)
10457 Guid uuidRegistryParent;
10458 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10459 Assert(fInRegistry); NOREF(fInRegistry);
10460 rc = diff->init(mParent,
10461 pMedium->i_getPreferredDiffFormat(),
10462 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10463 uuidRegistryParent);
10464 if (FAILED(rc)) throw rc;
10465
10466 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10467 * the push_back? Looks like we're going to release medium with the
10468 * wrong kind of lock (general issue with if we fail anywhere at all)
10469 * and an orphaned VDI in the snapshots folder. */
10470
10471 /* update the appropriate lock list */
10472 MediumLockList *pMediumLockList;
10473 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10474 AssertComRCThrowRC(rc);
10475 if (aOnline)
10476 {
10477 alock.release();
10478 /* The currently attached medium will be read-only, change
10479 * the lock type to read. */
10480 rc = pMediumLockList->Update(pMedium, false);
10481 alock.acquire();
10482 AssertComRCThrowRC(rc);
10483 }
10484
10485 /* release the locks before the potentially lengthy operation */
10486 alock.release();
10487 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10488 pMediumLockList,
10489 NULL /* aProgress */,
10490 true /* aWait */);
10491 alock.acquire();
10492 if (FAILED(rc)) throw rc;
10493
10494 /* actual lock list update is done in Medium::commitMedia */
10495
10496 rc = diff->i_addBackReference(mData->mUuid);
10497 AssertComRCThrowRC(rc);
10498
10499 /* add a new attachment */
10500 ComObjPtr<MediumAttachment> attachment;
10501 attachment.createObject();
10502 rc = attachment->init(this,
10503 diff,
10504 pAtt->i_getControllerName(),
10505 pAtt->i_getPort(),
10506 pAtt->i_getDevice(),
10507 DeviceType_HardDisk,
10508 true /* aImplicit */,
10509 false /* aPassthrough */,
10510 false /* aTempEject */,
10511 pAtt->i_getNonRotational(),
10512 pAtt->i_getDiscard(),
10513 pAtt->i_getHotPluggable(),
10514 pAtt->i_getBandwidthGroup());
10515 if (FAILED(rc)) throw rc;
10516
10517 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10518 AssertComRCThrowRC(rc);
10519 mMediaData->mAttachments.push_back(attachment);
10520 }
10521 }
10522 catch (HRESULT aRC) { rc = aRC; }
10523
10524 /* unlock all hard disks we locked when there is no VM */
10525 if (!aOnline)
10526 {
10527 ErrorInfoKeeper eik;
10528
10529 HRESULT rc1 = lockedMediaMap->Clear();
10530 AssertComRC(rc1);
10531 }
10532
10533 return rc;
10534}
10535
10536/**
10537 * Deletes implicit differencing hard disks created either by
10538 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10539 *
10540 * Note that to delete hard disks created by #AttachDevice() this method is
10541 * called from #fixupMedia() when the changes are rolled back.
10542 *
10543 * @note Locks this object and the media tree for writing.
10544 */
10545HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10546{
10547 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10548
10549 AutoCaller autoCaller(this);
10550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10551
10552 AutoMultiWriteLock2 alock(this->lockHandle(),
10553 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10554
10555 /* We absolutely must have backed up state. */
10556 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10557
10558 /* Check if there are any implicitly created diff images. */
10559 bool fImplicitDiffs = false;
10560 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10561 it != mMediaData->mAttachments.end();
10562 ++it)
10563 {
10564 const ComObjPtr<MediumAttachment> &pAtt = *it;
10565 if (pAtt->i_isImplicit())
10566 {
10567 fImplicitDiffs = true;
10568 break;
10569 }
10570 }
10571 /* If there is nothing to do, leave early. This saves lots of image locking
10572 * effort. It also avoids a MachineStateChanged event without real reason.
10573 * This is important e.g. when loading a VM config, because there should be
10574 * no events. Otherwise API clients can become thoroughly confused for
10575 * inaccessible VMs (the code for loading VM configs uses this method for
10576 * cleanup if the config makes no sense), as they take such events as an
10577 * indication that the VM is alive, and they would force the VM config to
10578 * be reread, leading to an endless loop. */
10579 if (!fImplicitDiffs)
10580 return S_OK;
10581
10582 HRESULT rc = S_OK;
10583 MachineState_T oldState = mData->mMachineState;
10584
10585 /* will release the lock before the potentially lengthy operation,
10586 * so protect with the special state (unless already protected) */
10587 if ( oldState != MachineState_Saving
10588 && oldState != MachineState_LiveSnapshotting
10589 && oldState != MachineState_RestoringSnapshot
10590 && oldState != MachineState_DeletingSnapshot
10591 && oldState != MachineState_DeletingSnapshotOnline
10592 && oldState != MachineState_DeletingSnapshotPaused
10593 )
10594 i_setMachineState(MachineState_SettingUp);
10595
10596 // use appropriate locked media map (online or offline)
10597 MediumLockListMap lockedMediaOffline;
10598 MediumLockListMap *lockedMediaMap;
10599 if (aOnline)
10600 lockedMediaMap = &mData->mSession.mLockedMedia;
10601 else
10602 lockedMediaMap = &lockedMediaOffline;
10603
10604 try
10605 {
10606 if (!aOnline)
10607 {
10608 /* lock all attached hard disks early to detect "in use"
10609 * situations before deleting actual diffs */
10610 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10611 it != mMediaData->mAttachments.end();
10612 ++it)
10613 {
10614 MediumAttachment* pAtt = *it;
10615 if (pAtt->i_getType() == DeviceType_HardDisk)
10616 {
10617 Medium* pMedium = pAtt->i_getMedium();
10618 Assert(pMedium);
10619
10620 MediumLockList *pMediumLockList(new MediumLockList());
10621 alock.release();
10622 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10623 false /* fMediumLockWrite */,
10624 NULL,
10625 *pMediumLockList);
10626 alock.acquire();
10627
10628 if (FAILED(rc))
10629 {
10630 delete pMediumLockList;
10631 throw rc;
10632 }
10633
10634 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10635 if (FAILED(rc))
10636 throw rc;
10637 }
10638 }
10639
10640 if (FAILED(rc))
10641 throw rc;
10642 } // end of offline
10643
10644 /* Lock lists are now up to date and include implicitly created media */
10645
10646 /* Go through remembered attachments and delete all implicitly created
10647 * diffs and fix up the attachment information */
10648 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10649 MediaData::AttachmentList implicitAtts;
10650 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10651 it != mMediaData->mAttachments.end();
10652 ++it)
10653 {
10654 ComObjPtr<MediumAttachment> pAtt = *it;
10655 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10656 if (pMedium.isNull())
10657 continue;
10658
10659 // Implicit attachments go on the list for deletion and back references are removed.
10660 if (pAtt->i_isImplicit())
10661 {
10662 /* Deassociate and mark for deletion */
10663 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10664 rc = pMedium->i_removeBackReference(mData->mUuid);
10665 if (FAILED(rc))
10666 throw rc;
10667 implicitAtts.push_back(pAtt);
10668 continue;
10669 }
10670
10671 /* Was this medium attached before? */
10672 if (!i_findAttachment(oldAtts, pMedium))
10673 {
10674 /* no: de-associate */
10675 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10676 rc = pMedium->i_removeBackReference(mData->mUuid);
10677 if (FAILED(rc))
10678 throw rc;
10679 continue;
10680 }
10681 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10682 }
10683
10684 /* If there are implicit attachments to delete, throw away the lock
10685 * map contents (which will unlock all media) since the medium
10686 * attachments will be rolled back. Below we need to completely
10687 * recreate the lock map anyway since it is infinitely complex to
10688 * do this incrementally (would need reconstructing each attachment
10689 * change, which would be extremely hairy). */
10690 if (implicitAtts.size() != 0)
10691 {
10692 ErrorInfoKeeper eik;
10693
10694 HRESULT rc1 = lockedMediaMap->Clear();
10695 AssertComRC(rc1);
10696 }
10697
10698 /* rollback hard disk changes */
10699 mMediaData.rollback();
10700
10701 MultiResult mrc(S_OK);
10702
10703 // Delete unused implicit diffs.
10704 if (implicitAtts.size() != 0)
10705 {
10706 alock.release();
10707
10708 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10709 {
10710 // Remove medium associated with this attachment.
10711 ComObjPtr<MediumAttachment> pAtt = *it;
10712 Assert(pAtt);
10713 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10714 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10715 Assert(pMedium);
10716
10717 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10718 // continue on delete failure, just collect error messages
10719 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10720 pMedium->i_getLocationFull().c_str() ));
10721 mrc = rc;
10722 }
10723
10724 alock.acquire();
10725
10726 /* if there is a VM recreate media lock map as mentioned above,
10727 * otherwise it is a waste of time and we leave things unlocked */
10728 if (aOnline)
10729 {
10730 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10731 /* must never be NULL, but better safe than sorry */
10732 if (!pMachine.isNull())
10733 {
10734 alock.release();
10735 rc = mData->mSession.mMachine->lockMedia();
10736 alock.acquire();
10737 if (FAILED(rc))
10738 throw rc;
10739 }
10740 }
10741 }
10742 }
10743 catch (HRESULT aRC) {rc = aRC;}
10744
10745 if (mData->mMachineState == MachineState_SettingUp)
10746 i_setMachineState(oldState);
10747
10748 /* unlock all hard disks we locked when there is no VM */
10749 if (!aOnline)
10750 {
10751 ErrorInfoKeeper eik;
10752
10753 HRESULT rc1 = lockedMediaMap->Clear();
10754 AssertComRC(rc1);
10755 }
10756
10757 return rc;
10758}
10759
10760
10761/**
10762 * Looks through the given list of media attachments for one with the given parameters
10763 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10764 * can be searched as well if needed.
10765 *
10766 * @param list
10767 * @param aControllerName
10768 * @param aControllerPort
10769 * @param aDevice
10770 * @return
10771 */
10772MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10773 IN_BSTR aControllerName,
10774 LONG aControllerPort,
10775 LONG aDevice)
10776{
10777 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10778 {
10779 MediumAttachment *pAttach = *it;
10780 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10781 return pAttach;
10782 }
10783
10784 return NULL;
10785}
10786
10787/**
10788 * Looks through the given list of media attachments for one with the given parameters
10789 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10790 * can be searched as well if needed.
10791 *
10792 * @param list
10793 * @param aControllerName
10794 * @param aControllerPort
10795 * @param aDevice
10796 * @return
10797 */
10798MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10799 ComObjPtr<Medium> pMedium)
10800{
10801 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10802 {
10803 MediumAttachment *pAttach = *it;
10804 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10805 if (pMediumThis == pMedium)
10806 return pAttach;
10807 }
10808
10809 return NULL;
10810}
10811
10812/**
10813 * Looks through the given list of media attachments for one with the given parameters
10814 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10815 * can be searched as well if needed.
10816 *
10817 * @param list
10818 * @param aControllerName
10819 * @param aControllerPort
10820 * @param aDevice
10821 * @return
10822 */
10823MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10824 Guid &id)
10825{
10826 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10827 {
10828 MediumAttachment *pAttach = *it;
10829 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10830 if (pMediumThis->i_getId() == id)
10831 return pAttach;
10832 }
10833
10834 return NULL;
10835}
10836
10837/**
10838 * Main implementation for Machine::DetachDevice. This also gets called
10839 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10840 *
10841 * @param pAttach Medium attachment to detach.
10842 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10843 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10844 * SnapshotMachine, and this must be its snapshot.
10845 * @return
10846 */
10847HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10848 AutoWriteLock &writeLock,
10849 Snapshot *pSnapshot)
10850{
10851 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10852 DeviceType_T mediumType = pAttach->i_getType();
10853
10854 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10855
10856 if (pAttach->i_isImplicit())
10857 {
10858 /* attempt to implicitly delete the implicitly created diff */
10859
10860 /// @todo move the implicit flag from MediumAttachment to Medium
10861 /// and forbid any hard disk operation when it is implicit. Or maybe
10862 /// a special media state for it to make it even more simple.
10863
10864 Assert(mMediaData.isBackedUp());
10865
10866 /* will release the lock before the potentially lengthy operation, so
10867 * protect with the special state */
10868 MachineState_T oldState = mData->mMachineState;
10869 i_setMachineState(MachineState_SettingUp);
10870
10871 writeLock.release();
10872
10873 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
10874 true /*aWait*/);
10875
10876 writeLock.acquire();
10877
10878 i_setMachineState(oldState);
10879
10880 if (FAILED(rc)) return rc;
10881 }
10882
10883 i_setModified(IsModified_Storage);
10884 mMediaData.backup();
10885 mMediaData->mAttachments.remove(pAttach);
10886
10887 if (!oldmedium.isNull())
10888 {
10889 // if this is from a snapshot, do not defer detachment to commitMedia()
10890 if (pSnapshot)
10891 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
10892 // else if non-hard disk media, do not defer detachment to commitMedia() either
10893 else if (mediumType != DeviceType_HardDisk)
10894 oldmedium->i_removeBackReference(mData->mUuid);
10895 }
10896
10897 return S_OK;
10898}
10899
10900/**
10901 * Goes thru all media of the given list and
10902 *
10903 * 1) calls i_detachDevice() on each of them for this machine and
10904 * 2) adds all Medium objects found in the process to the given list,
10905 * depending on cleanupMode.
10906 *
10907 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10908 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10909 * media to the list.
10910 *
10911 * This gets called from Machine::Unregister, both for the actual Machine and
10912 * the SnapshotMachine objects that might be found in the snapshots.
10913 *
10914 * Requires caller and locking. The machine lock must be passed in because it
10915 * will be passed on to i_detachDevice which needs it for temporary unlocking.
10916 *
10917 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
10918 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10919 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
10920 * Full, then all media get added;
10921 * otherwise no media get added.
10922 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10923 * @return
10924 */
10925HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
10926 Snapshot *pSnapshot,
10927 CleanupMode_T cleanupMode,
10928 MediaList &llMedia)
10929{
10930 Assert(isWriteLockOnCurrentThread());
10931
10932 HRESULT rc;
10933
10934 // make a temporary list because i_detachDevice invalidates iterators into
10935 // mMediaData->mAttachments
10936 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10937
10938 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
10939 {
10940 ComObjPtr<MediumAttachment> &pAttach = *it;
10941 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
10942
10943 if (!pMedium.isNull())
10944 {
10945 AutoCaller mac(pMedium);
10946 if (FAILED(mac.rc())) return mac.rc();
10947 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10948 DeviceType_T devType = pMedium->i_getDeviceType();
10949 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10950 && devType == DeviceType_HardDisk)
10951 || (cleanupMode == CleanupMode_Full)
10952 )
10953 {
10954 llMedia.push_back(pMedium);
10955 ComObjPtr<Medium> pParent = pMedium->i_getParent();
10956 /*
10957 * Search for medias which are not attached to any machine, but
10958 * in the chain to an attached disk. Mediums are only consided
10959 * if they are:
10960 * - have only one child
10961 * - no references to any machines
10962 * - are of normal medium type
10963 */
10964 while (!pParent.isNull())
10965 {
10966 AutoCaller mac1(pParent);
10967 if (FAILED(mac1.rc())) return mac1.rc();
10968 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10969 if (pParent->i_getChildren().size() == 1)
10970 {
10971 if ( pParent->i_getMachineBackRefCount() == 0
10972 && pParent->i_getType() == MediumType_Normal
10973 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10974 llMedia.push_back(pParent);
10975 }
10976 else
10977 break;
10978 pParent = pParent->i_getParent();
10979 }
10980 }
10981 }
10982
10983 // real machine: then we need to use the proper method
10984 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
10985
10986 if (FAILED(rc))
10987 return rc;
10988 }
10989
10990 return S_OK;
10991}
10992
10993/**
10994 * Perform deferred hard disk detachments.
10995 *
10996 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10997 * backed up).
10998 *
10999 * If @a aOnline is @c true then this method will also unlock the old hard disks
11000 * for which the new implicit diffs were created and will lock these new diffs for
11001 * writing.
11002 *
11003 * @param aOnline Whether the VM was online prior to this operation.
11004 *
11005 * @note Locks this object for writing!
11006 */
11007void Machine::i_commitMedia(bool aOnline /*= false*/)
11008{
11009 AutoCaller autoCaller(this);
11010 AssertComRCReturnVoid(autoCaller.rc());
11011
11012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11013
11014 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11015
11016 HRESULT rc = S_OK;
11017
11018 /* no attach/detach operations -- nothing to do */
11019 if (!mMediaData.isBackedUp())
11020 return;
11021
11022 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11023 bool fMediaNeedsLocking = false;
11024
11025 /* enumerate new attachments */
11026 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11027 it != mMediaData->mAttachments.end();
11028 ++it)
11029 {
11030 MediumAttachment *pAttach = *it;
11031
11032 pAttach->i_commit();
11033
11034 Medium* pMedium = pAttach->i_getMedium();
11035 bool fImplicit = pAttach->i_isImplicit();
11036
11037 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11038 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11039 fImplicit));
11040
11041 /** @todo convert all this Machine-based voodoo to MediumAttachment
11042 * based commit logic. */
11043 if (fImplicit)
11044 {
11045 /* convert implicit attachment to normal */
11046 pAttach->i_setImplicit(false);
11047
11048 if ( aOnline
11049 && pMedium
11050 && pAttach->i_getType() == DeviceType_HardDisk
11051 )
11052 {
11053 ComObjPtr<Medium> parent = pMedium->i_getParent();
11054 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11055
11056 /* update the appropriate lock list */
11057 MediumLockList *pMediumLockList;
11058 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11059 AssertComRC(rc);
11060 if (pMediumLockList)
11061 {
11062 /* unlock if there's a need to change the locking */
11063 if (!fMediaNeedsLocking)
11064 {
11065 rc = mData->mSession.mLockedMedia.Unlock();
11066 AssertComRC(rc);
11067 fMediaNeedsLocking = true;
11068 }
11069 rc = pMediumLockList->Update(parent, false);
11070 AssertComRC(rc);
11071 rc = pMediumLockList->Append(pMedium, true);
11072 AssertComRC(rc);
11073 }
11074 }
11075
11076 continue;
11077 }
11078
11079 if (pMedium)
11080 {
11081 /* was this medium attached before? */
11082 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11083 {
11084 MediumAttachment *pOldAttach = *oldIt;
11085 if (pOldAttach->i_getMedium() == pMedium)
11086 {
11087 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11088
11089 /* yes: remove from old to avoid de-association */
11090 oldAtts.erase(oldIt);
11091 break;
11092 }
11093 }
11094 }
11095 }
11096
11097 /* enumerate remaining old attachments and de-associate from the
11098 * current machine state */
11099 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11100 {
11101 MediumAttachment *pAttach = *it;
11102 Medium* pMedium = pAttach->i_getMedium();
11103
11104 /* Detach only hard disks, since DVD/floppy media is detached
11105 * instantly in MountMedium. */
11106 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11107 {
11108 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11109
11110 /* now de-associate from the current machine state */
11111 rc = pMedium->i_removeBackReference(mData->mUuid);
11112 AssertComRC(rc);
11113
11114 if (aOnline)
11115 {
11116 /* unlock since medium is not used anymore */
11117 MediumLockList *pMediumLockList;
11118 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11119 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11120 {
11121 /* this happens for online snapshots, there the attachment
11122 * is changing, but only to a diff image created under
11123 * the old one, so there is no separate lock list */
11124 Assert(!pMediumLockList);
11125 }
11126 else
11127 {
11128 AssertComRC(rc);
11129 if (pMediumLockList)
11130 {
11131 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11132 AssertComRC(rc);
11133 }
11134 }
11135 }
11136 }
11137 }
11138
11139 /* take media locks again so that the locking state is consistent */
11140 if (fMediaNeedsLocking)
11141 {
11142 Assert(aOnline);
11143 rc = mData->mSession.mLockedMedia.Lock();
11144 AssertComRC(rc);
11145 }
11146
11147 /* commit the hard disk changes */
11148 mMediaData.commit();
11149
11150 if (i_isSessionMachine())
11151 {
11152 /*
11153 * Update the parent machine to point to the new owner.
11154 * This is necessary because the stored parent will point to the
11155 * session machine otherwise and cause crashes or errors later
11156 * when the session machine gets invalid.
11157 */
11158 /** @todo Change the MediumAttachment class to behave like any other
11159 * class in this regard by creating peer MediumAttachment
11160 * objects for session machines and share the data with the peer
11161 * machine.
11162 */
11163 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11164 it != mMediaData->mAttachments.end();
11165 ++it)
11166 (*it)->i_updateParentMachine(mPeer);
11167
11168 /* attach new data to the primary machine and reshare it */
11169 mPeer->mMediaData.attach(mMediaData);
11170 }
11171
11172 return;
11173}
11174
11175/**
11176 * Perform deferred deletion of implicitly created diffs.
11177 *
11178 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11179 * backed up).
11180 *
11181 * @note Locks this object for writing!
11182 */
11183void Machine::i_rollbackMedia()
11184{
11185 AutoCaller autoCaller(this);
11186 AssertComRCReturnVoid(autoCaller.rc());
11187
11188 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11189 LogFlowThisFunc(("Entering rollbackMedia\n"));
11190
11191 HRESULT rc = S_OK;
11192
11193 /* no attach/detach operations -- nothing to do */
11194 if (!mMediaData.isBackedUp())
11195 return;
11196
11197 /* enumerate new attachments */
11198 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11199 it != mMediaData->mAttachments.end();
11200 ++it)
11201 {
11202 MediumAttachment *pAttach = *it;
11203 /* Fix up the backrefs for DVD/floppy media. */
11204 if (pAttach->i_getType() != DeviceType_HardDisk)
11205 {
11206 Medium* pMedium = pAttach->i_getMedium();
11207 if (pMedium)
11208 {
11209 rc = pMedium->i_removeBackReference(mData->mUuid);
11210 AssertComRC(rc);
11211 }
11212 }
11213
11214 (*it)->i_rollback();
11215
11216 pAttach = *it;
11217 /* Fix up the backrefs for DVD/floppy media. */
11218 if (pAttach->i_getType() != DeviceType_HardDisk)
11219 {
11220 Medium* pMedium = pAttach->i_getMedium();
11221 if (pMedium)
11222 {
11223 rc = pMedium->i_addBackReference(mData->mUuid);
11224 AssertComRC(rc);
11225 }
11226 }
11227 }
11228
11229 /** @todo convert all this Machine-based voodoo to MediumAttachment
11230 * based rollback logic. */
11231 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11232
11233 return;
11234}
11235
11236/**
11237 * Returns true if the settings file is located in the directory named exactly
11238 * as the machine; this means, among other things, that the machine directory
11239 * should be auto-renamed.
11240 *
11241 * @param aSettingsDir if not NULL, the full machine settings file directory
11242 * name will be assigned there.
11243 *
11244 * @note Doesn't lock anything.
11245 * @note Not thread safe (must be called from this object's lock).
11246 */
11247bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11248{
11249 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11250 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11251 if (aSettingsDir)
11252 *aSettingsDir = strMachineDirName;
11253 strMachineDirName.stripPath(); // vmname
11254 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11255 strConfigFileOnly.stripPath() // vmname.vbox
11256 .stripSuffix(); // vmname
11257 /** @todo hack, make somehow use of ComposeMachineFilename */
11258 if (mUserData->s.fDirectoryIncludesUUID)
11259 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11260
11261 AssertReturn(!strMachineDirName.isEmpty(), false);
11262 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11263
11264 return strMachineDirName == strConfigFileOnly;
11265}
11266
11267/**
11268 * Discards all changes to machine settings.
11269 *
11270 * @param aNotify Whether to notify the direct session about changes or not.
11271 *
11272 * @note Locks objects for writing!
11273 */
11274void Machine::i_rollback(bool aNotify)
11275{
11276 AutoCaller autoCaller(this);
11277 AssertComRCReturn(autoCaller.rc(), (void)0);
11278
11279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11280
11281 if (!mStorageControllers.isNull())
11282 {
11283 if (mStorageControllers.isBackedUp())
11284 {
11285 /* unitialize all new devices (absent in the backed up list). */
11286 StorageControllerList::const_iterator it = mStorageControllers->begin();
11287 StorageControllerList *backedList = mStorageControllers.backedUpData();
11288 while (it != mStorageControllers->end())
11289 {
11290 if ( std::find(backedList->begin(), backedList->end(), *it)
11291 == backedList->end()
11292 )
11293 {
11294 (*it)->uninit();
11295 }
11296 ++it;
11297 }
11298
11299 /* restore the list */
11300 mStorageControllers.rollback();
11301 }
11302
11303 /* rollback any changes to devices after restoring the list */
11304 if (mData->flModifications & IsModified_Storage)
11305 {
11306 StorageControllerList::const_iterator it = mStorageControllers->begin();
11307 while (it != mStorageControllers->end())
11308 {
11309 (*it)->i_rollback();
11310 ++it;
11311 }
11312 }
11313 }
11314
11315 if (!mUSBControllers.isNull())
11316 {
11317 if (mUSBControllers.isBackedUp())
11318 {
11319 /* unitialize all new devices (absent in the backed up list). */
11320 USBControllerList::const_iterator it = mUSBControllers->begin();
11321 USBControllerList *backedList = mUSBControllers.backedUpData();
11322 while (it != mUSBControllers->end())
11323 {
11324 if ( std::find(backedList->begin(), backedList->end(), *it)
11325 == backedList->end()
11326 )
11327 {
11328 (*it)->uninit();
11329 }
11330 ++it;
11331 }
11332
11333 /* restore the list */
11334 mUSBControllers.rollback();
11335 }
11336
11337 /* rollback any changes to devices after restoring the list */
11338 if (mData->flModifications & IsModified_USB)
11339 {
11340 USBControllerList::const_iterator it = mUSBControllers->begin();
11341 while (it != mUSBControllers->end())
11342 {
11343 (*it)->i_rollback();
11344 ++it;
11345 }
11346 }
11347 }
11348
11349 mUserData.rollback();
11350
11351 mHWData.rollback();
11352
11353 if (mData->flModifications & IsModified_Storage)
11354 i_rollbackMedia();
11355
11356 if (mBIOSSettings)
11357 mBIOSSettings->i_rollback();
11358
11359 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11360 mVRDEServer->i_rollback();
11361
11362 if (mAudioAdapter)
11363 mAudioAdapter->i_rollback();
11364
11365 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11366 mUSBDeviceFilters->i_rollback();
11367
11368 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11369 mBandwidthControl->i_rollback();
11370
11371 if (!mHWData.isNull())
11372 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11373 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11374 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11375 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11376
11377 if (mData->flModifications & IsModified_NetworkAdapters)
11378 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11379 if ( mNetworkAdapters[slot]
11380 && mNetworkAdapters[slot]->i_isModified())
11381 {
11382 mNetworkAdapters[slot]->i_rollback();
11383 networkAdapters[slot] = mNetworkAdapters[slot];
11384 }
11385
11386 if (mData->flModifications & IsModified_SerialPorts)
11387 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11388 if ( mSerialPorts[slot]
11389 && mSerialPorts[slot]->i_isModified())
11390 {
11391 mSerialPorts[slot]->i_rollback();
11392 serialPorts[slot] = mSerialPorts[slot];
11393 }
11394
11395 if (mData->flModifications & IsModified_ParallelPorts)
11396 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11397 if ( mParallelPorts[slot]
11398 && mParallelPorts[slot]->i_isModified())
11399 {
11400 mParallelPorts[slot]->i_rollback();
11401 parallelPorts[slot] = mParallelPorts[slot];
11402 }
11403
11404 if (aNotify)
11405 {
11406 /* inform the direct session about changes */
11407
11408 ComObjPtr<Machine> that = this;
11409 uint32_t flModifications = mData->flModifications;
11410 alock.release();
11411
11412 if (flModifications & IsModified_SharedFolders)
11413 that->i_onSharedFolderChange();
11414
11415 if (flModifications & IsModified_VRDEServer)
11416 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11417 if (flModifications & IsModified_USB)
11418 that->i_onUSBControllerChange();
11419
11420 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11421 if (networkAdapters[slot])
11422 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11423 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11424 if (serialPorts[slot])
11425 that->i_onSerialPortChange(serialPorts[slot]);
11426 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11427 if (parallelPorts[slot])
11428 that->i_onParallelPortChange(parallelPorts[slot]);
11429
11430 if (flModifications & IsModified_Storage)
11431 that->i_onStorageControllerChange();
11432
11433#if 0
11434 if (flModifications & IsModified_BandwidthControl)
11435 that->onBandwidthControlChange();
11436#endif
11437 }
11438}
11439
11440/**
11441 * Commits all the changes to machine settings.
11442 *
11443 * Note that this operation is supposed to never fail.
11444 *
11445 * @note Locks this object and children for writing.
11446 */
11447void Machine::i_commit()
11448{
11449 AutoCaller autoCaller(this);
11450 AssertComRCReturnVoid(autoCaller.rc());
11451
11452 AutoCaller peerCaller(mPeer);
11453 AssertComRCReturnVoid(peerCaller.rc());
11454
11455 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11456
11457 /*
11458 * use safe commit to ensure Snapshot machines (that share mUserData)
11459 * will still refer to a valid memory location
11460 */
11461 mUserData.commitCopy();
11462
11463 mHWData.commit();
11464
11465 if (mMediaData.isBackedUp())
11466 i_commitMedia(Global::IsOnline(mData->mMachineState));
11467
11468 mBIOSSettings->i_commit();
11469 mVRDEServer->i_commit();
11470 mAudioAdapter->i_commit();
11471 mUSBDeviceFilters->i_commit();
11472 mBandwidthControl->i_commit();
11473
11474 /* Since mNetworkAdapters is a list which might have been changed (resized)
11475 * without using the Backupable<> template we need to handle the copying
11476 * of the list entries manually, including the creation of peers for the
11477 * new objects. */
11478 bool commitNetworkAdapters = false;
11479 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11480 if (mPeer)
11481 {
11482 /* commit everything, even the ones which will go away */
11483 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11484 mNetworkAdapters[slot]->i_commit();
11485 /* copy over the new entries, creating a peer and uninit the original */
11486 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11487 for (size_t slot = 0; slot < newSize; slot++)
11488 {
11489 /* look if this adapter has a peer device */
11490 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11491 if (!peer)
11492 {
11493 /* no peer means the adapter is a newly created one;
11494 * create a peer owning data this data share it with */
11495 peer.createObject();
11496 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11497 }
11498 mPeer->mNetworkAdapters[slot] = peer;
11499 }
11500 /* uninit any no longer needed network adapters */
11501 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11502 mNetworkAdapters[slot]->uninit();
11503 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11504 {
11505 if (mPeer->mNetworkAdapters[slot])
11506 mPeer->mNetworkAdapters[slot]->uninit();
11507 }
11508 /* Keep the original network adapter count until this point, so that
11509 * discarding a chipset type change will not lose settings. */
11510 mNetworkAdapters.resize(newSize);
11511 mPeer->mNetworkAdapters.resize(newSize);
11512 }
11513 else
11514 {
11515 /* we have no peer (our parent is the newly created machine);
11516 * just commit changes to the network adapters */
11517 commitNetworkAdapters = true;
11518 }
11519 if (commitNetworkAdapters)
11520 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11521 mNetworkAdapters[slot]->i_commit();
11522
11523 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11524 mSerialPorts[slot]->i_commit();
11525 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11526 mParallelPorts[slot]->i_commit();
11527
11528 bool commitStorageControllers = false;
11529
11530 if (mStorageControllers.isBackedUp())
11531 {
11532 mStorageControllers.commit();
11533
11534 if (mPeer)
11535 {
11536 /* Commit all changes to new controllers (this will reshare data with
11537 * peers for those who have peers) */
11538 StorageControllerList *newList = new StorageControllerList();
11539 StorageControllerList::const_iterator it = mStorageControllers->begin();
11540 while (it != mStorageControllers->end())
11541 {
11542 (*it)->i_commit();
11543
11544 /* look if this controller has a peer device */
11545 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11546 if (!peer)
11547 {
11548 /* no peer means the device is a newly created one;
11549 * create a peer owning data this device share it with */
11550 peer.createObject();
11551 peer->init(mPeer, *it, true /* aReshare */);
11552 }
11553 else
11554 {
11555 /* remove peer from the old list */
11556 mPeer->mStorageControllers->remove(peer);
11557 }
11558 /* and add it to the new list */
11559 newList->push_back(peer);
11560
11561 ++it;
11562 }
11563
11564 /* uninit old peer's controllers that are left */
11565 it = mPeer->mStorageControllers->begin();
11566 while (it != mPeer->mStorageControllers->end())
11567 {
11568 (*it)->uninit();
11569 ++it;
11570 }
11571
11572 /* attach new list of controllers to our peer */
11573 mPeer->mStorageControllers.attach(newList);
11574 }
11575 else
11576 {
11577 /* we have no peer (our parent is the newly created machine);
11578 * just commit changes to devices */
11579 commitStorageControllers = true;
11580 }
11581 }
11582 else
11583 {
11584 /* the list of controllers itself is not changed,
11585 * just commit changes to controllers themselves */
11586 commitStorageControllers = true;
11587 }
11588
11589 if (commitStorageControllers)
11590 {
11591 StorageControllerList::const_iterator it = mStorageControllers->begin();
11592 while (it != mStorageControllers->end())
11593 {
11594 (*it)->i_commit();
11595 ++it;
11596 }
11597 }
11598
11599 bool commitUSBControllers = false;
11600
11601 if (mUSBControllers.isBackedUp())
11602 {
11603 mUSBControllers.commit();
11604
11605 if (mPeer)
11606 {
11607 /* Commit all changes to new controllers (this will reshare data with
11608 * peers for those who have peers) */
11609 USBControllerList *newList = new USBControllerList();
11610 USBControllerList::const_iterator it = mUSBControllers->begin();
11611 while (it != mUSBControllers->end())
11612 {
11613 (*it)->i_commit();
11614
11615 /* look if this controller has a peer device */
11616 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11617 if (!peer)
11618 {
11619 /* no peer means the device is a newly created one;
11620 * create a peer owning data this device share it with */
11621 peer.createObject();
11622 peer->init(mPeer, *it, true /* aReshare */);
11623 }
11624 else
11625 {
11626 /* remove peer from the old list */
11627 mPeer->mUSBControllers->remove(peer);
11628 }
11629 /* and add it to the new list */
11630 newList->push_back(peer);
11631
11632 ++it;
11633 }
11634
11635 /* uninit old peer's controllers that are left */
11636 it = mPeer->mUSBControllers->begin();
11637 while (it != mPeer->mUSBControllers->end())
11638 {
11639 (*it)->uninit();
11640 ++it;
11641 }
11642
11643 /* attach new list of controllers to our peer */
11644 mPeer->mUSBControllers.attach(newList);
11645 }
11646 else
11647 {
11648 /* we have no peer (our parent is the newly created machine);
11649 * just commit changes to devices */
11650 commitUSBControllers = true;
11651 }
11652 }
11653 else
11654 {
11655 /* the list of controllers itself is not changed,
11656 * just commit changes to controllers themselves */
11657 commitUSBControllers = true;
11658 }
11659
11660 if (commitUSBControllers)
11661 {
11662 USBControllerList::const_iterator it = mUSBControllers->begin();
11663 while (it != mUSBControllers->end())
11664 {
11665 (*it)->i_commit();
11666 ++it;
11667 }
11668 }
11669
11670 if (i_isSessionMachine())
11671 {
11672 /* attach new data to the primary machine and reshare it */
11673 mPeer->mUserData.attach(mUserData);
11674 mPeer->mHWData.attach(mHWData);
11675 /* mMediaData is reshared by fixupMedia */
11676 // mPeer->mMediaData.attach(mMediaData);
11677 Assert(mPeer->mMediaData.data() == mMediaData.data());
11678 }
11679}
11680
11681/**
11682 * Copies all the hardware data from the given machine.
11683 *
11684 * Currently, only called when the VM is being restored from a snapshot. In
11685 * particular, this implies that the VM is not running during this method's
11686 * call.
11687 *
11688 * @note This method must be called from under this object's lock.
11689 *
11690 * @note This method doesn't call #commit(), so all data remains backed up and
11691 * unsaved.
11692 */
11693void Machine::i_copyFrom(Machine *aThat)
11694{
11695 AssertReturnVoid(!i_isSnapshotMachine());
11696 AssertReturnVoid(aThat->i_isSnapshotMachine());
11697
11698 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11699
11700 mHWData.assignCopy(aThat->mHWData);
11701
11702 // create copies of all shared folders (mHWData after attaching a copy
11703 // contains just references to original objects)
11704 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11705 it != mHWData->mSharedFolders.end();
11706 ++it)
11707 {
11708 ComObjPtr<SharedFolder> folder;
11709 folder.createObject();
11710 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11711 AssertComRC(rc);
11712 *it = folder;
11713 }
11714
11715 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11716 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11717 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11718 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11719 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11720
11721 /* create private copies of all controllers */
11722 mStorageControllers.backup();
11723 mStorageControllers->clear();
11724 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11725 it != aThat->mStorageControllers->end();
11726 ++it)
11727 {
11728 ComObjPtr<StorageController> ctrl;
11729 ctrl.createObject();
11730 ctrl->initCopy(this, *it);
11731 mStorageControllers->push_back(ctrl);
11732 }
11733
11734 /* create private copies of all USB controllers */
11735 mUSBControllers.backup();
11736 mUSBControllers->clear();
11737 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11738 it != aThat->mUSBControllers->end();
11739 ++it)
11740 {
11741 ComObjPtr<USBController> ctrl;
11742 ctrl.createObject();
11743 ctrl->initCopy(this, *it);
11744 mUSBControllers->push_back(ctrl);
11745 }
11746
11747 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11748 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11749 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11750 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11751 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11752 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11753 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11754}
11755
11756/**
11757 * Returns whether the given storage controller is hotplug capable.
11758 *
11759 * @returns true if the controller supports hotplugging
11760 * false otherwise.
11761 * @param enmCtrlType The controller type to check for.
11762 */
11763bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11764{
11765 ComPtr<ISystemProperties> systemProperties;
11766 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11767 if (FAILED(rc))
11768 return false;
11769
11770 BOOL aHotplugCapable = FALSE;
11771 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11772
11773 return RT_BOOL(aHotplugCapable);
11774}
11775
11776#ifdef VBOX_WITH_RESOURCE_USAGE_API
11777
11778void Machine::i_getDiskList(MediaList &list)
11779{
11780 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11781 it != mMediaData->mAttachments.end();
11782 ++it)
11783 {
11784 MediumAttachment* pAttach = *it;
11785 /* just in case */
11786 AssertStmt(pAttach, continue);
11787
11788 AutoCaller localAutoCallerA(pAttach);
11789 if (FAILED(localAutoCallerA.rc())) continue;
11790
11791 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11792
11793 if (pAttach->i_getType() == DeviceType_HardDisk)
11794 list.push_back(pAttach->i_getMedium());
11795 }
11796}
11797
11798void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11799{
11800 AssertReturnVoid(isWriteLockOnCurrentThread());
11801 AssertPtrReturnVoid(aCollector);
11802
11803 pm::CollectorHAL *hal = aCollector->getHAL();
11804 /* Create sub metrics */
11805 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11806 "Percentage of processor time spent in user mode by the VM process.");
11807 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11808 "Percentage of processor time spent in kernel mode by the VM process.");
11809 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11810 "Size of resident portion of VM process in memory.");
11811 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11812 "Actual size of all VM disks combined.");
11813 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11814 "Network receive rate.");
11815 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11816 "Network transmit rate.");
11817 /* Create and register base metrics */
11818 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11819 cpuLoadUser, cpuLoadKernel);
11820 aCollector->registerBaseMetric(cpuLoad);
11821 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11822 ramUsageUsed);
11823 aCollector->registerBaseMetric(ramUsage);
11824 MediaList disks;
11825 i_getDiskList(disks);
11826 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11827 diskUsageUsed);
11828 aCollector->registerBaseMetric(diskUsage);
11829
11830 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11831 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11832 new pm::AggregateAvg()));
11833 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11834 new pm::AggregateMin()));
11835 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11836 new pm::AggregateMax()));
11837 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11838 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11839 new pm::AggregateAvg()));
11840 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11841 new pm::AggregateMin()));
11842 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11843 new pm::AggregateMax()));
11844
11845 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11846 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11847 new pm::AggregateAvg()));
11848 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11849 new pm::AggregateMin()));
11850 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11851 new pm::AggregateMax()));
11852
11853 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11854 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11855 new pm::AggregateAvg()));
11856 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11857 new pm::AggregateMin()));
11858 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11859 new pm::AggregateMax()));
11860
11861
11862 /* Guest metrics collector */
11863 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11864 aCollector->registerGuest(mCollectorGuest);
11865 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11866 this, __PRETTY_FUNCTION__, mCollectorGuest));
11867
11868 /* Create sub metrics */
11869 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11870 "Percentage of processor time spent in user mode as seen by the guest.");
11871 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11872 "Percentage of processor time spent in kernel mode as seen by the guest.");
11873 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11874 "Percentage of processor time spent idling as seen by the guest.");
11875
11876 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11877 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11878 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11879 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11880 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11881 pm::SubMetric *guestMemCache = new pm::SubMetric(
11882 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11883
11884 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
11885 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11886
11887 /* Create and register base metrics */
11888 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11889 machineNetRx, machineNetTx);
11890 aCollector->registerBaseMetric(machineNetRate);
11891
11892 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11893 guestLoadUser, guestLoadKernel, guestLoadIdle);
11894 aCollector->registerBaseMetric(guestCpuLoad);
11895
11896 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11897 guestMemTotal, guestMemFree,
11898 guestMemBalloon, guestMemShared,
11899 guestMemCache, guestPagedTotal);
11900 aCollector->registerBaseMetric(guestCpuMem);
11901
11902 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11903 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11904 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11905 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11906
11907 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11908 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11909 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11910 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11911
11912 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11913 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11914 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11915 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11916
11917 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11918 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11919 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11920 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11921
11922 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11923 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11924 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11925 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11926
11927 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11928 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11929 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11930 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11931
11932 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11933 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11934 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11935 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11936
11937 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11938 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11939 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11940 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11941
11942 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11943 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11944 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11945 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11946
11947 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11948 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11949 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11950 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11951
11952 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11953 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11954 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11955 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11956}
11957
11958void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11959{
11960 AssertReturnVoid(isWriteLockOnCurrentThread());
11961
11962 if (aCollector)
11963 {
11964 aCollector->unregisterMetricsFor(aMachine);
11965 aCollector->unregisterBaseMetricsFor(aMachine);
11966 }
11967}
11968
11969#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11970
11971
11972////////////////////////////////////////////////////////////////////////////////
11973
11974DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11975
11976HRESULT SessionMachine::FinalConstruct()
11977{
11978 LogFlowThisFunc(("\n"));
11979
11980 mClientToken = NULL;
11981
11982 return BaseFinalConstruct();
11983}
11984
11985void SessionMachine::FinalRelease()
11986{
11987 LogFlowThisFunc(("\n"));
11988
11989 Assert(!mClientToken);
11990 /* paranoia, should not hang around any more */
11991 if (mClientToken)
11992 {
11993 delete mClientToken;
11994 mClientToken = NULL;
11995 }
11996
11997 uninit(Uninit::Unexpected);
11998
11999 BaseFinalRelease();
12000}
12001
12002/**
12003 * @note Must be called only by Machine::LockMachine() from its own write lock.
12004 */
12005HRESULT SessionMachine::init(Machine *aMachine)
12006{
12007 LogFlowThisFuncEnter();
12008 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12009
12010 AssertReturn(aMachine, E_INVALIDARG);
12011
12012 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12013
12014 /* Enclose the state transition NotReady->InInit->Ready */
12015 AutoInitSpan autoInitSpan(this);
12016 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12017
12018 HRESULT rc = S_OK;
12019
12020 /* create the machine client token */
12021 try
12022 {
12023 mClientToken = new ClientToken(aMachine, this);
12024 if (!mClientToken->isReady())
12025 {
12026 delete mClientToken;
12027 mClientToken = NULL;
12028 rc = E_FAIL;
12029 }
12030 }
12031 catch (std::bad_alloc &)
12032 {
12033 rc = E_OUTOFMEMORY;
12034 }
12035 if (FAILED(rc))
12036 return rc;
12037
12038 /* memorize the peer Machine */
12039 unconst(mPeer) = aMachine;
12040 /* share the parent pointer */
12041 unconst(mParent) = aMachine->mParent;
12042
12043 /* take the pointers to data to share */
12044 mData.share(aMachine->mData);
12045 mSSData.share(aMachine->mSSData);
12046
12047 mUserData.share(aMachine->mUserData);
12048 mHWData.share(aMachine->mHWData);
12049 mMediaData.share(aMachine->mMediaData);
12050
12051 mStorageControllers.allocate();
12052 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12053 it != aMachine->mStorageControllers->end();
12054 ++it)
12055 {
12056 ComObjPtr<StorageController> ctl;
12057 ctl.createObject();
12058 ctl->init(this, *it);
12059 mStorageControllers->push_back(ctl);
12060 }
12061
12062 mUSBControllers.allocate();
12063 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12064 it != aMachine->mUSBControllers->end();
12065 ++it)
12066 {
12067 ComObjPtr<USBController> ctl;
12068 ctl.createObject();
12069 ctl->init(this, *it);
12070 mUSBControllers->push_back(ctl);
12071 }
12072
12073 unconst(mBIOSSettings).createObject();
12074 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12075 /* create another VRDEServer object that will be mutable */
12076 unconst(mVRDEServer).createObject();
12077 mVRDEServer->init(this, aMachine->mVRDEServer);
12078 /* create another audio adapter object that will be mutable */
12079 unconst(mAudioAdapter).createObject();
12080 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12081 /* create a list of serial ports that will be mutable */
12082 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12083 {
12084 unconst(mSerialPorts[slot]).createObject();
12085 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12086 }
12087 /* create a list of parallel ports that will be mutable */
12088 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12089 {
12090 unconst(mParallelPorts[slot]).createObject();
12091 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12092 }
12093
12094 /* create another USB device filters object that will be mutable */
12095 unconst(mUSBDeviceFilters).createObject();
12096 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12097
12098 /* create a list of network adapters that will be mutable */
12099 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12100 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12101 {
12102 unconst(mNetworkAdapters[slot]).createObject();
12103 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12104 }
12105
12106 /* create another bandwidth control object that will be mutable */
12107 unconst(mBandwidthControl).createObject();
12108 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12109
12110 /* default is to delete saved state on Saved -> PoweredOff transition */
12111 mRemoveSavedState = true;
12112
12113 /* Confirm a successful initialization when it's the case */
12114 autoInitSpan.setSucceeded();
12115
12116 miNATNetworksStarted = 0;
12117
12118 LogFlowThisFuncLeave();
12119 return rc;
12120}
12121
12122/**
12123 * Uninitializes this session object. If the reason is other than
12124 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12125 * or the client watcher code.
12126 *
12127 * @param aReason uninitialization reason
12128 *
12129 * @note Locks mParent + this object for writing.
12130 */
12131void SessionMachine::uninit(Uninit::Reason aReason)
12132{
12133 LogFlowThisFuncEnter();
12134 LogFlowThisFunc(("reason=%d\n", aReason));
12135
12136 /*
12137 * Strongly reference ourselves to prevent this object deletion after
12138 * mData->mSession.mMachine.setNull() below (which can release the last
12139 * reference and call the destructor). Important: this must be done before
12140 * accessing any members (and before AutoUninitSpan that does it as well).
12141 * This self reference will be released as the very last step on return.
12142 */
12143 ComObjPtr<SessionMachine> selfRef = this;
12144
12145 /* Enclose the state transition Ready->InUninit->NotReady */
12146 AutoUninitSpan autoUninitSpan(this);
12147 if (autoUninitSpan.uninitDone())
12148 {
12149 LogFlowThisFunc(("Already uninitialized\n"));
12150 LogFlowThisFuncLeave();
12151 return;
12152 }
12153
12154 if (autoUninitSpan.initFailed())
12155 {
12156 /* We've been called by init() because it's failed. It's not really
12157 * necessary (nor it's safe) to perform the regular uninit sequence
12158 * below, the following is enough.
12159 */
12160 LogFlowThisFunc(("Initialization failed.\n"));
12161 /* destroy the machine client token */
12162 if (mClientToken)
12163 {
12164 delete mClientToken;
12165 mClientToken = NULL;
12166 }
12167 uninitDataAndChildObjects();
12168 mData.free();
12169 unconst(mParent) = NULL;
12170 unconst(mPeer) = NULL;
12171 LogFlowThisFuncLeave();
12172 return;
12173 }
12174
12175 MachineState_T lastState;
12176 {
12177 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12178 lastState = mData->mMachineState;
12179 }
12180 NOREF(lastState);
12181
12182#ifdef VBOX_WITH_USB
12183 // release all captured USB devices, but do this before requesting the locks below
12184 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12185 {
12186 /* Console::captureUSBDevices() is called in the VM process only after
12187 * setting the machine state to Starting or Restoring.
12188 * Console::detachAllUSBDevices() will be called upon successful
12189 * termination. So, we need to release USB devices only if there was
12190 * an abnormal termination of a running VM.
12191 *
12192 * This is identical to SessionMachine::DetachAllUSBDevices except
12193 * for the aAbnormal argument. */
12194 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12195 AssertComRC(rc);
12196 NOREF(rc);
12197
12198 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12199 if (service)
12200 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12201 }
12202#endif /* VBOX_WITH_USB */
12203
12204 // we need to lock this object in uninit() because the lock is shared
12205 // with mPeer (as well as data we modify below). mParent lock is needed
12206 // by several calls to it, and USB needs host lock.
12207 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12208
12209#ifdef VBOX_WITH_RESOURCE_USAGE_API
12210 /*
12211 * It is safe to call Machine::i_unregisterMetrics() here because
12212 * PerformanceCollector::samplerCallback no longer accesses guest methods
12213 * holding the lock.
12214 */
12215 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12216 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12217 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12218 this, __PRETTY_FUNCTION__, mCollectorGuest));
12219 if (mCollectorGuest)
12220 {
12221 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12222 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12223 mCollectorGuest = NULL;
12224 }
12225#endif
12226
12227 if (aReason == Uninit::Abnormal)
12228 {
12229 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12230 Global::IsOnlineOrTransient(lastState)));
12231
12232 /* reset the state to Aborted */
12233 if (mData->mMachineState != MachineState_Aborted)
12234 i_setMachineState(MachineState_Aborted);
12235 }
12236
12237 // any machine settings modified?
12238 if (mData->flModifications)
12239 {
12240 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12241 i_rollback(false /* aNotify */);
12242 }
12243
12244 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12245 || !mConsoleTaskData.mSnapshot);
12246 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12247 {
12248 LogWarningThisFunc(("canceling failed save state request!\n"));
12249 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12250 }
12251 else if (!mConsoleTaskData.mSnapshot.isNull())
12252 {
12253 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12254
12255 /* delete all differencing hard disks created (this will also attach
12256 * their parents back by rolling back mMediaData) */
12257 i_rollbackMedia();
12258
12259 // delete the saved state file (it might have been already created)
12260 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12261 // think it's still in use
12262 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12263 mConsoleTaskData.mSnapshot->uninit();
12264 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12265 }
12266
12267 mData->mSession.mPID = NIL_RTPROCESS;
12268
12269 if (aReason == Uninit::Unexpected)
12270 {
12271 /* Uninitialization didn't come from #checkForDeath(), so tell the
12272 * client watcher thread to update the set of machines that have open
12273 * sessions. */
12274 mParent->i_updateClientWatcher();
12275 }
12276
12277 /* uninitialize all remote controls */
12278 if (mData->mSession.mRemoteControls.size())
12279 {
12280 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12281 mData->mSession.mRemoteControls.size()));
12282
12283 Data::Session::RemoteControlList::iterator it =
12284 mData->mSession.mRemoteControls.begin();
12285 while (it != mData->mSession.mRemoteControls.end())
12286 {
12287 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12288 HRESULT rc = (*it)->Uninitialize();
12289 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12290 if (FAILED(rc))
12291 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12292 ++it;
12293 }
12294 mData->mSession.mRemoteControls.clear();
12295 }
12296
12297 /* Remove all references to the NAT network service. The service will stop
12298 * if all references (also from other VMs) are removed. */
12299 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12300 {
12301 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12302 {
12303 NetworkAttachmentType_T type;
12304 HRESULT hrc;
12305
12306 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12307 if ( SUCCEEDED(hrc)
12308 && type == NetworkAttachmentType_NATNetwork)
12309 {
12310 Bstr name;
12311 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12312 if (SUCCEEDED(hrc))
12313 {
12314 multilock.release();
12315 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12316 mUserData->s.strName.c_str(), name.raw()));
12317 mParent->i_natNetworkRefDec(name.raw());
12318 multilock.acquire();
12319 }
12320 }
12321 }
12322 }
12323
12324 /*
12325 * An expected uninitialization can come only from #checkForDeath().
12326 * Otherwise it means that something's gone really wrong (for example,
12327 * the Session implementation has released the VirtualBox reference
12328 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12329 * etc). However, it's also possible, that the client releases the IPC
12330 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12331 * but the VirtualBox release event comes first to the server process.
12332 * This case is practically possible, so we should not assert on an
12333 * unexpected uninit, just log a warning.
12334 */
12335
12336 if ((aReason == Uninit::Unexpected))
12337 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12338
12339 if (aReason != Uninit::Normal)
12340 {
12341 mData->mSession.mDirectControl.setNull();
12342 }
12343 else
12344 {
12345 /* this must be null here (see #OnSessionEnd()) */
12346 Assert(mData->mSession.mDirectControl.isNull());
12347 Assert(mData->mSession.mState == SessionState_Unlocking);
12348 Assert(!mData->mSession.mProgress.isNull());
12349 }
12350 if (mData->mSession.mProgress)
12351 {
12352 if (aReason == Uninit::Normal)
12353 mData->mSession.mProgress->i_notifyComplete(S_OK);
12354 else
12355 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12356 COM_IIDOF(ISession),
12357 getComponentName(),
12358 tr("The VM session was aborted"));
12359 mData->mSession.mProgress.setNull();
12360 }
12361
12362 /* remove the association between the peer machine and this session machine */
12363 Assert( (SessionMachine*)mData->mSession.mMachine == this
12364 || aReason == Uninit::Unexpected);
12365
12366 /* reset the rest of session data */
12367 mData->mSession.mMachine.setNull();
12368 mData->mSession.mState = SessionState_Unlocked;
12369 mData->mSession.mType.setNull();
12370
12371 /* destroy the machine client token before leaving the exclusive lock */
12372 if (mClientToken)
12373 {
12374 delete mClientToken;
12375 mClientToken = NULL;
12376 }
12377
12378 /* fire an event */
12379 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12380
12381 uninitDataAndChildObjects();
12382
12383 /* free the essential data structure last */
12384 mData.free();
12385
12386 /* release the exclusive lock before setting the below two to NULL */
12387 multilock.release();
12388
12389 unconst(mParent) = NULL;
12390 unconst(mPeer) = NULL;
12391
12392 LogFlowThisFuncLeave();
12393}
12394
12395// util::Lockable interface
12396////////////////////////////////////////////////////////////////////////////////
12397
12398/**
12399 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12400 * with the primary Machine instance (mPeer).
12401 */
12402RWLockHandle *SessionMachine::lockHandle() const
12403{
12404 AssertReturn(mPeer != NULL, NULL);
12405 return mPeer->lockHandle();
12406}
12407
12408// IInternalMachineControl methods
12409////////////////////////////////////////////////////////////////////////////////
12410
12411/**
12412 * Passes collected guest statistics to performance collector object
12413 */
12414STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12415 ULONG aCpuKernel, ULONG aCpuIdle,
12416 ULONG aMemTotal, ULONG aMemFree,
12417 ULONG aMemBalloon, ULONG aMemShared,
12418 ULONG aMemCache, ULONG aPageTotal,
12419 ULONG aAllocVMM, ULONG aFreeVMM,
12420 ULONG aBalloonedVMM, ULONG aSharedVMM,
12421 ULONG aVmNetRx, ULONG aVmNetTx)
12422{
12423#ifdef VBOX_WITH_RESOURCE_USAGE_API
12424 if (mCollectorGuest)
12425 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12426 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12427 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12428 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12429
12430 return S_OK;
12431#else
12432 NOREF(aValidStats);
12433 NOREF(aCpuUser);
12434 NOREF(aCpuKernel);
12435 NOREF(aCpuIdle);
12436 NOREF(aMemTotal);
12437 NOREF(aMemFree);
12438 NOREF(aMemBalloon);
12439 NOREF(aMemShared);
12440 NOREF(aMemCache);
12441 NOREF(aPageTotal);
12442 NOREF(aAllocVMM);
12443 NOREF(aFreeVMM);
12444 NOREF(aBalloonedVMM);
12445 NOREF(aSharedVMM);
12446 NOREF(aVmNetRx);
12447 NOREF(aVmNetTx);
12448 return E_NOTIMPL;
12449#endif
12450}
12451
12452/**
12453 * @note Locks this object for writing.
12454 */
12455STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12456{
12457 AutoCaller autoCaller(this);
12458 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12459
12460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12461
12462 mRemoveSavedState = aRemove;
12463
12464 return S_OK;
12465}
12466
12467/**
12468 * @note Locks the same as #i_setMachineState() does.
12469 */
12470STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12471{
12472 return i_setMachineState(aMachineState);
12473}
12474
12475/**
12476 * @note Locks this object for writing.
12477 */
12478STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12479{
12480 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12481 AutoCaller autoCaller(this);
12482 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12483
12484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12485
12486 if (mData->mSession.mState != SessionState_Locked)
12487 return VBOX_E_INVALID_OBJECT_STATE;
12488
12489 if (!mData->mSession.mProgress.isNull())
12490 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12491
12492 /* If we didn't reference the NAT network service yet, add a reference to
12493 * force a start */
12494 if (miNATNetworksStarted < 1)
12495 {
12496 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12497 {
12498 NetworkAttachmentType_T type;
12499 HRESULT hrc;
12500 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12501 if ( SUCCEEDED(hrc)
12502 && type == NetworkAttachmentType_NATNetwork)
12503 {
12504 Bstr name;
12505 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12506 if (SUCCEEDED(hrc))
12507 {
12508 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12509 mUserData->s.strName.c_str(), name.raw()));
12510 mPeer->lockHandle()->unlockWrite();
12511 mParent->i_natNetworkRefInc(name.raw());
12512#ifdef RT_LOCK_STRICT
12513 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12514#else
12515 mPeer->lockHandle()->lockWrite();
12516#endif
12517 }
12518 }
12519 }
12520 miNATNetworksStarted++;
12521 }
12522
12523 LogFlowThisFunc(("returns S_OK.\n"));
12524 return S_OK;
12525}
12526
12527/**
12528 * @note Locks this object for writing.
12529 */
12530STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12531{
12532 AutoCaller autoCaller(this);
12533 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12534
12535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12536
12537 if (mData->mSession.mState != SessionState_Locked)
12538 return VBOX_E_INVALID_OBJECT_STATE;
12539
12540 /* Finalize the LaunchVMProcess progress object. */
12541 if (mData->mSession.mProgress)
12542 {
12543 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12544 mData->mSession.mProgress.setNull();
12545 }
12546
12547 if (SUCCEEDED((HRESULT)iResult))
12548 {
12549#ifdef VBOX_WITH_RESOURCE_USAGE_API
12550 /* The VM has been powered up successfully, so it makes sense
12551 * now to offer the performance metrics for a running machine
12552 * object. Doing it earlier wouldn't be safe. */
12553 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12554 mData->mSession.mPID);
12555#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12556 }
12557
12558 return S_OK;
12559}
12560
12561/**
12562 * @note Locks this object for writing.
12563 */
12564STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12565{
12566 LogFlowThisFuncEnter();
12567
12568 AutoCaller autoCaller(this);
12569 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12570
12571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12572
12573 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12574 E_FAIL);
12575
12576 /* create a progress object to track operation completion */
12577 ComObjPtr<Progress> pProgress;
12578 pProgress.createObject();
12579 pProgress->init(i_getVirtualBox(),
12580 static_cast<IMachine *>(this) /* aInitiator */,
12581 Bstr(tr("Stopping the virtual machine")).raw(),
12582 FALSE /* aCancelable */);
12583
12584 /* fill in the console task data */
12585 mConsoleTaskData.mLastState = mData->mMachineState;
12586 mConsoleTaskData.mProgress = pProgress;
12587
12588 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12589 i_setMachineState(MachineState_Stopping);
12590
12591 pProgress.queryInterfaceTo(aProgress);
12592
12593 return S_OK;
12594}
12595
12596/**
12597 * @note Locks this object for writing.
12598 */
12599STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12600{
12601 LogFlowThisFuncEnter();
12602
12603 AutoCaller autoCaller(this);
12604 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12605
12606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12607
12608 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12609 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12610 && mConsoleTaskData.mLastState != MachineState_Null,
12611 E_FAIL);
12612
12613 /*
12614 * On failure, set the state to the state we had when BeginPoweringDown()
12615 * was called (this is expected by Console::PowerDown() and the associated
12616 * task). On success the VM process already changed the state to
12617 * MachineState_PoweredOff, so no need to do anything.
12618 */
12619 if (FAILED(iResult))
12620 i_setMachineState(mConsoleTaskData.mLastState);
12621
12622 /* notify the progress object about operation completion */
12623 Assert(mConsoleTaskData.mProgress);
12624 if (SUCCEEDED(iResult))
12625 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12626 else
12627 {
12628 Utf8Str strErrMsg(aErrMsg);
12629 if (strErrMsg.length())
12630 mConsoleTaskData.mProgress->i_notifyComplete(iResult,
12631 COM_IIDOF(ISession),
12632 getComponentName(),
12633 strErrMsg.c_str());
12634 else
12635 mConsoleTaskData.mProgress->i_notifyComplete(iResult);
12636 }
12637
12638 /* clear out the temporary saved state data */
12639 mConsoleTaskData.mLastState = MachineState_Null;
12640 mConsoleTaskData.mProgress.setNull();
12641
12642 LogFlowThisFuncLeave();
12643 return S_OK;
12644}
12645
12646
12647/**
12648 * Goes through the USB filters of the given machine to see if the given
12649 * device matches any filter or not.
12650 *
12651 * @note Locks the same as USBController::hasMatchingFilter() does.
12652 */
12653STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12654 BOOL *aMatched,
12655 ULONG *aMaskedIfs)
12656{
12657 LogFlowThisFunc(("\n"));
12658
12659 AutoCaller autoCaller(this);
12660 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12661
12662#ifdef VBOX_WITH_USB
12663 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
12664#else
12665 NOREF(aUSBDevice);
12666 NOREF(aMaskedIfs);
12667 *aMatched = FALSE;
12668#endif
12669
12670 return S_OK;
12671}
12672
12673/**
12674 * @note Locks the same as Host::captureUSBDevice() does.
12675 */
12676STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12677{
12678 LogFlowThisFunc(("\n"));
12679
12680 AutoCaller autoCaller(this);
12681 AssertComRCReturnRC(autoCaller.rc());
12682
12683#ifdef VBOX_WITH_USB
12684 /* if captureDeviceForVM() fails, it must have set extended error info */
12685 clearError();
12686 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12687 if (FAILED(rc)) return rc;
12688
12689 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12690 AssertReturn(service, E_FAIL);
12691 return service->captureDeviceForVM(this, Guid(aId).ref());
12692#else
12693 NOREF(aId);
12694 return E_NOTIMPL;
12695#endif
12696}
12697
12698/**
12699 * @note Locks the same as Host::detachUSBDevice() does.
12700 */
12701STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12702{
12703 LogFlowThisFunc(("\n"));
12704
12705 AutoCaller autoCaller(this);
12706 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12707
12708#ifdef VBOX_WITH_USB
12709 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12710 AssertReturn(service, E_FAIL);
12711 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12712#else
12713 NOREF(aId);
12714 NOREF(aDone);
12715 return E_NOTIMPL;
12716#endif
12717}
12718
12719/**
12720 * Inserts all machine filters to the USB proxy service and then calls
12721 * Host::autoCaptureUSBDevices().
12722 *
12723 * Called by Console from the VM process upon VM startup.
12724 *
12725 * @note Locks what called methods lock.
12726 */
12727STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12728{
12729 LogFlowThisFunc(("\n"));
12730
12731 AutoCaller autoCaller(this);
12732 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12733
12734#ifdef VBOX_WITH_USB
12735 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12736 AssertComRC(rc);
12737 NOREF(rc);
12738
12739 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12740 AssertReturn(service, E_FAIL);
12741 return service->autoCaptureDevicesForVM(this);
12742#else
12743 return S_OK;
12744#endif
12745}
12746
12747/**
12748 * Removes all machine filters from the USB proxy service and then calls
12749 * Host::detachAllUSBDevices().
12750 *
12751 * Called by Console from the VM process upon normal VM termination or by
12752 * SessionMachine::uninit() upon abnormal VM termination (from under the
12753 * Machine/SessionMachine lock).
12754 *
12755 * @note Locks what called methods lock.
12756 */
12757STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12758{
12759 LogFlowThisFunc(("\n"));
12760
12761 AutoCaller autoCaller(this);
12762 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12763
12764#ifdef VBOX_WITH_USB
12765 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12766 AssertComRC(rc);
12767 NOREF(rc);
12768
12769 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12770 AssertReturn(service, E_FAIL);
12771 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12772#else
12773 NOREF(aDone);
12774 return S_OK;
12775#endif
12776}
12777
12778/**
12779 * @note Locks this object for writing.
12780 */
12781STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12782 IProgress **aProgress)
12783{
12784 LogFlowThisFuncEnter();
12785
12786 AssertReturn(aSession, E_INVALIDARG);
12787 AssertReturn(aProgress, E_INVALIDARG);
12788
12789 AutoCaller autoCaller(this);
12790
12791 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12792 /*
12793 * We don't assert below because it might happen that a non-direct session
12794 * informs us it is closed right after we've been uninitialized -- it's ok.
12795 */
12796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12797
12798 /* get IInternalSessionControl interface */
12799 ComPtr<IInternalSessionControl> control(aSession);
12800
12801 ComAssertRet(!control.isNull(), E_INVALIDARG);
12802
12803 /* Creating a Progress object requires the VirtualBox lock, and
12804 * thus locking it here is required by the lock order rules. */
12805 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12806
12807 if (control == mData->mSession.mDirectControl)
12808 {
12809 ComAssertRet(aProgress, E_POINTER);
12810
12811 /* The direct session is being normally closed by the client process
12812 * ----------------------------------------------------------------- */
12813
12814 /* go to the closing state (essential for all open*Session() calls and
12815 * for #checkForDeath()) */
12816 Assert(mData->mSession.mState == SessionState_Locked);
12817 mData->mSession.mState = SessionState_Unlocking;
12818
12819 /* set direct control to NULL to release the remote instance */
12820 mData->mSession.mDirectControl.setNull();
12821 LogFlowThisFunc(("Direct control is set to NULL\n"));
12822
12823 if (mData->mSession.mProgress)
12824 {
12825 /* finalize the progress, someone might wait if a frontend
12826 * closes the session before powering on the VM. */
12827 mData->mSession.mProgress->notifyComplete(E_FAIL,
12828 COM_IIDOF(ISession),
12829 getComponentName(),
12830 tr("The VM session was closed before any attempt to power it on"));
12831 mData->mSession.mProgress.setNull();
12832 }
12833
12834 /* Create the progress object the client will use to wait until
12835 * #checkForDeath() is called to uninitialize this session object after
12836 * it releases the IPC semaphore.
12837 * Note! Because we're "reusing" mProgress here, this must be a proxy
12838 * object just like for LaunchVMProcess. */
12839 Assert(mData->mSession.mProgress.isNull());
12840 ComObjPtr<ProgressProxy> progress;
12841 progress.createObject();
12842 ComPtr<IUnknown> pPeer(mPeer);
12843 progress->init(mParent, pPeer,
12844 Bstr(tr("Closing session")).raw(),
12845 FALSE /* aCancelable */);
12846 progress.queryInterfaceTo(aProgress);
12847 mData->mSession.mProgress = progress;
12848 }
12849 else
12850 {
12851 /* the remote session is being normally closed */
12852 Data::Session::RemoteControlList::iterator it =
12853 mData->mSession.mRemoteControls.begin();
12854 while (it != mData->mSession.mRemoteControls.end())
12855 {
12856 if (control == *it)
12857 break;
12858 ++it;
12859 }
12860 BOOL found = it != mData->mSession.mRemoteControls.end();
12861 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12862 E_INVALIDARG);
12863 // This MUST be erase(it), not remove(*it) as the latter triggers a
12864 // very nasty use after free due to the place where the value "lives".
12865 mData->mSession.mRemoteControls.erase(it);
12866 }
12867
12868 /* signal the client watcher thread, because the client is going away */
12869 mParent->i_updateClientWatcher();
12870
12871 LogFlowThisFuncLeave();
12872 return S_OK;
12873}
12874
12875/**
12876 * @note Locks this object for writing.
12877 */
12878STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12879{
12880 LogFlowThisFuncEnter();
12881
12882 CheckComArgOutPointerValid(aProgress);
12883 CheckComArgOutPointerValid(aStateFilePath);
12884
12885 AutoCaller autoCaller(this);
12886 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12887
12888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12889
12890 AssertReturn( mData->mMachineState == MachineState_Paused
12891 && mConsoleTaskData.mLastState == MachineState_Null
12892 && mConsoleTaskData.strStateFilePath.isEmpty(),
12893 E_FAIL);
12894
12895 /* create a progress object to track operation completion */
12896 ComObjPtr<Progress> pProgress;
12897 pProgress.createObject();
12898 pProgress->init(i_getVirtualBox(),
12899 static_cast<IMachine *>(this) /* aInitiator */,
12900 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12901 FALSE /* aCancelable */);
12902
12903 Utf8Str strStateFilePath;
12904 /* stateFilePath is null when the machine is not running */
12905 if (mData->mMachineState == MachineState_Paused)
12906 i_composeSavedStateFilename(strStateFilePath);
12907
12908 /* fill in the console task data */
12909 mConsoleTaskData.mLastState = mData->mMachineState;
12910 mConsoleTaskData.strStateFilePath = strStateFilePath;
12911 mConsoleTaskData.mProgress = pProgress;
12912
12913 /* set the state to Saving (this is expected by Console::SaveState()) */
12914 i_setMachineState(MachineState_Saving);
12915
12916 strStateFilePath.cloneTo(aStateFilePath);
12917 pProgress.queryInterfaceTo(aProgress);
12918
12919 return S_OK;
12920}
12921
12922/**
12923 * @note Locks mParent + this object for writing.
12924 */
12925STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12926{
12927 LogFlowThisFunc(("\n"));
12928
12929 AutoCaller autoCaller(this);
12930 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12931
12932 /* endSavingState() need mParent lock */
12933 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12934
12935 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12936 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12937 && mConsoleTaskData.mLastState != MachineState_Null
12938 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12939 E_FAIL);
12940
12941 /*
12942 * On failure, set the state to the state we had when BeginSavingState()
12943 * was called (this is expected by Console::SaveState() and the associated
12944 * task). On success the VM process already changed the state to
12945 * MachineState_Saved, so no need to do anything.
12946 */
12947 if (FAILED(iResult))
12948 i_setMachineState(mConsoleTaskData.mLastState);
12949
12950 return endSavingState(iResult, aErrMsg);
12951}
12952
12953/**
12954 * @note Locks this object for writing.
12955 */
12956STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12957{
12958 LogFlowThisFunc(("\n"));
12959
12960 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12961
12962 AutoCaller autoCaller(this);
12963 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12964
12965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12966
12967 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12968 || mData->mMachineState == MachineState_Teleported
12969 || mData->mMachineState == MachineState_Aborted
12970 , E_FAIL); /** @todo setError. */
12971
12972 Utf8Str stateFilePathFull = aSavedStateFile;
12973 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
12974 if (RT_FAILURE(vrc))
12975 return setError(VBOX_E_FILE_ERROR,
12976 tr("Invalid saved state file path '%ls' (%Rrc)"),
12977 aSavedStateFile,
12978 vrc);
12979
12980 mSSData->strStateFilePath = stateFilePathFull;
12981
12982 /* The below i_setMachineState() will detect the state transition and will
12983 * update the settings file */
12984
12985 return i_setMachineState(MachineState_Saved);
12986}
12987
12988STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12989 ComSafeArrayOut(BSTR, aValues),
12990 ComSafeArrayOut(LONG64, aTimestamps),
12991 ComSafeArrayOut(BSTR, aFlags))
12992{
12993 LogFlowThisFunc(("\n"));
12994
12995#ifdef VBOX_WITH_GUEST_PROPS
12996 using namespace guestProp;
12997
12998 AutoCaller autoCaller(this);
12999 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13000
13001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13002
13003 CheckComArgOutSafeArrayPointerValid(aNames);
13004 CheckComArgOutSafeArrayPointerValid(aValues);
13005 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13006 CheckComArgOutSafeArrayPointerValid(aFlags);
13007
13008 size_t cEntries = mHWData->mGuestProperties.size();
13009 com::SafeArray<BSTR> names(cEntries);
13010 com::SafeArray<BSTR> values(cEntries);
13011 com::SafeArray<LONG64> timestamps(cEntries);
13012 com::SafeArray<BSTR> flags(cEntries);
13013 unsigned i = 0;
13014 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13015 it != mHWData->mGuestProperties.end();
13016 ++it)
13017 {
13018 char szFlags[MAX_FLAGS_LEN + 1];
13019 it->first.cloneTo(&names[i]);
13020 it->second.strValue.cloneTo(&values[i]);
13021 timestamps[i] = it->second.mTimestamp;
13022 /* If it is NULL, keep it NULL. */
13023 if (it->second.mFlags)
13024 {
13025 writeFlags(it->second.mFlags, szFlags);
13026 Bstr(szFlags).cloneTo(&flags[i]);
13027 }
13028 else
13029 flags[i] = NULL;
13030 ++i;
13031 }
13032 names.detachTo(ComSafeArrayOutArg(aNames));
13033 values.detachTo(ComSafeArrayOutArg(aValues));
13034 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13035 flags.detachTo(ComSafeArrayOutArg(aFlags));
13036 return S_OK;
13037#else
13038 ReturnComNotImplemented();
13039#endif
13040}
13041
13042STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13043 IN_BSTR aValue,
13044 LONG64 aTimestamp,
13045 IN_BSTR aFlags)
13046{
13047 LogFlowThisFunc(("\n"));
13048
13049#ifdef VBOX_WITH_GUEST_PROPS
13050 using namespace guestProp;
13051
13052 CheckComArgStrNotEmptyOrNull(aName);
13053 CheckComArgNotNull(aValue);
13054 CheckComArgNotNull(aFlags);
13055
13056 try
13057 {
13058 /*
13059 * Convert input up front.
13060 */
13061 Utf8Str utf8Name(aName);
13062 uint32_t fFlags = NILFLAG;
13063 if (aFlags)
13064 {
13065 Utf8Str utf8Flags(aFlags);
13066 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13067 AssertRCReturn(vrc, E_INVALIDARG);
13068 }
13069
13070 /*
13071 * Now grab the object lock, validate the state and do the update.
13072 */
13073 AutoCaller autoCaller(this);
13074 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13075
13076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13077
13078 switch (mData->mMachineState)
13079 {
13080 case MachineState_Paused:
13081 case MachineState_Running:
13082 case MachineState_Teleporting:
13083 case MachineState_TeleportingPausedVM:
13084 case MachineState_LiveSnapshotting:
13085 case MachineState_DeletingSnapshotOnline:
13086 case MachineState_DeletingSnapshotPaused:
13087 case MachineState_Saving:
13088 case MachineState_Stopping:
13089 break;
13090
13091 default:
13092 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13093 VBOX_E_INVALID_VM_STATE);
13094 }
13095
13096 i_setModified(IsModified_MachineData);
13097 mHWData.backup();
13098
13099 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13100 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13101 if (it != mHWData->mGuestProperties.end())
13102 {
13103 if (!fDelete)
13104 {
13105 it->second.strValue = aValue;
13106 it->second.mTimestamp = aTimestamp;
13107 it->second.mFlags = fFlags;
13108 }
13109 else
13110 mHWData->mGuestProperties.erase(it);
13111
13112 mData->mGuestPropertiesModified = TRUE;
13113 }
13114 else if (!fDelete)
13115 {
13116 HWData::GuestProperty prop;
13117 prop.strValue = aValue;
13118 prop.mTimestamp = aTimestamp;
13119 prop.mFlags = fFlags;
13120
13121 mHWData->mGuestProperties[utf8Name] = prop;
13122 mData->mGuestPropertiesModified = TRUE;
13123 }
13124
13125 /*
13126 * Send a callback notification if appropriate
13127 */
13128 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13129 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13130 RTSTR_MAX,
13131 utf8Name.c_str(),
13132 RTSTR_MAX, NULL)
13133 )
13134 {
13135 alock.release();
13136
13137 mParent->i_onGuestPropertyChange(mData->mUuid,
13138 aName,
13139 aValue,
13140 aFlags);
13141 }
13142 }
13143 catch (...)
13144 {
13145 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13146 }
13147 return S_OK;
13148#else
13149 ReturnComNotImplemented();
13150#endif
13151}
13152
13153STDMETHODIMP SessionMachine::LockMedia()
13154{
13155 AutoCaller autoCaller(this);
13156 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13157
13158 AutoMultiWriteLock2 alock(this->lockHandle(),
13159 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13160
13161 AssertReturn( mData->mMachineState == MachineState_Starting
13162 || mData->mMachineState == MachineState_Restoring
13163 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13164
13165 clearError();
13166 alock.release();
13167 return lockMedia();
13168}
13169
13170STDMETHODIMP SessionMachine::UnlockMedia()
13171{
13172 HRESULT hrc = unlockMedia();
13173 return hrc;
13174}
13175
13176STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13177 IMediumAttachment **aNewAttachment)
13178{
13179 CheckComArgNotNull(aAttachment);
13180 CheckComArgOutPointerValid(aNewAttachment);
13181
13182 AutoCaller autoCaller(this);
13183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13184
13185 // request the host lock first, since might be calling Host methods for getting host drives;
13186 // next, protect the media tree all the while we're in here, as well as our member variables
13187 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13188 this->lockHandle(),
13189 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13190
13191 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13192
13193 Bstr ctrlName;
13194 LONG lPort;
13195 LONG lDevice;
13196 bool fTempEject;
13197 {
13198 AutoCaller autoAttachCaller(this);
13199 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13200
13201 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13202
13203 /* Need to query the details first, as the IMediumAttachment reference
13204 * might be to the original settings, which we are going to change. */
13205 ctrlName = pAttach->i_getControllerName();
13206 lPort = pAttach->i_getPort();
13207 lDevice = pAttach->i_getDevice();
13208 fTempEject = pAttach->i_getTempEject();
13209 }
13210
13211 if (!fTempEject)
13212 {
13213 /* Remember previously mounted medium. The medium before taking the
13214 * backup is not necessarily the same thing. */
13215 ComObjPtr<Medium> oldmedium;
13216 oldmedium = pAttach->i_getMedium();
13217
13218 i_setModified(IsModified_Storage);
13219 mMediaData.backup();
13220
13221 // The backup operation makes the pAttach reference point to the
13222 // old settings. Re-get the correct reference.
13223 pAttach = i_findAttachment(mMediaData->mAttachments,
13224 ctrlName.raw(),
13225 lPort,
13226 lDevice);
13227
13228 {
13229 AutoCaller autoAttachCaller(this);
13230 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13231
13232 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13233 if (!oldmedium.isNull())
13234 oldmedium->i_removeBackReference(mData->mUuid);
13235
13236 pAttach->i_updateMedium(NULL);
13237 pAttach->i_updateEjected();
13238 }
13239
13240 i_setModified(IsModified_Storage);
13241 }
13242 else
13243 {
13244 {
13245 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13246 pAttach->i_updateEjected();
13247 }
13248 }
13249
13250 pAttach.queryInterfaceTo(aNewAttachment);
13251
13252 return S_OK;
13253}
13254
13255// public methods only for internal purposes
13256/////////////////////////////////////////////////////////////////////////////
13257
13258#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13259/**
13260 * Called from the client watcher thread to check for expected or unexpected
13261 * death of the client process that has a direct session to this machine.
13262 *
13263 * On Win32 and on OS/2, this method is called only when we've got the
13264 * mutex (i.e. the client has either died or terminated normally) so it always
13265 * returns @c true (the client is terminated, the session machine is
13266 * uninitialized).
13267 *
13268 * On other platforms, the method returns @c true if the client process has
13269 * terminated normally or abnormally and the session machine was uninitialized,
13270 * and @c false if the client process is still alive.
13271 *
13272 * @note Locks this object for writing.
13273 */
13274bool SessionMachine::i_checkForDeath()
13275{
13276 Uninit::Reason reason;
13277 bool terminated = false;
13278
13279 /* Enclose autoCaller with a block because calling uninit() from under it
13280 * will deadlock. */
13281 {
13282 AutoCaller autoCaller(this);
13283 if (!autoCaller.isOk())
13284 {
13285 /* return true if not ready, to cause the client watcher to exclude
13286 * the corresponding session from watching */
13287 LogFlowThisFunc(("Already uninitialized!\n"));
13288 return true;
13289 }
13290
13291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13292
13293 /* Determine the reason of death: if the session state is Closing here,
13294 * everything is fine. Otherwise it means that the client did not call
13295 * OnSessionEnd() before it released the IPC semaphore. This may happen
13296 * either because the client process has abnormally terminated, or
13297 * because it simply forgot to call ISession::Close() before exiting. We
13298 * threat the latter also as an abnormal termination (see
13299 * Session::uninit() for details). */
13300 reason = mData->mSession.mState == SessionState_Unlocking ?
13301 Uninit::Normal :
13302 Uninit::Abnormal;
13303
13304 if (mClientToken)
13305 terminated = mClientToken->release();
13306 } /* AutoCaller block */
13307
13308 if (terminated)
13309 uninit(reason);
13310
13311 return terminated;
13312}
13313
13314void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13315{
13316 LogFlowThisFunc(("\n"));
13317
13318 strTokenId.setNull();
13319
13320 AutoCaller autoCaller(this);
13321 AssertComRCReturnVoid(autoCaller.rc());
13322
13323 Assert(mClientToken);
13324 if (mClientToken)
13325 mClientToken->getId(strTokenId);
13326}
13327#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13328IToken *SessionMachine::i_getToken()
13329{
13330 LogFlowThisFunc(("\n"));
13331
13332 AutoCaller autoCaller(this);
13333 AssertComRCReturn(autoCaller.rc(), NULL);
13334
13335 Assert(mClientToken);
13336 if (mClientToken)
13337 return mClientToken->getToken();
13338 else
13339 return NULL;
13340}
13341#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13342
13343Machine::ClientToken *SessionMachine::i_getClientToken()
13344{
13345 LogFlowThisFunc(("\n"));
13346
13347 AutoCaller autoCaller(this);
13348 AssertComRCReturn(autoCaller.rc(), NULL);
13349
13350 return mClientToken;
13351}
13352
13353
13354/**
13355 * @note Locks this object for reading.
13356 */
13357HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13358{
13359 LogFlowThisFunc(("\n"));
13360
13361 AutoCaller autoCaller(this);
13362 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13363
13364 ComPtr<IInternalSessionControl> directControl;
13365 {
13366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13367 directControl = mData->mSession.mDirectControl;
13368 }
13369
13370 /* ignore notifications sent after #OnSessionEnd() is called */
13371 if (!directControl)
13372 return S_OK;
13373
13374 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13375}
13376
13377/**
13378 * @note Locks this object for reading.
13379 */
13380HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13381 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13382 IN_BSTR aGuestIp, LONG aGuestPort)
13383{
13384 LogFlowThisFunc(("\n"));
13385
13386 AutoCaller autoCaller(this);
13387 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13388
13389 ComPtr<IInternalSessionControl> directControl;
13390 {
13391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13392 directControl = mData->mSession.mDirectControl;
13393 }
13394
13395 /* ignore notifications sent after #OnSessionEnd() is called */
13396 if (!directControl)
13397 return S_OK;
13398 /*
13399 * instead acting like callback we ask IVirtualBox deliver corresponding event
13400 */
13401
13402 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13403 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13404 return S_OK;
13405}
13406
13407/**
13408 * @note Locks this object for reading.
13409 */
13410HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13411{
13412 LogFlowThisFunc(("\n"));
13413
13414 AutoCaller autoCaller(this);
13415 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13416
13417 ComPtr<IInternalSessionControl> directControl;
13418 {
13419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13420 directControl = mData->mSession.mDirectControl;
13421 }
13422
13423 /* ignore notifications sent after #OnSessionEnd() is called */
13424 if (!directControl)
13425 return S_OK;
13426
13427 return directControl->OnSerialPortChange(serialPort);
13428}
13429
13430/**
13431 * @note Locks this object for reading.
13432 */
13433HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13434{
13435 LogFlowThisFunc(("\n"));
13436
13437 AutoCaller autoCaller(this);
13438 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13439
13440 ComPtr<IInternalSessionControl> directControl;
13441 {
13442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13443 directControl = mData->mSession.mDirectControl;
13444 }
13445
13446 /* ignore notifications sent after #OnSessionEnd() is called */
13447 if (!directControl)
13448 return S_OK;
13449
13450 return directControl->OnParallelPortChange(parallelPort);
13451}
13452
13453/**
13454 * @note Locks this object for reading.
13455 */
13456HRESULT SessionMachine::i_onStorageControllerChange()
13457{
13458 LogFlowThisFunc(("\n"));
13459
13460 AutoCaller autoCaller(this);
13461 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13462
13463 ComPtr<IInternalSessionControl> directControl;
13464 {
13465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13466 directControl = mData->mSession.mDirectControl;
13467 }
13468
13469 /* ignore notifications sent after #OnSessionEnd() is called */
13470 if (!directControl)
13471 return S_OK;
13472
13473 return directControl->OnStorageControllerChange();
13474}
13475
13476/**
13477 * @note Locks this object for reading.
13478 */
13479HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13480{
13481 LogFlowThisFunc(("\n"));
13482
13483 AutoCaller autoCaller(this);
13484 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13485
13486 ComPtr<IInternalSessionControl> directControl;
13487 {
13488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13489 directControl = mData->mSession.mDirectControl;
13490 }
13491
13492 /* ignore notifications sent after #OnSessionEnd() is called */
13493 if (!directControl)
13494 return S_OK;
13495
13496 return directControl->OnMediumChange(aAttachment, aForce);
13497}
13498
13499/**
13500 * @note Locks this object for reading.
13501 */
13502HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13503{
13504 LogFlowThisFunc(("\n"));
13505
13506 AutoCaller autoCaller(this);
13507 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13508
13509 ComPtr<IInternalSessionControl> directControl;
13510 {
13511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13512 directControl = mData->mSession.mDirectControl;
13513 }
13514
13515 /* ignore notifications sent after #OnSessionEnd() is called */
13516 if (!directControl)
13517 return S_OK;
13518
13519 return directControl->OnCPUChange(aCPU, aRemove);
13520}
13521
13522HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13523{
13524 LogFlowThisFunc(("\n"));
13525
13526 AutoCaller autoCaller(this);
13527 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13528
13529 ComPtr<IInternalSessionControl> directControl;
13530 {
13531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13532 directControl = mData->mSession.mDirectControl;
13533 }
13534
13535 /* ignore notifications sent after #OnSessionEnd() is called */
13536 if (!directControl)
13537 return S_OK;
13538
13539 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13540}
13541
13542/**
13543 * @note Locks this object for reading.
13544 */
13545HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13546{
13547 LogFlowThisFunc(("\n"));
13548
13549 AutoCaller autoCaller(this);
13550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13551
13552 ComPtr<IInternalSessionControl> directControl;
13553 {
13554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13555 directControl = mData->mSession.mDirectControl;
13556 }
13557
13558 /* ignore notifications sent after #OnSessionEnd() is called */
13559 if (!directControl)
13560 return S_OK;
13561
13562 return directControl->OnVRDEServerChange(aRestart);
13563}
13564
13565/**
13566 * @note Locks this object for reading.
13567 */
13568HRESULT SessionMachine::i_onVideoCaptureChange()
13569{
13570 LogFlowThisFunc(("\n"));
13571
13572 AutoCaller autoCaller(this);
13573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13574
13575 ComPtr<IInternalSessionControl> directControl;
13576 {
13577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13578 directControl = mData->mSession.mDirectControl;
13579 }
13580
13581 /* ignore notifications sent after #OnSessionEnd() is called */
13582 if (!directControl)
13583 return S_OK;
13584
13585 return directControl->OnVideoCaptureChange();
13586}
13587
13588/**
13589 * @note Locks this object for reading.
13590 */
13591HRESULT SessionMachine::i_onUSBControllerChange()
13592{
13593 LogFlowThisFunc(("\n"));
13594
13595 AutoCaller autoCaller(this);
13596 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13597
13598 ComPtr<IInternalSessionControl> directControl;
13599 {
13600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13601 directControl = mData->mSession.mDirectControl;
13602 }
13603
13604 /* ignore notifications sent after #OnSessionEnd() is called */
13605 if (!directControl)
13606 return S_OK;
13607
13608 return directControl->OnUSBControllerChange();
13609}
13610
13611/**
13612 * @note Locks this object for reading.
13613 */
13614HRESULT SessionMachine::i_onSharedFolderChange()
13615{
13616 LogFlowThisFunc(("\n"));
13617
13618 AutoCaller autoCaller(this);
13619 AssertComRCReturnRC(autoCaller.rc());
13620
13621 ComPtr<IInternalSessionControl> directControl;
13622 {
13623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13624 directControl = mData->mSession.mDirectControl;
13625 }
13626
13627 /* ignore notifications sent after #OnSessionEnd() is called */
13628 if (!directControl)
13629 return S_OK;
13630
13631 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13632}
13633
13634/**
13635 * @note Locks this object for reading.
13636 */
13637HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13638{
13639 LogFlowThisFunc(("\n"));
13640
13641 AutoCaller autoCaller(this);
13642 AssertComRCReturnRC(autoCaller.rc());
13643
13644 ComPtr<IInternalSessionControl> directControl;
13645 {
13646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13647 directControl = mData->mSession.mDirectControl;
13648 }
13649
13650 /* ignore notifications sent after #OnSessionEnd() is called */
13651 if (!directControl)
13652 return S_OK;
13653
13654 return directControl->OnClipboardModeChange(aClipboardMode);
13655}
13656
13657/**
13658 * @note Locks this object for reading.
13659 */
13660HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13661{
13662 LogFlowThisFunc(("\n"));
13663
13664 AutoCaller autoCaller(this);
13665 AssertComRCReturnRC(autoCaller.rc());
13666
13667 ComPtr<IInternalSessionControl> directControl;
13668 {
13669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13670 directControl = mData->mSession.mDirectControl;
13671 }
13672
13673 /* ignore notifications sent after #OnSessionEnd() is called */
13674 if (!directControl)
13675 return S_OK;
13676
13677 return directControl->OnDnDModeChange(aDnDMode);
13678}
13679
13680/**
13681 * @note Locks this object for reading.
13682 */
13683HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13684{
13685 LogFlowThisFunc(("\n"));
13686
13687 AutoCaller autoCaller(this);
13688 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13689
13690 ComPtr<IInternalSessionControl> directControl;
13691 {
13692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13693 directControl = mData->mSession.mDirectControl;
13694 }
13695
13696 /* ignore notifications sent after #OnSessionEnd() is called */
13697 if (!directControl)
13698 return S_OK;
13699
13700 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13701}
13702
13703/**
13704 * @note Locks this object for reading.
13705 */
13706HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13707{
13708 LogFlowThisFunc(("\n"));
13709
13710 AutoCaller autoCaller(this);
13711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13712
13713 ComPtr<IInternalSessionControl> directControl;
13714 {
13715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13716 directControl = mData->mSession.mDirectControl;
13717 }
13718
13719 /* ignore notifications sent after #OnSessionEnd() is called */
13720 if (!directControl)
13721 return S_OK;
13722
13723 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13724}
13725
13726/**
13727 * Returns @c true if this machine's USB controller reports it has a matching
13728 * filter for the given USB device and @c false otherwise.
13729 *
13730 * @note locks this object for reading.
13731 */
13732bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13733{
13734 AutoCaller autoCaller(this);
13735 /* silently return if not ready -- this method may be called after the
13736 * direct machine session has been called */
13737 if (!autoCaller.isOk())
13738 return false;
13739
13740#ifdef VBOX_WITH_USB
13741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13742
13743 switch (mData->mMachineState)
13744 {
13745 case MachineState_Starting:
13746 case MachineState_Restoring:
13747 case MachineState_TeleportingIn:
13748 case MachineState_Paused:
13749 case MachineState_Running:
13750 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13751 * elsewhere... */
13752 alock.release();
13753 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13754 default: break;
13755 }
13756#else
13757 NOREF(aDevice);
13758 NOREF(aMaskedIfs);
13759#endif
13760 return false;
13761}
13762
13763/**
13764 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13765 */
13766HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13767 IVirtualBoxErrorInfo *aError,
13768 ULONG aMaskedIfs)
13769{
13770 LogFlowThisFunc(("\n"));
13771
13772 AutoCaller autoCaller(this);
13773
13774 /* This notification may happen after the machine object has been
13775 * uninitialized (the session was closed), so don't assert. */
13776 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13777
13778 ComPtr<IInternalSessionControl> directControl;
13779 {
13780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13781 directControl = mData->mSession.mDirectControl;
13782 }
13783
13784 /* fail on notifications sent after #OnSessionEnd() is called, it is
13785 * expected by the caller */
13786 if (!directControl)
13787 return E_FAIL;
13788
13789 /* No locks should be held at this point. */
13790 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13791 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13792
13793 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13794}
13795
13796/**
13797 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13798 */
13799HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13800 IVirtualBoxErrorInfo *aError)
13801{
13802 LogFlowThisFunc(("\n"));
13803
13804 AutoCaller autoCaller(this);
13805
13806 /* This notification may happen after the machine object has been
13807 * uninitialized (the session was closed), so don't assert. */
13808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13809
13810 ComPtr<IInternalSessionControl> directControl;
13811 {
13812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13813 directControl = mData->mSession.mDirectControl;
13814 }
13815
13816 /* fail on notifications sent after #OnSessionEnd() is called, it is
13817 * expected by the caller */
13818 if (!directControl)
13819 return E_FAIL;
13820
13821 /* No locks should be held at this point. */
13822 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13823 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13824
13825 return directControl->OnUSBDeviceDetach(aId, aError);
13826}
13827
13828// protected methods
13829/////////////////////////////////////////////////////////////////////////////
13830
13831/**
13832 * Helper method to finalize saving the state.
13833 *
13834 * @note Must be called from under this object's lock.
13835 *
13836 * @param aRc S_OK if the snapshot has been taken successfully
13837 * @param aErrMsg human readable error message for failure
13838 *
13839 * @note Locks mParent + this objects for writing.
13840 */
13841HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13842{
13843 LogFlowThisFuncEnter();
13844
13845 AutoCaller autoCaller(this);
13846 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13847
13848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13849
13850 HRESULT rc = S_OK;
13851
13852 if (SUCCEEDED(aRc))
13853 {
13854 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13855
13856 /* save all VM settings */
13857 rc = i_saveSettings(NULL);
13858 // no need to check whether VirtualBox.xml needs saving also since
13859 // we can't have a name change pending at this point
13860 }
13861 else
13862 {
13863 // delete the saved state file (it might have been already created);
13864 // we need not check whether this is shared with a snapshot here because
13865 // we certainly created this saved state file here anew
13866 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13867 }
13868
13869 /* notify the progress object about operation completion */
13870 Assert(mConsoleTaskData.mProgress);
13871 if (SUCCEEDED(aRc))
13872 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13873 else
13874 {
13875 if (aErrMsg.length())
13876 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13877 COM_IIDOF(ISession),
13878 getComponentName(),
13879 aErrMsg.c_str());
13880 else
13881 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13882 }
13883
13884 /* clear out the temporary saved state data */
13885 mConsoleTaskData.mLastState = MachineState_Null;
13886 mConsoleTaskData.strStateFilePath.setNull();
13887 mConsoleTaskData.mProgress.setNull();
13888
13889 LogFlowThisFuncLeave();
13890 return rc;
13891}
13892
13893/**
13894 * Deletes the given file if it is no longer in use by either the current machine state
13895 * (if the machine is "saved") or any of the machine's snapshots.
13896 *
13897 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13898 * but is different for each SnapshotMachine. When calling this, the order of calling this
13899 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13900 * is therefore critical. I know, it's all rather messy.
13901 *
13902 * @param strStateFile
13903 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13904 * the test for whether the saved state file is in use.
13905 */
13906void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13907 Snapshot *pSnapshotToIgnore)
13908{
13909 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13910 if ( (strStateFile.isNotEmpty())
13911 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13912 )
13913 // ... and it must also not be shared with other snapshots
13914 if ( !mData->mFirstSnapshot
13915 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13916 // this checks the SnapshotMachine's state file paths
13917 )
13918 RTFileDelete(strStateFile.c_str());
13919}
13920
13921/**
13922 * Locks the attached media.
13923 *
13924 * All attached hard disks are locked for writing and DVD/floppy are locked for
13925 * reading. Parents of attached hard disks (if any) are locked for reading.
13926 *
13927 * This method also performs accessibility check of all media it locks: if some
13928 * media is inaccessible, the method will return a failure and a bunch of
13929 * extended error info objects per each inaccessible medium.
13930 *
13931 * Note that this method is atomic: if it returns a success, all media are
13932 * locked as described above; on failure no media is locked at all (all
13933 * succeeded individual locks will be undone).
13934 *
13935 * The caller is responsible for doing the necessary state sanity checks.
13936 *
13937 * The locks made by this method must be undone by calling #unlockMedia() when
13938 * no more needed.
13939 */
13940HRESULT SessionMachine::lockMedia()
13941{
13942 AutoCaller autoCaller(this);
13943 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13944
13945 AutoMultiWriteLock2 alock(this->lockHandle(),
13946 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13947
13948 /* bail out if trying to lock things with already set up locking */
13949 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13950
13951 MultiResult mrc(S_OK);
13952
13953 /* Collect locking information for all medium objects attached to the VM. */
13954 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13955 it != mMediaData->mAttachments.end();
13956 ++it)
13957 {
13958 MediumAttachment* pAtt = *it;
13959 DeviceType_T devType = pAtt->i_getType();
13960 Medium *pMedium = pAtt->i_getMedium();
13961
13962 MediumLockList *pMediumLockList(new MediumLockList());
13963 // There can be attachments without a medium (floppy/dvd), and thus
13964 // it's impossible to create a medium lock list. It still makes sense
13965 // to have the empty medium lock list in the map in case a medium is
13966 // attached later.
13967 if (pMedium != NULL)
13968 {
13969 MediumType_T mediumType = pMedium->i_getType();
13970 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13971 || mediumType == MediumType_Shareable;
13972 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13973
13974 alock.release();
13975 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13976 !fIsReadOnlyLock /* fMediumLockWrite */,
13977 NULL,
13978 *pMediumLockList);
13979 alock.acquire();
13980 if (FAILED(mrc))
13981 {
13982 delete pMediumLockList;
13983 mData->mSession.mLockedMedia.Clear();
13984 break;
13985 }
13986 }
13987
13988 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13989 if (FAILED(rc))
13990 {
13991 mData->mSession.mLockedMedia.Clear();
13992 mrc = setError(rc,
13993 tr("Collecting locking information for all attached media failed"));
13994 break;
13995 }
13996 }
13997
13998 if (SUCCEEDED(mrc))
13999 {
14000 /* Now lock all media. If this fails, nothing is locked. */
14001 alock.release();
14002 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14003 alock.acquire();
14004 if (FAILED(rc))
14005 {
14006 mrc = setError(rc,
14007 tr("Locking of attached media failed"));
14008 }
14009 }
14010
14011 return mrc;
14012}
14013
14014/**
14015 * Undoes the locks made by by #lockMedia().
14016 */
14017HRESULT SessionMachine::unlockMedia()
14018{
14019 AutoCaller autoCaller(this);
14020 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14021
14022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14023
14024 /* we may be holding important error info on the current thread;
14025 * preserve it */
14026 ErrorInfoKeeper eik;
14027
14028 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14029 AssertComRC(rc);
14030 return rc;
14031}
14032
14033/**
14034 * Helper to change the machine state (reimplementation).
14035 *
14036 * @note Locks this object for writing.
14037 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14038 * it can cause crashes in random places due to unexpectedly committing
14039 * the current settings. The caller is responsible for that. The call
14040 * to saveStateSettings is fine, because this method does not commit.
14041 */
14042HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14043{
14044 LogFlowThisFuncEnter();
14045 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14046
14047 AutoCaller autoCaller(this);
14048 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14049
14050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14051
14052 MachineState_T oldMachineState = mData->mMachineState;
14053
14054 AssertMsgReturn(oldMachineState != aMachineState,
14055 ("oldMachineState=%s, aMachineState=%s\n",
14056 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14057 E_FAIL);
14058
14059 HRESULT rc = S_OK;
14060
14061 int stsFlags = 0;
14062 bool deleteSavedState = false;
14063
14064 /* detect some state transitions */
14065
14066 if ( ( oldMachineState == MachineState_Saved
14067 && aMachineState == MachineState_Restoring)
14068 || ( ( oldMachineState == MachineState_PoweredOff
14069 || oldMachineState == MachineState_Teleported
14070 || oldMachineState == MachineState_Aborted
14071 )
14072 && ( aMachineState == MachineState_TeleportingIn
14073 || aMachineState == MachineState_Starting
14074 )
14075 )
14076 )
14077 {
14078 /* The EMT thread is about to start */
14079
14080 /* Nothing to do here for now... */
14081
14082 /// @todo NEWMEDIA don't let mDVDDrive and other children
14083 /// change anything when in the Starting/Restoring state
14084 }
14085 else if ( ( oldMachineState == MachineState_Running
14086 || oldMachineState == MachineState_Paused
14087 || oldMachineState == MachineState_Teleporting
14088 || oldMachineState == MachineState_LiveSnapshotting
14089 || oldMachineState == MachineState_Stuck
14090 || oldMachineState == MachineState_Starting
14091 || oldMachineState == MachineState_Stopping
14092 || oldMachineState == MachineState_Saving
14093 || oldMachineState == MachineState_Restoring
14094 || oldMachineState == MachineState_TeleportingPausedVM
14095 || oldMachineState == MachineState_TeleportingIn
14096 )
14097 && ( aMachineState == MachineState_PoweredOff
14098 || aMachineState == MachineState_Saved
14099 || aMachineState == MachineState_Teleported
14100 || aMachineState == MachineState_Aborted
14101 )
14102 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14103 * snapshot */
14104 && ( mConsoleTaskData.mSnapshot.isNull()
14105 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14106 )
14107 )
14108 {
14109 /* The EMT thread has just stopped, unlock attached media. Note that as
14110 * opposed to locking that is done from Console, we do unlocking here
14111 * because the VM process may have aborted before having a chance to
14112 * properly unlock all media it locked. */
14113
14114 unlockMedia();
14115 }
14116
14117 if (oldMachineState == MachineState_Restoring)
14118 {
14119 if (aMachineState != MachineState_Saved)
14120 {
14121 /*
14122 * delete the saved state file once the machine has finished
14123 * restoring from it (note that Console sets the state from
14124 * Restoring to Saved if the VM couldn't restore successfully,
14125 * to give the user an ability to fix an error and retry --
14126 * we keep the saved state file in this case)
14127 */
14128 deleteSavedState = true;
14129 }
14130 }
14131 else if ( oldMachineState == MachineState_Saved
14132 && ( aMachineState == MachineState_PoweredOff
14133 || aMachineState == MachineState_Aborted
14134 || aMachineState == MachineState_Teleported
14135 )
14136 )
14137 {
14138 /*
14139 * delete the saved state after Console::ForgetSavedState() is called
14140 * or if the VM process (owning a direct VM session) crashed while the
14141 * VM was Saved
14142 */
14143
14144 /// @todo (dmik)
14145 // Not sure that deleting the saved state file just because of the
14146 // client death before it attempted to restore the VM is a good
14147 // thing. But when it crashes we need to go to the Aborted state
14148 // which cannot have the saved state file associated... The only
14149 // way to fix this is to make the Aborted condition not a VM state
14150 // but a bool flag: i.e., when a crash occurs, set it to true and
14151 // change the state to PoweredOff or Saved depending on the
14152 // saved state presence.
14153
14154 deleteSavedState = true;
14155 mData->mCurrentStateModified = TRUE;
14156 stsFlags |= SaveSTS_CurStateModified;
14157 }
14158
14159 if ( aMachineState == MachineState_Starting
14160 || aMachineState == MachineState_Restoring
14161 || aMachineState == MachineState_TeleportingIn
14162 )
14163 {
14164 /* set the current state modified flag to indicate that the current
14165 * state is no more identical to the state in the
14166 * current snapshot */
14167 if (!mData->mCurrentSnapshot.isNull())
14168 {
14169 mData->mCurrentStateModified = TRUE;
14170 stsFlags |= SaveSTS_CurStateModified;
14171 }
14172 }
14173
14174 if (deleteSavedState)
14175 {
14176 if (mRemoveSavedState)
14177 {
14178 Assert(!mSSData->strStateFilePath.isEmpty());
14179
14180 // it is safe to delete the saved state file if ...
14181 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14182 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14183 // ... none of the snapshots share the saved state file
14184 )
14185 RTFileDelete(mSSData->strStateFilePath.c_str());
14186 }
14187
14188 mSSData->strStateFilePath.setNull();
14189 stsFlags |= SaveSTS_StateFilePath;
14190 }
14191
14192 /* redirect to the underlying peer machine */
14193 mPeer->i_setMachineState(aMachineState);
14194
14195 if ( aMachineState == MachineState_PoweredOff
14196 || aMachineState == MachineState_Teleported
14197 || aMachineState == MachineState_Aborted
14198 || aMachineState == MachineState_Saved)
14199 {
14200 /* the machine has stopped execution
14201 * (or the saved state file was adopted) */
14202 stsFlags |= SaveSTS_StateTimeStamp;
14203 }
14204
14205 if ( ( oldMachineState == MachineState_PoweredOff
14206 || oldMachineState == MachineState_Aborted
14207 || oldMachineState == MachineState_Teleported
14208 )
14209 && aMachineState == MachineState_Saved)
14210 {
14211 /* the saved state file was adopted */
14212 Assert(!mSSData->strStateFilePath.isEmpty());
14213 stsFlags |= SaveSTS_StateFilePath;
14214 }
14215
14216#ifdef VBOX_WITH_GUEST_PROPS
14217 if ( aMachineState == MachineState_PoweredOff
14218 || aMachineState == MachineState_Aborted
14219 || aMachineState == MachineState_Teleported)
14220 {
14221 /* Make sure any transient guest properties get removed from the
14222 * property store on shutdown. */
14223
14224 HWData::GuestPropertyMap::const_iterator it;
14225 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14226 if (!fNeedsSaving)
14227 for (it = mHWData->mGuestProperties.begin();
14228 it != mHWData->mGuestProperties.end(); ++it)
14229 if ( (it->second.mFlags & guestProp::TRANSIENT)
14230 || (it->second.mFlags & guestProp::TRANSRESET))
14231 {
14232 fNeedsSaving = true;
14233 break;
14234 }
14235 if (fNeedsSaving)
14236 {
14237 mData->mCurrentStateModified = TRUE;
14238 stsFlags |= SaveSTS_CurStateModified;
14239 }
14240 }
14241#endif
14242
14243 rc = i_saveStateSettings(stsFlags);
14244
14245 if ( ( oldMachineState != MachineState_PoweredOff
14246 && oldMachineState != MachineState_Aborted
14247 && oldMachineState != MachineState_Teleported
14248 )
14249 && ( aMachineState == MachineState_PoweredOff
14250 || aMachineState == MachineState_Aborted
14251 || aMachineState == MachineState_Teleported
14252 )
14253 )
14254 {
14255 /* we've been shut down for any reason */
14256 /* no special action so far */
14257 }
14258
14259 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14260 LogFlowThisFuncLeave();
14261 return rc;
14262}
14263
14264/**
14265 * Sends the current machine state value to the VM process.
14266 *
14267 * @note Locks this object for reading, then calls a client process.
14268 */
14269HRESULT SessionMachine::i_updateMachineStateOnClient()
14270{
14271 AutoCaller autoCaller(this);
14272 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14273
14274 ComPtr<IInternalSessionControl> directControl;
14275 {
14276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14277 AssertReturn(!!mData, E_FAIL);
14278 directControl = mData->mSession.mDirectControl;
14279
14280 /* directControl may be already set to NULL here in #OnSessionEnd()
14281 * called too early by the direct session process while there is still
14282 * some operation (like deleting the snapshot) in progress. The client
14283 * process in this case is waiting inside Session::close() for the
14284 * "end session" process object to complete, while #uninit() called by
14285 * #checkForDeath() on the Watcher thread is waiting for the pending
14286 * operation to complete. For now, we accept this inconsistent behavior
14287 * and simply do nothing here. */
14288
14289 if (mData->mSession.mState == SessionState_Unlocking)
14290 return S_OK;
14291
14292 AssertReturn(!directControl.isNull(), E_FAIL);
14293 }
14294
14295 return directControl->UpdateMachineState(mData->mMachineState);
14296}
14297
14298HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14299{
14300 NOREF(aRemove);
14301 ReturnComNotImplemented();
14302}
14303
14304HRESULT Machine::updateState(MachineState_T aState)
14305{
14306 NOREF(aState);
14307 ReturnComNotImplemented();
14308}
14309
14310HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14311{
14312 NOREF(aProgress);
14313 ReturnComNotImplemented();
14314}
14315
14316HRESULT Machine::endPowerUp(LONG aResult)
14317{
14318 NOREF(aResult);
14319 ReturnComNotImplemented();
14320}
14321
14322HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14323{
14324 NOREF(aProgress);
14325 ReturnComNotImplemented();
14326}
14327
14328HRESULT Machine::endPoweringDown(LONG aResult,
14329 const com::Utf8Str &aErrMsg)
14330{
14331 NOREF(aResult);
14332 NOREF(aErrMsg);
14333 ReturnComNotImplemented();
14334}
14335
14336HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14337 BOOL *aMatched,
14338 ULONG *aMaskedInterfaces)
14339{
14340 NOREF(aDevice);
14341 NOREF(aMatched);
14342 NOREF(aMaskedInterfaces);
14343 ReturnComNotImplemented();
14344
14345}
14346
14347HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14348{
14349 NOREF(aId);
14350 ReturnComNotImplemented();
14351}
14352
14353HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14354 BOOL aDone)
14355{
14356 NOREF(aId);
14357 NOREF(aDone);
14358 ReturnComNotImplemented();
14359}
14360
14361HRESULT Machine::autoCaptureUSBDevices()
14362{
14363 ReturnComNotImplemented();
14364}
14365
14366HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14367{
14368 NOREF(aDone);
14369 ReturnComNotImplemented();
14370}
14371
14372HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14373 ComPtr<IProgress> &aProgress)
14374{
14375 NOREF(aSession);
14376 NOREF(aProgress);
14377 ReturnComNotImplemented();
14378}
14379
14380HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14381 com::Utf8Str &aStateFilePath)
14382{
14383 NOREF(aProgress);
14384 NOREF(aStateFilePath);
14385 ReturnComNotImplemented();
14386}
14387
14388HRESULT Machine::endSavingState(LONG aResult,
14389 const com::Utf8Str &aErrMsg)
14390{
14391 NOREF(aResult);
14392 NOREF(aErrMsg);
14393 ReturnComNotImplemented();
14394}
14395
14396HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14397{
14398 NOREF(aSavedStateFile);
14399 ReturnComNotImplemented();
14400}
14401
14402HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14403 const com::Utf8Str &aName,
14404 const com::Utf8Str &aDescription,
14405 const ComPtr<IProgress> &aConsoleProgress,
14406 BOOL aFTakingSnapshotOnline,
14407 com::Utf8Str &aStateFilePath)
14408{
14409 NOREF(aInitiator);
14410 NOREF(aName);
14411 NOREF(aDescription);
14412 NOREF(aConsoleProgress);
14413 NOREF(aFTakingSnapshotOnline);
14414 NOREF(aStateFilePath);
14415 ReturnComNotImplemented();
14416}
14417
14418HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14419{
14420 NOREF(aSuccess);
14421 ReturnComNotImplemented();
14422}
14423
14424HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14425 const com::Guid &aStartId,
14426 const com::Guid &aEndId,
14427 BOOL aDeleteAllChildren,
14428 MachineState_T *aMachineState,
14429 ComPtr<IProgress> &aProgress)
14430{
14431 NOREF(aInitiator);
14432 NOREF(aStartId);
14433 NOREF(aEndId);
14434 NOREF(aDeleteAllChildren);
14435 NOREF(aMachineState);
14436 NOREF(aProgress);
14437 ReturnComNotImplemented();
14438}
14439
14440HRESULT Machine::finishOnlineMergeMedium()
14441{
14442 ReturnComNotImplemented();
14443}
14444
14445HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14446 const ComPtr<ISnapshot> &aSnapshot,
14447 MachineState_T *aMachineState,
14448 ComPtr<IProgress> &aProgress)
14449{
14450 NOREF(aInitiator);
14451 NOREF(aSnapshot);
14452 NOREF(aMachineState);
14453 NOREF(aProgress);
14454 ReturnComNotImplemented();
14455}
14456
14457HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14458 std::vector<com::Utf8Str> &aValues,
14459 std::vector<LONG64> &aTimestamps,
14460 std::vector<com::Utf8Str> &aFlags)
14461{
14462 NOREF(aNames);
14463 NOREF(aValues);
14464 NOREF(aTimestamps);
14465 NOREF(aFlags);
14466 ReturnComNotImplemented();
14467}
14468
14469HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14470 const com::Utf8Str &aValue,
14471 LONG64 aTimestamp,
14472 const com::Utf8Str &aFlags)
14473{
14474 NOREF(aName);
14475 NOREF(aValue);
14476 NOREF(aTimestamp);
14477 NOREF(aFlags);
14478 ReturnComNotImplemented();
14479}
14480
14481HRESULT Machine::lockMedia()
14482{
14483 ReturnComNotImplemented();
14484}
14485
14486HRESULT Machine::unlockMedia()
14487{
14488 ReturnComNotImplemented();
14489}
14490
14491HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14492 ComPtr<IMediumAttachment> &aNewAttachment)
14493{
14494 NOREF(aAttachment);
14495 NOREF(aNewAttachment);
14496 ReturnComNotImplemented();
14497}
14498
14499HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14500 ULONG aCpuUser,
14501 ULONG aCpuKernel,
14502 ULONG aCpuIdle,
14503 ULONG aMemTotal,
14504 ULONG aMemFree,
14505 ULONG aMemBalloon,
14506 ULONG aMemShared,
14507 ULONG aMemCache,
14508 ULONG aPagedTotal,
14509 ULONG aMemAllocTotal,
14510 ULONG aMemFreeTotal,
14511 ULONG aMemBalloonTotal,
14512 ULONG aMemSharedTotal,
14513 ULONG aVmNetRx,
14514 ULONG aVmNetTx)
14515{
14516 NOREF(aValidStats);
14517 NOREF(aCpuUser);
14518 NOREF(aCpuKernel);
14519 NOREF(aCpuIdle);
14520 NOREF(aMemTotal);
14521 NOREF(aMemFree);
14522 NOREF(aMemBalloon);
14523 NOREF(aMemShared);
14524 NOREF(aMemCache);
14525 NOREF(aPagedTotal);
14526 NOREF(aMemAllocTotal);
14527 NOREF(aMemFreeTotal);
14528 NOREF(aMemBalloonTotal);
14529 NOREF(aMemSharedTotal);
14530 NOREF(aVmNetRx);
14531 NOREF(aVmNetTx);
14532 ReturnComNotImplemented();
14533}
Note: See TracBrowser for help on using the repository browser.

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