VirtualBox

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

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

Main: VM startup logging on windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 498.2 KB
Line 
1/* $Id: MachineImpl.cpp 52171 2014-07-24 15:14:18Z 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) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail. See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
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 * Returns the full path to the machine's (hardened) startup log file.
7152 */
7153Utf8Str Machine::i_getStartupLogFilename(void)
7154{
7155 Utf8Str strFilename;
7156 getLogFolder(strFilename);
7157 Assert(strFilename.length());
7158 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7159 return strFilename;
7160}
7161
7162
7163/**
7164 * Composes a unique saved state filename based on the current system time. The filename is
7165 * granular to the second so this will work so long as no more than one snapshot is taken on
7166 * a machine per second.
7167 *
7168 * Before version 4.1, we used this formula for saved state files:
7169 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7170 * which no longer works because saved state files can now be shared between the saved state of the
7171 * "saved" machine and an online snapshot, and the following would cause problems:
7172 * 1) save machine
7173 * 2) create online snapshot from that machine state --> reusing saved state file
7174 * 3) save machine again --> filename would be reused, breaking the online snapshot
7175 *
7176 * So instead we now use a timestamp.
7177 *
7178 * @param str
7179 */
7180
7181void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7182{
7183 AutoCaller autoCaller(this);
7184 AssertComRCReturnVoid(autoCaller.rc());
7185
7186 {
7187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7188 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7189 }
7190
7191 RTTIMESPEC ts;
7192 RTTimeNow(&ts);
7193 RTTIME time;
7194 RTTimeExplode(&time, &ts);
7195
7196 strStateFilePath += RTPATH_DELIMITER;
7197 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7198 time.i32Year, time.u8Month, time.u8MonthDay,
7199 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7200}
7201
7202/**
7203 * Returns the full path to the default video capture file.
7204 */
7205void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7206{
7207 AutoCaller autoCaller(this);
7208 AssertComRCReturnVoid(autoCaller.rc());
7209
7210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7211
7212 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7213 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7214 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7215}
7216
7217/**
7218 * Returns whether at least one USB controller is present for the VM.
7219 */
7220bool Machine::i_isUSBControllerPresent()
7221{
7222 AutoCaller autoCaller(this);
7223 AssertComRCReturn(autoCaller.rc(), false);
7224
7225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 return (mUSBControllers->size() > 0);
7228}
7229
7230/**
7231 * @note Locks this object for writing, calls the client process
7232 * (inside the lock).
7233 */
7234HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7235 const Utf8Str &strFrontend,
7236 const Utf8Str &strEnvironment,
7237 ProgressProxy *aProgress)
7238{
7239 LogFlowThisFuncEnter();
7240
7241 AssertReturn(aControl, E_FAIL);
7242 AssertReturn(aProgress, E_FAIL);
7243 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7244
7245 AutoCaller autoCaller(this);
7246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7247
7248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7249
7250 if (!mData->mRegistered)
7251 return setError(E_UNEXPECTED,
7252 tr("The machine '%s' is not registered"),
7253 mUserData->s.strName.c_str());
7254
7255 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7256
7257 if ( mData->mSession.mState == SessionState_Locked
7258 || mData->mSession.mState == SessionState_Spawning
7259 || mData->mSession.mState == SessionState_Unlocking)
7260 return setError(VBOX_E_INVALID_OBJECT_STATE,
7261 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7262 mUserData->s.strName.c_str());
7263
7264 /* may not be busy */
7265 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7266
7267 /* get the path to the executable */
7268 char szPath[RTPATH_MAX];
7269 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7270 size_t cchBufLeft = strlen(szPath);
7271 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7272 szPath[cchBufLeft] = 0;
7273 char *pszNamePart = szPath + cchBufLeft;
7274 cchBufLeft = sizeof(szPath) - cchBufLeft;
7275
7276 int vrc = VINF_SUCCESS;
7277 RTPROCESS pid = NIL_RTPROCESS;
7278
7279 RTENV env = RTENV_DEFAULT;
7280
7281 if (!strEnvironment.isEmpty())
7282 {
7283 char *newEnvStr = NULL;
7284
7285 do
7286 {
7287 /* clone the current environment */
7288 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7289 AssertRCBreakStmt(vrc2, vrc = vrc2);
7290
7291 newEnvStr = RTStrDup(strEnvironment.c_str());
7292 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7293
7294 /* put new variables to the environment
7295 * (ignore empty variable names here since RTEnv API
7296 * intentionally doesn't do that) */
7297 char *var = newEnvStr;
7298 for (char *p = newEnvStr; *p; ++p)
7299 {
7300 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7301 {
7302 *p = '\0';
7303 if (*var)
7304 {
7305 char *val = strchr(var, '=');
7306 if (val)
7307 {
7308 *val++ = '\0';
7309 vrc2 = RTEnvSetEx(env, var, val);
7310 }
7311 else
7312 vrc2 = RTEnvUnsetEx(env, var);
7313 if (RT_FAILURE(vrc2))
7314 break;
7315 }
7316 var = p + 1;
7317 }
7318 }
7319 if (RT_SUCCESS(vrc2) && *var)
7320 vrc2 = RTEnvPutEx(env, var);
7321
7322 AssertRCBreakStmt(vrc2, vrc = vrc2);
7323 }
7324 while (0);
7325
7326 if (newEnvStr != NULL)
7327 RTStrFree(newEnvStr);
7328 }
7329
7330 /* Hardened startup logging */
7331#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7332 Utf8Str strSupStartLogArg("--sup-startup-log=");
7333 {
7334 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7335 RTFileDelete(strStartupLogFile.c_str());
7336 strSupStartLogArg.append(strStartupLogFile);
7337 }
7338 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7339#else
7340 const char *pszSupStartupLogArg = NULL;
7341#endif
7342
7343
7344#ifdef VBOX_WITH_QTGUI
7345 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7346 {
7347# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7348 /* Modify the base path so that we don't need to use ".." below. */
7349 RTPathStripTrailingSlash(szPath);
7350 RTPathStripFilename(szPath);
7351 cchBufLeft = strlen(szPath);
7352 pszNamePart = szPath + cchBufLeft;
7353 cchBufLeft = sizeof(szPath) - cchBufLeft;
7354
7355# define OSX_APP_NAME "VirtualBoxVM"
7356# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7357
7358 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7359 if ( strAppOverride.contains(".")
7360 || strAppOverride.contains("/")
7361 || strAppOverride.contains("\\")
7362 || strAppOverride.contains(":"))
7363 strAppOverride.setNull();
7364 Utf8Str strAppPath;
7365 if (!strAppOverride.isEmpty())
7366 {
7367 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7368 Utf8Str strFullPath(szPath);
7369 strFullPath.append(strAppPath);
7370 /* there is a race, but people using this deserve the failure */
7371 if (!RTFileExists(strFullPath.c_str()))
7372 strAppOverride.setNull();
7373 }
7374 if (strAppOverride.isEmpty())
7375 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7376 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7377 strcpy(pszNamePart, strAppPath.c_str());
7378# else
7379 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7380 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7381 strcpy(pszNamePart, s_szVirtualBox_exe);
7382# endif
7383
7384 Utf8Str idStr = mData->mUuid.toString();
7385 const char *apszArgs[] =
7386 {
7387 szPath,
7388 "--comment", mUserData->s.strName.c_str(),
7389 "--startvm", idStr.c_str(),
7390 "--no-startvm-errormsgbox",
7391 pszSupStartupLogArg,
7392 NULL
7393 };
7394 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7395 }
7396#else /* !VBOX_WITH_QTGUI */
7397 if (0)
7398 ;
7399#endif /* VBOX_WITH_QTGUI */
7400
7401 else
7402
7403#ifdef VBOX_WITH_VBOXSDL
7404 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7405 {
7406 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7407 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7408 strcpy(pszNamePart, s_szVBoxSDL_exe);
7409
7410 Utf8Str idStr = mData->mUuid.toString();
7411 const char *apszArgs[] =
7412 {
7413 szPath,
7414 "--comment", mUserData->s.strName.c_str(),
7415 "--startvm", idStr.c_str(),
7416 pszSupStartupLogArg,
7417 NULL
7418 };
7419 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7420 }
7421#else /* !VBOX_WITH_VBOXSDL */
7422 if (0)
7423 ;
7424#endif /* !VBOX_WITH_VBOXSDL */
7425
7426 else
7427
7428#ifdef VBOX_WITH_HEADLESS
7429 if ( strFrontend == "headless"
7430 || strFrontend == "capture"
7431 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7432 )
7433 {
7434 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7435 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7436 * and a VM works even if the server has not been installed.
7437 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7438 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7439 * differently in 4.0 and 3.x.
7440 */
7441 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7442 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7443 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7444
7445 Utf8Str idStr = mData->mUuid.toString();
7446 const char *apszArgs[] =
7447 {
7448 szPath,
7449 "--comment", mUserData->s.strName.c_str(),
7450 "--startvm", idStr.c_str(),
7451 "--vrde", "config",
7452 0, /* For "--capture". */
7453 0, /* For "--sup-startup-log". */
7454 0
7455 };
7456 unsigned iArg = 7;
7457 if (strFrontend == "capture")
7458 apszArgs[iArg++] = "--capture";
7459 apszArgs[iArg++] = pszSupStartupLogArg;
7460
7461# ifdef RT_OS_WINDOWS
7462 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7463# else
7464 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7465# endif
7466 }
7467#else /* !VBOX_WITH_HEADLESS */
7468 if (0)
7469 ;
7470#endif /* !VBOX_WITH_HEADLESS */
7471 else
7472 {
7473 RTEnvDestroy(env);
7474 return setError(E_INVALIDARG,
7475 tr("Invalid frontend name: '%s'"),
7476 strFrontend.c_str());
7477 }
7478
7479 RTEnvDestroy(env);
7480
7481 if (RT_FAILURE(vrc))
7482 return setError(VBOX_E_IPRT_ERROR,
7483 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7484 mUserData->s.strName.c_str(), vrc);
7485
7486 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7487
7488 /*
7489 * Note that we don't release the lock here before calling the client,
7490 * because it doesn't need to call us back if called with a NULL argument.
7491 * Releasing the lock here is dangerous because we didn't prepare the
7492 * launch data yet, but the client we've just started may happen to be
7493 * too fast and call LockMachine() that will fail (because of PID, etc.),
7494 * so that the Machine will never get out of the Spawning session state.
7495 */
7496
7497 /* inform the session that it will be a remote one */
7498 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7499#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7500 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7501#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7502 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7503#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7504 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7505
7506 if (FAILED(rc))
7507 {
7508 /* restore the session state */
7509 mData->mSession.mState = SessionState_Unlocked;
7510 alock.release();
7511 mParent->i_addProcessToReap(pid);
7512 /* The failure may occur w/o any error info (from RPC), so provide one */
7513 return setError(VBOX_E_VM_ERROR,
7514 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7515 }
7516
7517 /* attach launch data to the machine */
7518 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7519 mData->mSession.mRemoteControls.push_back(aControl);
7520 mData->mSession.mProgress = aProgress;
7521 mData->mSession.mPID = pid;
7522 mData->mSession.mState = SessionState_Spawning;
7523 mData->mSession.mType = strFrontend;
7524
7525 alock.release();
7526 mParent->i_addProcessToReap(pid);
7527
7528 LogFlowThisFuncLeave();
7529 return S_OK;
7530}
7531
7532/**
7533 * Returns @c true if the given session machine instance has an open direct
7534 * session (and optionally also for direct sessions which are closing) and
7535 * returns the session control machine instance if so.
7536 *
7537 * Note that when the method returns @c false, the arguments remain unchanged.
7538 *
7539 * @param aMachine Session machine object.
7540 * @param aControl Direct session control object (optional).
7541 * @param aAllowClosing If true then additionally a session which is currently
7542 * being closed will also be allowed.
7543 *
7544 * @note locks this object for reading.
7545 */
7546bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7547 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7548 bool aAllowClosing /*= false*/)
7549{
7550 AutoLimitedCaller autoCaller(this);
7551 AssertComRCReturn(autoCaller.rc(), false);
7552
7553 /* just return false for inaccessible machines */
7554 if (getObjectState().getState() != ObjectState::Ready)
7555 return false;
7556
7557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7558
7559 if ( mData->mSession.mState == SessionState_Locked
7560 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7561 )
7562 {
7563 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7564
7565 aMachine = mData->mSession.mMachine;
7566
7567 if (aControl != NULL)
7568 *aControl = mData->mSession.mDirectControl;
7569
7570 return true;
7571 }
7572
7573 return false;
7574}
7575
7576/**
7577 * Returns @c true if the given machine has an spawning direct session.
7578 *
7579 * @note locks this object for reading.
7580 */
7581bool Machine::i_isSessionSpawning()
7582{
7583 AutoLimitedCaller autoCaller(this);
7584 AssertComRCReturn(autoCaller.rc(), false);
7585
7586 /* just return false for inaccessible machines */
7587 if (getObjectState().getState() != ObjectState::Ready)
7588 return false;
7589
7590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7591
7592 if (mData->mSession.mState == SessionState_Spawning)
7593 return true;
7594
7595 return false;
7596}
7597
7598/**
7599 * Called from the client watcher thread to check for unexpected client process
7600 * death during Session_Spawning state (e.g. before it successfully opened a
7601 * direct session).
7602 *
7603 * On Win32 and on OS/2, this method is called only when we've got the
7604 * direct client's process termination notification, so it always returns @c
7605 * true.
7606 *
7607 * On other platforms, this method returns @c true if the client process is
7608 * terminated and @c false if it's still alive.
7609 *
7610 * @note Locks this object for writing.
7611 */
7612bool Machine::i_checkForSpawnFailure()
7613{
7614 AutoCaller autoCaller(this);
7615 if (!autoCaller.isOk())
7616 {
7617 /* nothing to do */
7618 LogFlowThisFunc(("Already uninitialized!\n"));
7619 return true;
7620 }
7621
7622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7623
7624 if (mData->mSession.mState != SessionState_Spawning)
7625 {
7626 /* nothing to do */
7627 LogFlowThisFunc(("Not spawning any more!\n"));
7628 return true;
7629 }
7630
7631 HRESULT rc = S_OK;
7632
7633 /* PID not yet initialized, skip check. */
7634 if (mData->mSession.mPID == NIL_RTPROCESS)
7635 return false;
7636
7637 RTPROCSTATUS status;
7638 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7639
7640 if (vrc != VERR_PROCESS_RUNNING)
7641 {
7642 Utf8Str strExtraInfo;
7643
7644#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7645 /* If the startup logfile exists and is of non-zero length, tell the
7646 user to look there for more details to encourage them to attach it
7647 when reporting startup issues. */
7648 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7649 uint64_t cbStartupLogFile = 0;
7650 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7651 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7652 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7653#endif
7654
7655 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7656 rc = setError(E_FAIL,
7657 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d%s"),
7658 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7659 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7660 rc = setError(E_FAIL,
7661 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7662 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7663 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7664 rc = setError(E_FAIL,
7665 tr("The virtual machine '%s' has terminated abnormally"),
7666 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7667 else
7668 rc = setError(E_FAIL,
7669 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7670 i_getName().c_str(), vrc, strExtraInfo.c_str());
7671 }
7672
7673 if (FAILED(rc))
7674 {
7675 /* Close the remote session, remove the remote control from the list
7676 * and reset session state to Closed (@note keep the code in sync with
7677 * the relevant part in LockMachine()). */
7678
7679 Assert(mData->mSession.mRemoteControls.size() == 1);
7680 if (mData->mSession.mRemoteControls.size() == 1)
7681 {
7682 ErrorInfoKeeper eik;
7683 mData->mSession.mRemoteControls.front()->Uninitialize();
7684 }
7685
7686 mData->mSession.mRemoteControls.clear();
7687 mData->mSession.mState = SessionState_Unlocked;
7688
7689 /* finalize the progress after setting the state */
7690 if (!mData->mSession.mProgress.isNull())
7691 {
7692 mData->mSession.mProgress->notifyComplete(rc);
7693 mData->mSession.mProgress.setNull();
7694 }
7695
7696 mData->mSession.mPID = NIL_RTPROCESS;
7697
7698 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7699 return true;
7700 }
7701
7702 return false;
7703}
7704
7705/**
7706 * Checks whether the machine can be registered. If so, commits and saves
7707 * all settings.
7708 *
7709 * @note Must be called from mParent's write lock. Locks this object and
7710 * children for writing.
7711 */
7712HRESULT Machine::i_prepareRegister()
7713{
7714 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7715
7716 AutoLimitedCaller autoCaller(this);
7717 AssertComRCReturnRC(autoCaller.rc());
7718
7719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7720
7721 /* wait for state dependents to drop to zero */
7722 i_ensureNoStateDependencies();
7723
7724 if (!mData->mAccessible)
7725 return setError(VBOX_E_INVALID_OBJECT_STATE,
7726 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7727 mUserData->s.strName.c_str(),
7728 mData->mUuid.toString().c_str());
7729
7730 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7731
7732 if (mData->mRegistered)
7733 return setError(VBOX_E_INVALID_OBJECT_STATE,
7734 tr("The machine '%s' with UUID {%s} is already registered"),
7735 mUserData->s.strName.c_str(),
7736 mData->mUuid.toString().c_str());
7737
7738 HRESULT rc = S_OK;
7739
7740 // Ensure the settings are saved. If we are going to be registered and
7741 // no config file exists yet, create it by calling i_saveSettings() too.
7742 if ( (mData->flModifications)
7743 || (!mData->pMachineConfigFile->fileExists())
7744 )
7745 {
7746 rc = i_saveSettings(NULL);
7747 // no need to check whether VirtualBox.xml needs saving too since
7748 // we can't have a machine XML file rename pending
7749 if (FAILED(rc)) return rc;
7750 }
7751
7752 /* more config checking goes here */
7753
7754 if (SUCCEEDED(rc))
7755 {
7756 /* we may have had implicit modifications we want to fix on success */
7757 i_commit();
7758
7759 mData->mRegistered = true;
7760 }
7761 else
7762 {
7763 /* we may have had implicit modifications we want to cancel on failure*/
7764 i_rollback(false /* aNotify */);
7765 }
7766
7767 return rc;
7768}
7769
7770/**
7771 * Increases the number of objects dependent on the machine state or on the
7772 * registered state. Guarantees that these two states will not change at least
7773 * until #releaseStateDependency() is called.
7774 *
7775 * Depending on the @a aDepType value, additional state checks may be made.
7776 * These checks will set extended error info on failure. See
7777 * #checkStateDependency() for more info.
7778 *
7779 * If this method returns a failure, the dependency is not added and the caller
7780 * is not allowed to rely on any particular machine state or registration state
7781 * value and may return the failed result code to the upper level.
7782 *
7783 * @param aDepType Dependency type to add.
7784 * @param aState Current machine state (NULL if not interested).
7785 * @param aRegistered Current registered state (NULL if not interested).
7786 *
7787 * @note Locks this object for writing.
7788 */
7789HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7790 MachineState_T *aState /* = NULL */,
7791 BOOL *aRegistered /* = NULL */)
7792{
7793 AutoCaller autoCaller(this);
7794 AssertComRCReturnRC(autoCaller.rc());
7795
7796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7797
7798 HRESULT rc = i_checkStateDependency(aDepType);
7799 if (FAILED(rc)) return rc;
7800
7801 {
7802 if (mData->mMachineStateChangePending != 0)
7803 {
7804 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7805 * drop to zero so don't add more. It may make sense to wait a bit
7806 * and retry before reporting an error (since the pending state
7807 * transition should be really quick) but let's just assert for
7808 * now to see if it ever happens on practice. */
7809
7810 AssertFailed();
7811
7812 return setError(E_ACCESSDENIED,
7813 tr("Machine state change is in progress. Please retry the operation later."));
7814 }
7815
7816 ++mData->mMachineStateDeps;
7817 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7818 }
7819
7820 if (aState)
7821 *aState = mData->mMachineState;
7822 if (aRegistered)
7823 *aRegistered = mData->mRegistered;
7824
7825 return S_OK;
7826}
7827
7828/**
7829 * Decreases the number of objects dependent on the machine state.
7830 * Must always complete the #addStateDependency() call after the state
7831 * dependency is no more necessary.
7832 */
7833void Machine::i_releaseStateDependency()
7834{
7835 AutoCaller autoCaller(this);
7836 AssertComRCReturnVoid(autoCaller.rc());
7837
7838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7839
7840 /* releaseStateDependency() w/o addStateDependency()? */
7841 AssertReturnVoid(mData->mMachineStateDeps != 0);
7842 -- mData->mMachineStateDeps;
7843
7844 if (mData->mMachineStateDeps == 0)
7845 {
7846 /* inform i_ensureNoStateDependencies() that there are no more deps */
7847 if (mData->mMachineStateChangePending != 0)
7848 {
7849 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7850 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7851 }
7852 }
7853}
7854
7855Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7856{
7857 /* start with nothing found */
7858 Utf8Str strResult("");
7859
7860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7861
7862 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7863 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7864 // found:
7865 strResult = it->second; // source is a Utf8Str
7866
7867 return strResult;
7868}
7869
7870// protected methods
7871/////////////////////////////////////////////////////////////////////////////
7872
7873/**
7874 * Performs machine state checks based on the @a aDepType value. If a check
7875 * fails, this method will set extended error info, otherwise it will return
7876 * S_OK. It is supposed, that on failure, the caller will immediately return
7877 * the return value of this method to the upper level.
7878 *
7879 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7880 *
7881 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7882 * current state of this machine object allows to change settings of the
7883 * machine (i.e. the machine is not registered, or registered but not running
7884 * and not saved). It is useful to call this method from Machine setters
7885 * before performing any change.
7886 *
7887 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7888 * as for MutableStateDep except that if the machine is saved, S_OK is also
7889 * returned. This is useful in setters which allow changing machine
7890 * properties when it is in the saved state.
7891 *
7892 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7893 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7894 * Aborted).
7895 *
7896 * @param aDepType Dependency type to check.
7897 *
7898 * @note Non Machine based classes should use #addStateDependency() and
7899 * #releaseStateDependency() methods or the smart AutoStateDependency
7900 * template.
7901 *
7902 * @note This method must be called from under this object's read or write
7903 * lock.
7904 */
7905HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7906{
7907 switch (aDepType)
7908 {
7909 case AnyStateDep:
7910 {
7911 break;
7912 }
7913 case MutableStateDep:
7914 {
7915 if ( mData->mRegistered
7916 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7917 Paused should actually be included here... (Live Migration) */
7918 || ( mData->mMachineState != MachineState_Paused
7919 && mData->mMachineState != MachineState_Running
7920 && mData->mMachineState != MachineState_Aborted
7921 && mData->mMachineState != MachineState_Teleported
7922 && mData->mMachineState != MachineState_PoweredOff
7923 )
7924 )
7925 )
7926 return setError(VBOX_E_INVALID_VM_STATE,
7927 tr("The machine is not mutable (state is %s)"),
7928 Global::stringifyMachineState(mData->mMachineState));
7929 break;
7930 }
7931 case MutableOrSavedStateDep:
7932 {
7933 if ( mData->mRegistered
7934 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7935 Paused should actually be included here... (Live Migration) */
7936 || ( mData->mMachineState != MachineState_Paused
7937 && mData->mMachineState != MachineState_Running
7938 && mData->mMachineState != MachineState_Aborted
7939 && mData->mMachineState != MachineState_Teleported
7940 && mData->mMachineState != MachineState_Saved
7941 && mData->mMachineState != MachineState_PoweredOff
7942 )
7943 )
7944 )
7945 return setError(VBOX_E_INVALID_VM_STATE,
7946 tr("The machine is not mutable (state is %s)"),
7947 Global::stringifyMachineState(mData->mMachineState));
7948 break;
7949 }
7950 case OfflineStateDep:
7951 {
7952 if ( mData->mRegistered
7953 && ( !i_isSessionMachine()
7954 || ( mData->mMachineState != MachineState_PoweredOff
7955 && mData->mMachineState != MachineState_Saved
7956 && mData->mMachineState != MachineState_Aborted
7957 && mData->mMachineState != MachineState_Teleported
7958 )
7959 )
7960 )
7961 return setError(VBOX_E_INVALID_VM_STATE,
7962 tr("The machine is not offline (state is %s)"),
7963 Global::stringifyMachineState(mData->mMachineState));
7964 break;
7965 }
7966 }
7967
7968 return S_OK;
7969}
7970
7971/**
7972 * Helper to initialize all associated child objects and allocate data
7973 * structures.
7974 *
7975 * This method must be called as a part of the object's initialization procedure
7976 * (usually done in the #init() method).
7977 *
7978 * @note Must be called only from #init() or from #registeredInit().
7979 */
7980HRESULT Machine::initDataAndChildObjects()
7981{
7982 AutoCaller autoCaller(this);
7983 AssertComRCReturnRC(autoCaller.rc());
7984 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
7985 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7986
7987 AssertReturn(!mData->mAccessible, E_FAIL);
7988
7989 /* allocate data structures */
7990 mSSData.allocate();
7991 mUserData.allocate();
7992 mHWData.allocate();
7993 mMediaData.allocate();
7994 mStorageControllers.allocate();
7995 mUSBControllers.allocate();
7996
7997 /* initialize mOSTypeId */
7998 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
7999
8000 /* create associated BIOS settings object */
8001 unconst(mBIOSSettings).createObject();
8002 mBIOSSettings->init(this);
8003
8004 /* create an associated VRDE object (default is disabled) */
8005 unconst(mVRDEServer).createObject();
8006 mVRDEServer->init(this);
8007
8008 /* create associated serial port objects */
8009 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8010 {
8011 unconst(mSerialPorts[slot]).createObject();
8012 mSerialPorts[slot]->init(this, slot);
8013 }
8014
8015 /* create associated parallel port objects */
8016 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8017 {
8018 unconst(mParallelPorts[slot]).createObject();
8019 mParallelPorts[slot]->init(this, slot);
8020 }
8021
8022 /* create the audio adapter object (always present, default is disabled) */
8023 unconst(mAudioAdapter).createObject();
8024 mAudioAdapter->init(this);
8025
8026 /* create the USB device filters object (always present) */
8027 unconst(mUSBDeviceFilters).createObject();
8028 mUSBDeviceFilters->init(this);
8029
8030 /* create associated network adapter objects */
8031 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8032 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8033 {
8034 unconst(mNetworkAdapters[slot]).createObject();
8035 mNetworkAdapters[slot]->init(this, slot);
8036 }
8037
8038 /* create the bandwidth control */
8039 unconst(mBandwidthControl).createObject();
8040 mBandwidthControl->init(this);
8041
8042 return S_OK;
8043}
8044
8045/**
8046 * Helper to uninitialize all associated child objects and to free all data
8047 * structures.
8048 *
8049 * This method must be called as a part of the object's uninitialization
8050 * procedure (usually done in the #uninit() method).
8051 *
8052 * @note Must be called only from #uninit() or from #registeredInit().
8053 */
8054void Machine::uninitDataAndChildObjects()
8055{
8056 AutoCaller autoCaller(this);
8057 AssertComRCReturnVoid(autoCaller.rc());
8058 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8059 || getObjectState().getState() == ObjectState::Limited);
8060
8061 /* tell all our other child objects we've been uninitialized */
8062 if (mBandwidthControl)
8063 {
8064 mBandwidthControl->uninit();
8065 unconst(mBandwidthControl).setNull();
8066 }
8067
8068 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8069 {
8070 if (mNetworkAdapters[slot])
8071 {
8072 mNetworkAdapters[slot]->uninit();
8073 unconst(mNetworkAdapters[slot]).setNull();
8074 }
8075 }
8076
8077 if (mUSBDeviceFilters)
8078 {
8079 mUSBDeviceFilters->uninit();
8080 unconst(mUSBDeviceFilters).setNull();
8081 }
8082
8083 if (mAudioAdapter)
8084 {
8085 mAudioAdapter->uninit();
8086 unconst(mAudioAdapter).setNull();
8087 }
8088
8089 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8090 {
8091 if (mParallelPorts[slot])
8092 {
8093 mParallelPorts[slot]->uninit();
8094 unconst(mParallelPorts[slot]).setNull();
8095 }
8096 }
8097
8098 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8099 {
8100 if (mSerialPorts[slot])
8101 {
8102 mSerialPorts[slot]->uninit();
8103 unconst(mSerialPorts[slot]).setNull();
8104 }
8105 }
8106
8107 if (mVRDEServer)
8108 {
8109 mVRDEServer->uninit();
8110 unconst(mVRDEServer).setNull();
8111 }
8112
8113 if (mBIOSSettings)
8114 {
8115 mBIOSSettings->uninit();
8116 unconst(mBIOSSettings).setNull();
8117 }
8118
8119 /* Deassociate media (only when a real Machine or a SnapshotMachine
8120 * instance is uninitialized; SessionMachine instances refer to real
8121 * Machine media). This is necessary for a clean re-initialization of
8122 * the VM after successfully re-checking the accessibility state. Note
8123 * that in case of normal Machine or SnapshotMachine uninitialization (as
8124 * a result of unregistering or deleting the snapshot), outdated media
8125 * attachments will already be uninitialized and deleted, so this
8126 * code will not affect them. */
8127 if ( !!mMediaData
8128 && (!i_isSessionMachine())
8129 )
8130 {
8131 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8132 it != mMediaData->mAttachments.end();
8133 ++it)
8134 {
8135 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8136 if (pMedium.isNull())
8137 continue;
8138 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8139 AssertComRC(rc);
8140 }
8141 }
8142
8143 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8144 {
8145 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8146 if (mData->mFirstSnapshot)
8147 {
8148 // snapshots tree is protected by machine write lock; strictly
8149 // this isn't necessary here since we're deleting the entire
8150 // machine, but otherwise we assert in Snapshot::uninit()
8151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8152 mData->mFirstSnapshot->uninit();
8153 mData->mFirstSnapshot.setNull();
8154 }
8155
8156 mData->mCurrentSnapshot.setNull();
8157 }
8158
8159 /* free data structures (the essential mData structure is not freed here
8160 * since it may be still in use) */
8161 mMediaData.free();
8162 mStorageControllers.free();
8163 mUSBControllers.free();
8164 mHWData.free();
8165 mUserData.free();
8166 mSSData.free();
8167}
8168
8169/**
8170 * Returns a pointer to the Machine object for this machine that acts like a
8171 * parent for complex machine data objects such as shared folders, etc.
8172 *
8173 * For primary Machine objects and for SnapshotMachine objects, returns this
8174 * object's pointer itself. For SessionMachine objects, returns the peer
8175 * (primary) machine pointer.
8176 */
8177Machine* Machine::i_getMachine()
8178{
8179 if (i_isSessionMachine())
8180 return (Machine*)mPeer;
8181 return this;
8182}
8183
8184/**
8185 * Makes sure that there are no machine state dependents. If necessary, waits
8186 * for the number of dependents to drop to zero.
8187 *
8188 * Make sure this method is called from under this object's write lock to
8189 * guarantee that no new dependents may be added when this method returns
8190 * control to the caller.
8191 *
8192 * @note Locks this object for writing. The lock will be released while waiting
8193 * (if necessary).
8194 *
8195 * @warning To be used only in methods that change the machine state!
8196 */
8197void Machine::i_ensureNoStateDependencies()
8198{
8199 AssertReturnVoid(isWriteLockOnCurrentThread());
8200
8201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8202
8203 /* Wait for all state dependents if necessary */
8204 if (mData->mMachineStateDeps != 0)
8205 {
8206 /* lazy semaphore creation */
8207 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8208 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8209
8210 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8211 mData->mMachineStateDeps));
8212
8213 ++mData->mMachineStateChangePending;
8214
8215 /* reset the semaphore before waiting, the last dependent will signal
8216 * it */
8217 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8218
8219 alock.release();
8220
8221 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8222
8223 alock.acquire();
8224
8225 -- mData->mMachineStateChangePending;
8226 }
8227}
8228
8229/**
8230 * Changes the machine state and informs callbacks.
8231 *
8232 * This method is not intended to fail so it either returns S_OK or asserts (and
8233 * returns a failure).
8234 *
8235 * @note Locks this object for writing.
8236 */
8237HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8238{
8239 LogFlowThisFuncEnter();
8240 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8241
8242 AutoCaller autoCaller(this);
8243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8244
8245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8246
8247 /* wait for state dependents to drop to zero */
8248 i_ensureNoStateDependencies();
8249
8250 if (mData->mMachineState != aMachineState)
8251 {
8252 mData->mMachineState = aMachineState;
8253
8254 RTTimeNow(&mData->mLastStateChange);
8255
8256 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8257 }
8258
8259 LogFlowThisFuncLeave();
8260 return S_OK;
8261}
8262
8263/**
8264 * Searches for a shared folder with the given logical name
8265 * in the collection of shared folders.
8266 *
8267 * @param aName logical name of the shared folder
8268 * @param aSharedFolder where to return the found object
8269 * @param aSetError whether to set the error info if the folder is
8270 * not found
8271 * @return
8272 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8273 *
8274 * @note
8275 * must be called from under the object's lock!
8276 */
8277HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8278 ComObjPtr<SharedFolder> &aSharedFolder,
8279 bool aSetError /* = false */)
8280{
8281 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8282 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8283 it != mHWData->mSharedFolders.end();
8284 ++it)
8285 {
8286 SharedFolder *pSF = *it;
8287 AutoCaller autoCaller(pSF);
8288 if (pSF->i_getName() == aName)
8289 {
8290 aSharedFolder = pSF;
8291 rc = S_OK;
8292 break;
8293 }
8294 }
8295
8296 if (aSetError && FAILED(rc))
8297 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8298
8299 return rc;
8300}
8301
8302/**
8303 * Initializes all machine instance data from the given settings structures
8304 * from XML. The exception is the machine UUID which needs special handling
8305 * depending on the caller's use case, so the caller needs to set that herself.
8306 *
8307 * This gets called in several contexts during machine initialization:
8308 *
8309 * -- When machine XML exists on disk already and needs to be loaded into memory,
8310 * for example, from registeredInit() to load all registered machines on
8311 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8312 * attached to the machine should be part of some media registry already.
8313 *
8314 * -- During OVF import, when a machine config has been constructed from an
8315 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8316 * ensure that the media listed as attachments in the config (which have
8317 * been imported from the OVF) receive the correct registry ID.
8318 *
8319 * -- During VM cloning.
8320 *
8321 * @param config Machine settings from XML.
8322 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8323 * for each attached medium in the config.
8324 * @return
8325 */
8326HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8327 const Guid *puuidRegistry)
8328{
8329 // copy name, description, OS type, teleporter, UTC etc.
8330 mUserData->s = config.machineUserData;
8331
8332 // Decode the Icon overide data from config userdata and set onto Machine.
8333 #define DECODE_STR_MAX _1M
8334 const char* pszStr = config.machineUserData.ovIcon.c_str();
8335 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8336 if (cbOut > DECODE_STR_MAX)
8337 return setError(E_FAIL,
8338 tr("Icon Data too long.'%d' > '%d'"),
8339 cbOut,
8340 DECODE_STR_MAX);
8341 com::SafeArray<BYTE> iconByte(cbOut);
8342 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8343 if (FAILED(rc))
8344 return setError(E_FAIL,
8345 tr("Failure to Decode Icon Data. '%s' (%d)"),
8346 pszStr,
8347 rc);
8348 mUserData->mIcon.resize(iconByte.size());
8349 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8350
8351 // look up the object by Id to check it is valid
8352 ComPtr<IGuestOSType> guestOSType;
8353 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8354 guestOSType.asOutParam());
8355 if (FAILED(rc)) return rc;
8356
8357 // stateFile (optional)
8358 if (config.strStateFile.isEmpty())
8359 mSSData->strStateFilePath.setNull();
8360 else
8361 {
8362 Utf8Str stateFilePathFull(config.strStateFile);
8363 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8364 if (RT_FAILURE(vrc))
8365 return setError(E_FAIL,
8366 tr("Invalid saved state file path '%s' (%Rrc)"),
8367 config.strStateFile.c_str(),
8368 vrc);
8369 mSSData->strStateFilePath = stateFilePathFull;
8370 }
8371
8372 // snapshot folder needs special processing so set it again
8373 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8374 if (FAILED(rc)) return rc;
8375
8376 /* Copy the extra data items (Not in any case config is already the same as
8377 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8378 * make sure the extra data map is copied). */
8379 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8380
8381 /* currentStateModified (optional, default is true) */
8382 mData->mCurrentStateModified = config.fCurrentStateModified;
8383
8384 mData->mLastStateChange = config.timeLastStateChange;
8385
8386 /*
8387 * note: all mUserData members must be assigned prior this point because
8388 * we need to commit changes in order to let mUserData be shared by all
8389 * snapshot machine instances.
8390 */
8391 mUserData.commitCopy();
8392
8393 // machine registry, if present (must be loaded before snapshots)
8394 if (config.canHaveOwnMediaRegistry())
8395 {
8396 // determine machine folder
8397 Utf8Str strMachineFolder = i_getSettingsFileFull();
8398 strMachineFolder.stripFilename();
8399 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8400 config.mediaRegistry,
8401 strMachineFolder);
8402 if (FAILED(rc)) return rc;
8403 }
8404
8405 /* Snapshot node (optional) */
8406 size_t cRootSnapshots;
8407 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8408 {
8409 // there must be only one root snapshot
8410 Assert(cRootSnapshots == 1);
8411
8412 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8413
8414 rc = i_loadSnapshot(snap,
8415 config.uuidCurrentSnapshot,
8416 NULL); // no parent == first snapshot
8417 if (FAILED(rc)) return rc;
8418 }
8419
8420 // hardware data
8421 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8422 if (FAILED(rc)) return rc;
8423
8424 // load storage controllers
8425 rc = i_loadStorageControllers(config.storageMachine,
8426 puuidRegistry,
8427 NULL /* puuidSnapshot */);
8428 if (FAILED(rc)) return rc;
8429
8430 /*
8431 * NOTE: the assignment below must be the last thing to do,
8432 * otherwise it will be not possible to change the settings
8433 * somewhere in the code above because all setters will be
8434 * blocked by i_checkStateDependency(MutableStateDep).
8435 */
8436
8437 /* set the machine state to Aborted or Saved when appropriate */
8438 if (config.fAborted)
8439 {
8440 mSSData->strStateFilePath.setNull();
8441
8442 /* no need to use i_setMachineState() during init() */
8443 mData->mMachineState = MachineState_Aborted;
8444 }
8445 else if (!mSSData->strStateFilePath.isEmpty())
8446 {
8447 /* no need to use i_setMachineState() during init() */
8448 mData->mMachineState = MachineState_Saved;
8449 }
8450
8451 // after loading settings, we are no longer different from the XML on disk
8452 mData->flModifications = 0;
8453
8454 return S_OK;
8455}
8456
8457/**
8458 * Recursively loads all snapshots starting from the given.
8459 *
8460 * @param aNode <Snapshot> node.
8461 * @param aCurSnapshotId Current snapshot ID from the settings file.
8462 * @param aParentSnapshot Parent snapshot.
8463 */
8464HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8465 const Guid &aCurSnapshotId,
8466 Snapshot *aParentSnapshot)
8467{
8468 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8469 AssertReturn(!i_isSessionMachine(), E_FAIL);
8470
8471 HRESULT rc = S_OK;
8472
8473 Utf8Str strStateFile;
8474 if (!data.strStateFile.isEmpty())
8475 {
8476 /* optional */
8477 strStateFile = data.strStateFile;
8478 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8479 if (RT_FAILURE(vrc))
8480 return setError(E_FAIL,
8481 tr("Invalid saved state file path '%s' (%Rrc)"),
8482 strStateFile.c_str(),
8483 vrc);
8484 }
8485
8486 /* create a snapshot machine object */
8487 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8488 pSnapshotMachine.createObject();
8489 rc = pSnapshotMachine->initFromSettings(this,
8490 data.hardware,
8491 &data.debugging,
8492 &data.autostart,
8493 data.storage,
8494 data.uuid.ref(),
8495 strStateFile);
8496 if (FAILED(rc)) return rc;
8497
8498 /* create a snapshot object */
8499 ComObjPtr<Snapshot> pSnapshot;
8500 pSnapshot.createObject();
8501 /* initialize the snapshot */
8502 rc = pSnapshot->init(mParent, // VirtualBox object
8503 data.uuid,
8504 data.strName,
8505 data.strDescription,
8506 data.timestamp,
8507 pSnapshotMachine,
8508 aParentSnapshot);
8509 if (FAILED(rc)) return rc;
8510
8511 /* memorize the first snapshot if necessary */
8512 if (!mData->mFirstSnapshot)
8513 mData->mFirstSnapshot = pSnapshot;
8514
8515 /* memorize the current snapshot when appropriate */
8516 if ( !mData->mCurrentSnapshot
8517 && pSnapshot->i_getId() == aCurSnapshotId
8518 )
8519 mData->mCurrentSnapshot = pSnapshot;
8520
8521 // now create the children
8522 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8523 it != data.llChildSnapshots.end();
8524 ++it)
8525 {
8526 const settings::Snapshot &childData = *it;
8527 // recurse
8528 rc = i_loadSnapshot(childData,
8529 aCurSnapshotId,
8530 pSnapshot); // parent = the one we created above
8531 if (FAILED(rc)) return rc;
8532 }
8533
8534 return rc;
8535}
8536
8537/**
8538 * Loads settings into mHWData.
8539 *
8540 * @param data Reference to the hardware settings.
8541 * @param pDbg Pointer to the debugging settings.
8542 * @param pAutostart Pointer to the autostart settings.
8543 */
8544HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8545 const settings::Autostart *pAutostart)
8546{
8547 AssertReturn(!i_isSessionMachine(), E_FAIL);
8548
8549 HRESULT rc = S_OK;
8550
8551 try
8552 {
8553 /* The hardware version attribute (optional). */
8554 mHWData->mHWVersion = data.strVersion;
8555 mHWData->mHardwareUUID = data.uuid;
8556
8557 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8558 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8559 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8560 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8561 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8562 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8563 mHWData->mPAEEnabled = data.fPAE;
8564 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8565 mHWData->mLongMode = data.enmLongMode;
8566 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8567 mHWData->mCPUCount = data.cCPUs;
8568 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8569 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8570
8571 // cpu
8572 if (mHWData->mCPUHotPlugEnabled)
8573 {
8574 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8575 it != data.llCpus.end();
8576 ++it)
8577 {
8578 const settings::Cpu &cpu = *it;
8579
8580 mHWData->mCPUAttached[cpu.ulId] = true;
8581 }
8582 }
8583
8584 // cpuid leafs
8585 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8586 it != data.llCpuIdLeafs.end();
8587 ++it)
8588 {
8589 const settings::CpuIdLeaf &leaf = *it;
8590
8591 switch (leaf.ulId)
8592 {
8593 case 0x0:
8594 case 0x1:
8595 case 0x2:
8596 case 0x3:
8597 case 0x4:
8598 case 0x5:
8599 case 0x6:
8600 case 0x7:
8601 case 0x8:
8602 case 0x9:
8603 case 0xA:
8604 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8605 break;
8606
8607 case 0x80000000:
8608 case 0x80000001:
8609 case 0x80000002:
8610 case 0x80000003:
8611 case 0x80000004:
8612 case 0x80000005:
8613 case 0x80000006:
8614 case 0x80000007:
8615 case 0x80000008:
8616 case 0x80000009:
8617 case 0x8000000A:
8618 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8619 break;
8620
8621 default:
8622 /* just ignore */
8623 break;
8624 }
8625 }
8626
8627 mHWData->mMemorySize = data.ulMemorySizeMB;
8628 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8629
8630 // boot order
8631 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8632 {
8633 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8634 if (it == data.mapBootOrder.end())
8635 mHWData->mBootOrder[i] = DeviceType_Null;
8636 else
8637 mHWData->mBootOrder[i] = it->second;
8638 }
8639
8640 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8641 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8642 mHWData->mMonitorCount = data.cMonitors;
8643 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8644 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8645 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8646 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8647 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8648 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8649 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8650 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8651 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8652 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8653 if (!data.strVideoCaptureFile.isEmpty())
8654 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8655 else
8656 mHWData->mVideoCaptureFile.setNull();
8657 mHWData->mFirmwareType = data.firmwareType;
8658 mHWData->mPointingHIDType = data.pointingHIDType;
8659 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8660 mHWData->mChipsetType = data.chipsetType;
8661 mHWData->mParavirtProvider = data.paravirtProvider;
8662 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8663 mHWData->mHPETEnabled = data.fHPETEnabled;
8664
8665 /* VRDEServer */
8666 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8667 if (FAILED(rc)) return rc;
8668
8669 /* BIOS */
8670 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8671 if (FAILED(rc)) return rc;
8672
8673 // Bandwidth control (must come before network adapters)
8674 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8675 if (FAILED(rc)) return rc;
8676
8677 /* Shared folders */
8678 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8679 it != data.usbSettings.llUSBControllers.end();
8680 ++it)
8681 {
8682 const settings::USBController &settingsCtrl = *it;
8683 ComObjPtr<USBController> newCtrl;
8684
8685 newCtrl.createObject();
8686 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8687 mUSBControllers->push_back(newCtrl);
8688 }
8689
8690 /* USB device filters */
8691 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8692 if (FAILED(rc)) return rc;
8693
8694 // network adapters
8695 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8696 uint32_t oldCount = mNetworkAdapters.size();
8697 if (newCount > oldCount)
8698 {
8699 mNetworkAdapters.resize(newCount);
8700 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8701 {
8702 unconst(mNetworkAdapters[slot]).createObject();
8703 mNetworkAdapters[slot]->init(this, slot);
8704 }
8705 }
8706 else if (newCount < oldCount)
8707 mNetworkAdapters.resize(newCount);
8708 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8709 it != data.llNetworkAdapters.end();
8710 ++it)
8711 {
8712 const settings::NetworkAdapter &nic = *it;
8713
8714 /* slot unicity is guaranteed by XML Schema */
8715 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8716 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8717 if (FAILED(rc)) return rc;
8718 }
8719
8720 // serial ports
8721 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8722 it != data.llSerialPorts.end();
8723 ++it)
8724 {
8725 const settings::SerialPort &s = *it;
8726
8727 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8728 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8729 if (FAILED(rc)) return rc;
8730 }
8731
8732 // parallel ports (optional)
8733 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8734 it != data.llParallelPorts.end();
8735 ++it)
8736 {
8737 const settings::ParallelPort &p = *it;
8738
8739 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8740 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8741 if (FAILED(rc)) return rc;
8742 }
8743
8744 /* AudioAdapter */
8745 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8746 if (FAILED(rc)) return rc;
8747
8748 /* Shared folders */
8749 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8750 it != data.llSharedFolders.end();
8751 ++it)
8752 {
8753 const settings::SharedFolder &sf = *it;
8754
8755 ComObjPtr<SharedFolder> sharedFolder;
8756 /* Check for double entries. Not allowed! */
8757 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8758 if (SUCCEEDED(rc))
8759 return setError(VBOX_E_OBJECT_IN_USE,
8760 tr("Shared folder named '%s' already exists"),
8761 sf.strName.c_str());
8762
8763 /* Create the new shared folder. Don't break on error. This will be
8764 * reported when the machine starts. */
8765 sharedFolder.createObject();
8766 rc = sharedFolder->init(i_getMachine(),
8767 sf.strName,
8768 sf.strHostPath,
8769 RT_BOOL(sf.fWritable),
8770 RT_BOOL(sf.fAutoMount),
8771 false /* fFailOnError */);
8772 if (FAILED(rc)) return rc;
8773 mHWData->mSharedFolders.push_back(sharedFolder);
8774 }
8775
8776 // Clipboard
8777 mHWData->mClipboardMode = data.clipboardMode;
8778
8779 // drag'n'drop
8780 mHWData->mDnDMode = data.dndMode;
8781
8782 // guest settings
8783 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8784
8785 // IO settings
8786 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8787 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8788
8789 // Host PCI devices
8790 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8791 it != data.pciAttachments.end();
8792 ++it)
8793 {
8794 const settings::HostPCIDeviceAttachment &hpda = *it;
8795 ComObjPtr<PCIDeviceAttachment> pda;
8796
8797 pda.createObject();
8798 pda->i_loadSettings(this, hpda);
8799 mHWData->mPCIDeviceAssignments.push_back(pda);
8800 }
8801
8802 /*
8803 * (The following isn't really real hardware, but it lives in HWData
8804 * for reasons of convenience.)
8805 */
8806
8807#ifdef VBOX_WITH_GUEST_PROPS
8808 /* Guest properties (optional) */
8809 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8810 it != data.llGuestProperties.end();
8811 ++it)
8812 {
8813 const settings::GuestProperty &prop = *it;
8814 uint32_t fFlags = guestProp::NILFLAG;
8815 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8816 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8817 mHWData->mGuestProperties[prop.strName] = property;
8818 }
8819
8820 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8821#endif /* VBOX_WITH_GUEST_PROPS defined */
8822
8823 rc = i_loadDebugging(pDbg);
8824 if (FAILED(rc))
8825 return rc;
8826
8827 mHWData->mAutostart = *pAutostart;
8828
8829 /* default frontend */
8830 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8831 }
8832 catch(std::bad_alloc &)
8833 {
8834 return E_OUTOFMEMORY;
8835 }
8836
8837 AssertComRC(rc);
8838 return rc;
8839}
8840
8841/**
8842 * Called from Machine::loadHardware() to load the debugging settings of the
8843 * machine.
8844 *
8845 * @param pDbg Pointer to the settings.
8846 */
8847HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8848{
8849 mHWData->mDebugging = *pDbg;
8850 /* no more processing currently required, this will probably change. */
8851 return S_OK;
8852}
8853
8854/**
8855 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8856 *
8857 * @param data
8858 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8859 * @param puuidSnapshot
8860 * @return
8861 */
8862HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8863 const Guid *puuidRegistry,
8864 const Guid *puuidSnapshot)
8865{
8866 AssertReturn(!i_isSessionMachine(), E_FAIL);
8867
8868 HRESULT rc = S_OK;
8869
8870 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8871 it != data.llStorageControllers.end();
8872 ++it)
8873 {
8874 const settings::StorageController &ctlData = *it;
8875
8876 ComObjPtr<StorageController> pCtl;
8877 /* Try to find one with the name first. */
8878 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8879 if (SUCCEEDED(rc))
8880 return setError(VBOX_E_OBJECT_IN_USE,
8881 tr("Storage controller named '%s' already exists"),
8882 ctlData.strName.c_str());
8883
8884 pCtl.createObject();
8885 rc = pCtl->init(this,
8886 ctlData.strName,
8887 ctlData.storageBus,
8888 ctlData.ulInstance,
8889 ctlData.fBootable);
8890 if (FAILED(rc)) return rc;
8891
8892 mStorageControllers->push_back(pCtl);
8893
8894 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8895 if (FAILED(rc)) return rc;
8896
8897 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8898 if (FAILED(rc)) return rc;
8899
8900 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8901 if (FAILED(rc)) return rc;
8902
8903 /* Set IDE emulation settings (only for AHCI controller). */
8904 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8905 {
8906 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8907 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8908 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8909 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8910 )
8911 return rc;
8912 }
8913
8914 /* Load the attached devices now. */
8915 rc = i_loadStorageDevices(pCtl,
8916 ctlData,
8917 puuidRegistry,
8918 puuidSnapshot);
8919 if (FAILED(rc)) return rc;
8920 }
8921
8922 return S_OK;
8923}
8924
8925/**
8926 * Called from i_loadStorageControllers for a controller's devices.
8927 *
8928 * @param aStorageController
8929 * @param data
8930 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8931 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8932 * @return
8933 */
8934HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8935 const settings::StorageController &data,
8936 const Guid *puuidRegistry,
8937 const Guid *puuidSnapshot)
8938{
8939 HRESULT rc = S_OK;
8940
8941 /* paranoia: detect duplicate attachments */
8942 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8943 it != data.llAttachedDevices.end();
8944 ++it)
8945 {
8946 const settings::AttachedDevice &ad = *it;
8947
8948 for (settings::AttachedDevicesList::const_iterator it2 = it;
8949 it2 != data.llAttachedDevices.end();
8950 ++it2)
8951 {
8952 if (it == it2)
8953 continue;
8954
8955 const settings::AttachedDevice &ad2 = *it2;
8956
8957 if ( ad.lPort == ad2.lPort
8958 && ad.lDevice == ad2.lDevice)
8959 {
8960 return setError(E_FAIL,
8961 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8962 aStorageController->i_getName().c_str(),
8963 ad.lPort,
8964 ad.lDevice,
8965 mUserData->s.strName.c_str());
8966 }
8967 }
8968 }
8969
8970 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8971 it != data.llAttachedDevices.end();
8972 ++it)
8973 {
8974 const settings::AttachedDevice &dev = *it;
8975 ComObjPtr<Medium> medium;
8976
8977 switch (dev.deviceType)
8978 {
8979 case DeviceType_Floppy:
8980 case DeviceType_DVD:
8981 if (dev.strHostDriveSrc.isNotEmpty())
8982 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
8983 false /* fRefresh */, medium);
8984 else
8985 rc = mParent->i_findRemoveableMedium(dev.deviceType,
8986 dev.uuid,
8987 false /* fRefresh */,
8988 false /* aSetError */,
8989 medium);
8990 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8991 // This is not an error. The host drive or UUID might have vanished, so just go
8992 // ahead without this removeable medium attachment
8993 rc = S_OK;
8994 break;
8995
8996 case DeviceType_HardDisk:
8997 {
8998 /* find a hard disk by UUID */
8999 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9000 if (FAILED(rc))
9001 {
9002 if (i_isSnapshotMachine())
9003 {
9004 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9005 // so the user knows that the bad disk is in a snapshot somewhere
9006 com::ErrorInfo info;
9007 return setError(E_FAIL,
9008 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9009 puuidSnapshot->raw(),
9010 info.getText().raw());
9011 }
9012 else
9013 return rc;
9014 }
9015
9016 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9017
9018 if (medium->i_getType() == MediumType_Immutable)
9019 {
9020 if (i_isSnapshotMachine())
9021 return setError(E_FAIL,
9022 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9023 "of the virtual machine '%s' ('%s')"),
9024 medium->i_getLocationFull().c_str(),
9025 dev.uuid.raw(),
9026 puuidSnapshot->raw(),
9027 mUserData->s.strName.c_str(),
9028 mData->m_strConfigFileFull.c_str());
9029
9030 return setError(E_FAIL,
9031 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9032 medium->i_getLocationFull().c_str(),
9033 dev.uuid.raw(),
9034 mUserData->s.strName.c_str(),
9035 mData->m_strConfigFileFull.c_str());
9036 }
9037
9038 if (medium->i_getType() == MediumType_MultiAttach)
9039 {
9040 if (i_isSnapshotMachine())
9041 return setError(E_FAIL,
9042 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9043 "of the virtual machine '%s' ('%s')"),
9044 medium->i_getLocationFull().c_str(),
9045 dev.uuid.raw(),
9046 puuidSnapshot->raw(),
9047 mUserData->s.strName.c_str(),
9048 mData->m_strConfigFileFull.c_str());
9049
9050 return setError(E_FAIL,
9051 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9052 medium->i_getLocationFull().c_str(),
9053 dev.uuid.raw(),
9054 mUserData->s.strName.c_str(),
9055 mData->m_strConfigFileFull.c_str());
9056 }
9057
9058 if ( !i_isSnapshotMachine()
9059 && medium->i_getChildren().size() != 0
9060 )
9061 return setError(E_FAIL,
9062 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9063 "because it has %d differencing child hard disks"),
9064 medium->i_getLocationFull().c_str(),
9065 dev.uuid.raw(),
9066 mUserData->s.strName.c_str(),
9067 mData->m_strConfigFileFull.c_str(),
9068 medium->i_getChildren().size());
9069
9070 if (i_findAttachment(mMediaData->mAttachments,
9071 medium))
9072 return setError(E_FAIL,
9073 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9074 medium->i_getLocationFull().c_str(),
9075 dev.uuid.raw(),
9076 mUserData->s.strName.c_str(),
9077 mData->m_strConfigFileFull.c_str());
9078
9079 break;
9080 }
9081
9082 default:
9083 return setError(E_FAIL,
9084 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9085 medium->i_getLocationFull().c_str(),
9086 mUserData->s.strName.c_str(),
9087 mData->m_strConfigFileFull.c_str());
9088 }
9089
9090 if (FAILED(rc))
9091 break;
9092
9093 /* Bandwidth groups are loaded at this point. */
9094 ComObjPtr<BandwidthGroup> pBwGroup;
9095
9096 if (!dev.strBwGroup.isEmpty())
9097 {
9098 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9099 if (FAILED(rc))
9100 return setError(E_FAIL,
9101 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9102 medium->i_getLocationFull().c_str(),
9103 dev.strBwGroup.c_str(),
9104 mUserData->s.strName.c_str(),
9105 mData->m_strConfigFileFull.c_str());
9106 pBwGroup->i_reference();
9107 }
9108
9109 const Bstr controllerName = aStorageController->i_getName();
9110 ComObjPtr<MediumAttachment> pAttachment;
9111 pAttachment.createObject();
9112 rc = pAttachment->init(this,
9113 medium,
9114 controllerName,
9115 dev.lPort,
9116 dev.lDevice,
9117 dev.deviceType,
9118 false,
9119 dev.fPassThrough,
9120 dev.fTempEject,
9121 dev.fNonRotational,
9122 dev.fDiscard,
9123 dev.fHotPluggable,
9124 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9125 if (FAILED(rc)) break;
9126
9127 /* associate the medium with this machine and snapshot */
9128 if (!medium.isNull())
9129 {
9130 AutoCaller medCaller(medium);
9131 if (FAILED(medCaller.rc())) return medCaller.rc();
9132 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9133
9134 if (i_isSnapshotMachine())
9135 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9136 else
9137 rc = medium->i_addBackReference(mData->mUuid);
9138 /* If the medium->addBackReference fails it sets an appropriate
9139 * error message, so no need to do any guesswork here. */
9140
9141 if (puuidRegistry)
9142 // caller wants registry ID to be set on all attached media (OVF import case)
9143 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9144 }
9145
9146 if (FAILED(rc))
9147 break;
9148
9149 /* back up mMediaData to let registeredInit() properly rollback on failure
9150 * (= limited accessibility) */
9151 i_setModified(IsModified_Storage);
9152 mMediaData.backup();
9153 mMediaData->mAttachments.push_back(pAttachment);
9154 }
9155
9156 return rc;
9157}
9158
9159/**
9160 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9161 *
9162 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9163 * @param aSnapshot where to return the found snapshot
9164 * @param aSetError true to set extended error info on failure
9165 */
9166HRESULT Machine::i_findSnapshotById(const Guid &aId,
9167 ComObjPtr<Snapshot> &aSnapshot,
9168 bool aSetError /* = false */)
9169{
9170 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9171
9172 if (!mData->mFirstSnapshot)
9173 {
9174 if (aSetError)
9175 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9176 return E_FAIL;
9177 }
9178
9179 if (aId.isZero())
9180 aSnapshot = mData->mFirstSnapshot;
9181 else
9182 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9183
9184 if (!aSnapshot)
9185 {
9186 if (aSetError)
9187 return setError(E_FAIL,
9188 tr("Could not find a snapshot with UUID {%s}"),
9189 aId.toString().c_str());
9190 return E_FAIL;
9191 }
9192
9193 return S_OK;
9194}
9195
9196/**
9197 * Returns the snapshot with the given name or fails of no such snapshot.
9198 *
9199 * @param aName snapshot name to find
9200 * @param aSnapshot where to return the found snapshot
9201 * @param aSetError true to set extended error info on failure
9202 */
9203HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9204 ComObjPtr<Snapshot> &aSnapshot,
9205 bool aSetError /* = false */)
9206{
9207 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9208
9209 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9210
9211 if (!mData->mFirstSnapshot)
9212 {
9213 if (aSetError)
9214 return setError(VBOX_E_OBJECT_NOT_FOUND,
9215 tr("This machine does not have any snapshots"));
9216 return VBOX_E_OBJECT_NOT_FOUND;
9217 }
9218
9219 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9220
9221 if (!aSnapshot)
9222 {
9223 if (aSetError)
9224 return setError(VBOX_E_OBJECT_NOT_FOUND,
9225 tr("Could not find a snapshot named '%s'"), strName.c_str());
9226 return VBOX_E_OBJECT_NOT_FOUND;
9227 }
9228
9229 return S_OK;
9230}
9231
9232/**
9233 * Returns a storage controller object with the given name.
9234 *
9235 * @param aName storage controller name to find
9236 * @param aStorageController where to return the found storage controller
9237 * @param aSetError true to set extended error info on failure
9238 */
9239HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9240 ComObjPtr<StorageController> &aStorageController,
9241 bool aSetError /* = false */)
9242{
9243 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9244
9245 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9246 it != mStorageControllers->end();
9247 ++it)
9248 {
9249 if ((*it)->i_getName() == aName)
9250 {
9251 aStorageController = (*it);
9252 return S_OK;
9253 }
9254 }
9255
9256 if (aSetError)
9257 return setError(VBOX_E_OBJECT_NOT_FOUND,
9258 tr("Could not find a storage controller named '%s'"),
9259 aName.c_str());
9260 return VBOX_E_OBJECT_NOT_FOUND;
9261}
9262
9263/**
9264 * Returns a USB controller object with the given name.
9265 *
9266 * @param aName USB controller name to find
9267 * @param aUSBController where to return the found USB controller
9268 * @param aSetError true to set extended error info on failure
9269 */
9270HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9271 ComObjPtr<USBController> &aUSBController,
9272 bool aSetError /* = false */)
9273{
9274 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9275
9276 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9277 it != mUSBControllers->end();
9278 ++it)
9279 {
9280 if ((*it)->i_getName() == aName)
9281 {
9282 aUSBController = (*it);
9283 return S_OK;
9284 }
9285 }
9286
9287 if (aSetError)
9288 return setError(VBOX_E_OBJECT_NOT_FOUND,
9289 tr("Could not find a storage controller named '%s'"),
9290 aName.c_str());
9291 return VBOX_E_OBJECT_NOT_FOUND;
9292}
9293
9294/**
9295 * Returns the number of USB controller instance of the given type.
9296 *
9297 * @param enmType USB controller type.
9298 */
9299ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9300{
9301 ULONG cCtrls = 0;
9302
9303 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9304 it != mUSBControllers->end();
9305 ++it)
9306 {
9307 if ((*it)->i_getControllerType() == enmType)
9308 cCtrls++;
9309 }
9310
9311 return cCtrls;
9312}
9313
9314HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9315 MediaData::AttachmentList &atts)
9316{
9317 AutoCaller autoCaller(this);
9318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9319
9320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9321
9322 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9323 it != mMediaData->mAttachments.end();
9324 ++it)
9325 {
9326 const ComObjPtr<MediumAttachment> &pAtt = *it;
9327 // should never happen, but deal with NULL pointers in the list.
9328 AssertStmt(!pAtt.isNull(), continue);
9329
9330 // getControllerName() needs caller+read lock
9331 AutoCaller autoAttCaller(pAtt);
9332 if (FAILED(autoAttCaller.rc()))
9333 {
9334 atts.clear();
9335 return autoAttCaller.rc();
9336 }
9337 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9338
9339 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9340 atts.push_back(pAtt);
9341 }
9342
9343 return S_OK;
9344}
9345
9346
9347/**
9348 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9349 * file if the machine name was changed and about creating a new settings file
9350 * if this is a new machine.
9351 *
9352 * @note Must be never called directly but only from #saveSettings().
9353 */
9354HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9355{
9356 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9357
9358 HRESULT rc = S_OK;
9359
9360 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9361
9362 /// @todo need to handle primary group change, too
9363
9364 /* attempt to rename the settings file if machine name is changed */
9365 if ( mUserData->s.fNameSync
9366 && mUserData.isBackedUp()
9367 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9368 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9369 )
9370 {
9371 bool dirRenamed = false;
9372 bool fileRenamed = false;
9373
9374 Utf8Str configFile, newConfigFile;
9375 Utf8Str configFilePrev, newConfigFilePrev;
9376 Utf8Str configDir, newConfigDir;
9377
9378 do
9379 {
9380 int vrc = VINF_SUCCESS;
9381
9382 Utf8Str name = mUserData.backedUpData()->s.strName;
9383 Utf8Str newName = mUserData->s.strName;
9384 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9385 if (group == "/")
9386 group.setNull();
9387 Utf8Str newGroup = mUserData->s.llGroups.front();
9388 if (newGroup == "/")
9389 newGroup.setNull();
9390
9391 configFile = mData->m_strConfigFileFull;
9392
9393 /* first, rename the directory if it matches the group and machine name */
9394 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9395 group.c_str(), RTPATH_DELIMITER, name.c_str());
9396 /** @todo hack, make somehow use of ComposeMachineFilename */
9397 if (mUserData->s.fDirectoryIncludesUUID)
9398 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9399 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9400 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9401 /** @todo hack, make somehow use of ComposeMachineFilename */
9402 if (mUserData->s.fDirectoryIncludesUUID)
9403 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9404 configDir = configFile;
9405 configDir.stripFilename();
9406 newConfigDir = configDir;
9407 if ( configDir.length() >= groupPlusName.length()
9408 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9409 groupPlusName.c_str()))
9410 {
9411 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9412 Utf8Str newConfigBaseDir(newConfigDir);
9413 newConfigDir.append(newGroupPlusName);
9414 /* consistency: use \ if appropriate on the platform */
9415 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9416 /* new dir and old dir cannot be equal here because of 'if'
9417 * above and because name != newName */
9418 Assert(configDir != newConfigDir);
9419 if (!fSettingsFileIsNew)
9420 {
9421 /* perform real rename only if the machine is not new */
9422 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9423 if ( vrc == VERR_FILE_NOT_FOUND
9424 || vrc == VERR_PATH_NOT_FOUND)
9425 {
9426 /* create the parent directory, then retry renaming */
9427 Utf8Str parent(newConfigDir);
9428 parent.stripFilename();
9429 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9430 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9431 }
9432 if (RT_FAILURE(vrc))
9433 {
9434 rc = setError(E_FAIL,
9435 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9436 configDir.c_str(),
9437 newConfigDir.c_str(),
9438 vrc);
9439 break;
9440 }
9441 /* delete subdirectories which are no longer needed */
9442 Utf8Str dir(configDir);
9443 dir.stripFilename();
9444 while (dir != newConfigBaseDir && dir != ".")
9445 {
9446 vrc = RTDirRemove(dir.c_str());
9447 if (RT_FAILURE(vrc))
9448 break;
9449 dir.stripFilename();
9450 }
9451 dirRenamed = true;
9452 }
9453 }
9454
9455 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9456 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9457
9458 /* then try to rename the settings file itself */
9459 if (newConfigFile != configFile)
9460 {
9461 /* get the path to old settings file in renamed directory */
9462 configFile = Utf8StrFmt("%s%c%s",
9463 newConfigDir.c_str(),
9464 RTPATH_DELIMITER,
9465 RTPathFilename(configFile.c_str()));
9466 if (!fSettingsFileIsNew)
9467 {
9468 /* perform real rename only if the machine is not new */
9469 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9470 if (RT_FAILURE(vrc))
9471 {
9472 rc = setError(E_FAIL,
9473 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9474 configFile.c_str(),
9475 newConfigFile.c_str(),
9476 vrc);
9477 break;
9478 }
9479 fileRenamed = true;
9480 configFilePrev = configFile;
9481 configFilePrev += "-prev";
9482 newConfigFilePrev = newConfigFile;
9483 newConfigFilePrev += "-prev";
9484 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9485 }
9486 }
9487
9488 // update m_strConfigFileFull amd mConfigFile
9489 mData->m_strConfigFileFull = newConfigFile;
9490 // compute the relative path too
9491 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9492
9493 // store the old and new so that VirtualBox::i_saveSettings() can update
9494 // the media registry
9495 if ( mData->mRegistered
9496 && (configDir != newConfigDir || configFile != newConfigFile))
9497 {
9498 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9499
9500 if (pfNeedsGlobalSaveSettings)
9501 *pfNeedsGlobalSaveSettings = true;
9502 }
9503
9504 // in the saved state file path, replace the old directory with the new directory
9505 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9506 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9507
9508 // and do the same thing for the saved state file paths of all the online snapshots
9509 if (mData->mFirstSnapshot)
9510 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9511 newConfigDir.c_str());
9512 }
9513 while (0);
9514
9515 if (FAILED(rc))
9516 {
9517 /* silently try to rename everything back */
9518 if (fileRenamed)
9519 {
9520 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9521 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9522 }
9523 if (dirRenamed)
9524 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9525 }
9526
9527 if (FAILED(rc)) return rc;
9528 }
9529
9530 if (fSettingsFileIsNew)
9531 {
9532 /* create a virgin config file */
9533 int vrc = VINF_SUCCESS;
9534
9535 /* ensure the settings directory exists */
9536 Utf8Str path(mData->m_strConfigFileFull);
9537 path.stripFilename();
9538 if (!RTDirExists(path.c_str()))
9539 {
9540 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9541 if (RT_FAILURE(vrc))
9542 {
9543 return setError(E_FAIL,
9544 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9545 path.c_str(),
9546 vrc);
9547 }
9548 }
9549
9550 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9551 path = Utf8Str(mData->m_strConfigFileFull);
9552 RTFILE f = NIL_RTFILE;
9553 vrc = RTFileOpen(&f, path.c_str(),
9554 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9555 if (RT_FAILURE(vrc))
9556 return setError(E_FAIL,
9557 tr("Could not create the settings file '%s' (%Rrc)"),
9558 path.c_str(),
9559 vrc);
9560 RTFileClose(f);
9561 }
9562
9563 return rc;
9564}
9565
9566/**
9567 * Saves and commits machine data, user data and hardware data.
9568 *
9569 * Note that on failure, the data remains uncommitted.
9570 *
9571 * @a aFlags may combine the following flags:
9572 *
9573 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9574 * Used when saving settings after an operation that makes them 100%
9575 * correspond to the settings from the current snapshot.
9576 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9577 * #isReallyModified() returns false. This is necessary for cases when we
9578 * change machine data directly, not through the backup()/commit() mechanism.
9579 * - SaveS_Force: settings will be saved without doing a deep compare of the
9580 * settings structures. This is used when this is called because snapshots
9581 * have changed to avoid the overhead of the deep compare.
9582 *
9583 * @note Must be called from under this object's write lock. Locks children for
9584 * writing.
9585 *
9586 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9587 * initialized to false and that will be set to true by this function if
9588 * the caller must invoke VirtualBox::i_saveSettings() because the global
9589 * settings have changed. This will happen if a machine rename has been
9590 * saved and the global machine and media registries will therefore need
9591 * updating.
9592 */
9593HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9594 int aFlags /*= 0*/)
9595{
9596 LogFlowThisFuncEnter();
9597
9598 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9599
9600 /* make sure child objects are unable to modify the settings while we are
9601 * saving them */
9602 i_ensureNoStateDependencies();
9603
9604 AssertReturn(!i_isSnapshotMachine(),
9605 E_FAIL);
9606
9607 HRESULT rc = S_OK;
9608 bool fNeedsWrite = false;
9609
9610 /* First, prepare to save settings. It will care about renaming the
9611 * settings directory and file if the machine name was changed and about
9612 * creating a new settings file if this is a new machine. */
9613 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9614 if (FAILED(rc)) return rc;
9615
9616 // keep a pointer to the current settings structures
9617 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9618 settings::MachineConfigFile *pNewConfig = NULL;
9619
9620 try
9621 {
9622 // make a fresh one to have everyone write stuff into
9623 pNewConfig = new settings::MachineConfigFile(NULL);
9624 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9625
9626 // now go and copy all the settings data from COM to the settings structures
9627 // (this calles i_saveSettings() on all the COM objects in the machine)
9628 i_copyMachineDataToSettings(*pNewConfig);
9629
9630 if (aFlags & SaveS_ResetCurStateModified)
9631 {
9632 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9633 mData->mCurrentStateModified = FALSE;
9634 fNeedsWrite = true; // always, no need to compare
9635 }
9636 else if (aFlags & SaveS_Force)
9637 {
9638 fNeedsWrite = true; // always, no need to compare
9639 }
9640 else
9641 {
9642 if (!mData->mCurrentStateModified)
9643 {
9644 // do a deep compare of the settings that we just saved with the settings
9645 // previously stored in the config file; this invokes MachineConfigFile::operator==
9646 // which does a deep compare of all the settings, which is expensive but less expensive
9647 // than writing out XML in vain
9648 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9649
9650 // could still be modified if any settings changed
9651 mData->mCurrentStateModified = fAnySettingsChanged;
9652
9653 fNeedsWrite = fAnySettingsChanged;
9654 }
9655 else
9656 fNeedsWrite = true;
9657 }
9658
9659 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9660
9661 if (fNeedsWrite)
9662 // now spit it all out!
9663 pNewConfig->write(mData->m_strConfigFileFull);
9664
9665 mData->pMachineConfigFile = pNewConfig;
9666 delete pOldConfig;
9667 i_commit();
9668
9669 // after saving settings, we are no longer different from the XML on disk
9670 mData->flModifications = 0;
9671 }
9672 catch (HRESULT err)
9673 {
9674 // we assume that error info is set by the thrower
9675 rc = err;
9676
9677 // restore old config
9678 delete pNewConfig;
9679 mData->pMachineConfigFile = pOldConfig;
9680 }
9681 catch (...)
9682 {
9683 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9684 }
9685
9686 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9687 {
9688 /* Fire the data change event, even on failure (since we've already
9689 * committed all data). This is done only for SessionMachines because
9690 * mutable Machine instances are always not registered (i.e. private
9691 * to the client process that creates them) and thus don't need to
9692 * inform callbacks. */
9693 if (i_isSessionMachine())
9694 mParent->i_onMachineDataChange(mData->mUuid);
9695 }
9696
9697 LogFlowThisFunc(("rc=%08X\n", rc));
9698 LogFlowThisFuncLeave();
9699 return rc;
9700}
9701
9702/**
9703 * Implementation for saving the machine settings into the given
9704 * settings::MachineConfigFile instance. This copies machine extradata
9705 * from the previous machine config file in the instance data, if any.
9706 *
9707 * This gets called from two locations:
9708 *
9709 * -- Machine::i_saveSettings(), during the regular XML writing;
9710 *
9711 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9712 * exported to OVF and we write the VirtualBox proprietary XML
9713 * into a <vbox:Machine> tag.
9714 *
9715 * This routine fills all the fields in there, including snapshots, *except*
9716 * for the following:
9717 *
9718 * -- fCurrentStateModified. There is some special logic associated with that.
9719 *
9720 * The caller can then call MachineConfigFile::write() or do something else
9721 * with it.
9722 *
9723 * Caller must hold the machine lock!
9724 *
9725 * This throws XML errors and HRESULT, so the caller must have a catch block!
9726 */
9727void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9728{
9729 // deep copy extradata
9730 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9731
9732 config.uuid = mData->mUuid;
9733
9734 // copy name, description, OS type, teleport, UTC etc.
9735 config.machineUserData = mUserData->s;
9736
9737 // Encode the Icon Override data from Machine and store on config userdata.
9738 com::SafeArray<BYTE> iconByte;
9739 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9740 ssize_t cbData = iconByte.size();
9741 if (cbData > 0)
9742 {
9743 ssize_t cchOut = RTBase64EncodedLength(cbData);
9744 Utf8Str strIconData;
9745 strIconData.reserve(cchOut+1);
9746 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9747 strIconData.mutableRaw(), strIconData.capacity(),
9748 NULL);
9749 if (RT_FAILURE(vrc))
9750 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9751 strIconData.jolt();
9752 config.machineUserData.ovIcon = strIconData;
9753 }
9754 else
9755 config.machineUserData.ovIcon.setNull();
9756
9757 if ( mData->mMachineState == MachineState_Saved
9758 || mData->mMachineState == MachineState_Restoring
9759 // when deleting a snapshot we may or may not have a saved state in the current state,
9760 // so let's not assert here please
9761 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9762 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9763 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9764 && (!mSSData->strStateFilePath.isEmpty())
9765 )
9766 )
9767 {
9768 Assert(!mSSData->strStateFilePath.isEmpty());
9769 /* try to make the file name relative to the settings file dir */
9770 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9771 }
9772 else
9773 {
9774 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9775 config.strStateFile.setNull();
9776 }
9777
9778 if (mData->mCurrentSnapshot)
9779 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9780 else
9781 config.uuidCurrentSnapshot.clear();
9782
9783 config.timeLastStateChange = mData->mLastStateChange;
9784 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9785 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9786
9787 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9788 if (FAILED(rc)) throw rc;
9789
9790 rc = i_saveStorageControllers(config.storageMachine);
9791 if (FAILED(rc)) throw rc;
9792
9793 // save machine's media registry if this is VirtualBox 4.0 or later
9794 if (config.canHaveOwnMediaRegistry())
9795 {
9796 // determine machine folder
9797 Utf8Str strMachineFolder = i_getSettingsFileFull();
9798 strMachineFolder.stripFilename();
9799 mParent->i_saveMediaRegistry(config.mediaRegistry,
9800 i_getId(), // only media with registry ID == machine UUID
9801 strMachineFolder);
9802 // this throws HRESULT
9803 }
9804
9805 // save snapshots
9806 rc = i_saveAllSnapshots(config);
9807 if (FAILED(rc)) throw rc;
9808}
9809
9810/**
9811 * Saves all snapshots of the machine into the given machine config file. Called
9812 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9813 * @param config
9814 * @return
9815 */
9816HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9817{
9818 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9819
9820 HRESULT rc = S_OK;
9821
9822 try
9823 {
9824 config.llFirstSnapshot.clear();
9825
9826 if (mData->mFirstSnapshot)
9827 {
9828 settings::Snapshot snapNew;
9829 config.llFirstSnapshot.push_back(snapNew);
9830
9831 // get reference to the fresh copy of the snapshot on the list and
9832 // work on that copy directly to avoid excessive copying later
9833 settings::Snapshot &snap = config.llFirstSnapshot.front();
9834
9835 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9836 if (FAILED(rc)) throw rc;
9837 }
9838
9839// if (mType == IsSessionMachine)
9840// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9841
9842 }
9843 catch (HRESULT err)
9844 {
9845 /* we assume that error info is set by the thrower */
9846 rc = err;
9847 }
9848 catch (...)
9849 {
9850 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9851 }
9852
9853 return rc;
9854}
9855
9856/**
9857 * Saves the VM hardware configuration. It is assumed that the
9858 * given node is empty.
9859 *
9860 * @param data Reference to the settings object for the hardware config.
9861 * @param pDbg Pointer to the settings object for the debugging config
9862 * which happens to live in mHWData.
9863 * @param pAutostart Pointer to the settings object for the autostart config
9864 * which happens to live in mHWData.
9865 */
9866HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9867 settings::Autostart *pAutostart)
9868{
9869 HRESULT rc = S_OK;
9870
9871 try
9872 {
9873 /* The hardware version attribute (optional).
9874 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9875 if ( mHWData->mHWVersion == "1"
9876 && mSSData->strStateFilePath.isEmpty()
9877 )
9878 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9879 other point needs to be found where this can be done. */
9880
9881 data.strVersion = mHWData->mHWVersion;
9882 data.uuid = mHWData->mHardwareUUID;
9883
9884 // CPU
9885 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9886 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9887 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9888 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9889 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9890 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9891 data.fPAE = !!mHWData->mPAEEnabled;
9892 data.enmLongMode = mHWData->mLongMode;
9893 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9894 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9895
9896 /* Standard and Extended CPUID leafs. */
9897 data.llCpuIdLeafs.clear();
9898 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9899 {
9900 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9901 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9902 }
9903 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9904 {
9905 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9906 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9907 }
9908
9909 data.cCPUs = mHWData->mCPUCount;
9910 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9911 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9912
9913 data.llCpus.clear();
9914 if (data.fCpuHotPlug)
9915 {
9916 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9917 {
9918 if (mHWData->mCPUAttached[idx])
9919 {
9920 settings::Cpu cpu;
9921 cpu.ulId = idx;
9922 data.llCpus.push_back(cpu);
9923 }
9924 }
9925 }
9926
9927 // memory
9928 data.ulMemorySizeMB = mHWData->mMemorySize;
9929 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9930
9931 // firmware
9932 data.firmwareType = mHWData->mFirmwareType;
9933
9934 // HID
9935 data.pointingHIDType = mHWData->mPointingHIDType;
9936 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9937
9938 // chipset
9939 data.chipsetType = mHWData->mChipsetType;
9940
9941 // paravirt
9942 data.paravirtProvider = mHWData->mParavirtProvider;
9943
9944
9945 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9946
9947 // HPET
9948 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9949
9950 // boot order
9951 data.mapBootOrder.clear();
9952 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9953 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9954
9955 // display
9956 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9957 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9958 data.cMonitors = mHWData->mMonitorCount;
9959 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9960 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9961 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9962 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9963 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
9964 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
9965 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9966 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
9967 {
9968 if (mHWData->maVideoCaptureScreens[i])
9969 ASMBitSet(&data.u64VideoCaptureScreens, i);
9970 else
9971 ASMBitClear(&data.u64VideoCaptureScreens, i);
9972 }
9973 /* store relative video capture file if possible */
9974 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
9975
9976 /* VRDEServer settings (optional) */
9977 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
9978 if (FAILED(rc)) throw rc;
9979
9980 /* BIOS (required) */
9981 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
9982 if (FAILED(rc)) throw rc;
9983
9984 /* USB Controller (required) */
9985 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
9986 {
9987 ComObjPtr<USBController> ctrl = *it;
9988 settings::USBController settingsCtrl;
9989
9990 settingsCtrl.strName = ctrl->i_getName();
9991 settingsCtrl.enmType = ctrl->i_getControllerType();
9992
9993 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
9994 }
9995
9996 /* USB device filters (required) */
9997 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
9998 if (FAILED(rc)) throw rc;
9999
10000 /* Network adapters (required) */
10001 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10002 data.llNetworkAdapters.clear();
10003 /* Write out only the nominal number of network adapters for this
10004 * chipset type. Since Machine::commit() hasn't been called there
10005 * may be extra NIC settings in the vector. */
10006 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10007 {
10008 settings::NetworkAdapter nic;
10009 nic.ulSlot = slot;
10010 /* paranoia check... must not be NULL, but must not crash either. */
10011 if (mNetworkAdapters[slot])
10012 {
10013 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10014 if (FAILED(rc)) throw rc;
10015
10016 data.llNetworkAdapters.push_back(nic);
10017 }
10018 }
10019
10020 /* Serial ports */
10021 data.llSerialPorts.clear();
10022 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10023 {
10024 settings::SerialPort s;
10025 s.ulSlot = slot;
10026 rc = mSerialPorts[slot]->i_saveSettings(s);
10027 if (FAILED(rc)) return rc;
10028
10029 data.llSerialPorts.push_back(s);
10030 }
10031
10032 /* Parallel ports */
10033 data.llParallelPorts.clear();
10034 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10035 {
10036 settings::ParallelPort p;
10037 p.ulSlot = slot;
10038 rc = mParallelPorts[slot]->i_saveSettings(p);
10039 if (FAILED(rc)) return rc;
10040
10041 data.llParallelPorts.push_back(p);
10042 }
10043
10044 /* Audio adapter */
10045 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10046 if (FAILED(rc)) return rc;
10047
10048 /* Shared folders */
10049 data.llSharedFolders.clear();
10050 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10051 it != mHWData->mSharedFolders.end();
10052 ++it)
10053 {
10054 SharedFolder *pSF = *it;
10055 AutoCaller sfCaller(pSF);
10056 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10057 settings::SharedFolder sf;
10058 sf.strName = pSF->i_getName();
10059 sf.strHostPath = pSF->i_getHostPath();
10060 sf.fWritable = !!pSF->i_isWritable();
10061 sf.fAutoMount = !!pSF->i_isAutoMounted();
10062
10063 data.llSharedFolders.push_back(sf);
10064 }
10065
10066 // clipboard
10067 data.clipboardMode = mHWData->mClipboardMode;
10068
10069 // drag'n'drop
10070 data.dndMode = mHWData->mDnDMode;
10071
10072 /* Guest */
10073 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10074
10075 // IO settings
10076 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10077 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10078
10079 /* BandwidthControl (required) */
10080 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10081 if (FAILED(rc)) throw rc;
10082
10083 /* Host PCI devices */
10084 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10085 it != mHWData->mPCIDeviceAssignments.end();
10086 ++it)
10087 {
10088 ComObjPtr<PCIDeviceAttachment> pda = *it;
10089 settings::HostPCIDeviceAttachment hpda;
10090
10091 rc = pda->i_saveSettings(hpda);
10092 if (FAILED(rc)) throw rc;
10093
10094 data.pciAttachments.push_back(hpda);
10095 }
10096
10097
10098 // guest properties
10099 data.llGuestProperties.clear();
10100#ifdef VBOX_WITH_GUEST_PROPS
10101 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10102 it != mHWData->mGuestProperties.end();
10103 ++it)
10104 {
10105 HWData::GuestProperty property = it->second;
10106
10107 /* Remove transient guest properties at shutdown unless we
10108 * are saving state */
10109 if ( ( mData->mMachineState == MachineState_PoweredOff
10110 || mData->mMachineState == MachineState_Aborted
10111 || mData->mMachineState == MachineState_Teleported)
10112 && ( property.mFlags & guestProp::TRANSIENT
10113 || property.mFlags & guestProp::TRANSRESET))
10114 continue;
10115 settings::GuestProperty prop;
10116 prop.strName = it->first;
10117 prop.strValue = property.strValue;
10118 prop.timestamp = property.mTimestamp;
10119 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10120 guestProp::writeFlags(property.mFlags, szFlags);
10121 prop.strFlags = szFlags;
10122
10123 data.llGuestProperties.push_back(prop);
10124 }
10125
10126 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10127 /* I presume this doesn't require a backup(). */
10128 mData->mGuestPropertiesModified = FALSE;
10129#endif /* VBOX_WITH_GUEST_PROPS defined */
10130
10131 *pDbg = mHWData->mDebugging;
10132 *pAutostart = mHWData->mAutostart;
10133
10134 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10135 }
10136 catch(std::bad_alloc &)
10137 {
10138 return E_OUTOFMEMORY;
10139 }
10140
10141 AssertComRC(rc);
10142 return rc;
10143}
10144
10145/**
10146 * Saves the storage controller configuration.
10147 *
10148 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10149 */
10150HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10151{
10152 data.llStorageControllers.clear();
10153
10154 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10155 it != mStorageControllers->end();
10156 ++it)
10157 {
10158 HRESULT rc;
10159 ComObjPtr<StorageController> pCtl = *it;
10160
10161 settings::StorageController ctl;
10162 ctl.strName = pCtl->i_getName();
10163 ctl.controllerType = pCtl->i_getControllerType();
10164 ctl.storageBus = pCtl->i_getStorageBus();
10165 ctl.ulInstance = pCtl->i_getInstance();
10166 ctl.fBootable = pCtl->i_getBootable();
10167
10168 /* Save the port count. */
10169 ULONG portCount;
10170 rc = pCtl->COMGETTER(PortCount)(&portCount);
10171 ComAssertComRCRet(rc, rc);
10172 ctl.ulPortCount = portCount;
10173
10174 /* Save fUseHostIOCache */
10175 BOOL fUseHostIOCache;
10176 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10177 ComAssertComRCRet(rc, rc);
10178 ctl.fUseHostIOCache = !!fUseHostIOCache;
10179
10180 /* Save IDE emulation settings. */
10181 if (ctl.controllerType == StorageControllerType_IntelAhci)
10182 {
10183 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10184 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10185 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10186 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10187 )
10188 ComAssertComRCRet(rc, rc);
10189 }
10190
10191 /* save the devices now. */
10192 rc = i_saveStorageDevices(pCtl, ctl);
10193 ComAssertComRCRet(rc, rc);
10194
10195 data.llStorageControllers.push_back(ctl);
10196 }
10197
10198 return S_OK;
10199}
10200
10201/**
10202 * Saves the hard disk configuration.
10203 */
10204HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10205 settings::StorageController &data)
10206{
10207 MediaData::AttachmentList atts;
10208
10209 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10210 if (FAILED(rc)) return rc;
10211
10212 data.llAttachedDevices.clear();
10213 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10214 it != atts.end();
10215 ++it)
10216 {
10217 settings::AttachedDevice dev;
10218 IMediumAttachment *iA = *it;
10219 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10220 Medium *pMedium = pAttach->i_getMedium();
10221
10222 dev.deviceType = pAttach->i_getType();
10223 dev.lPort = pAttach->i_getPort();
10224 dev.lDevice = pAttach->i_getDevice();
10225 dev.fPassThrough = pAttach->i_getPassthrough();
10226 dev.fHotPluggable = pAttach->i_getHotPluggable();
10227 if (pMedium)
10228 {
10229 if (pMedium->i_isHostDrive())
10230 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10231 else
10232 dev.uuid = pMedium->i_getId();
10233 dev.fTempEject = pAttach->i_getTempEject();
10234 dev.fNonRotational = pAttach->i_getNonRotational();
10235 dev.fDiscard = pAttach->i_getDiscard();
10236 }
10237
10238 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10239
10240 data.llAttachedDevices.push_back(dev);
10241 }
10242
10243 return S_OK;
10244}
10245
10246/**
10247 * Saves machine state settings as defined by aFlags
10248 * (SaveSTS_* values).
10249 *
10250 * @param aFlags Combination of SaveSTS_* flags.
10251 *
10252 * @note Locks objects for writing.
10253 */
10254HRESULT Machine::i_saveStateSettings(int aFlags)
10255{
10256 if (aFlags == 0)
10257 return S_OK;
10258
10259 AutoCaller autoCaller(this);
10260 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10261
10262 /* This object's write lock is also necessary to serialize file access
10263 * (prevent concurrent reads and writes) */
10264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10265
10266 HRESULT rc = S_OK;
10267
10268 Assert(mData->pMachineConfigFile);
10269
10270 try
10271 {
10272 if (aFlags & SaveSTS_CurStateModified)
10273 mData->pMachineConfigFile->fCurrentStateModified = true;
10274
10275 if (aFlags & SaveSTS_StateFilePath)
10276 {
10277 if (!mSSData->strStateFilePath.isEmpty())
10278 /* try to make the file name relative to the settings file dir */
10279 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10280 else
10281 mData->pMachineConfigFile->strStateFile.setNull();
10282 }
10283
10284 if (aFlags & SaveSTS_StateTimeStamp)
10285 {
10286 Assert( mData->mMachineState != MachineState_Aborted
10287 || mSSData->strStateFilePath.isEmpty());
10288
10289 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10290
10291 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10292//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10293 }
10294
10295 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10296 }
10297 catch (...)
10298 {
10299 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10300 }
10301
10302 return rc;
10303}
10304
10305/**
10306 * Ensures that the given medium is added to a media registry. If this machine
10307 * was created with 4.0 or later, then the machine registry is used. Otherwise
10308 * the global VirtualBox media registry is used.
10309 *
10310 * Caller must NOT hold machine lock, media tree or any medium locks!
10311 *
10312 * @param pMedium
10313 */
10314void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10315{
10316 /* Paranoia checks: do not hold machine or media tree locks. */
10317 AssertReturnVoid(!isWriteLockOnCurrentThread());
10318 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10319
10320 ComObjPtr<Medium> pBase;
10321 {
10322 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10323 pBase = pMedium->i_getBase();
10324 }
10325
10326 /* Paranoia checks: do not hold medium locks. */
10327 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10328 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10329
10330 // decide which medium registry to use now that the medium is attached:
10331 Guid uuid;
10332 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10333 // machine XML is VirtualBox 4.0 or higher:
10334 uuid = i_getId(); // machine UUID
10335 else
10336 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10337
10338 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10339 mParent->i_markRegistryModified(uuid);
10340
10341 /* For more complex hard disk structures it can happen that the base
10342 * medium isn't yet associated with any medium registry. Do that now. */
10343 if (pMedium != pBase)
10344 {
10345 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10346 mParent->i_markRegistryModified(uuid);
10347 }
10348}
10349
10350/**
10351 * Creates differencing hard disks for all normal hard disks attached to this
10352 * machine and a new set of attachments to refer to created disks.
10353 *
10354 * Used when taking a snapshot or when deleting the current state. Gets called
10355 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10356 *
10357 * This method assumes that mMediaData contains the original hard disk attachments
10358 * it needs to create diffs for. On success, these attachments will be replaced
10359 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10360 * called to delete created diffs which will also rollback mMediaData and restore
10361 * whatever was backed up before calling this method.
10362 *
10363 * Attachments with non-normal hard disks are left as is.
10364 *
10365 * If @a aOnline is @c false then the original hard disks that require implicit
10366 * diffs will be locked for reading. Otherwise it is assumed that they are
10367 * already locked for writing (when the VM was started). Note that in the latter
10368 * case it is responsibility of the caller to lock the newly created diffs for
10369 * writing if this method succeeds.
10370 *
10371 * @param aProgress Progress object to run (must contain at least as
10372 * many operations left as the number of hard disks
10373 * attached).
10374 * @param aOnline Whether the VM was online prior to this operation.
10375 *
10376 * @note The progress object is not marked as completed, neither on success nor
10377 * on failure. This is a responsibility of the caller.
10378 *
10379 * @note Locks this object and the media tree for writing.
10380 */
10381HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10382 ULONG aWeight,
10383 bool aOnline)
10384{
10385 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10386
10387 AutoCaller autoCaller(this);
10388 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10389
10390 AutoMultiWriteLock2 alock(this->lockHandle(),
10391 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10392
10393 /* must be in a protective state because we release the lock below */
10394 AssertReturn( mData->mMachineState == MachineState_Saving
10395 || mData->mMachineState == MachineState_LiveSnapshotting
10396 || mData->mMachineState == MachineState_RestoringSnapshot
10397 || mData->mMachineState == MachineState_DeletingSnapshot
10398 , E_FAIL);
10399
10400 HRESULT rc = S_OK;
10401
10402 // use appropriate locked media map (online or offline)
10403 MediumLockListMap lockedMediaOffline;
10404 MediumLockListMap *lockedMediaMap;
10405 if (aOnline)
10406 lockedMediaMap = &mData->mSession.mLockedMedia;
10407 else
10408 lockedMediaMap = &lockedMediaOffline;
10409
10410 try
10411 {
10412 if (!aOnline)
10413 {
10414 /* lock all attached hard disks early to detect "in use"
10415 * situations before creating actual diffs */
10416 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10417 it != mMediaData->mAttachments.end();
10418 ++it)
10419 {
10420 MediumAttachment* pAtt = *it;
10421 if (pAtt->i_getType() == DeviceType_HardDisk)
10422 {
10423 Medium* pMedium = pAtt->i_getMedium();
10424 Assert(pMedium);
10425
10426 MediumLockList *pMediumLockList(new MediumLockList());
10427 alock.release();
10428 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10429 false /* fMediumLockWrite */,
10430 NULL,
10431 *pMediumLockList);
10432 alock.acquire();
10433 if (FAILED(rc))
10434 {
10435 delete pMediumLockList;
10436 throw rc;
10437 }
10438 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10439 if (FAILED(rc))
10440 {
10441 throw setError(rc,
10442 tr("Collecting locking information for all attached media failed"));
10443 }
10444 }
10445 }
10446
10447 /* Now lock all media. If this fails, nothing is locked. */
10448 alock.release();
10449 rc = lockedMediaMap->Lock();
10450 alock.acquire();
10451 if (FAILED(rc))
10452 {
10453 throw setError(rc,
10454 tr("Locking of attached media failed"));
10455 }
10456 }
10457
10458 /* remember the current list (note that we don't use backup() since
10459 * mMediaData may be already backed up) */
10460 MediaData::AttachmentList atts = mMediaData->mAttachments;
10461
10462 /* start from scratch */
10463 mMediaData->mAttachments.clear();
10464
10465 /* go through remembered attachments and create diffs for normal hard
10466 * disks and attach them */
10467 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10468 it != atts.end();
10469 ++it)
10470 {
10471 MediumAttachment* pAtt = *it;
10472
10473 DeviceType_T devType = pAtt->i_getType();
10474 Medium* pMedium = pAtt->i_getMedium();
10475
10476 if ( devType != DeviceType_HardDisk
10477 || pMedium == NULL
10478 || pMedium->i_getType() != MediumType_Normal)
10479 {
10480 /* copy the attachment as is */
10481
10482 /** @todo the progress object created in Console::TakeSnaphot
10483 * only expects operations for hard disks. Later other
10484 * device types need to show up in the progress as well. */
10485 if (devType == DeviceType_HardDisk)
10486 {
10487 if (pMedium == NULL)
10488 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10489 aWeight); // weight
10490 else
10491 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10492 pMedium->i_getBase()->i_getName().c_str()).raw(),
10493 aWeight); // weight
10494 }
10495
10496 mMediaData->mAttachments.push_back(pAtt);
10497 continue;
10498 }
10499
10500 /* need a diff */
10501 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10502 pMedium->i_getBase()->i_getName().c_str()).raw(),
10503 aWeight); // weight
10504
10505 Utf8Str strFullSnapshotFolder;
10506 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10507
10508 ComObjPtr<Medium> diff;
10509 diff.createObject();
10510 // store the diff in the same registry as the parent
10511 // (this cannot fail here because we can't create implicit diffs for
10512 // unregistered images)
10513 Guid uuidRegistryParent;
10514 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10515 Assert(fInRegistry); NOREF(fInRegistry);
10516 rc = diff->init(mParent,
10517 pMedium->i_getPreferredDiffFormat(),
10518 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10519 uuidRegistryParent);
10520 if (FAILED(rc)) throw rc;
10521
10522 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10523 * the push_back? Looks like we're going to release medium with the
10524 * wrong kind of lock (general issue with if we fail anywhere at all)
10525 * and an orphaned VDI in the snapshots folder. */
10526
10527 /* update the appropriate lock list */
10528 MediumLockList *pMediumLockList;
10529 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10530 AssertComRCThrowRC(rc);
10531 if (aOnline)
10532 {
10533 alock.release();
10534 /* The currently attached medium will be read-only, change
10535 * the lock type to read. */
10536 rc = pMediumLockList->Update(pMedium, false);
10537 alock.acquire();
10538 AssertComRCThrowRC(rc);
10539 }
10540
10541 /* release the locks before the potentially lengthy operation */
10542 alock.release();
10543 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10544 pMediumLockList,
10545 NULL /* aProgress */,
10546 true /* aWait */);
10547 alock.acquire();
10548 if (FAILED(rc)) throw rc;
10549
10550 /* actual lock list update is done in Medium::commitMedia */
10551
10552 rc = diff->i_addBackReference(mData->mUuid);
10553 AssertComRCThrowRC(rc);
10554
10555 /* add a new attachment */
10556 ComObjPtr<MediumAttachment> attachment;
10557 attachment.createObject();
10558 rc = attachment->init(this,
10559 diff,
10560 pAtt->i_getControllerName(),
10561 pAtt->i_getPort(),
10562 pAtt->i_getDevice(),
10563 DeviceType_HardDisk,
10564 true /* aImplicit */,
10565 false /* aPassthrough */,
10566 false /* aTempEject */,
10567 pAtt->i_getNonRotational(),
10568 pAtt->i_getDiscard(),
10569 pAtt->i_getHotPluggable(),
10570 pAtt->i_getBandwidthGroup());
10571 if (FAILED(rc)) throw rc;
10572
10573 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10574 AssertComRCThrowRC(rc);
10575 mMediaData->mAttachments.push_back(attachment);
10576 }
10577 }
10578 catch (HRESULT aRC) { rc = aRC; }
10579
10580 /* unlock all hard disks we locked when there is no VM */
10581 if (!aOnline)
10582 {
10583 ErrorInfoKeeper eik;
10584
10585 HRESULT rc1 = lockedMediaMap->Clear();
10586 AssertComRC(rc1);
10587 }
10588
10589 return rc;
10590}
10591
10592/**
10593 * Deletes implicit differencing hard disks created either by
10594 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10595 *
10596 * Note that to delete hard disks created by #AttachDevice() this method is
10597 * called from #fixupMedia() when the changes are rolled back.
10598 *
10599 * @note Locks this object and the media tree for writing.
10600 */
10601HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10602{
10603 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10604
10605 AutoCaller autoCaller(this);
10606 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10607
10608 AutoMultiWriteLock2 alock(this->lockHandle(),
10609 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10610
10611 /* We absolutely must have backed up state. */
10612 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10613
10614 /* Check if there are any implicitly created diff images. */
10615 bool fImplicitDiffs = false;
10616 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10617 it != mMediaData->mAttachments.end();
10618 ++it)
10619 {
10620 const ComObjPtr<MediumAttachment> &pAtt = *it;
10621 if (pAtt->i_isImplicit())
10622 {
10623 fImplicitDiffs = true;
10624 break;
10625 }
10626 }
10627 /* If there is nothing to do, leave early. This saves lots of image locking
10628 * effort. It also avoids a MachineStateChanged event without real reason.
10629 * This is important e.g. when loading a VM config, because there should be
10630 * no events. Otherwise API clients can become thoroughly confused for
10631 * inaccessible VMs (the code for loading VM configs uses this method for
10632 * cleanup if the config makes no sense), as they take such events as an
10633 * indication that the VM is alive, and they would force the VM config to
10634 * be reread, leading to an endless loop. */
10635 if (!fImplicitDiffs)
10636 return S_OK;
10637
10638 HRESULT rc = S_OK;
10639 MachineState_T oldState = mData->mMachineState;
10640
10641 /* will release the lock before the potentially lengthy operation,
10642 * so protect with the special state (unless already protected) */
10643 if ( oldState != MachineState_Saving
10644 && oldState != MachineState_LiveSnapshotting
10645 && oldState != MachineState_RestoringSnapshot
10646 && oldState != MachineState_DeletingSnapshot
10647 && oldState != MachineState_DeletingSnapshotOnline
10648 && oldState != MachineState_DeletingSnapshotPaused
10649 )
10650 i_setMachineState(MachineState_SettingUp);
10651
10652 // use appropriate locked media map (online or offline)
10653 MediumLockListMap lockedMediaOffline;
10654 MediumLockListMap *lockedMediaMap;
10655 if (aOnline)
10656 lockedMediaMap = &mData->mSession.mLockedMedia;
10657 else
10658 lockedMediaMap = &lockedMediaOffline;
10659
10660 try
10661 {
10662 if (!aOnline)
10663 {
10664 /* lock all attached hard disks early to detect "in use"
10665 * situations before deleting actual diffs */
10666 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10667 it != mMediaData->mAttachments.end();
10668 ++it)
10669 {
10670 MediumAttachment* pAtt = *it;
10671 if (pAtt->i_getType() == DeviceType_HardDisk)
10672 {
10673 Medium* pMedium = pAtt->i_getMedium();
10674 Assert(pMedium);
10675
10676 MediumLockList *pMediumLockList(new MediumLockList());
10677 alock.release();
10678 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10679 false /* fMediumLockWrite */,
10680 NULL,
10681 *pMediumLockList);
10682 alock.acquire();
10683
10684 if (FAILED(rc))
10685 {
10686 delete pMediumLockList;
10687 throw rc;
10688 }
10689
10690 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10691 if (FAILED(rc))
10692 throw rc;
10693 }
10694 }
10695
10696 if (FAILED(rc))
10697 throw rc;
10698 } // end of offline
10699
10700 /* Lock lists are now up to date and include implicitly created media */
10701
10702 /* Go through remembered attachments and delete all implicitly created
10703 * diffs and fix up the attachment information */
10704 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10705 MediaData::AttachmentList implicitAtts;
10706 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10707 it != mMediaData->mAttachments.end();
10708 ++it)
10709 {
10710 ComObjPtr<MediumAttachment> pAtt = *it;
10711 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10712 if (pMedium.isNull())
10713 continue;
10714
10715 // Implicit attachments go on the list for deletion and back references are removed.
10716 if (pAtt->i_isImplicit())
10717 {
10718 /* Deassociate and mark for deletion */
10719 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10720 rc = pMedium->i_removeBackReference(mData->mUuid);
10721 if (FAILED(rc))
10722 throw rc;
10723 implicitAtts.push_back(pAtt);
10724 continue;
10725 }
10726
10727 /* Was this medium attached before? */
10728 if (!i_findAttachment(oldAtts, pMedium))
10729 {
10730 /* no: de-associate */
10731 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10732 rc = pMedium->i_removeBackReference(mData->mUuid);
10733 if (FAILED(rc))
10734 throw rc;
10735 continue;
10736 }
10737 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10738 }
10739
10740 /* If there are implicit attachments to delete, throw away the lock
10741 * map contents (which will unlock all media) since the medium
10742 * attachments will be rolled back. Below we need to completely
10743 * recreate the lock map anyway since it is infinitely complex to
10744 * do this incrementally (would need reconstructing each attachment
10745 * change, which would be extremely hairy). */
10746 if (implicitAtts.size() != 0)
10747 {
10748 ErrorInfoKeeper eik;
10749
10750 HRESULT rc1 = lockedMediaMap->Clear();
10751 AssertComRC(rc1);
10752 }
10753
10754 /* rollback hard disk changes */
10755 mMediaData.rollback();
10756
10757 MultiResult mrc(S_OK);
10758
10759 // Delete unused implicit diffs.
10760 if (implicitAtts.size() != 0)
10761 {
10762 alock.release();
10763
10764 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10765 {
10766 // Remove medium associated with this attachment.
10767 ComObjPtr<MediumAttachment> pAtt = *it;
10768 Assert(pAtt);
10769 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10770 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10771 Assert(pMedium);
10772
10773 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10774 // continue on delete failure, just collect error messages
10775 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10776 pMedium->i_getLocationFull().c_str() ));
10777 mrc = rc;
10778 }
10779
10780 alock.acquire();
10781
10782 /* if there is a VM recreate media lock map as mentioned above,
10783 * otherwise it is a waste of time and we leave things unlocked */
10784 if (aOnline)
10785 {
10786 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10787 /* must never be NULL, but better safe than sorry */
10788 if (!pMachine.isNull())
10789 {
10790 alock.release();
10791 rc = mData->mSession.mMachine->lockMedia();
10792 alock.acquire();
10793 if (FAILED(rc))
10794 throw rc;
10795 }
10796 }
10797 }
10798 }
10799 catch (HRESULT aRC) {rc = aRC;}
10800
10801 if (mData->mMachineState == MachineState_SettingUp)
10802 i_setMachineState(oldState);
10803
10804 /* unlock all hard disks we locked when there is no VM */
10805 if (!aOnline)
10806 {
10807 ErrorInfoKeeper eik;
10808
10809 HRESULT rc1 = lockedMediaMap->Clear();
10810 AssertComRC(rc1);
10811 }
10812
10813 return rc;
10814}
10815
10816
10817/**
10818 * Looks through the given list of media attachments for one with the given parameters
10819 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10820 * can be searched as well if needed.
10821 *
10822 * @param list
10823 * @param aControllerName
10824 * @param aControllerPort
10825 * @param aDevice
10826 * @return
10827 */
10828MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10829 IN_BSTR aControllerName,
10830 LONG aControllerPort,
10831 LONG aDevice)
10832{
10833 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10834 {
10835 MediumAttachment *pAttach = *it;
10836 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10837 return pAttach;
10838 }
10839
10840 return NULL;
10841}
10842
10843/**
10844 * Looks through the given list of media attachments for one with the given parameters
10845 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10846 * can be searched as well if needed.
10847 *
10848 * @param list
10849 * @param aControllerName
10850 * @param aControllerPort
10851 * @param aDevice
10852 * @return
10853 */
10854MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10855 ComObjPtr<Medium> pMedium)
10856{
10857 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10858 {
10859 MediumAttachment *pAttach = *it;
10860 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10861 if (pMediumThis == pMedium)
10862 return pAttach;
10863 }
10864
10865 return NULL;
10866}
10867
10868/**
10869 * Looks through the given list of media attachments for one with the given parameters
10870 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10871 * can be searched as well if needed.
10872 *
10873 * @param list
10874 * @param aControllerName
10875 * @param aControllerPort
10876 * @param aDevice
10877 * @return
10878 */
10879MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10880 Guid &id)
10881{
10882 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10883 {
10884 MediumAttachment *pAttach = *it;
10885 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10886 if (pMediumThis->i_getId() == id)
10887 return pAttach;
10888 }
10889
10890 return NULL;
10891}
10892
10893/**
10894 * Main implementation for Machine::DetachDevice. This also gets called
10895 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10896 *
10897 * @param pAttach Medium attachment to detach.
10898 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10899 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10900 * SnapshotMachine, and this must be its snapshot.
10901 * @return
10902 */
10903HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10904 AutoWriteLock &writeLock,
10905 Snapshot *pSnapshot)
10906{
10907 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10908 DeviceType_T mediumType = pAttach->i_getType();
10909
10910 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10911
10912 if (pAttach->i_isImplicit())
10913 {
10914 /* attempt to implicitly delete the implicitly created diff */
10915
10916 /// @todo move the implicit flag from MediumAttachment to Medium
10917 /// and forbid any hard disk operation when it is implicit. Or maybe
10918 /// a special media state for it to make it even more simple.
10919
10920 Assert(mMediaData.isBackedUp());
10921
10922 /* will release the lock before the potentially lengthy operation, so
10923 * protect with the special state */
10924 MachineState_T oldState = mData->mMachineState;
10925 i_setMachineState(MachineState_SettingUp);
10926
10927 writeLock.release();
10928
10929 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
10930 true /*aWait*/);
10931
10932 writeLock.acquire();
10933
10934 i_setMachineState(oldState);
10935
10936 if (FAILED(rc)) return rc;
10937 }
10938
10939 i_setModified(IsModified_Storage);
10940 mMediaData.backup();
10941 mMediaData->mAttachments.remove(pAttach);
10942
10943 if (!oldmedium.isNull())
10944 {
10945 // if this is from a snapshot, do not defer detachment to commitMedia()
10946 if (pSnapshot)
10947 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
10948 // else if non-hard disk media, do not defer detachment to commitMedia() either
10949 else if (mediumType != DeviceType_HardDisk)
10950 oldmedium->i_removeBackReference(mData->mUuid);
10951 }
10952
10953 return S_OK;
10954}
10955
10956/**
10957 * Goes thru all media of the given list and
10958 *
10959 * 1) calls i_detachDevice() on each of them for this machine and
10960 * 2) adds all Medium objects found in the process to the given list,
10961 * depending on cleanupMode.
10962 *
10963 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10964 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10965 * media to the list.
10966 *
10967 * This gets called from Machine::Unregister, both for the actual Machine and
10968 * the SnapshotMachine objects that might be found in the snapshots.
10969 *
10970 * Requires caller and locking. The machine lock must be passed in because it
10971 * will be passed on to i_detachDevice which needs it for temporary unlocking.
10972 *
10973 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
10974 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10975 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
10976 * Full, then all media get added;
10977 * otherwise no media get added.
10978 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10979 * @return
10980 */
10981HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
10982 Snapshot *pSnapshot,
10983 CleanupMode_T cleanupMode,
10984 MediaList &llMedia)
10985{
10986 Assert(isWriteLockOnCurrentThread());
10987
10988 HRESULT rc;
10989
10990 // make a temporary list because i_detachDevice invalidates iterators into
10991 // mMediaData->mAttachments
10992 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10993
10994 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
10995 {
10996 ComObjPtr<MediumAttachment> &pAttach = *it;
10997 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
10998
10999 if (!pMedium.isNull())
11000 {
11001 AutoCaller mac(pMedium);
11002 if (FAILED(mac.rc())) return mac.rc();
11003 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11004 DeviceType_T devType = pMedium->i_getDeviceType();
11005 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11006 && devType == DeviceType_HardDisk)
11007 || (cleanupMode == CleanupMode_Full)
11008 )
11009 {
11010 llMedia.push_back(pMedium);
11011 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11012 /*
11013 * Search for medias which are not attached to any machine, but
11014 * in the chain to an attached disk. Mediums are only consided
11015 * if they are:
11016 * - have only one child
11017 * - no references to any machines
11018 * - are of normal medium type
11019 */
11020 while (!pParent.isNull())
11021 {
11022 AutoCaller mac1(pParent);
11023 if (FAILED(mac1.rc())) return mac1.rc();
11024 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11025 if (pParent->i_getChildren().size() == 1)
11026 {
11027 if ( pParent->i_getMachineBackRefCount() == 0
11028 && pParent->i_getType() == MediumType_Normal
11029 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11030 llMedia.push_back(pParent);
11031 }
11032 else
11033 break;
11034 pParent = pParent->i_getParent();
11035 }
11036 }
11037 }
11038
11039 // real machine: then we need to use the proper method
11040 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11041
11042 if (FAILED(rc))
11043 return rc;
11044 }
11045
11046 return S_OK;
11047}
11048
11049/**
11050 * Perform deferred hard disk detachments.
11051 *
11052 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11053 * backed up).
11054 *
11055 * If @a aOnline is @c true then this method will also unlock the old hard disks
11056 * for which the new implicit diffs were created and will lock these new diffs for
11057 * writing.
11058 *
11059 * @param aOnline Whether the VM was online prior to this operation.
11060 *
11061 * @note Locks this object for writing!
11062 */
11063void Machine::i_commitMedia(bool aOnline /*= false*/)
11064{
11065 AutoCaller autoCaller(this);
11066 AssertComRCReturnVoid(autoCaller.rc());
11067
11068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11069
11070 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11071
11072 HRESULT rc = S_OK;
11073
11074 /* no attach/detach operations -- nothing to do */
11075 if (!mMediaData.isBackedUp())
11076 return;
11077
11078 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11079 bool fMediaNeedsLocking = false;
11080
11081 /* enumerate new attachments */
11082 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11083 it != mMediaData->mAttachments.end();
11084 ++it)
11085 {
11086 MediumAttachment *pAttach = *it;
11087
11088 pAttach->i_commit();
11089
11090 Medium* pMedium = pAttach->i_getMedium();
11091 bool fImplicit = pAttach->i_isImplicit();
11092
11093 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11094 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11095 fImplicit));
11096
11097 /** @todo convert all this Machine-based voodoo to MediumAttachment
11098 * based commit logic. */
11099 if (fImplicit)
11100 {
11101 /* convert implicit attachment to normal */
11102 pAttach->i_setImplicit(false);
11103
11104 if ( aOnline
11105 && pMedium
11106 && pAttach->i_getType() == DeviceType_HardDisk
11107 )
11108 {
11109 ComObjPtr<Medium> parent = pMedium->i_getParent();
11110 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11111
11112 /* update the appropriate lock list */
11113 MediumLockList *pMediumLockList;
11114 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11115 AssertComRC(rc);
11116 if (pMediumLockList)
11117 {
11118 /* unlock if there's a need to change the locking */
11119 if (!fMediaNeedsLocking)
11120 {
11121 rc = mData->mSession.mLockedMedia.Unlock();
11122 AssertComRC(rc);
11123 fMediaNeedsLocking = true;
11124 }
11125 rc = pMediumLockList->Update(parent, false);
11126 AssertComRC(rc);
11127 rc = pMediumLockList->Append(pMedium, true);
11128 AssertComRC(rc);
11129 }
11130 }
11131
11132 continue;
11133 }
11134
11135 if (pMedium)
11136 {
11137 /* was this medium attached before? */
11138 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11139 {
11140 MediumAttachment *pOldAttach = *oldIt;
11141 if (pOldAttach->i_getMedium() == pMedium)
11142 {
11143 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11144
11145 /* yes: remove from old to avoid de-association */
11146 oldAtts.erase(oldIt);
11147 break;
11148 }
11149 }
11150 }
11151 }
11152
11153 /* enumerate remaining old attachments and de-associate from the
11154 * current machine state */
11155 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11156 {
11157 MediumAttachment *pAttach = *it;
11158 Medium* pMedium = pAttach->i_getMedium();
11159
11160 /* Detach only hard disks, since DVD/floppy media is detached
11161 * instantly in MountMedium. */
11162 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11163 {
11164 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11165
11166 /* now de-associate from the current machine state */
11167 rc = pMedium->i_removeBackReference(mData->mUuid);
11168 AssertComRC(rc);
11169
11170 if (aOnline)
11171 {
11172 /* unlock since medium is not used anymore */
11173 MediumLockList *pMediumLockList;
11174 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11175 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11176 {
11177 /* this happens for online snapshots, there the attachment
11178 * is changing, but only to a diff image created under
11179 * the old one, so there is no separate lock list */
11180 Assert(!pMediumLockList);
11181 }
11182 else
11183 {
11184 AssertComRC(rc);
11185 if (pMediumLockList)
11186 {
11187 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11188 AssertComRC(rc);
11189 }
11190 }
11191 }
11192 }
11193 }
11194
11195 /* take media locks again so that the locking state is consistent */
11196 if (fMediaNeedsLocking)
11197 {
11198 Assert(aOnline);
11199 rc = mData->mSession.mLockedMedia.Lock();
11200 AssertComRC(rc);
11201 }
11202
11203 /* commit the hard disk changes */
11204 mMediaData.commit();
11205
11206 if (i_isSessionMachine())
11207 {
11208 /*
11209 * Update the parent machine to point to the new owner.
11210 * This is necessary because the stored parent will point to the
11211 * session machine otherwise and cause crashes or errors later
11212 * when the session machine gets invalid.
11213 */
11214 /** @todo Change the MediumAttachment class to behave like any other
11215 * class in this regard by creating peer MediumAttachment
11216 * objects for session machines and share the data with the peer
11217 * machine.
11218 */
11219 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11220 it != mMediaData->mAttachments.end();
11221 ++it)
11222 (*it)->i_updateParentMachine(mPeer);
11223
11224 /* attach new data to the primary machine and reshare it */
11225 mPeer->mMediaData.attach(mMediaData);
11226 }
11227
11228 return;
11229}
11230
11231/**
11232 * Perform deferred deletion of implicitly created diffs.
11233 *
11234 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11235 * backed up).
11236 *
11237 * @note Locks this object for writing!
11238 */
11239void Machine::i_rollbackMedia()
11240{
11241 AutoCaller autoCaller(this);
11242 AssertComRCReturnVoid(autoCaller.rc());
11243
11244 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11245 LogFlowThisFunc(("Entering rollbackMedia\n"));
11246
11247 HRESULT rc = S_OK;
11248
11249 /* no attach/detach operations -- nothing to do */
11250 if (!mMediaData.isBackedUp())
11251 return;
11252
11253 /* enumerate new attachments */
11254 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11255 it != mMediaData->mAttachments.end();
11256 ++it)
11257 {
11258 MediumAttachment *pAttach = *it;
11259 /* Fix up the backrefs for DVD/floppy media. */
11260 if (pAttach->i_getType() != DeviceType_HardDisk)
11261 {
11262 Medium* pMedium = pAttach->i_getMedium();
11263 if (pMedium)
11264 {
11265 rc = pMedium->i_removeBackReference(mData->mUuid);
11266 AssertComRC(rc);
11267 }
11268 }
11269
11270 (*it)->i_rollback();
11271
11272 pAttach = *it;
11273 /* Fix up the backrefs for DVD/floppy media. */
11274 if (pAttach->i_getType() != DeviceType_HardDisk)
11275 {
11276 Medium* pMedium = pAttach->i_getMedium();
11277 if (pMedium)
11278 {
11279 rc = pMedium->i_addBackReference(mData->mUuid);
11280 AssertComRC(rc);
11281 }
11282 }
11283 }
11284
11285 /** @todo convert all this Machine-based voodoo to MediumAttachment
11286 * based rollback logic. */
11287 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11288
11289 return;
11290}
11291
11292/**
11293 * Returns true if the settings file is located in the directory named exactly
11294 * as the machine; this means, among other things, that the machine directory
11295 * should be auto-renamed.
11296 *
11297 * @param aSettingsDir if not NULL, the full machine settings file directory
11298 * name will be assigned there.
11299 *
11300 * @note Doesn't lock anything.
11301 * @note Not thread safe (must be called from this object's lock).
11302 */
11303bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11304{
11305 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11306 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11307 if (aSettingsDir)
11308 *aSettingsDir = strMachineDirName;
11309 strMachineDirName.stripPath(); // vmname
11310 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11311 strConfigFileOnly.stripPath() // vmname.vbox
11312 .stripSuffix(); // vmname
11313 /** @todo hack, make somehow use of ComposeMachineFilename */
11314 if (mUserData->s.fDirectoryIncludesUUID)
11315 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11316
11317 AssertReturn(!strMachineDirName.isEmpty(), false);
11318 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11319
11320 return strMachineDirName == strConfigFileOnly;
11321}
11322
11323/**
11324 * Discards all changes to machine settings.
11325 *
11326 * @param aNotify Whether to notify the direct session about changes or not.
11327 *
11328 * @note Locks objects for writing!
11329 */
11330void Machine::i_rollback(bool aNotify)
11331{
11332 AutoCaller autoCaller(this);
11333 AssertComRCReturn(autoCaller.rc(), (void)0);
11334
11335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11336
11337 if (!mStorageControllers.isNull())
11338 {
11339 if (mStorageControllers.isBackedUp())
11340 {
11341 /* unitialize all new devices (absent in the backed up list). */
11342 StorageControllerList::const_iterator it = mStorageControllers->begin();
11343 StorageControllerList *backedList = mStorageControllers.backedUpData();
11344 while (it != mStorageControllers->end())
11345 {
11346 if ( std::find(backedList->begin(), backedList->end(), *it)
11347 == backedList->end()
11348 )
11349 {
11350 (*it)->uninit();
11351 }
11352 ++it;
11353 }
11354
11355 /* restore the list */
11356 mStorageControllers.rollback();
11357 }
11358
11359 /* rollback any changes to devices after restoring the list */
11360 if (mData->flModifications & IsModified_Storage)
11361 {
11362 StorageControllerList::const_iterator it = mStorageControllers->begin();
11363 while (it != mStorageControllers->end())
11364 {
11365 (*it)->i_rollback();
11366 ++it;
11367 }
11368 }
11369 }
11370
11371 if (!mUSBControllers.isNull())
11372 {
11373 if (mUSBControllers.isBackedUp())
11374 {
11375 /* unitialize all new devices (absent in the backed up list). */
11376 USBControllerList::const_iterator it = mUSBControllers->begin();
11377 USBControllerList *backedList = mUSBControllers.backedUpData();
11378 while (it != mUSBControllers->end())
11379 {
11380 if ( std::find(backedList->begin(), backedList->end(), *it)
11381 == backedList->end()
11382 )
11383 {
11384 (*it)->uninit();
11385 }
11386 ++it;
11387 }
11388
11389 /* restore the list */
11390 mUSBControllers.rollback();
11391 }
11392
11393 /* rollback any changes to devices after restoring the list */
11394 if (mData->flModifications & IsModified_USB)
11395 {
11396 USBControllerList::const_iterator it = mUSBControllers->begin();
11397 while (it != mUSBControllers->end())
11398 {
11399 (*it)->i_rollback();
11400 ++it;
11401 }
11402 }
11403 }
11404
11405 mUserData.rollback();
11406
11407 mHWData.rollback();
11408
11409 if (mData->flModifications & IsModified_Storage)
11410 i_rollbackMedia();
11411
11412 if (mBIOSSettings)
11413 mBIOSSettings->i_rollback();
11414
11415 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11416 mVRDEServer->i_rollback();
11417
11418 if (mAudioAdapter)
11419 mAudioAdapter->i_rollback();
11420
11421 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11422 mUSBDeviceFilters->i_rollback();
11423
11424 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11425 mBandwidthControl->i_rollback();
11426
11427 if (!mHWData.isNull())
11428 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11429 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11430 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11431 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11432
11433 if (mData->flModifications & IsModified_NetworkAdapters)
11434 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11435 if ( mNetworkAdapters[slot]
11436 && mNetworkAdapters[slot]->i_isModified())
11437 {
11438 mNetworkAdapters[slot]->i_rollback();
11439 networkAdapters[slot] = mNetworkAdapters[slot];
11440 }
11441
11442 if (mData->flModifications & IsModified_SerialPorts)
11443 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11444 if ( mSerialPorts[slot]
11445 && mSerialPorts[slot]->i_isModified())
11446 {
11447 mSerialPorts[slot]->i_rollback();
11448 serialPorts[slot] = mSerialPorts[slot];
11449 }
11450
11451 if (mData->flModifications & IsModified_ParallelPorts)
11452 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11453 if ( mParallelPorts[slot]
11454 && mParallelPorts[slot]->i_isModified())
11455 {
11456 mParallelPorts[slot]->i_rollback();
11457 parallelPorts[slot] = mParallelPorts[slot];
11458 }
11459
11460 if (aNotify)
11461 {
11462 /* inform the direct session about changes */
11463
11464 ComObjPtr<Machine> that = this;
11465 uint32_t flModifications = mData->flModifications;
11466 alock.release();
11467
11468 if (flModifications & IsModified_SharedFolders)
11469 that->i_onSharedFolderChange();
11470
11471 if (flModifications & IsModified_VRDEServer)
11472 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11473 if (flModifications & IsModified_USB)
11474 that->i_onUSBControllerChange();
11475
11476 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11477 if (networkAdapters[slot])
11478 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11479 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11480 if (serialPorts[slot])
11481 that->i_onSerialPortChange(serialPorts[slot]);
11482 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11483 if (parallelPorts[slot])
11484 that->i_onParallelPortChange(parallelPorts[slot]);
11485
11486 if (flModifications & IsModified_Storage)
11487 that->i_onStorageControllerChange();
11488
11489#if 0
11490 if (flModifications & IsModified_BandwidthControl)
11491 that->onBandwidthControlChange();
11492#endif
11493 }
11494}
11495
11496/**
11497 * Commits all the changes to machine settings.
11498 *
11499 * Note that this operation is supposed to never fail.
11500 *
11501 * @note Locks this object and children for writing.
11502 */
11503void Machine::i_commit()
11504{
11505 AutoCaller autoCaller(this);
11506 AssertComRCReturnVoid(autoCaller.rc());
11507
11508 AutoCaller peerCaller(mPeer);
11509 AssertComRCReturnVoid(peerCaller.rc());
11510
11511 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11512
11513 /*
11514 * use safe commit to ensure Snapshot machines (that share mUserData)
11515 * will still refer to a valid memory location
11516 */
11517 mUserData.commitCopy();
11518
11519 mHWData.commit();
11520
11521 if (mMediaData.isBackedUp())
11522 i_commitMedia(Global::IsOnline(mData->mMachineState));
11523
11524 mBIOSSettings->i_commit();
11525 mVRDEServer->i_commit();
11526 mAudioAdapter->i_commit();
11527 mUSBDeviceFilters->i_commit();
11528 mBandwidthControl->i_commit();
11529
11530 /* Since mNetworkAdapters is a list which might have been changed (resized)
11531 * without using the Backupable<> template we need to handle the copying
11532 * of the list entries manually, including the creation of peers for the
11533 * new objects. */
11534 bool commitNetworkAdapters = false;
11535 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11536 if (mPeer)
11537 {
11538 /* commit everything, even the ones which will go away */
11539 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11540 mNetworkAdapters[slot]->i_commit();
11541 /* copy over the new entries, creating a peer and uninit the original */
11542 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11543 for (size_t slot = 0; slot < newSize; slot++)
11544 {
11545 /* look if this adapter has a peer device */
11546 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11547 if (!peer)
11548 {
11549 /* no peer means the adapter is a newly created one;
11550 * create a peer owning data this data share it with */
11551 peer.createObject();
11552 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11553 }
11554 mPeer->mNetworkAdapters[slot] = peer;
11555 }
11556 /* uninit any no longer needed network adapters */
11557 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11558 mNetworkAdapters[slot]->uninit();
11559 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11560 {
11561 if (mPeer->mNetworkAdapters[slot])
11562 mPeer->mNetworkAdapters[slot]->uninit();
11563 }
11564 /* Keep the original network adapter count until this point, so that
11565 * discarding a chipset type change will not lose settings. */
11566 mNetworkAdapters.resize(newSize);
11567 mPeer->mNetworkAdapters.resize(newSize);
11568 }
11569 else
11570 {
11571 /* we have no peer (our parent is the newly created machine);
11572 * just commit changes to the network adapters */
11573 commitNetworkAdapters = true;
11574 }
11575 if (commitNetworkAdapters)
11576 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11577 mNetworkAdapters[slot]->i_commit();
11578
11579 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11580 mSerialPorts[slot]->i_commit();
11581 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11582 mParallelPorts[slot]->i_commit();
11583
11584 bool commitStorageControllers = false;
11585
11586 if (mStorageControllers.isBackedUp())
11587 {
11588 mStorageControllers.commit();
11589
11590 if (mPeer)
11591 {
11592 /* Commit all changes to new controllers (this will reshare data with
11593 * peers for those who have peers) */
11594 StorageControllerList *newList = new StorageControllerList();
11595 StorageControllerList::const_iterator it = mStorageControllers->begin();
11596 while (it != mStorageControllers->end())
11597 {
11598 (*it)->i_commit();
11599
11600 /* look if this controller has a peer device */
11601 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11602 if (!peer)
11603 {
11604 /* no peer means the device is a newly created one;
11605 * create a peer owning data this device share it with */
11606 peer.createObject();
11607 peer->init(mPeer, *it, true /* aReshare */);
11608 }
11609 else
11610 {
11611 /* remove peer from the old list */
11612 mPeer->mStorageControllers->remove(peer);
11613 }
11614 /* and add it to the new list */
11615 newList->push_back(peer);
11616
11617 ++it;
11618 }
11619
11620 /* uninit old peer's controllers that are left */
11621 it = mPeer->mStorageControllers->begin();
11622 while (it != mPeer->mStorageControllers->end())
11623 {
11624 (*it)->uninit();
11625 ++it;
11626 }
11627
11628 /* attach new list of controllers to our peer */
11629 mPeer->mStorageControllers.attach(newList);
11630 }
11631 else
11632 {
11633 /* we have no peer (our parent is the newly created machine);
11634 * just commit changes to devices */
11635 commitStorageControllers = true;
11636 }
11637 }
11638 else
11639 {
11640 /* the list of controllers itself is not changed,
11641 * just commit changes to controllers themselves */
11642 commitStorageControllers = true;
11643 }
11644
11645 if (commitStorageControllers)
11646 {
11647 StorageControllerList::const_iterator it = mStorageControllers->begin();
11648 while (it != mStorageControllers->end())
11649 {
11650 (*it)->i_commit();
11651 ++it;
11652 }
11653 }
11654
11655 bool commitUSBControllers = false;
11656
11657 if (mUSBControllers.isBackedUp())
11658 {
11659 mUSBControllers.commit();
11660
11661 if (mPeer)
11662 {
11663 /* Commit all changes to new controllers (this will reshare data with
11664 * peers for those who have peers) */
11665 USBControllerList *newList = new USBControllerList();
11666 USBControllerList::const_iterator it = mUSBControllers->begin();
11667 while (it != mUSBControllers->end())
11668 {
11669 (*it)->i_commit();
11670
11671 /* look if this controller has a peer device */
11672 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11673 if (!peer)
11674 {
11675 /* no peer means the device is a newly created one;
11676 * create a peer owning data this device share it with */
11677 peer.createObject();
11678 peer->init(mPeer, *it, true /* aReshare */);
11679 }
11680 else
11681 {
11682 /* remove peer from the old list */
11683 mPeer->mUSBControllers->remove(peer);
11684 }
11685 /* and add it to the new list */
11686 newList->push_back(peer);
11687
11688 ++it;
11689 }
11690
11691 /* uninit old peer's controllers that are left */
11692 it = mPeer->mUSBControllers->begin();
11693 while (it != mPeer->mUSBControllers->end())
11694 {
11695 (*it)->uninit();
11696 ++it;
11697 }
11698
11699 /* attach new list of controllers to our peer */
11700 mPeer->mUSBControllers.attach(newList);
11701 }
11702 else
11703 {
11704 /* we have no peer (our parent is the newly created machine);
11705 * just commit changes to devices */
11706 commitUSBControllers = true;
11707 }
11708 }
11709 else
11710 {
11711 /* the list of controllers itself is not changed,
11712 * just commit changes to controllers themselves */
11713 commitUSBControllers = true;
11714 }
11715
11716 if (commitUSBControllers)
11717 {
11718 USBControllerList::const_iterator it = mUSBControllers->begin();
11719 while (it != mUSBControllers->end())
11720 {
11721 (*it)->i_commit();
11722 ++it;
11723 }
11724 }
11725
11726 if (i_isSessionMachine())
11727 {
11728 /* attach new data to the primary machine and reshare it */
11729 mPeer->mUserData.attach(mUserData);
11730 mPeer->mHWData.attach(mHWData);
11731 /* mMediaData is reshared by fixupMedia */
11732 // mPeer->mMediaData.attach(mMediaData);
11733 Assert(mPeer->mMediaData.data() == mMediaData.data());
11734 }
11735}
11736
11737/**
11738 * Copies all the hardware data from the given machine.
11739 *
11740 * Currently, only called when the VM is being restored from a snapshot. In
11741 * particular, this implies that the VM is not running during this method's
11742 * call.
11743 *
11744 * @note This method must be called from under this object's lock.
11745 *
11746 * @note This method doesn't call #commit(), so all data remains backed up and
11747 * unsaved.
11748 */
11749void Machine::i_copyFrom(Machine *aThat)
11750{
11751 AssertReturnVoid(!i_isSnapshotMachine());
11752 AssertReturnVoid(aThat->i_isSnapshotMachine());
11753
11754 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11755
11756 mHWData.assignCopy(aThat->mHWData);
11757
11758 // create copies of all shared folders (mHWData after attaching a copy
11759 // contains just references to original objects)
11760 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11761 it != mHWData->mSharedFolders.end();
11762 ++it)
11763 {
11764 ComObjPtr<SharedFolder> folder;
11765 folder.createObject();
11766 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11767 AssertComRC(rc);
11768 *it = folder;
11769 }
11770
11771 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11772 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11773 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11774 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11775 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11776
11777 /* create private copies of all controllers */
11778 mStorageControllers.backup();
11779 mStorageControllers->clear();
11780 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11781 it != aThat->mStorageControllers->end();
11782 ++it)
11783 {
11784 ComObjPtr<StorageController> ctrl;
11785 ctrl.createObject();
11786 ctrl->initCopy(this, *it);
11787 mStorageControllers->push_back(ctrl);
11788 }
11789
11790 /* create private copies of all USB controllers */
11791 mUSBControllers.backup();
11792 mUSBControllers->clear();
11793 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11794 it != aThat->mUSBControllers->end();
11795 ++it)
11796 {
11797 ComObjPtr<USBController> ctrl;
11798 ctrl.createObject();
11799 ctrl->initCopy(this, *it);
11800 mUSBControllers->push_back(ctrl);
11801 }
11802
11803 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11804 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11805 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11806 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11807 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11808 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11809 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11810}
11811
11812/**
11813 * Returns whether the given storage controller is hotplug capable.
11814 *
11815 * @returns true if the controller supports hotplugging
11816 * false otherwise.
11817 * @param enmCtrlType The controller type to check for.
11818 */
11819bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11820{
11821 ComPtr<ISystemProperties> systemProperties;
11822 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11823 if (FAILED(rc))
11824 return false;
11825
11826 BOOL aHotplugCapable = FALSE;
11827 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11828
11829 return RT_BOOL(aHotplugCapable);
11830}
11831
11832#ifdef VBOX_WITH_RESOURCE_USAGE_API
11833
11834void Machine::i_getDiskList(MediaList &list)
11835{
11836 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11837 it != mMediaData->mAttachments.end();
11838 ++it)
11839 {
11840 MediumAttachment* pAttach = *it;
11841 /* just in case */
11842 AssertStmt(pAttach, continue);
11843
11844 AutoCaller localAutoCallerA(pAttach);
11845 if (FAILED(localAutoCallerA.rc())) continue;
11846
11847 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11848
11849 if (pAttach->i_getType() == DeviceType_HardDisk)
11850 list.push_back(pAttach->i_getMedium());
11851 }
11852}
11853
11854void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11855{
11856 AssertReturnVoid(isWriteLockOnCurrentThread());
11857 AssertPtrReturnVoid(aCollector);
11858
11859 pm::CollectorHAL *hal = aCollector->getHAL();
11860 /* Create sub metrics */
11861 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11862 "Percentage of processor time spent in user mode by the VM process.");
11863 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11864 "Percentage of processor time spent in kernel mode by the VM process.");
11865 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11866 "Size of resident portion of VM process in memory.");
11867 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11868 "Actual size of all VM disks combined.");
11869 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11870 "Network receive rate.");
11871 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11872 "Network transmit rate.");
11873 /* Create and register base metrics */
11874 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11875 cpuLoadUser, cpuLoadKernel);
11876 aCollector->registerBaseMetric(cpuLoad);
11877 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11878 ramUsageUsed);
11879 aCollector->registerBaseMetric(ramUsage);
11880 MediaList disks;
11881 i_getDiskList(disks);
11882 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11883 diskUsageUsed);
11884 aCollector->registerBaseMetric(diskUsage);
11885
11886 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11887 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11888 new pm::AggregateAvg()));
11889 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11890 new pm::AggregateMin()));
11891 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11892 new pm::AggregateMax()));
11893 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11894 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11895 new pm::AggregateAvg()));
11896 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11897 new pm::AggregateMin()));
11898 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11899 new pm::AggregateMax()));
11900
11901 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11902 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11903 new pm::AggregateAvg()));
11904 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11905 new pm::AggregateMin()));
11906 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11907 new pm::AggregateMax()));
11908
11909 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11910 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11911 new pm::AggregateAvg()));
11912 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11913 new pm::AggregateMin()));
11914 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11915 new pm::AggregateMax()));
11916
11917
11918 /* Guest metrics collector */
11919 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11920 aCollector->registerGuest(mCollectorGuest);
11921 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11922 this, __PRETTY_FUNCTION__, mCollectorGuest));
11923
11924 /* Create sub metrics */
11925 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11926 "Percentage of processor time spent in user mode as seen by the guest.");
11927 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11928 "Percentage of processor time spent in kernel mode as seen by the guest.");
11929 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11930 "Percentage of processor time spent idling as seen by the guest.");
11931
11932 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11933 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11934 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11935 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11936 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11937 pm::SubMetric *guestMemCache = new pm::SubMetric(
11938 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11939
11940 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
11941 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11942
11943 /* Create and register base metrics */
11944 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11945 machineNetRx, machineNetTx);
11946 aCollector->registerBaseMetric(machineNetRate);
11947
11948 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11949 guestLoadUser, guestLoadKernel, guestLoadIdle);
11950 aCollector->registerBaseMetric(guestCpuLoad);
11951
11952 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11953 guestMemTotal, guestMemFree,
11954 guestMemBalloon, guestMemShared,
11955 guestMemCache, guestPagedTotal);
11956 aCollector->registerBaseMetric(guestCpuMem);
11957
11958 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11959 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11960 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11961 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11962
11963 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11964 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11965 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11966 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11967
11968 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11969 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11970 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11971 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11972
11973 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11974 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11975 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11976 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11977
11978 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11979 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11980 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11981 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11982
11983 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11984 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11985 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11986 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11987
11988 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11989 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11990 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11991 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11992
11993 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11994 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11995 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11996 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11997
11998 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11999 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12000 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12001 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12002
12003 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12004 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12005 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12006 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12007
12008 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12009 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12010 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12011 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12012}
12013
12014void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12015{
12016 AssertReturnVoid(isWriteLockOnCurrentThread());
12017
12018 if (aCollector)
12019 {
12020 aCollector->unregisterMetricsFor(aMachine);
12021 aCollector->unregisterBaseMetricsFor(aMachine);
12022 }
12023}
12024
12025#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12026
12027
12028////////////////////////////////////////////////////////////////////////////////
12029
12030DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12031
12032HRESULT SessionMachine::FinalConstruct()
12033{
12034 LogFlowThisFunc(("\n"));
12035
12036 mClientToken = NULL;
12037
12038 return BaseFinalConstruct();
12039}
12040
12041void SessionMachine::FinalRelease()
12042{
12043 LogFlowThisFunc(("\n"));
12044
12045 Assert(!mClientToken);
12046 /* paranoia, should not hang around any more */
12047 if (mClientToken)
12048 {
12049 delete mClientToken;
12050 mClientToken = NULL;
12051 }
12052
12053 uninit(Uninit::Unexpected);
12054
12055 BaseFinalRelease();
12056}
12057
12058/**
12059 * @note Must be called only by Machine::LockMachine() from its own write lock.
12060 */
12061HRESULT SessionMachine::init(Machine *aMachine)
12062{
12063 LogFlowThisFuncEnter();
12064 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12065
12066 AssertReturn(aMachine, E_INVALIDARG);
12067
12068 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12069
12070 /* Enclose the state transition NotReady->InInit->Ready */
12071 AutoInitSpan autoInitSpan(this);
12072 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12073
12074 HRESULT rc = S_OK;
12075
12076 /* create the machine client token */
12077 try
12078 {
12079 mClientToken = new ClientToken(aMachine, this);
12080 if (!mClientToken->isReady())
12081 {
12082 delete mClientToken;
12083 mClientToken = NULL;
12084 rc = E_FAIL;
12085 }
12086 }
12087 catch (std::bad_alloc &)
12088 {
12089 rc = E_OUTOFMEMORY;
12090 }
12091 if (FAILED(rc))
12092 return rc;
12093
12094 /* memorize the peer Machine */
12095 unconst(mPeer) = aMachine;
12096 /* share the parent pointer */
12097 unconst(mParent) = aMachine->mParent;
12098
12099 /* take the pointers to data to share */
12100 mData.share(aMachine->mData);
12101 mSSData.share(aMachine->mSSData);
12102
12103 mUserData.share(aMachine->mUserData);
12104 mHWData.share(aMachine->mHWData);
12105 mMediaData.share(aMachine->mMediaData);
12106
12107 mStorageControllers.allocate();
12108 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12109 it != aMachine->mStorageControllers->end();
12110 ++it)
12111 {
12112 ComObjPtr<StorageController> ctl;
12113 ctl.createObject();
12114 ctl->init(this, *it);
12115 mStorageControllers->push_back(ctl);
12116 }
12117
12118 mUSBControllers.allocate();
12119 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12120 it != aMachine->mUSBControllers->end();
12121 ++it)
12122 {
12123 ComObjPtr<USBController> ctl;
12124 ctl.createObject();
12125 ctl->init(this, *it);
12126 mUSBControllers->push_back(ctl);
12127 }
12128
12129 unconst(mBIOSSettings).createObject();
12130 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12131 /* create another VRDEServer object that will be mutable */
12132 unconst(mVRDEServer).createObject();
12133 mVRDEServer->init(this, aMachine->mVRDEServer);
12134 /* create another audio adapter object that will be mutable */
12135 unconst(mAudioAdapter).createObject();
12136 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12137 /* create a list of serial ports that will be mutable */
12138 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12139 {
12140 unconst(mSerialPorts[slot]).createObject();
12141 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12142 }
12143 /* create a list of parallel ports that will be mutable */
12144 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12145 {
12146 unconst(mParallelPorts[slot]).createObject();
12147 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12148 }
12149
12150 /* create another USB device filters object that will be mutable */
12151 unconst(mUSBDeviceFilters).createObject();
12152 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12153
12154 /* create a list of network adapters that will be mutable */
12155 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12156 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12157 {
12158 unconst(mNetworkAdapters[slot]).createObject();
12159 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12160 }
12161
12162 /* create another bandwidth control object that will be mutable */
12163 unconst(mBandwidthControl).createObject();
12164 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12165
12166 /* default is to delete saved state on Saved -> PoweredOff transition */
12167 mRemoveSavedState = true;
12168
12169 /* Confirm a successful initialization when it's the case */
12170 autoInitSpan.setSucceeded();
12171
12172 miNATNetworksStarted = 0;
12173
12174 LogFlowThisFuncLeave();
12175 return rc;
12176}
12177
12178/**
12179 * Uninitializes this session object. If the reason is other than
12180 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12181 * or the client watcher code.
12182 *
12183 * @param aReason uninitialization reason
12184 *
12185 * @note Locks mParent + this object for writing.
12186 */
12187void SessionMachine::uninit(Uninit::Reason aReason)
12188{
12189 LogFlowThisFuncEnter();
12190 LogFlowThisFunc(("reason=%d\n", aReason));
12191
12192 /*
12193 * Strongly reference ourselves to prevent this object deletion after
12194 * mData->mSession.mMachine.setNull() below (which can release the last
12195 * reference and call the destructor). Important: this must be done before
12196 * accessing any members (and before AutoUninitSpan that does it as well).
12197 * This self reference will be released as the very last step on return.
12198 */
12199 ComObjPtr<SessionMachine> selfRef = this;
12200
12201 /* Enclose the state transition Ready->InUninit->NotReady */
12202 AutoUninitSpan autoUninitSpan(this);
12203 if (autoUninitSpan.uninitDone())
12204 {
12205 LogFlowThisFunc(("Already uninitialized\n"));
12206 LogFlowThisFuncLeave();
12207 return;
12208 }
12209
12210 if (autoUninitSpan.initFailed())
12211 {
12212 /* We've been called by init() because it's failed. It's not really
12213 * necessary (nor it's safe) to perform the regular uninit sequence
12214 * below, the following is enough.
12215 */
12216 LogFlowThisFunc(("Initialization failed.\n"));
12217 /* destroy the machine client token */
12218 if (mClientToken)
12219 {
12220 delete mClientToken;
12221 mClientToken = NULL;
12222 }
12223 uninitDataAndChildObjects();
12224 mData.free();
12225 unconst(mParent) = NULL;
12226 unconst(mPeer) = NULL;
12227 LogFlowThisFuncLeave();
12228 return;
12229 }
12230
12231 MachineState_T lastState;
12232 {
12233 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12234 lastState = mData->mMachineState;
12235 }
12236 NOREF(lastState);
12237
12238#ifdef VBOX_WITH_USB
12239 // release all captured USB devices, but do this before requesting the locks below
12240 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12241 {
12242 /* Console::captureUSBDevices() is called in the VM process only after
12243 * setting the machine state to Starting or Restoring.
12244 * Console::detachAllUSBDevices() will be called upon successful
12245 * termination. So, we need to release USB devices only if there was
12246 * an abnormal termination of a running VM.
12247 *
12248 * This is identical to SessionMachine::DetachAllUSBDevices except
12249 * for the aAbnormal argument. */
12250 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12251 AssertComRC(rc);
12252 NOREF(rc);
12253
12254 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12255 if (service)
12256 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12257 }
12258#endif /* VBOX_WITH_USB */
12259
12260 // we need to lock this object in uninit() because the lock is shared
12261 // with mPeer (as well as data we modify below). mParent lock is needed
12262 // by several calls to it, and USB needs host lock.
12263 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12264
12265#ifdef VBOX_WITH_RESOURCE_USAGE_API
12266 /*
12267 * It is safe to call Machine::i_unregisterMetrics() here because
12268 * PerformanceCollector::samplerCallback no longer accesses guest methods
12269 * holding the lock.
12270 */
12271 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12272 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12273 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12274 this, __PRETTY_FUNCTION__, mCollectorGuest));
12275 if (mCollectorGuest)
12276 {
12277 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12278 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12279 mCollectorGuest = NULL;
12280 }
12281#endif
12282
12283 if (aReason == Uninit::Abnormal)
12284 {
12285 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12286 Global::IsOnlineOrTransient(lastState)));
12287
12288 /* reset the state to Aborted */
12289 if (mData->mMachineState != MachineState_Aborted)
12290 i_setMachineState(MachineState_Aborted);
12291 }
12292
12293 // any machine settings modified?
12294 if (mData->flModifications)
12295 {
12296 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12297 i_rollback(false /* aNotify */);
12298 }
12299
12300 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12301 || !mConsoleTaskData.mSnapshot);
12302 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12303 {
12304 LogWarningThisFunc(("canceling failed save state request!\n"));
12305 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12306 }
12307 else if (!mConsoleTaskData.mSnapshot.isNull())
12308 {
12309 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12310
12311 /* delete all differencing hard disks created (this will also attach
12312 * their parents back by rolling back mMediaData) */
12313 i_rollbackMedia();
12314
12315 // delete the saved state file (it might have been already created)
12316 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12317 // think it's still in use
12318 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12319 mConsoleTaskData.mSnapshot->uninit();
12320 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12321 }
12322
12323 mData->mSession.mPID = NIL_RTPROCESS;
12324
12325 if (aReason == Uninit::Unexpected)
12326 {
12327 /* Uninitialization didn't come from #checkForDeath(), so tell the
12328 * client watcher thread to update the set of machines that have open
12329 * sessions. */
12330 mParent->i_updateClientWatcher();
12331 }
12332
12333 /* uninitialize all remote controls */
12334 if (mData->mSession.mRemoteControls.size())
12335 {
12336 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12337 mData->mSession.mRemoteControls.size()));
12338
12339 Data::Session::RemoteControlList::iterator it =
12340 mData->mSession.mRemoteControls.begin();
12341 while (it != mData->mSession.mRemoteControls.end())
12342 {
12343 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12344 HRESULT rc = (*it)->Uninitialize();
12345 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12346 if (FAILED(rc))
12347 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12348 ++it;
12349 }
12350 mData->mSession.mRemoteControls.clear();
12351 }
12352
12353 /* Remove all references to the NAT network service. The service will stop
12354 * if all references (also from other VMs) are removed. */
12355 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12356 {
12357 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12358 {
12359 NetworkAttachmentType_T type;
12360 HRESULT hrc;
12361
12362 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12363 if ( SUCCEEDED(hrc)
12364 && type == NetworkAttachmentType_NATNetwork)
12365 {
12366 Bstr name;
12367 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12368 if (SUCCEEDED(hrc))
12369 {
12370 multilock.release();
12371 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12372 mUserData->s.strName.c_str(), name.raw()));
12373 mParent->i_natNetworkRefDec(name.raw());
12374 multilock.acquire();
12375 }
12376 }
12377 }
12378 }
12379
12380 /*
12381 * An expected uninitialization can come only from #checkForDeath().
12382 * Otherwise it means that something's gone really wrong (for example,
12383 * the Session implementation has released the VirtualBox reference
12384 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12385 * etc). However, it's also possible, that the client releases the IPC
12386 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12387 * but the VirtualBox release event comes first to the server process.
12388 * This case is practically possible, so we should not assert on an
12389 * unexpected uninit, just log a warning.
12390 */
12391
12392 if ((aReason == Uninit::Unexpected))
12393 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12394
12395 if (aReason != Uninit::Normal)
12396 {
12397 mData->mSession.mDirectControl.setNull();
12398 }
12399 else
12400 {
12401 /* this must be null here (see #OnSessionEnd()) */
12402 Assert(mData->mSession.mDirectControl.isNull());
12403 Assert(mData->mSession.mState == SessionState_Unlocking);
12404 Assert(!mData->mSession.mProgress.isNull());
12405 }
12406 if (mData->mSession.mProgress)
12407 {
12408 if (aReason == Uninit::Normal)
12409 mData->mSession.mProgress->i_notifyComplete(S_OK);
12410 else
12411 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12412 COM_IIDOF(ISession),
12413 getComponentName(),
12414 tr("The VM session was aborted"));
12415 mData->mSession.mProgress.setNull();
12416 }
12417
12418 /* remove the association between the peer machine and this session machine */
12419 Assert( (SessionMachine*)mData->mSession.mMachine == this
12420 || aReason == Uninit::Unexpected);
12421
12422 /* reset the rest of session data */
12423 mData->mSession.mMachine.setNull();
12424 mData->mSession.mState = SessionState_Unlocked;
12425 mData->mSession.mType.setNull();
12426
12427 /* destroy the machine client token before leaving the exclusive lock */
12428 if (mClientToken)
12429 {
12430 delete mClientToken;
12431 mClientToken = NULL;
12432 }
12433
12434 /* fire an event */
12435 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12436
12437 uninitDataAndChildObjects();
12438
12439 /* free the essential data structure last */
12440 mData.free();
12441
12442 /* release the exclusive lock before setting the below two to NULL */
12443 multilock.release();
12444
12445 unconst(mParent) = NULL;
12446 unconst(mPeer) = NULL;
12447
12448 LogFlowThisFuncLeave();
12449}
12450
12451// util::Lockable interface
12452////////////////////////////////////////////////////////////////////////////////
12453
12454/**
12455 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12456 * with the primary Machine instance (mPeer).
12457 */
12458RWLockHandle *SessionMachine::lockHandle() const
12459{
12460 AssertReturn(mPeer != NULL, NULL);
12461 return mPeer->lockHandle();
12462}
12463
12464// IInternalMachineControl methods
12465////////////////////////////////////////////////////////////////////////////////
12466
12467/**
12468 * Passes collected guest statistics to performance collector object
12469 */
12470STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12471 ULONG aCpuKernel, ULONG aCpuIdle,
12472 ULONG aMemTotal, ULONG aMemFree,
12473 ULONG aMemBalloon, ULONG aMemShared,
12474 ULONG aMemCache, ULONG aPageTotal,
12475 ULONG aAllocVMM, ULONG aFreeVMM,
12476 ULONG aBalloonedVMM, ULONG aSharedVMM,
12477 ULONG aVmNetRx, ULONG aVmNetTx)
12478{
12479#ifdef VBOX_WITH_RESOURCE_USAGE_API
12480 if (mCollectorGuest)
12481 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12482 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12483 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12484 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12485
12486 return S_OK;
12487#else
12488 NOREF(aValidStats);
12489 NOREF(aCpuUser);
12490 NOREF(aCpuKernel);
12491 NOREF(aCpuIdle);
12492 NOREF(aMemTotal);
12493 NOREF(aMemFree);
12494 NOREF(aMemBalloon);
12495 NOREF(aMemShared);
12496 NOREF(aMemCache);
12497 NOREF(aPageTotal);
12498 NOREF(aAllocVMM);
12499 NOREF(aFreeVMM);
12500 NOREF(aBalloonedVMM);
12501 NOREF(aSharedVMM);
12502 NOREF(aVmNetRx);
12503 NOREF(aVmNetTx);
12504 return E_NOTIMPL;
12505#endif
12506}
12507
12508/**
12509 * @note Locks this object for writing.
12510 */
12511STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12512{
12513 AutoCaller autoCaller(this);
12514 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12515
12516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12517
12518 mRemoveSavedState = aRemove;
12519
12520 return S_OK;
12521}
12522
12523/**
12524 * @note Locks the same as #i_setMachineState() does.
12525 */
12526STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12527{
12528 return i_setMachineState(aMachineState);
12529}
12530
12531/**
12532 * @note Locks this object for writing.
12533 */
12534STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12535{
12536 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12537 AutoCaller autoCaller(this);
12538 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12539
12540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12541
12542 if (mData->mSession.mState != SessionState_Locked)
12543 return VBOX_E_INVALID_OBJECT_STATE;
12544
12545 if (!mData->mSession.mProgress.isNull())
12546 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12547
12548 /* If we didn't reference the NAT network service yet, add a reference to
12549 * force a start */
12550 if (miNATNetworksStarted < 1)
12551 {
12552 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12553 {
12554 NetworkAttachmentType_T type;
12555 HRESULT hrc;
12556 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12557 if ( SUCCEEDED(hrc)
12558 && type == NetworkAttachmentType_NATNetwork)
12559 {
12560 Bstr name;
12561 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12562 if (SUCCEEDED(hrc))
12563 {
12564 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12565 mUserData->s.strName.c_str(), name.raw()));
12566 mPeer->lockHandle()->unlockWrite();
12567 mParent->i_natNetworkRefInc(name.raw());
12568#ifdef RT_LOCK_STRICT
12569 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12570#else
12571 mPeer->lockHandle()->lockWrite();
12572#endif
12573 }
12574 }
12575 }
12576 miNATNetworksStarted++;
12577 }
12578
12579 LogFlowThisFunc(("returns S_OK.\n"));
12580 return S_OK;
12581}
12582
12583/**
12584 * @note Locks this object for writing.
12585 */
12586STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12587{
12588 AutoCaller autoCaller(this);
12589 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12590
12591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12592
12593 if (mData->mSession.mState != SessionState_Locked)
12594 return VBOX_E_INVALID_OBJECT_STATE;
12595
12596 /* Finalize the LaunchVMProcess progress object. */
12597 if (mData->mSession.mProgress)
12598 {
12599 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12600 mData->mSession.mProgress.setNull();
12601 }
12602
12603 if (SUCCEEDED((HRESULT)iResult))
12604 {
12605#ifdef VBOX_WITH_RESOURCE_USAGE_API
12606 /* The VM has been powered up successfully, so it makes sense
12607 * now to offer the performance metrics for a running machine
12608 * object. Doing it earlier wouldn't be safe. */
12609 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12610 mData->mSession.mPID);
12611#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12612 }
12613
12614 return S_OK;
12615}
12616
12617/**
12618 * @note Locks this object for writing.
12619 */
12620STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12621{
12622 LogFlowThisFuncEnter();
12623
12624 AutoCaller autoCaller(this);
12625 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12626
12627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12628
12629 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12630 E_FAIL);
12631
12632 /* create a progress object to track operation completion */
12633 ComObjPtr<Progress> pProgress;
12634 pProgress.createObject();
12635 pProgress->init(i_getVirtualBox(),
12636 static_cast<IMachine *>(this) /* aInitiator */,
12637 Bstr(tr("Stopping the virtual machine")).raw(),
12638 FALSE /* aCancelable */);
12639
12640 /* fill in the console task data */
12641 mConsoleTaskData.mLastState = mData->mMachineState;
12642 mConsoleTaskData.mProgress = pProgress;
12643
12644 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12645 i_setMachineState(MachineState_Stopping);
12646
12647 pProgress.queryInterfaceTo(aProgress);
12648
12649 return S_OK;
12650}
12651
12652/**
12653 * @note Locks this object for writing.
12654 */
12655STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12656{
12657 LogFlowThisFuncEnter();
12658
12659 AutoCaller autoCaller(this);
12660 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12661
12662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12663
12664 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12665 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12666 && mConsoleTaskData.mLastState != MachineState_Null,
12667 E_FAIL);
12668
12669 /*
12670 * On failure, set the state to the state we had when BeginPoweringDown()
12671 * was called (this is expected by Console::PowerDown() and the associated
12672 * task). On success the VM process already changed the state to
12673 * MachineState_PoweredOff, so no need to do anything.
12674 */
12675 if (FAILED(iResult))
12676 i_setMachineState(mConsoleTaskData.mLastState);
12677
12678 /* notify the progress object about operation completion */
12679 Assert(mConsoleTaskData.mProgress);
12680 if (SUCCEEDED(iResult))
12681 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12682 else
12683 {
12684 Utf8Str strErrMsg(aErrMsg);
12685 if (strErrMsg.length())
12686 mConsoleTaskData.mProgress->i_notifyComplete(iResult,
12687 COM_IIDOF(ISession),
12688 getComponentName(),
12689 strErrMsg.c_str());
12690 else
12691 mConsoleTaskData.mProgress->i_notifyComplete(iResult);
12692 }
12693
12694 /* clear out the temporary saved state data */
12695 mConsoleTaskData.mLastState = MachineState_Null;
12696 mConsoleTaskData.mProgress.setNull();
12697
12698 LogFlowThisFuncLeave();
12699 return S_OK;
12700}
12701
12702
12703/**
12704 * Goes through the USB filters of the given machine to see if the given
12705 * device matches any filter or not.
12706 *
12707 * @note Locks the same as USBController::hasMatchingFilter() does.
12708 */
12709STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12710 BOOL *aMatched,
12711 ULONG *aMaskedIfs)
12712{
12713 LogFlowThisFunc(("\n"));
12714
12715 AutoCaller autoCaller(this);
12716 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12717
12718#ifdef VBOX_WITH_USB
12719 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
12720#else
12721 NOREF(aUSBDevice);
12722 NOREF(aMaskedIfs);
12723 *aMatched = FALSE;
12724#endif
12725
12726 return S_OK;
12727}
12728
12729/**
12730 * @note Locks the same as Host::captureUSBDevice() does.
12731 */
12732STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12733{
12734 LogFlowThisFunc(("\n"));
12735
12736 AutoCaller autoCaller(this);
12737 AssertComRCReturnRC(autoCaller.rc());
12738
12739#ifdef VBOX_WITH_USB
12740 /* if captureDeviceForVM() fails, it must have set extended error info */
12741 clearError();
12742 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12743 if (FAILED(rc)) return rc;
12744
12745 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12746 AssertReturn(service, E_FAIL);
12747 return service->captureDeviceForVM(this, Guid(aId).ref());
12748#else
12749 NOREF(aId);
12750 return E_NOTIMPL;
12751#endif
12752}
12753
12754/**
12755 * @note Locks the same as Host::detachUSBDevice() does.
12756 */
12757STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12758{
12759 LogFlowThisFunc(("\n"));
12760
12761 AutoCaller autoCaller(this);
12762 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12763
12764#ifdef VBOX_WITH_USB
12765 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12766 AssertReturn(service, E_FAIL);
12767 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12768#else
12769 NOREF(aId);
12770 NOREF(aDone);
12771 return E_NOTIMPL;
12772#endif
12773}
12774
12775/**
12776 * Inserts all machine filters to the USB proxy service and then calls
12777 * Host::autoCaptureUSBDevices().
12778 *
12779 * Called by Console from the VM process upon VM startup.
12780 *
12781 * @note Locks what called methods lock.
12782 */
12783STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12784{
12785 LogFlowThisFunc(("\n"));
12786
12787 AutoCaller autoCaller(this);
12788 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12789
12790#ifdef VBOX_WITH_USB
12791 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12792 AssertComRC(rc);
12793 NOREF(rc);
12794
12795 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12796 AssertReturn(service, E_FAIL);
12797 return service->autoCaptureDevicesForVM(this);
12798#else
12799 return S_OK;
12800#endif
12801}
12802
12803/**
12804 * Removes all machine filters from the USB proxy service and then calls
12805 * Host::detachAllUSBDevices().
12806 *
12807 * Called by Console from the VM process upon normal VM termination or by
12808 * SessionMachine::uninit() upon abnormal VM termination (from under the
12809 * Machine/SessionMachine lock).
12810 *
12811 * @note Locks what called methods lock.
12812 */
12813STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12814{
12815 LogFlowThisFunc(("\n"));
12816
12817 AutoCaller autoCaller(this);
12818 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12819
12820#ifdef VBOX_WITH_USB
12821 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12822 AssertComRC(rc);
12823 NOREF(rc);
12824
12825 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12826 AssertReturn(service, E_FAIL);
12827 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12828#else
12829 NOREF(aDone);
12830 return S_OK;
12831#endif
12832}
12833
12834/**
12835 * @note Locks this object for writing.
12836 */
12837STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12838 IProgress **aProgress)
12839{
12840 LogFlowThisFuncEnter();
12841
12842 AssertReturn(aSession, E_INVALIDARG);
12843 AssertReturn(aProgress, E_INVALIDARG);
12844
12845 AutoCaller autoCaller(this);
12846
12847 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12848 /*
12849 * We don't assert below because it might happen that a non-direct session
12850 * informs us it is closed right after we've been uninitialized -- it's ok.
12851 */
12852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12853
12854 /* get IInternalSessionControl interface */
12855 ComPtr<IInternalSessionControl> control(aSession);
12856
12857 ComAssertRet(!control.isNull(), E_INVALIDARG);
12858
12859 /* Creating a Progress object requires the VirtualBox lock, and
12860 * thus locking it here is required by the lock order rules. */
12861 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12862
12863 if (control == mData->mSession.mDirectControl)
12864 {
12865 ComAssertRet(aProgress, E_POINTER);
12866
12867 /* The direct session is being normally closed by the client process
12868 * ----------------------------------------------------------------- */
12869
12870 /* go to the closing state (essential for all open*Session() calls and
12871 * for #checkForDeath()) */
12872 Assert(mData->mSession.mState == SessionState_Locked);
12873 mData->mSession.mState = SessionState_Unlocking;
12874
12875 /* set direct control to NULL to release the remote instance */
12876 mData->mSession.mDirectControl.setNull();
12877 LogFlowThisFunc(("Direct control is set to NULL\n"));
12878
12879 if (mData->mSession.mProgress)
12880 {
12881 /* finalize the progress, someone might wait if a frontend
12882 * closes the session before powering on the VM. */
12883 mData->mSession.mProgress->notifyComplete(E_FAIL,
12884 COM_IIDOF(ISession),
12885 getComponentName(),
12886 tr("The VM session was closed before any attempt to power it on"));
12887 mData->mSession.mProgress.setNull();
12888 }
12889
12890 /* Create the progress object the client will use to wait until
12891 * #checkForDeath() is called to uninitialize this session object after
12892 * it releases the IPC semaphore.
12893 * Note! Because we're "reusing" mProgress here, this must be a proxy
12894 * object just like for LaunchVMProcess. */
12895 Assert(mData->mSession.mProgress.isNull());
12896 ComObjPtr<ProgressProxy> progress;
12897 progress.createObject();
12898 ComPtr<IUnknown> pPeer(mPeer);
12899 progress->init(mParent, pPeer,
12900 Bstr(tr("Closing session")).raw(),
12901 FALSE /* aCancelable */);
12902 progress.queryInterfaceTo(aProgress);
12903 mData->mSession.mProgress = progress;
12904 }
12905 else
12906 {
12907 /* the remote session is being normally closed */
12908 Data::Session::RemoteControlList::iterator it =
12909 mData->mSession.mRemoteControls.begin();
12910 while (it != mData->mSession.mRemoteControls.end())
12911 {
12912 if (control == *it)
12913 break;
12914 ++it;
12915 }
12916 BOOL found = it != mData->mSession.mRemoteControls.end();
12917 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12918 E_INVALIDARG);
12919 // This MUST be erase(it), not remove(*it) as the latter triggers a
12920 // very nasty use after free due to the place where the value "lives".
12921 mData->mSession.mRemoteControls.erase(it);
12922 }
12923
12924 /* signal the client watcher thread, because the client is going away */
12925 mParent->i_updateClientWatcher();
12926
12927 LogFlowThisFuncLeave();
12928 return S_OK;
12929}
12930
12931/**
12932 * @note Locks this object for writing.
12933 */
12934STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12935{
12936 LogFlowThisFuncEnter();
12937
12938 CheckComArgOutPointerValid(aProgress);
12939 CheckComArgOutPointerValid(aStateFilePath);
12940
12941 AutoCaller autoCaller(this);
12942 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12943
12944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12945
12946 AssertReturn( mData->mMachineState == MachineState_Paused
12947 && mConsoleTaskData.mLastState == MachineState_Null
12948 && mConsoleTaskData.strStateFilePath.isEmpty(),
12949 E_FAIL);
12950
12951 /* create a progress object to track operation completion */
12952 ComObjPtr<Progress> pProgress;
12953 pProgress.createObject();
12954 pProgress->init(i_getVirtualBox(),
12955 static_cast<IMachine *>(this) /* aInitiator */,
12956 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12957 FALSE /* aCancelable */);
12958
12959 Utf8Str strStateFilePath;
12960 /* stateFilePath is null when the machine is not running */
12961 if (mData->mMachineState == MachineState_Paused)
12962 i_composeSavedStateFilename(strStateFilePath);
12963
12964 /* fill in the console task data */
12965 mConsoleTaskData.mLastState = mData->mMachineState;
12966 mConsoleTaskData.strStateFilePath = strStateFilePath;
12967 mConsoleTaskData.mProgress = pProgress;
12968
12969 /* set the state to Saving (this is expected by Console::SaveState()) */
12970 i_setMachineState(MachineState_Saving);
12971
12972 strStateFilePath.cloneTo(aStateFilePath);
12973 pProgress.queryInterfaceTo(aProgress);
12974
12975 return S_OK;
12976}
12977
12978/**
12979 * @note Locks mParent + this object for writing.
12980 */
12981STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12982{
12983 LogFlowThisFunc(("\n"));
12984
12985 AutoCaller autoCaller(this);
12986 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12987
12988 /* endSavingState() need mParent lock */
12989 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12990
12991 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12992 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12993 && mConsoleTaskData.mLastState != MachineState_Null
12994 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12995 E_FAIL);
12996
12997 /*
12998 * On failure, set the state to the state we had when BeginSavingState()
12999 * was called (this is expected by Console::SaveState() and the associated
13000 * task). On success the VM process already changed the state to
13001 * MachineState_Saved, so no need to do anything.
13002 */
13003 if (FAILED(iResult))
13004 i_setMachineState(mConsoleTaskData.mLastState);
13005
13006 return endSavingState(iResult, aErrMsg);
13007}
13008
13009/**
13010 * @note Locks this object for writing.
13011 */
13012STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13013{
13014 LogFlowThisFunc(("\n"));
13015
13016 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13017
13018 AutoCaller autoCaller(this);
13019 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13020
13021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13022
13023 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13024 || mData->mMachineState == MachineState_Teleported
13025 || mData->mMachineState == MachineState_Aborted
13026 , E_FAIL); /** @todo setError. */
13027
13028 Utf8Str stateFilePathFull = aSavedStateFile;
13029 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
13030 if (RT_FAILURE(vrc))
13031 return setError(VBOX_E_FILE_ERROR,
13032 tr("Invalid saved state file path '%ls' (%Rrc)"),
13033 aSavedStateFile,
13034 vrc);
13035
13036 mSSData->strStateFilePath = stateFilePathFull;
13037
13038 /* The below i_setMachineState() will detect the state transition and will
13039 * update the settings file */
13040
13041 return i_setMachineState(MachineState_Saved);
13042}
13043
13044STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13045 ComSafeArrayOut(BSTR, aValues),
13046 ComSafeArrayOut(LONG64, aTimestamps),
13047 ComSafeArrayOut(BSTR, aFlags))
13048{
13049 LogFlowThisFunc(("\n"));
13050
13051#ifdef VBOX_WITH_GUEST_PROPS
13052 using namespace guestProp;
13053
13054 AutoCaller autoCaller(this);
13055 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13056
13057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13058
13059 CheckComArgOutSafeArrayPointerValid(aNames);
13060 CheckComArgOutSafeArrayPointerValid(aValues);
13061 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13062 CheckComArgOutSafeArrayPointerValid(aFlags);
13063
13064 size_t cEntries = mHWData->mGuestProperties.size();
13065 com::SafeArray<BSTR> names(cEntries);
13066 com::SafeArray<BSTR> values(cEntries);
13067 com::SafeArray<LONG64> timestamps(cEntries);
13068 com::SafeArray<BSTR> flags(cEntries);
13069 unsigned i = 0;
13070 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13071 it != mHWData->mGuestProperties.end();
13072 ++it)
13073 {
13074 char szFlags[MAX_FLAGS_LEN + 1];
13075 it->first.cloneTo(&names[i]);
13076 it->second.strValue.cloneTo(&values[i]);
13077 timestamps[i] = it->second.mTimestamp;
13078 /* If it is NULL, keep it NULL. */
13079 if (it->second.mFlags)
13080 {
13081 writeFlags(it->second.mFlags, szFlags);
13082 Bstr(szFlags).cloneTo(&flags[i]);
13083 }
13084 else
13085 flags[i] = NULL;
13086 ++i;
13087 }
13088 names.detachTo(ComSafeArrayOutArg(aNames));
13089 values.detachTo(ComSafeArrayOutArg(aValues));
13090 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13091 flags.detachTo(ComSafeArrayOutArg(aFlags));
13092 return S_OK;
13093#else
13094 ReturnComNotImplemented();
13095#endif
13096}
13097
13098STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13099 IN_BSTR aValue,
13100 LONG64 aTimestamp,
13101 IN_BSTR aFlags)
13102{
13103 LogFlowThisFunc(("\n"));
13104
13105#ifdef VBOX_WITH_GUEST_PROPS
13106 using namespace guestProp;
13107
13108 CheckComArgStrNotEmptyOrNull(aName);
13109 CheckComArgNotNull(aValue);
13110 CheckComArgNotNull(aFlags);
13111
13112 try
13113 {
13114 /*
13115 * Convert input up front.
13116 */
13117 Utf8Str utf8Name(aName);
13118 uint32_t fFlags = NILFLAG;
13119 if (aFlags)
13120 {
13121 Utf8Str utf8Flags(aFlags);
13122 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13123 AssertRCReturn(vrc, E_INVALIDARG);
13124 }
13125
13126 /*
13127 * Now grab the object lock, validate the state and do the update.
13128 */
13129 AutoCaller autoCaller(this);
13130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13131
13132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13133
13134 switch (mData->mMachineState)
13135 {
13136 case MachineState_Paused:
13137 case MachineState_Running:
13138 case MachineState_Teleporting:
13139 case MachineState_TeleportingPausedVM:
13140 case MachineState_LiveSnapshotting:
13141 case MachineState_DeletingSnapshotOnline:
13142 case MachineState_DeletingSnapshotPaused:
13143 case MachineState_Saving:
13144 case MachineState_Stopping:
13145 break;
13146
13147 default:
13148 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13149 VBOX_E_INVALID_VM_STATE);
13150 }
13151
13152 i_setModified(IsModified_MachineData);
13153 mHWData.backup();
13154
13155 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13156 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13157 if (it != mHWData->mGuestProperties.end())
13158 {
13159 if (!fDelete)
13160 {
13161 it->second.strValue = aValue;
13162 it->second.mTimestamp = aTimestamp;
13163 it->second.mFlags = fFlags;
13164 }
13165 else
13166 mHWData->mGuestProperties.erase(it);
13167
13168 mData->mGuestPropertiesModified = TRUE;
13169 }
13170 else if (!fDelete)
13171 {
13172 HWData::GuestProperty prop;
13173 prop.strValue = aValue;
13174 prop.mTimestamp = aTimestamp;
13175 prop.mFlags = fFlags;
13176
13177 mHWData->mGuestProperties[utf8Name] = prop;
13178 mData->mGuestPropertiesModified = TRUE;
13179 }
13180
13181 /*
13182 * Send a callback notification if appropriate
13183 */
13184 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13185 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13186 RTSTR_MAX,
13187 utf8Name.c_str(),
13188 RTSTR_MAX, NULL)
13189 )
13190 {
13191 alock.release();
13192
13193 mParent->i_onGuestPropertyChange(mData->mUuid,
13194 aName,
13195 aValue,
13196 aFlags);
13197 }
13198 }
13199 catch (...)
13200 {
13201 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13202 }
13203 return S_OK;
13204#else
13205 ReturnComNotImplemented();
13206#endif
13207}
13208
13209STDMETHODIMP SessionMachine::LockMedia()
13210{
13211 AutoCaller autoCaller(this);
13212 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13213
13214 AutoMultiWriteLock2 alock(this->lockHandle(),
13215 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13216
13217 AssertReturn( mData->mMachineState == MachineState_Starting
13218 || mData->mMachineState == MachineState_Restoring
13219 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13220
13221 clearError();
13222 alock.release();
13223 return lockMedia();
13224}
13225
13226STDMETHODIMP SessionMachine::UnlockMedia()
13227{
13228 HRESULT hrc = unlockMedia();
13229 return hrc;
13230}
13231
13232STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13233 IMediumAttachment **aNewAttachment)
13234{
13235 CheckComArgNotNull(aAttachment);
13236 CheckComArgOutPointerValid(aNewAttachment);
13237
13238 AutoCaller autoCaller(this);
13239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13240
13241 // request the host lock first, since might be calling Host methods for getting host drives;
13242 // next, protect the media tree all the while we're in here, as well as our member variables
13243 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13244 this->lockHandle(),
13245 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13246
13247 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13248
13249 Bstr ctrlName;
13250 LONG lPort;
13251 LONG lDevice;
13252 bool fTempEject;
13253 {
13254 AutoCaller autoAttachCaller(this);
13255 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13256
13257 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13258
13259 /* Need to query the details first, as the IMediumAttachment reference
13260 * might be to the original settings, which we are going to change. */
13261 ctrlName = pAttach->i_getControllerName();
13262 lPort = pAttach->i_getPort();
13263 lDevice = pAttach->i_getDevice();
13264 fTempEject = pAttach->i_getTempEject();
13265 }
13266
13267 if (!fTempEject)
13268 {
13269 /* Remember previously mounted medium. The medium before taking the
13270 * backup is not necessarily the same thing. */
13271 ComObjPtr<Medium> oldmedium;
13272 oldmedium = pAttach->i_getMedium();
13273
13274 i_setModified(IsModified_Storage);
13275 mMediaData.backup();
13276
13277 // The backup operation makes the pAttach reference point to the
13278 // old settings. Re-get the correct reference.
13279 pAttach = i_findAttachment(mMediaData->mAttachments,
13280 ctrlName.raw(),
13281 lPort,
13282 lDevice);
13283
13284 {
13285 AutoCaller autoAttachCaller(this);
13286 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13287
13288 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13289 if (!oldmedium.isNull())
13290 oldmedium->i_removeBackReference(mData->mUuid);
13291
13292 pAttach->i_updateMedium(NULL);
13293 pAttach->i_updateEjected();
13294 }
13295
13296 i_setModified(IsModified_Storage);
13297 }
13298 else
13299 {
13300 {
13301 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13302 pAttach->i_updateEjected();
13303 }
13304 }
13305
13306 pAttach.queryInterfaceTo(aNewAttachment);
13307
13308 return S_OK;
13309}
13310
13311// public methods only for internal purposes
13312/////////////////////////////////////////////////////////////////////////////
13313
13314#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13315/**
13316 * Called from the client watcher thread to check for expected or unexpected
13317 * death of the client process that has a direct session to this machine.
13318 *
13319 * On Win32 and on OS/2, this method is called only when we've got the
13320 * mutex (i.e. the client has either died or terminated normally) so it always
13321 * returns @c true (the client is terminated, the session machine is
13322 * uninitialized).
13323 *
13324 * On other platforms, the method returns @c true if the client process has
13325 * terminated normally or abnormally and the session machine was uninitialized,
13326 * and @c false if the client process is still alive.
13327 *
13328 * @note Locks this object for writing.
13329 */
13330bool SessionMachine::i_checkForDeath()
13331{
13332 Uninit::Reason reason;
13333 bool terminated = false;
13334
13335 /* Enclose autoCaller with a block because calling uninit() from under it
13336 * will deadlock. */
13337 {
13338 AutoCaller autoCaller(this);
13339 if (!autoCaller.isOk())
13340 {
13341 /* return true if not ready, to cause the client watcher to exclude
13342 * the corresponding session from watching */
13343 LogFlowThisFunc(("Already uninitialized!\n"));
13344 return true;
13345 }
13346
13347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13348
13349 /* Determine the reason of death: if the session state is Closing here,
13350 * everything is fine. Otherwise it means that the client did not call
13351 * OnSessionEnd() before it released the IPC semaphore. This may happen
13352 * either because the client process has abnormally terminated, or
13353 * because it simply forgot to call ISession::Close() before exiting. We
13354 * threat the latter also as an abnormal termination (see
13355 * Session::uninit() for details). */
13356 reason = mData->mSession.mState == SessionState_Unlocking ?
13357 Uninit::Normal :
13358 Uninit::Abnormal;
13359
13360 if (mClientToken)
13361 terminated = mClientToken->release();
13362 } /* AutoCaller block */
13363
13364 if (terminated)
13365 uninit(reason);
13366
13367 return terminated;
13368}
13369
13370void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13371{
13372 LogFlowThisFunc(("\n"));
13373
13374 strTokenId.setNull();
13375
13376 AutoCaller autoCaller(this);
13377 AssertComRCReturnVoid(autoCaller.rc());
13378
13379 Assert(mClientToken);
13380 if (mClientToken)
13381 mClientToken->getId(strTokenId);
13382}
13383#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13384IToken *SessionMachine::i_getToken()
13385{
13386 LogFlowThisFunc(("\n"));
13387
13388 AutoCaller autoCaller(this);
13389 AssertComRCReturn(autoCaller.rc(), NULL);
13390
13391 Assert(mClientToken);
13392 if (mClientToken)
13393 return mClientToken->getToken();
13394 else
13395 return NULL;
13396}
13397#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13398
13399Machine::ClientToken *SessionMachine::i_getClientToken()
13400{
13401 LogFlowThisFunc(("\n"));
13402
13403 AutoCaller autoCaller(this);
13404 AssertComRCReturn(autoCaller.rc(), NULL);
13405
13406 return mClientToken;
13407}
13408
13409
13410/**
13411 * @note Locks this object for reading.
13412 */
13413HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13414{
13415 LogFlowThisFunc(("\n"));
13416
13417 AutoCaller autoCaller(this);
13418 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13419
13420 ComPtr<IInternalSessionControl> directControl;
13421 {
13422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13423 directControl = mData->mSession.mDirectControl;
13424 }
13425
13426 /* ignore notifications sent after #OnSessionEnd() is called */
13427 if (!directControl)
13428 return S_OK;
13429
13430 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13431}
13432
13433/**
13434 * @note Locks this object for reading.
13435 */
13436HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13437 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13438 IN_BSTR aGuestIp, LONG aGuestPort)
13439{
13440 LogFlowThisFunc(("\n"));
13441
13442 AutoCaller autoCaller(this);
13443 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13444
13445 ComPtr<IInternalSessionControl> directControl;
13446 {
13447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13448 directControl = mData->mSession.mDirectControl;
13449 }
13450
13451 /* ignore notifications sent after #OnSessionEnd() is called */
13452 if (!directControl)
13453 return S_OK;
13454 /*
13455 * instead acting like callback we ask IVirtualBox deliver corresponding event
13456 */
13457
13458 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13459 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13460 return S_OK;
13461}
13462
13463/**
13464 * @note Locks this object for reading.
13465 */
13466HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13467{
13468 LogFlowThisFunc(("\n"));
13469
13470 AutoCaller autoCaller(this);
13471 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13472
13473 ComPtr<IInternalSessionControl> directControl;
13474 {
13475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13476 directControl = mData->mSession.mDirectControl;
13477 }
13478
13479 /* ignore notifications sent after #OnSessionEnd() is called */
13480 if (!directControl)
13481 return S_OK;
13482
13483 return directControl->OnSerialPortChange(serialPort);
13484}
13485
13486/**
13487 * @note Locks this object for reading.
13488 */
13489HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13490{
13491 LogFlowThisFunc(("\n"));
13492
13493 AutoCaller autoCaller(this);
13494 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13495
13496 ComPtr<IInternalSessionControl> directControl;
13497 {
13498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13499 directControl = mData->mSession.mDirectControl;
13500 }
13501
13502 /* ignore notifications sent after #OnSessionEnd() is called */
13503 if (!directControl)
13504 return S_OK;
13505
13506 return directControl->OnParallelPortChange(parallelPort);
13507}
13508
13509/**
13510 * @note Locks this object for reading.
13511 */
13512HRESULT SessionMachine::i_onStorageControllerChange()
13513{
13514 LogFlowThisFunc(("\n"));
13515
13516 AutoCaller autoCaller(this);
13517 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13518
13519 ComPtr<IInternalSessionControl> directControl;
13520 {
13521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13522 directControl = mData->mSession.mDirectControl;
13523 }
13524
13525 /* ignore notifications sent after #OnSessionEnd() is called */
13526 if (!directControl)
13527 return S_OK;
13528
13529 return directControl->OnStorageControllerChange();
13530}
13531
13532/**
13533 * @note Locks this object for reading.
13534 */
13535HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13536{
13537 LogFlowThisFunc(("\n"));
13538
13539 AutoCaller autoCaller(this);
13540 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13541
13542 ComPtr<IInternalSessionControl> directControl;
13543 {
13544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13545 directControl = mData->mSession.mDirectControl;
13546 }
13547
13548 /* ignore notifications sent after #OnSessionEnd() is called */
13549 if (!directControl)
13550 return S_OK;
13551
13552 return directControl->OnMediumChange(aAttachment, aForce);
13553}
13554
13555/**
13556 * @note Locks this object for reading.
13557 */
13558HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13559{
13560 LogFlowThisFunc(("\n"));
13561
13562 AutoCaller autoCaller(this);
13563 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13564
13565 ComPtr<IInternalSessionControl> directControl;
13566 {
13567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13568 directControl = mData->mSession.mDirectControl;
13569 }
13570
13571 /* ignore notifications sent after #OnSessionEnd() is called */
13572 if (!directControl)
13573 return S_OK;
13574
13575 return directControl->OnCPUChange(aCPU, aRemove);
13576}
13577
13578HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13579{
13580 LogFlowThisFunc(("\n"));
13581
13582 AutoCaller autoCaller(this);
13583 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13584
13585 ComPtr<IInternalSessionControl> directControl;
13586 {
13587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13588 directControl = mData->mSession.mDirectControl;
13589 }
13590
13591 /* ignore notifications sent after #OnSessionEnd() is called */
13592 if (!directControl)
13593 return S_OK;
13594
13595 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13596}
13597
13598/**
13599 * @note Locks this object for reading.
13600 */
13601HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13602{
13603 LogFlowThisFunc(("\n"));
13604
13605 AutoCaller autoCaller(this);
13606 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13607
13608 ComPtr<IInternalSessionControl> directControl;
13609 {
13610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13611 directControl = mData->mSession.mDirectControl;
13612 }
13613
13614 /* ignore notifications sent after #OnSessionEnd() is called */
13615 if (!directControl)
13616 return S_OK;
13617
13618 return directControl->OnVRDEServerChange(aRestart);
13619}
13620
13621/**
13622 * @note Locks this object for reading.
13623 */
13624HRESULT SessionMachine::i_onVideoCaptureChange()
13625{
13626 LogFlowThisFunc(("\n"));
13627
13628 AutoCaller autoCaller(this);
13629 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13630
13631 ComPtr<IInternalSessionControl> directControl;
13632 {
13633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13634 directControl = mData->mSession.mDirectControl;
13635 }
13636
13637 /* ignore notifications sent after #OnSessionEnd() is called */
13638 if (!directControl)
13639 return S_OK;
13640
13641 return directControl->OnVideoCaptureChange();
13642}
13643
13644/**
13645 * @note Locks this object for reading.
13646 */
13647HRESULT SessionMachine::i_onUSBControllerChange()
13648{
13649 LogFlowThisFunc(("\n"));
13650
13651 AutoCaller autoCaller(this);
13652 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13653
13654 ComPtr<IInternalSessionControl> directControl;
13655 {
13656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13657 directControl = mData->mSession.mDirectControl;
13658 }
13659
13660 /* ignore notifications sent after #OnSessionEnd() is called */
13661 if (!directControl)
13662 return S_OK;
13663
13664 return directControl->OnUSBControllerChange();
13665}
13666
13667/**
13668 * @note Locks this object for reading.
13669 */
13670HRESULT SessionMachine::i_onSharedFolderChange()
13671{
13672 LogFlowThisFunc(("\n"));
13673
13674 AutoCaller autoCaller(this);
13675 AssertComRCReturnRC(autoCaller.rc());
13676
13677 ComPtr<IInternalSessionControl> directControl;
13678 {
13679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13680 directControl = mData->mSession.mDirectControl;
13681 }
13682
13683 /* ignore notifications sent after #OnSessionEnd() is called */
13684 if (!directControl)
13685 return S_OK;
13686
13687 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13688}
13689
13690/**
13691 * @note Locks this object for reading.
13692 */
13693HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13694{
13695 LogFlowThisFunc(("\n"));
13696
13697 AutoCaller autoCaller(this);
13698 AssertComRCReturnRC(autoCaller.rc());
13699
13700 ComPtr<IInternalSessionControl> directControl;
13701 {
13702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13703 directControl = mData->mSession.mDirectControl;
13704 }
13705
13706 /* ignore notifications sent after #OnSessionEnd() is called */
13707 if (!directControl)
13708 return S_OK;
13709
13710 return directControl->OnClipboardModeChange(aClipboardMode);
13711}
13712
13713/**
13714 * @note Locks this object for reading.
13715 */
13716HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13717{
13718 LogFlowThisFunc(("\n"));
13719
13720 AutoCaller autoCaller(this);
13721 AssertComRCReturnRC(autoCaller.rc());
13722
13723 ComPtr<IInternalSessionControl> directControl;
13724 {
13725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13726 directControl = mData->mSession.mDirectControl;
13727 }
13728
13729 /* ignore notifications sent after #OnSessionEnd() is called */
13730 if (!directControl)
13731 return S_OK;
13732
13733 return directControl->OnDnDModeChange(aDnDMode);
13734}
13735
13736/**
13737 * @note Locks this object for reading.
13738 */
13739HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13740{
13741 LogFlowThisFunc(("\n"));
13742
13743 AutoCaller autoCaller(this);
13744 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13745
13746 ComPtr<IInternalSessionControl> directControl;
13747 {
13748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13749 directControl = mData->mSession.mDirectControl;
13750 }
13751
13752 /* ignore notifications sent after #OnSessionEnd() is called */
13753 if (!directControl)
13754 return S_OK;
13755
13756 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13757}
13758
13759/**
13760 * @note Locks this object for reading.
13761 */
13762HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13763{
13764 LogFlowThisFunc(("\n"));
13765
13766 AutoCaller autoCaller(this);
13767 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13768
13769 ComPtr<IInternalSessionControl> directControl;
13770 {
13771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13772 directControl = mData->mSession.mDirectControl;
13773 }
13774
13775 /* ignore notifications sent after #OnSessionEnd() is called */
13776 if (!directControl)
13777 return S_OK;
13778
13779 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13780}
13781
13782/**
13783 * Returns @c true if this machine's USB controller reports it has a matching
13784 * filter for the given USB device and @c false otherwise.
13785 *
13786 * @note locks this object for reading.
13787 */
13788bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13789{
13790 AutoCaller autoCaller(this);
13791 /* silently return if not ready -- this method may be called after the
13792 * direct machine session has been called */
13793 if (!autoCaller.isOk())
13794 return false;
13795
13796#ifdef VBOX_WITH_USB
13797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13798
13799 switch (mData->mMachineState)
13800 {
13801 case MachineState_Starting:
13802 case MachineState_Restoring:
13803 case MachineState_TeleportingIn:
13804 case MachineState_Paused:
13805 case MachineState_Running:
13806 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13807 * elsewhere... */
13808 alock.release();
13809 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13810 default: break;
13811 }
13812#else
13813 NOREF(aDevice);
13814 NOREF(aMaskedIfs);
13815#endif
13816 return false;
13817}
13818
13819/**
13820 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13821 */
13822HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13823 IVirtualBoxErrorInfo *aError,
13824 ULONG aMaskedIfs)
13825{
13826 LogFlowThisFunc(("\n"));
13827
13828 AutoCaller autoCaller(this);
13829
13830 /* This notification may happen after the machine object has been
13831 * uninitialized (the session was closed), so don't assert. */
13832 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13833
13834 ComPtr<IInternalSessionControl> directControl;
13835 {
13836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13837 directControl = mData->mSession.mDirectControl;
13838 }
13839
13840 /* fail on notifications sent after #OnSessionEnd() is called, it is
13841 * expected by the caller */
13842 if (!directControl)
13843 return E_FAIL;
13844
13845 /* No locks should be held at this point. */
13846 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13847 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13848
13849 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13850}
13851
13852/**
13853 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13854 */
13855HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13856 IVirtualBoxErrorInfo *aError)
13857{
13858 LogFlowThisFunc(("\n"));
13859
13860 AutoCaller autoCaller(this);
13861
13862 /* This notification may happen after the machine object has been
13863 * uninitialized (the session was closed), so don't assert. */
13864 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13865
13866 ComPtr<IInternalSessionControl> directControl;
13867 {
13868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13869 directControl = mData->mSession.mDirectControl;
13870 }
13871
13872 /* fail on notifications sent after #OnSessionEnd() is called, it is
13873 * expected by the caller */
13874 if (!directControl)
13875 return E_FAIL;
13876
13877 /* No locks should be held at this point. */
13878 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13879 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13880
13881 return directControl->OnUSBDeviceDetach(aId, aError);
13882}
13883
13884// protected methods
13885/////////////////////////////////////////////////////////////////////////////
13886
13887/**
13888 * Helper method to finalize saving the state.
13889 *
13890 * @note Must be called from under this object's lock.
13891 *
13892 * @param aRc S_OK if the snapshot has been taken successfully
13893 * @param aErrMsg human readable error message for failure
13894 *
13895 * @note Locks mParent + this objects for writing.
13896 */
13897HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13898{
13899 LogFlowThisFuncEnter();
13900
13901 AutoCaller autoCaller(this);
13902 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13903
13904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13905
13906 HRESULT rc = S_OK;
13907
13908 if (SUCCEEDED(aRc))
13909 {
13910 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13911
13912 /* save all VM settings */
13913 rc = i_saveSettings(NULL);
13914 // no need to check whether VirtualBox.xml needs saving also since
13915 // we can't have a name change pending at this point
13916 }
13917 else
13918 {
13919 // delete the saved state file (it might have been already created);
13920 // we need not check whether this is shared with a snapshot here because
13921 // we certainly created this saved state file here anew
13922 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13923 }
13924
13925 /* notify the progress object about operation completion */
13926 Assert(mConsoleTaskData.mProgress);
13927 if (SUCCEEDED(aRc))
13928 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13929 else
13930 {
13931 if (aErrMsg.length())
13932 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13933 COM_IIDOF(ISession),
13934 getComponentName(),
13935 aErrMsg.c_str());
13936 else
13937 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13938 }
13939
13940 /* clear out the temporary saved state data */
13941 mConsoleTaskData.mLastState = MachineState_Null;
13942 mConsoleTaskData.strStateFilePath.setNull();
13943 mConsoleTaskData.mProgress.setNull();
13944
13945 LogFlowThisFuncLeave();
13946 return rc;
13947}
13948
13949/**
13950 * Deletes the given file if it is no longer in use by either the current machine state
13951 * (if the machine is "saved") or any of the machine's snapshots.
13952 *
13953 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13954 * but is different for each SnapshotMachine. When calling this, the order of calling this
13955 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13956 * is therefore critical. I know, it's all rather messy.
13957 *
13958 * @param strStateFile
13959 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13960 * the test for whether the saved state file is in use.
13961 */
13962void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13963 Snapshot *pSnapshotToIgnore)
13964{
13965 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13966 if ( (strStateFile.isNotEmpty())
13967 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13968 )
13969 // ... and it must also not be shared with other snapshots
13970 if ( !mData->mFirstSnapshot
13971 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13972 // this checks the SnapshotMachine's state file paths
13973 )
13974 RTFileDelete(strStateFile.c_str());
13975}
13976
13977/**
13978 * Locks the attached media.
13979 *
13980 * All attached hard disks are locked for writing and DVD/floppy are locked for
13981 * reading. Parents of attached hard disks (if any) are locked for reading.
13982 *
13983 * This method also performs accessibility check of all media it locks: if some
13984 * media is inaccessible, the method will return a failure and a bunch of
13985 * extended error info objects per each inaccessible medium.
13986 *
13987 * Note that this method is atomic: if it returns a success, all media are
13988 * locked as described above; on failure no media is locked at all (all
13989 * succeeded individual locks will be undone).
13990 *
13991 * The caller is responsible for doing the necessary state sanity checks.
13992 *
13993 * The locks made by this method must be undone by calling #unlockMedia() when
13994 * no more needed.
13995 */
13996HRESULT SessionMachine::lockMedia()
13997{
13998 AutoCaller autoCaller(this);
13999 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14000
14001 AutoMultiWriteLock2 alock(this->lockHandle(),
14002 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14003
14004 /* bail out if trying to lock things with already set up locking */
14005 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14006
14007 MultiResult mrc(S_OK);
14008
14009 /* Collect locking information for all medium objects attached to the VM. */
14010 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14011 it != mMediaData->mAttachments.end();
14012 ++it)
14013 {
14014 MediumAttachment* pAtt = *it;
14015 DeviceType_T devType = pAtt->i_getType();
14016 Medium *pMedium = pAtt->i_getMedium();
14017
14018 MediumLockList *pMediumLockList(new MediumLockList());
14019 // There can be attachments without a medium (floppy/dvd), and thus
14020 // it's impossible to create a medium lock list. It still makes sense
14021 // to have the empty medium lock list in the map in case a medium is
14022 // attached later.
14023 if (pMedium != NULL)
14024 {
14025 MediumType_T mediumType = pMedium->i_getType();
14026 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14027 || mediumType == MediumType_Shareable;
14028 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14029
14030 alock.release();
14031 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14032 !fIsReadOnlyLock /* fMediumLockWrite */,
14033 NULL,
14034 *pMediumLockList);
14035 alock.acquire();
14036 if (FAILED(mrc))
14037 {
14038 delete pMediumLockList;
14039 mData->mSession.mLockedMedia.Clear();
14040 break;
14041 }
14042 }
14043
14044 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14045 if (FAILED(rc))
14046 {
14047 mData->mSession.mLockedMedia.Clear();
14048 mrc = setError(rc,
14049 tr("Collecting locking information for all attached media failed"));
14050 break;
14051 }
14052 }
14053
14054 if (SUCCEEDED(mrc))
14055 {
14056 /* Now lock all media. If this fails, nothing is locked. */
14057 alock.release();
14058 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14059 alock.acquire();
14060 if (FAILED(rc))
14061 {
14062 mrc = setError(rc,
14063 tr("Locking of attached media failed"));
14064 }
14065 }
14066
14067 return mrc;
14068}
14069
14070/**
14071 * Undoes the locks made by by #lockMedia().
14072 */
14073HRESULT SessionMachine::unlockMedia()
14074{
14075 AutoCaller autoCaller(this);
14076 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14077
14078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14079
14080 /* we may be holding important error info on the current thread;
14081 * preserve it */
14082 ErrorInfoKeeper eik;
14083
14084 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14085 AssertComRC(rc);
14086 return rc;
14087}
14088
14089/**
14090 * Helper to change the machine state (reimplementation).
14091 *
14092 * @note Locks this object for writing.
14093 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14094 * it can cause crashes in random places due to unexpectedly committing
14095 * the current settings. The caller is responsible for that. The call
14096 * to saveStateSettings is fine, because this method does not commit.
14097 */
14098HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14099{
14100 LogFlowThisFuncEnter();
14101 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14102
14103 AutoCaller autoCaller(this);
14104 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14105
14106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14107
14108 MachineState_T oldMachineState = mData->mMachineState;
14109
14110 AssertMsgReturn(oldMachineState != aMachineState,
14111 ("oldMachineState=%s, aMachineState=%s\n",
14112 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14113 E_FAIL);
14114
14115 HRESULT rc = S_OK;
14116
14117 int stsFlags = 0;
14118 bool deleteSavedState = false;
14119
14120 /* detect some state transitions */
14121
14122 if ( ( oldMachineState == MachineState_Saved
14123 && aMachineState == MachineState_Restoring)
14124 || ( ( oldMachineState == MachineState_PoweredOff
14125 || oldMachineState == MachineState_Teleported
14126 || oldMachineState == MachineState_Aborted
14127 )
14128 && ( aMachineState == MachineState_TeleportingIn
14129 || aMachineState == MachineState_Starting
14130 )
14131 )
14132 )
14133 {
14134 /* The EMT thread is about to start */
14135
14136 /* Nothing to do here for now... */
14137
14138 /// @todo NEWMEDIA don't let mDVDDrive and other children
14139 /// change anything when in the Starting/Restoring state
14140 }
14141 else if ( ( oldMachineState == MachineState_Running
14142 || oldMachineState == MachineState_Paused
14143 || oldMachineState == MachineState_Teleporting
14144 || oldMachineState == MachineState_LiveSnapshotting
14145 || oldMachineState == MachineState_Stuck
14146 || oldMachineState == MachineState_Starting
14147 || oldMachineState == MachineState_Stopping
14148 || oldMachineState == MachineState_Saving
14149 || oldMachineState == MachineState_Restoring
14150 || oldMachineState == MachineState_TeleportingPausedVM
14151 || oldMachineState == MachineState_TeleportingIn
14152 )
14153 && ( aMachineState == MachineState_PoweredOff
14154 || aMachineState == MachineState_Saved
14155 || aMachineState == MachineState_Teleported
14156 || aMachineState == MachineState_Aborted
14157 )
14158 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14159 * snapshot */
14160 && ( mConsoleTaskData.mSnapshot.isNull()
14161 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14162 )
14163 )
14164 {
14165 /* The EMT thread has just stopped, unlock attached media. Note that as
14166 * opposed to locking that is done from Console, we do unlocking here
14167 * because the VM process may have aborted before having a chance to
14168 * properly unlock all media it locked. */
14169
14170 unlockMedia();
14171 }
14172
14173 if (oldMachineState == MachineState_Restoring)
14174 {
14175 if (aMachineState != MachineState_Saved)
14176 {
14177 /*
14178 * delete the saved state file once the machine has finished
14179 * restoring from it (note that Console sets the state from
14180 * Restoring to Saved if the VM couldn't restore successfully,
14181 * to give the user an ability to fix an error and retry --
14182 * we keep the saved state file in this case)
14183 */
14184 deleteSavedState = true;
14185 }
14186 }
14187 else if ( oldMachineState == MachineState_Saved
14188 && ( aMachineState == MachineState_PoweredOff
14189 || aMachineState == MachineState_Aborted
14190 || aMachineState == MachineState_Teleported
14191 )
14192 )
14193 {
14194 /*
14195 * delete the saved state after Console::ForgetSavedState() is called
14196 * or if the VM process (owning a direct VM session) crashed while the
14197 * VM was Saved
14198 */
14199
14200 /// @todo (dmik)
14201 // Not sure that deleting the saved state file just because of the
14202 // client death before it attempted to restore the VM is a good
14203 // thing. But when it crashes we need to go to the Aborted state
14204 // which cannot have the saved state file associated... The only
14205 // way to fix this is to make the Aborted condition not a VM state
14206 // but a bool flag: i.e., when a crash occurs, set it to true and
14207 // change the state to PoweredOff or Saved depending on the
14208 // saved state presence.
14209
14210 deleteSavedState = true;
14211 mData->mCurrentStateModified = TRUE;
14212 stsFlags |= SaveSTS_CurStateModified;
14213 }
14214
14215 if ( aMachineState == MachineState_Starting
14216 || aMachineState == MachineState_Restoring
14217 || aMachineState == MachineState_TeleportingIn
14218 )
14219 {
14220 /* set the current state modified flag to indicate that the current
14221 * state is no more identical to the state in the
14222 * current snapshot */
14223 if (!mData->mCurrentSnapshot.isNull())
14224 {
14225 mData->mCurrentStateModified = TRUE;
14226 stsFlags |= SaveSTS_CurStateModified;
14227 }
14228 }
14229
14230 if (deleteSavedState)
14231 {
14232 if (mRemoveSavedState)
14233 {
14234 Assert(!mSSData->strStateFilePath.isEmpty());
14235
14236 // it is safe to delete the saved state file if ...
14237 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14238 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14239 // ... none of the snapshots share the saved state file
14240 )
14241 RTFileDelete(mSSData->strStateFilePath.c_str());
14242 }
14243
14244 mSSData->strStateFilePath.setNull();
14245 stsFlags |= SaveSTS_StateFilePath;
14246 }
14247
14248 /* redirect to the underlying peer machine */
14249 mPeer->i_setMachineState(aMachineState);
14250
14251 if ( aMachineState == MachineState_PoweredOff
14252 || aMachineState == MachineState_Teleported
14253 || aMachineState == MachineState_Aborted
14254 || aMachineState == MachineState_Saved)
14255 {
14256 /* the machine has stopped execution
14257 * (or the saved state file was adopted) */
14258 stsFlags |= SaveSTS_StateTimeStamp;
14259 }
14260
14261 if ( ( oldMachineState == MachineState_PoweredOff
14262 || oldMachineState == MachineState_Aborted
14263 || oldMachineState == MachineState_Teleported
14264 )
14265 && aMachineState == MachineState_Saved)
14266 {
14267 /* the saved state file was adopted */
14268 Assert(!mSSData->strStateFilePath.isEmpty());
14269 stsFlags |= SaveSTS_StateFilePath;
14270 }
14271
14272#ifdef VBOX_WITH_GUEST_PROPS
14273 if ( aMachineState == MachineState_PoweredOff
14274 || aMachineState == MachineState_Aborted
14275 || aMachineState == MachineState_Teleported)
14276 {
14277 /* Make sure any transient guest properties get removed from the
14278 * property store on shutdown. */
14279
14280 HWData::GuestPropertyMap::const_iterator it;
14281 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14282 if (!fNeedsSaving)
14283 for (it = mHWData->mGuestProperties.begin();
14284 it != mHWData->mGuestProperties.end(); ++it)
14285 if ( (it->second.mFlags & guestProp::TRANSIENT)
14286 || (it->second.mFlags & guestProp::TRANSRESET))
14287 {
14288 fNeedsSaving = true;
14289 break;
14290 }
14291 if (fNeedsSaving)
14292 {
14293 mData->mCurrentStateModified = TRUE;
14294 stsFlags |= SaveSTS_CurStateModified;
14295 }
14296 }
14297#endif
14298
14299 rc = i_saveStateSettings(stsFlags);
14300
14301 if ( ( oldMachineState != MachineState_PoweredOff
14302 && oldMachineState != MachineState_Aborted
14303 && oldMachineState != MachineState_Teleported
14304 )
14305 && ( aMachineState == MachineState_PoweredOff
14306 || aMachineState == MachineState_Aborted
14307 || aMachineState == MachineState_Teleported
14308 )
14309 )
14310 {
14311 /* we've been shut down for any reason */
14312 /* no special action so far */
14313 }
14314
14315 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14316 LogFlowThisFuncLeave();
14317 return rc;
14318}
14319
14320/**
14321 * Sends the current machine state value to the VM process.
14322 *
14323 * @note Locks this object for reading, then calls a client process.
14324 */
14325HRESULT SessionMachine::i_updateMachineStateOnClient()
14326{
14327 AutoCaller autoCaller(this);
14328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14329
14330 ComPtr<IInternalSessionControl> directControl;
14331 {
14332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14333 AssertReturn(!!mData, E_FAIL);
14334 directControl = mData->mSession.mDirectControl;
14335
14336 /* directControl may be already set to NULL here in #OnSessionEnd()
14337 * called too early by the direct session process while there is still
14338 * some operation (like deleting the snapshot) in progress. The client
14339 * process in this case is waiting inside Session::close() for the
14340 * "end session" process object to complete, while #uninit() called by
14341 * #checkForDeath() on the Watcher thread is waiting for the pending
14342 * operation to complete. For now, we accept this inconsistent behavior
14343 * and simply do nothing here. */
14344
14345 if (mData->mSession.mState == SessionState_Unlocking)
14346 return S_OK;
14347
14348 AssertReturn(!directControl.isNull(), E_FAIL);
14349 }
14350
14351 return directControl->UpdateMachineState(mData->mMachineState);
14352}
14353
14354HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14355{
14356 NOREF(aRemove);
14357 ReturnComNotImplemented();
14358}
14359
14360HRESULT Machine::updateState(MachineState_T aState)
14361{
14362 NOREF(aState);
14363 ReturnComNotImplemented();
14364}
14365
14366HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14367{
14368 NOREF(aProgress);
14369 ReturnComNotImplemented();
14370}
14371
14372HRESULT Machine::endPowerUp(LONG aResult)
14373{
14374 NOREF(aResult);
14375 ReturnComNotImplemented();
14376}
14377
14378HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14379{
14380 NOREF(aProgress);
14381 ReturnComNotImplemented();
14382}
14383
14384HRESULT Machine::endPoweringDown(LONG aResult,
14385 const com::Utf8Str &aErrMsg)
14386{
14387 NOREF(aResult);
14388 NOREF(aErrMsg);
14389 ReturnComNotImplemented();
14390}
14391
14392HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14393 BOOL *aMatched,
14394 ULONG *aMaskedInterfaces)
14395{
14396 NOREF(aDevice);
14397 NOREF(aMatched);
14398 NOREF(aMaskedInterfaces);
14399 ReturnComNotImplemented();
14400
14401}
14402
14403HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14404{
14405 NOREF(aId);
14406 ReturnComNotImplemented();
14407}
14408
14409HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14410 BOOL aDone)
14411{
14412 NOREF(aId);
14413 NOREF(aDone);
14414 ReturnComNotImplemented();
14415}
14416
14417HRESULT Machine::autoCaptureUSBDevices()
14418{
14419 ReturnComNotImplemented();
14420}
14421
14422HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14423{
14424 NOREF(aDone);
14425 ReturnComNotImplemented();
14426}
14427
14428HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14429 ComPtr<IProgress> &aProgress)
14430{
14431 NOREF(aSession);
14432 NOREF(aProgress);
14433 ReturnComNotImplemented();
14434}
14435
14436HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14437 com::Utf8Str &aStateFilePath)
14438{
14439 NOREF(aProgress);
14440 NOREF(aStateFilePath);
14441 ReturnComNotImplemented();
14442}
14443
14444HRESULT Machine::endSavingState(LONG aResult,
14445 const com::Utf8Str &aErrMsg)
14446{
14447 NOREF(aResult);
14448 NOREF(aErrMsg);
14449 ReturnComNotImplemented();
14450}
14451
14452HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14453{
14454 NOREF(aSavedStateFile);
14455 ReturnComNotImplemented();
14456}
14457
14458HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14459 const com::Utf8Str &aName,
14460 const com::Utf8Str &aDescription,
14461 const ComPtr<IProgress> &aConsoleProgress,
14462 BOOL aFTakingSnapshotOnline,
14463 com::Utf8Str &aStateFilePath)
14464{
14465 NOREF(aInitiator);
14466 NOREF(aName);
14467 NOREF(aDescription);
14468 NOREF(aConsoleProgress);
14469 NOREF(aFTakingSnapshotOnline);
14470 NOREF(aStateFilePath);
14471 ReturnComNotImplemented();
14472}
14473
14474HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14475{
14476 NOREF(aSuccess);
14477 ReturnComNotImplemented();
14478}
14479
14480HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14481 const com::Guid &aStartId,
14482 const com::Guid &aEndId,
14483 BOOL aDeleteAllChildren,
14484 MachineState_T *aMachineState,
14485 ComPtr<IProgress> &aProgress)
14486{
14487 NOREF(aInitiator);
14488 NOREF(aStartId);
14489 NOREF(aEndId);
14490 NOREF(aDeleteAllChildren);
14491 NOREF(aMachineState);
14492 NOREF(aProgress);
14493 ReturnComNotImplemented();
14494}
14495
14496HRESULT Machine::finishOnlineMergeMedium()
14497{
14498 ReturnComNotImplemented();
14499}
14500
14501HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14502 const ComPtr<ISnapshot> &aSnapshot,
14503 MachineState_T *aMachineState,
14504 ComPtr<IProgress> &aProgress)
14505{
14506 NOREF(aInitiator);
14507 NOREF(aSnapshot);
14508 NOREF(aMachineState);
14509 NOREF(aProgress);
14510 ReturnComNotImplemented();
14511}
14512
14513HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14514 std::vector<com::Utf8Str> &aValues,
14515 std::vector<LONG64> &aTimestamps,
14516 std::vector<com::Utf8Str> &aFlags)
14517{
14518 NOREF(aNames);
14519 NOREF(aValues);
14520 NOREF(aTimestamps);
14521 NOREF(aFlags);
14522 ReturnComNotImplemented();
14523}
14524
14525HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14526 const com::Utf8Str &aValue,
14527 LONG64 aTimestamp,
14528 const com::Utf8Str &aFlags)
14529{
14530 NOREF(aName);
14531 NOREF(aValue);
14532 NOREF(aTimestamp);
14533 NOREF(aFlags);
14534 ReturnComNotImplemented();
14535}
14536
14537HRESULT Machine::lockMedia()
14538{
14539 ReturnComNotImplemented();
14540}
14541
14542HRESULT Machine::unlockMedia()
14543{
14544 ReturnComNotImplemented();
14545}
14546
14547HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14548 ComPtr<IMediumAttachment> &aNewAttachment)
14549{
14550 NOREF(aAttachment);
14551 NOREF(aNewAttachment);
14552 ReturnComNotImplemented();
14553}
14554
14555HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14556 ULONG aCpuUser,
14557 ULONG aCpuKernel,
14558 ULONG aCpuIdle,
14559 ULONG aMemTotal,
14560 ULONG aMemFree,
14561 ULONG aMemBalloon,
14562 ULONG aMemShared,
14563 ULONG aMemCache,
14564 ULONG aPagedTotal,
14565 ULONG aMemAllocTotal,
14566 ULONG aMemFreeTotal,
14567 ULONG aMemBalloonTotal,
14568 ULONG aMemSharedTotal,
14569 ULONG aVmNetRx,
14570 ULONG aVmNetTx)
14571{
14572 NOREF(aValidStats);
14573 NOREF(aCpuUser);
14574 NOREF(aCpuKernel);
14575 NOREF(aCpuIdle);
14576 NOREF(aMemTotal);
14577 NOREF(aMemFree);
14578 NOREF(aMemBalloon);
14579 NOREF(aMemShared);
14580 NOREF(aMemCache);
14581 NOREF(aPagedTotal);
14582 NOREF(aMemAllocTotal);
14583 NOREF(aMemFreeTotal);
14584 NOREF(aMemBalloonTotal);
14585 NOREF(aMemSharedTotal);
14586 NOREF(aVmNetRx);
14587 NOREF(aVmNetTx);
14588 ReturnComNotImplemented();
14589}
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