VirtualBox

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

Last change on this file since 54314 was 54177, checked in by vboxsync, 10 years ago

Main/Machine: typo in rsevious change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 499.3 KB
Line 
1/* $Id: MachineImpl.cpp 54177 2015-02-12 13:08:10Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#ifdef VBOX_WITH_DTRACE_R3_MAIN
90# include "dtrace/VBoxAPI.h"
91#endif
92
93#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
94# define HOSTSUFF_EXE ".exe"
95#else /* !RT_OS_WINDOWS */
96# define HOSTSUFF_EXE ""
97#endif /* !RT_OS_WINDOWS */
98
99// defines / prototypes
100/////////////////////////////////////////////////////////////////////////////
101
102/////////////////////////////////////////////////////////////////////////////
103// Machine::Data structure
104/////////////////////////////////////////////////////////////////////////////
105
106Machine::Data::Data()
107{
108 mRegistered = FALSE;
109 pMachineConfigFile = NULL;
110 /* Contains hints on what has changed when the user is using the VM (config
111 * changes, running the VM, ...). This is used to decide if a config needs
112 * to be written to disk. */
113 flModifications = 0;
114 /* VM modification usually also trigger setting the current state to
115 * "Modified". Although this is not always the case. An e.g. is the VM
116 * initialization phase or when snapshot related data is changed. The
117 * actually behavior is controlled by the following flag. */
118 m_fAllowStateModification = false;
119 mAccessible = FALSE;
120 /* mUuid is initialized in Machine::init() */
121
122 mMachineState = MachineState_PoweredOff;
123 RTTimeNow(&mLastStateChange);
124
125 mMachineStateDeps = 0;
126 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
127 mMachineStateChangePending = 0;
128
129 mCurrentStateModified = TRUE;
130 mGuestPropertiesModified = FALSE;
131
132 mSession.mPID = NIL_RTPROCESS;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mTripleFaultReset = false;
197 mHPETEnabled = false;
198
199 /* default boot order: floppy - DVD - HDD */
200 mBootOrder[0] = DeviceType_Floppy;
201 mBootOrder[1] = DeviceType_DVD;
202 mBootOrder[2] = DeviceType_HardDisk;
203 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
204 mBootOrder[i] = DeviceType_Null;
205
206 mClipboardMode = ClipboardMode_Disabled;
207 mDnDMode = DnDMode_Disabled;
208 mGuestPropertyNotificationPatterns = "";
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222
223 /* Maximum CPU execution cap by default. */
224 mCpuExecutionCap = 100;
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HDData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::MediaData::MediaData()
236{
237}
238
239Machine::MediaData::~MediaData()
240{
241}
242
243/////////////////////////////////////////////////////////////////////////////
244// Machine class
245/////////////////////////////////////////////////////////////////////////////
246
247// constructor / destructor
248/////////////////////////////////////////////////////////////////////////////
249
250Machine::Machine() :
251#ifdef VBOX_WITH_RESOURCE_USAGE_API
252 mCollectorGuest(NULL),
253#endif
254 mPeer(NULL),
255 mParent(NULL),
256 mSerialPorts(),
257 mParallelPorts(),
258 uRegistryNeedsSaving(0)
259{}
260
261Machine::~Machine()
262{}
263
264HRESULT Machine::FinalConstruct()
265{
266 LogFlowThisFunc(("\n"));
267 return BaseFinalConstruct();
268}
269
270void Machine::FinalRelease()
271{
272 LogFlowThisFunc(("\n"));
273 uninit();
274 BaseFinalRelease();
275}
276
277/**
278 * Initializes a new machine instance; this init() variant creates a new, empty machine.
279 * This gets called from VirtualBox::CreateMachine().
280 *
281 * @param aParent Associated parent object
282 * @param strConfigFile Local file system path to the VM settings file (can
283 * be relative to the VirtualBox config directory).
284 * @param strName name for the machine
285 * @param llGroups list of groups for the machine
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Apply BIOS defaults */
349 mBIOSSettings->i_applyDefaults(aOsType);
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Let the OS type select 64-bit ness. */
360 mHWData->mLongMode = aOsType->i_is64Bit()
361 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 }
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 i_allowStateModification();
367
368 /* commit all changes made during the initialization */
369 i_commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param aConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = i_registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 i_allowStateModification();
469
470 i_commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->i_unregisterMachineMedia(i_getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent
607 * @return
608 */
609HRESULT Machine::initImpl(VirtualBox *aParent,
610 const Utf8Str &strConfigFile)
611{
612 LogFlowThisFuncEnter();
613
614 AssertReturn(aParent, E_INVALIDARG);
615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
616
617 HRESULT rc = S_OK;
618
619 /* share the parent weakly */
620 unconst(mParent) = aParent;
621
622 /* allocate the essential machine data structure (the rest will be
623 * allocated later by initDataAndChildObjects() */
624 mData.allocate();
625
626 /* memorize the config file name (as provided) */
627 mData->m_strConfigFile = strConfigFile;
628
629 /* get the full file name */
630 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
631 if (RT_FAILURE(vrc1))
632 return setError(VBOX_E_FILE_ERROR,
633 tr("Invalid machine settings file name '%s' (%Rrc)"),
634 strConfigFile.c_str(),
635 vrc1);
636
637 LogFlowThisFuncLeave();
638
639 return rc;
640}
641
642/**
643 * Tries to create a machine settings file in the path stored in the machine
644 * instance data. Used when a new machine is created to fail gracefully if
645 * the settings file could not be written (e.g. because machine dir is read-only).
646 * @return
647 */
648HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
649{
650 HRESULT rc = S_OK;
651
652 // when we create a new machine, we must be able to create the settings file
653 RTFILE f = NIL_RTFILE;
654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
655 if ( RT_SUCCESS(vrc)
656 || vrc == VERR_SHARING_VIOLATION
657 )
658 {
659 if (RT_SUCCESS(vrc))
660 RTFileClose(f);
661 if (!fForceOverwrite)
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Machine settings file '%s' already exists"),
664 mData->m_strConfigFileFull.c_str());
665 else
666 {
667 /* try to delete the config file, as otherwise the creation
668 * of a new settings file will fail. */
669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
670 if (RT_FAILURE(vrc2))
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Could not delete the existing settings file '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(), vrc2);
674 }
675 }
676 else if ( vrc != VERR_FILE_NOT_FOUND
677 && vrc != VERR_PATH_NOT_FOUND
678 )
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Invalid machine settings file name '%s' (%Rrc)"),
681 mData->m_strConfigFileFull.c_str(),
682 vrc);
683 return rc;
684}
685
686/**
687 * Initializes the registered machine by loading the settings file.
688 * This method is separated from #init() in order to make it possible to
689 * retry the operation after VirtualBox startup instead of refusing to
690 * startup the whole VirtualBox server in case if the settings file of some
691 * registered VM is invalid or inaccessible.
692 *
693 * @note Must be always called from this object's write lock
694 * (unless called from #init() that doesn't need any locking).
695 * @note Locks the mUSBController method for writing.
696 * @note Subclasses must not call this method.
697 */
698HRESULT Machine::i_registeredInit()
699{
700 AssertReturn(!i_isSessionMachine(), E_FAIL);
701 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
702 AssertReturn(mData->mUuid.isValid(), E_FAIL);
703 AssertReturn(!mData->mAccessible, E_FAIL);
704
705 HRESULT rc = initDataAndChildObjects();
706
707 if (SUCCEEDED(rc))
708 {
709 /* Temporarily reset the registered flag in order to let setters
710 * potentially called from loadSettings() succeed (isMutable() used in
711 * all setters will return FALSE for a Machine instance if mRegistered
712 * is TRUE). */
713 mData->mRegistered = FALSE;
714
715 try
716 {
717 // load and parse machine XML; this will throw on XML or logic errors
718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
719
720 if (mData->mUuid != mData->pMachineConfigFile->uuid)
721 throw setError(E_FAIL,
722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
723 mData->pMachineConfigFile->uuid.raw(),
724 mData->m_strConfigFileFull.c_str(),
725 mData->mUuid.toString().c_str(),
726 mParent->i_settingsFilePath().c_str());
727
728 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
729 NULL /* const Guid *puuidRegistry */);
730 if (FAILED(rc)) throw rc;
731 }
732 catch (HRESULT err)
733 {
734 /* we assume that error info is set by the thrower */
735 rc = err;
736 }
737 catch (...)
738 {
739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
740 }
741
742 /* Restore the registered flag (even on failure) */
743 mData->mRegistered = TRUE;
744 }
745
746 if (SUCCEEDED(rc))
747 {
748 /* Set mAccessible to TRUE only if we successfully locked and loaded
749 * the settings file */
750 mData->mAccessible = TRUE;
751
752 /* commit all changes made during loading the settings file */
753 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
754 /// @todo r=klaus for some reason the settings loading logic backs up
755 // the settings, and therefore a commit is needed. Should probably be changed.
756 }
757 else
758 {
759 /* If the machine is registered, then, instead of returning a
760 * failure, we mark it as inaccessible and set the result to
761 * success to give it a try later */
762
763 /* fetch the current error info */
764 mData->mAccessError = com::ErrorInfo();
765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
766 mData->mUuid.raw(),
767 mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 i_rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->i_unregisterMachineMedia(i_getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 i_saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!i_isSnapshotMachine());
820 Assert(!i_isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it does
835 * VirtualBox::addCaller() for the duration of the
836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 LogWarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(i_getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->i_unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
879 i_rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// Wrapped IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
894{
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 aParent = pVirtualBox;
898
899 return S_OK;
900}
901
902
903HRESULT Machine::getAccessible(BOOL *aAccessible)
904{
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->i_dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = i_registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->i_onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
959 {
960 /* return shortly */
961 aAccessError = NULL;
962 return S_OK;
963 }
964
965 HRESULT rc = S_OK;
966
967 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
968 rc = errorInfo.createObject();
969 if (SUCCEEDED(rc))
970 {
971 errorInfo->init(mData->mAccessError.getResultCode(),
972 mData->mAccessError.getInterfaceID().ref(),
973 Utf8Str(mData->mAccessError.getComponent()).c_str(),
974 Utf8Str(mData->mAccessError.getText()));
975 aAccessError = errorInfo;
976 }
977
978 return rc;
979}
980
981HRESULT Machine::getName(com::Utf8Str &aName)
982{
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 aName = mUserData->s.strName;
986
987 return S_OK;
988}
989
990HRESULT Machine::setName(const com::Utf8Str &aName)
991{
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995
996 if (test.isValid())
997 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
998
999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 HRESULT rc = i_checkStateDependency(MutableStateDep);
1002 if (FAILED(rc)) return rc;
1003
1004 i_setModified(IsModified_MachineData);
1005 mUserData.backup();
1006 mUserData->s.strName = aName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1012{
1013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 aDescription = mUserData->s.strDescription;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1021{
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 // this can be done in principle in any state as it doesn't affect the VM
1025 // significantly, but play safe by not messing around while complex
1026 // activities are going on
1027 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1028 if (FAILED(rc)) return rc;
1029
1030 i_setModified(IsModified_MachineData);
1031 mUserData.backup();
1032 mUserData->s.strDescription = aDescription;
1033
1034 return S_OK;
1035}
1036
1037HRESULT Machine::getId(com::Guid &aId)
1038{
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 aId = mData->mUuid;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049 aGroups.resize(mUserData->s.llGroups.size());
1050 size_t i = 0;
1051 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1052 it != mUserData->s.llGroups.end(); ++it, ++i)
1053 aGroups[i] = (*it);
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1059{
1060 StringsList llGroups;
1061 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1062 if (FAILED(rc))
1063 return rc;
1064
1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1066
1067 // changing machine groups is possible while the VM is offline
1068 rc = i_checkStateDependency(OfflineStateDep);
1069 if (FAILED(rc)) return rc;
1070
1071 i_setModified(IsModified_MachineData);
1072 mUserData.backup();
1073 mUserData->s.llGroups = llGroups;
1074
1075 return S_OK;
1076}
1077
1078HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1079{
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 aOSTypeId = mUserData->s.strOsType;
1083
1084 return S_OK;
1085}
1086
1087HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1088{
1089 /* look up the object by Id to check it is valid */
1090 ComPtr<IGuestOSType> guestOSType;
1091 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1092 if (FAILED(rc)) return rc;
1093
1094 /* when setting, always use the "etalon" value for consistency -- lookup
1095 * by ID is case-insensitive and the input value may have different case */
1096 Bstr osTypeId;
1097 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1098 if (FAILED(rc)) return rc;
1099
1100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1101
1102 rc = i_checkStateDependency(MutableStateDep);
1103 if (FAILED(rc)) return rc;
1104
1105 i_setModified(IsModified_MachineData);
1106 mUserData.backup();
1107 mUserData->s.strOsType = osTypeId;
1108
1109 return S_OK;
1110}
1111
1112HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1113{
1114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1115
1116 *aFirmwareType = mHWData->mFirmwareType;
1117
1118 return S_OK;
1119}
1120
1121HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1122{
1123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1124
1125 HRESULT rc = i_checkStateDependency(MutableStateDep);
1126 if (FAILED(rc)) return rc;
1127
1128 i_setModified(IsModified_MachineData);
1129 mHWData.backup();
1130 mHWData->mFirmwareType = aFirmwareType;
1131
1132 return S_OK;
1133}
1134
1135HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1136{
1137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1138
1139 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1140
1141 return S_OK;
1142}
1143
1144HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1145{
1146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 HRESULT rc = i_checkStateDependency(MutableStateDep);
1149 if (FAILED(rc)) return rc;
1150
1151 i_setModified(IsModified_MachineData);
1152 mHWData.backup();
1153 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aPointingHIDType = mHWData->mPointingHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mPointingHIDType = aPointingHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aChipsetType = mHWData->mChipsetType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 if (aChipsetType != mHWData->mChipsetType)
1198 {
1199 i_setModified(IsModified_MachineData);
1200 mHWData.backup();
1201 mHWData->mChipsetType = aChipsetType;
1202
1203 // Resize network adapter array, to be finalized on commit/rollback.
1204 // We must not throw away entries yet, otherwise settings are lost
1205 // without a way to roll back.
1206 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1207 size_t oldCount = mNetworkAdapters.size();
1208 if (newCount > oldCount)
1209 {
1210 mNetworkAdapters.resize(newCount);
1211 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1212 {
1213 unconst(mNetworkAdapters[slot]).createObject();
1214 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1215 }
1216 }
1217 }
1218
1219 return S_OK;
1220}
1221
1222HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1223{
1224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1225
1226 *aParavirtProvider = mHWData->mParavirtProvider;
1227
1228 return S_OK;
1229}
1230
1231HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1232{
1233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 HRESULT rc = i_checkStateDependency(MutableStateDep);
1236 if (FAILED(rc)) return rc;
1237
1238 if (aParavirtProvider != mHWData->mParavirtProvider)
1239 {
1240 i_setModified(IsModified_MachineData);
1241 mHWData.backup();
1242 mHWData->mParavirtProvider = aParavirtProvider;
1243 }
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1249{
1250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 *aParavirtProvider = mHWData->mParavirtProvider;
1253 switch (mHWData->mParavirtProvider)
1254 {
1255 case ParavirtProvider_None:
1256 case ParavirtProvider_HyperV:
1257 case ParavirtProvider_Minimal:
1258 break;
1259
1260 /* Resolve dynamic provider types to the effective types. */
1261 default:
1262 {
1263 ComPtr<IGuestOSType> ptrGuestOSType;
1264 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1266
1267 Bstr guestTypeFamilyId;
1268 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1269 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1270 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1271
1272 switch (mHWData->mParavirtProvider)
1273 {
1274 case ParavirtProvider_Legacy:
1275 {
1276 if (fOsXGuest)
1277 *aParavirtProvider = ParavirtProvider_Minimal;
1278 else
1279 *aParavirtProvider = ParavirtProvider_None;
1280 break;
1281 }
1282
1283 case ParavirtProvider_Default:
1284 {
1285 if (fOsXGuest)
1286 *aParavirtProvider = ParavirtProvider_Minimal;
1287 else if ( mUserData->s.strOsType == "Windows10"
1288 || mUserData->s.strOsType == "Windows10_64"
1289 || mUserData->s.strOsType == "Windows81"
1290 || mUserData->s.strOsType == "Windows81_64"
1291 || mUserData->s.strOsType == "Windows8"
1292 || mUserData->s.strOsType == "Windows8_64"
1293 || mUserData->s.strOsType == "Windows7"
1294 || mUserData->s.strOsType == "Windows7_64"
1295 || mUserData->s.strOsType == "WindowsVista"
1296 || mUserData->s.strOsType == "WindowsVista_64"
1297 || mUserData->s.strOsType == "Windows2012"
1298 || mUserData->s.strOsType == "Windows2012_64"
1299 || mUserData->s.strOsType == "Windows2008"
1300 || mUserData->s.strOsType == "Windows2008_64")
1301 {
1302 *aParavirtProvider = ParavirtProvider_HyperV;
1303 }
1304 else
1305 *aParavirtProvider = ParavirtProvider_None;
1306 break;
1307 }
1308 }
1309 break;
1310 }
1311 }
1312
1313 Assert( *aParavirtProvider == ParavirtProvider_None
1314 || *aParavirtProvider == ParavirtProvider_Minimal
1315 || *aParavirtProvider == ParavirtProvider_HyperV);
1316 return S_OK;
1317}
1318
1319HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1320{
1321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1322
1323 aHardwareVersion = mHWData->mHWVersion;
1324
1325 return S_OK;
1326}
1327
1328HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1329{
1330 /* check known version */
1331 Utf8Str hwVersion = aHardwareVersion;
1332 if ( hwVersion.compare("1") != 0
1333 && hwVersion.compare("2") != 0)
1334 return setError(E_INVALIDARG,
1335 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1336
1337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1338
1339 HRESULT rc = i_checkStateDependency(MutableStateDep);
1340 if (FAILED(rc)) return rc;
1341
1342 i_setModified(IsModified_MachineData);
1343 mHWData.backup();
1344 mHWData->mHWVersion = aHardwareVersion;
1345
1346 return S_OK;
1347}
1348
1349HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1350{
1351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 if (!mHWData->mHardwareUUID.isZero())
1354 aHardwareUUID = mHWData->mHardwareUUID;
1355 else
1356 aHardwareUUID = mData->mUuid;
1357
1358 return S_OK;
1359}
1360
1361HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1362{
1363 if (!aHardwareUUID.isValid())
1364 return E_INVALIDARG;
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT rc = i_checkStateDependency(MutableStateDep);
1369 if (FAILED(rc)) return rc;
1370
1371 i_setModified(IsModified_MachineData);
1372 mHWData.backup();
1373 if (aHardwareUUID == mData->mUuid)
1374 mHWData->mHardwareUUID.clear();
1375 else
1376 mHWData->mHardwareUUID = aHardwareUUID;
1377
1378 return S_OK;
1379}
1380
1381HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1382{
1383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1384
1385 *aMemorySize = mHWData->mMemorySize;
1386
1387 return S_OK;
1388}
1389
1390HRESULT Machine::setMemorySize(ULONG aMemorySize)
1391{
1392 /* check RAM limits */
1393 if ( aMemorySize < MM_RAM_MIN_IN_MB
1394 || aMemorySize > MM_RAM_MAX_IN_MB
1395 )
1396 return setError(E_INVALIDARG,
1397 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1398 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1399
1400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1401
1402 HRESULT rc = i_checkStateDependency(MutableStateDep);
1403 if (FAILED(rc)) return rc;
1404
1405 i_setModified(IsModified_MachineData);
1406 mHWData.backup();
1407 mHWData->mMemorySize = aMemorySize;
1408
1409 return S_OK;
1410}
1411
1412HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1413{
1414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 *aCPUCount = mHWData->mCPUCount;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::setCPUCount(ULONG aCPUCount)
1422{
1423 /* check CPU limits */
1424 if ( aCPUCount < SchemaDefs::MinCPUCount
1425 || aCPUCount > SchemaDefs::MaxCPUCount
1426 )
1427 return setError(E_INVALIDARG,
1428 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1429 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1430
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1434 if (mHWData->mCPUHotPlugEnabled)
1435 {
1436 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1437 {
1438 if (mHWData->mCPUAttached[idx])
1439 return setError(E_INVALIDARG,
1440 tr("There is still a CPU attached to socket %lu."
1441 "Detach the CPU before removing the socket"),
1442 aCPUCount, idx+1);
1443 }
1444 }
1445
1446 HRESULT rc = i_checkStateDependency(MutableStateDep);
1447 if (FAILED(rc)) return rc;
1448
1449 i_setModified(IsModified_MachineData);
1450 mHWData.backup();
1451 mHWData->mCPUCount = aCPUCount;
1452
1453 return S_OK;
1454}
1455
1456HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1457{
1458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1461
1462 return S_OK;
1463}
1464
1465HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1466{
1467 HRESULT rc = S_OK;
1468
1469 /* check throttle limits */
1470 if ( aCPUExecutionCap < 1
1471 || aCPUExecutionCap > 100
1472 )
1473 return setError(E_INVALIDARG,
1474 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1475 aCPUExecutionCap, 1, 100);
1476
1477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1478
1479 alock.release();
1480 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1481 alock.acquire();
1482 if (FAILED(rc)) return rc;
1483
1484 i_setModified(IsModified_MachineData);
1485 mHWData.backup();
1486 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1487
1488 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1489 if (Global::IsOnline(mData->mMachineState))
1490 i_saveSettings(NULL);
1491
1492 return S_OK;
1493}
1494
1495HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1496{
1497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1498
1499 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1500
1501 return S_OK;
1502}
1503
1504HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1505{
1506 HRESULT rc = S_OK;
1507
1508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1509
1510 rc = i_checkStateDependency(MutableStateDep);
1511 if (FAILED(rc)) return rc;
1512
1513 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1514 {
1515 if (aCPUHotPlugEnabled)
1516 {
1517 i_setModified(IsModified_MachineData);
1518 mHWData.backup();
1519
1520 /* Add the amount of CPUs currently attached */
1521 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1522 mHWData->mCPUAttached[i] = true;
1523 }
1524 else
1525 {
1526 /*
1527 * We can disable hotplug only if the amount of maximum CPUs is equal
1528 * to the amount of attached CPUs
1529 */
1530 unsigned cCpusAttached = 0;
1531 unsigned iHighestId = 0;
1532
1533 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1534 {
1535 if (mHWData->mCPUAttached[i])
1536 {
1537 cCpusAttached++;
1538 iHighestId = i;
1539 }
1540 }
1541
1542 if ( (cCpusAttached != mHWData->mCPUCount)
1543 || (iHighestId >= mHWData->mCPUCount))
1544 return setError(E_INVALIDARG,
1545 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1546
1547 i_setModified(IsModified_MachineData);
1548 mHWData.backup();
1549 }
1550 }
1551
1552 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1553
1554 return rc;
1555}
1556
1557HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1558{
1559#ifdef VBOX_WITH_USB_CARDREADER
1560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1561
1562 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1563
1564 return S_OK;
1565#else
1566 NOREF(aEmulatedUSBCardReaderEnabled);
1567 return E_NOTIMPL;
1568#endif
1569}
1570
1571HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1572{
1573#ifdef VBOX_WITH_USB_CARDREADER
1574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1575
1576 HRESULT rc = i_checkStateDependency(MutableStateDep);
1577 if (FAILED(rc)) return rc;
1578
1579 i_setModified(IsModified_MachineData);
1580 mHWData.backup();
1581 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1582
1583 return S_OK;
1584#else
1585 NOREF(aEmulatedUSBCardReaderEnabled);
1586 return E_NOTIMPL;
1587#endif
1588}
1589
1590HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1591{
1592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1593
1594 *aHPETEnabled = mHWData->mHPETEnabled;
1595
1596 return S_OK;
1597}
1598
1599HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1600{
1601 HRESULT rc = S_OK;
1602
1603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1604
1605 rc = i_checkStateDependency(MutableStateDep);
1606 if (FAILED(rc)) return rc;
1607
1608 i_setModified(IsModified_MachineData);
1609 mHWData.backup();
1610
1611 mHWData->mHPETEnabled = aHPETEnabled;
1612
1613 return rc;
1614}
1615
1616HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1617{
1618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1619
1620 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1621 return S_OK;
1622}
1623
1624HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1625{
1626 HRESULT rc = S_OK;
1627
1628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1633
1634 alock.release();
1635 rc = i_onVideoCaptureChange();
1636 alock.acquire();
1637 if (FAILED(rc))
1638 {
1639 /*
1640 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1641 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1642 * determine if it should start or stop capturing. Therefore we need to manually
1643 * undo change.
1644 */
1645 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1646 return rc;
1647 }
1648
1649 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1650 if (Global::IsOnline(mData->mMachineState))
1651 i_saveSettings(NULL);
1652
1653 return rc;
1654}
1655
1656HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1657{
1658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1659 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1660 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1661 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1662 return S_OK;
1663}
1664
1665HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1666{
1667 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1668 bool fChanged = false;
1669
1670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1671
1672 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1673 {
1674 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1675 {
1676 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1677 fChanged = true;
1678 }
1679 }
1680 if (fChanged)
1681 {
1682 alock.release();
1683 HRESULT rc = i_onVideoCaptureChange();
1684 alock.acquire();
1685 if (FAILED(rc)) return rc;
1686 i_setModified(IsModified_MachineData);
1687
1688 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1689 if (Global::IsOnline(mData->mMachineState))
1690 i_saveSettings(NULL);
1691 }
1692
1693 return S_OK;
1694}
1695
1696HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699 if (mHWData->mVideoCaptureFile.isEmpty())
1700 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1701 else
1702 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1703 return S_OK;
1704}
1705
1706HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1707{
1708 Utf8Str strFile(aVideoCaptureFile);
1709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1710
1711 if ( Global::IsOnline(mData->mMachineState)
1712 && mHWData->mVideoCaptureEnabled)
1713 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1714
1715 if (!RTPathStartsWithRoot(strFile.c_str()))
1716 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1717
1718 if (!strFile.isEmpty())
1719 {
1720 Utf8Str defaultFile;
1721 i_getDefaultVideoCaptureFile(defaultFile);
1722 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1723 strFile.setNull();
1724 }
1725
1726 i_setModified(IsModified_MachineData);
1727 mHWData.backup();
1728 mHWData->mVideoCaptureFile = strFile;
1729
1730 return S_OK;
1731}
1732
1733HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1734{
1735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1736 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1737 return S_OK;
1738}
1739
1740HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1741{
1742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1743
1744 if ( Global::IsOnline(mData->mMachineState)
1745 && mHWData->mVideoCaptureEnabled)
1746 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1747
1748 i_setModified(IsModified_MachineData);
1749 mHWData.backup();
1750 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1751
1752 return S_OK;
1753}
1754
1755HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1756{
1757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1758 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1759 return S_OK;
1760}
1761
1762HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1763{
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 if ( Global::IsOnline(mData->mMachineState)
1767 && mHWData->mVideoCaptureEnabled)
1768 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1769
1770 i_setModified(IsModified_MachineData);
1771 mHWData.backup();
1772 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1773
1774 return S_OK;
1775}
1776
1777HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1778{
1779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1780 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1781 return S_OK;
1782}
1783
1784HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1785{
1786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1787
1788 if ( Global::IsOnline(mData->mMachineState)
1789 && mHWData->mVideoCaptureEnabled)
1790 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1791
1792 i_setModified(IsModified_MachineData);
1793 mHWData.backup();
1794 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1795
1796 return S_OK;
1797}
1798
1799HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1800{
1801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1802 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1803 return S_OK;
1804}
1805
1806HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1807{
1808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1809
1810 if ( Global::IsOnline(mData->mMachineState)
1811 && mHWData->mVideoCaptureEnabled)
1812 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1813
1814 i_setModified(IsModified_MachineData);
1815 mHWData.backup();
1816 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1817
1818 return S_OK;
1819}
1820
1821HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1822{
1823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1824 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1825 return S_OK;
1826}
1827
1828HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1829{
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831
1832 if ( Global::IsOnline(mData->mMachineState)
1833 && mHWData->mVideoCaptureEnabled)
1834 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1835
1836 i_setModified(IsModified_MachineData);
1837 mHWData.backup();
1838 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1839
1840 return S_OK;
1841}
1842
1843HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1844{
1845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1846 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1847 return S_OK;
1848}
1849
1850HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1851{
1852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1853
1854 if ( Global::IsOnline(mData->mMachineState)
1855 && mHWData->mVideoCaptureEnabled)
1856 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1857
1858 i_setModified(IsModified_MachineData);
1859 mHWData.backup();
1860 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1861
1862 return S_OK;
1863}
1864
1865HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1866{
1867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1868
1869 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1870 return S_OK;
1871}
1872
1873HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1874{
1875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 if ( Global::IsOnline(mData->mMachineState)
1878 && mHWData->mVideoCaptureEnabled)
1879 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1880
1881 i_setModified(IsModified_MachineData);
1882 mHWData.backup();
1883 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1884
1885 return S_OK;
1886}
1887
1888HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1889{
1890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1893
1894 return S_OK;
1895}
1896
1897HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1898{
1899 switch (aGraphicsControllerType)
1900 {
1901 case GraphicsControllerType_Null:
1902 case GraphicsControllerType_VBoxVGA:
1903#ifdef VBOX_WITH_VMSVGA
1904 case GraphicsControllerType_VMSVGA:
1905#endif
1906 break;
1907 default:
1908 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1909 }
1910
1911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1912
1913 HRESULT rc = i_checkStateDependency(MutableStateDep);
1914 if (FAILED(rc)) return rc;
1915
1916 i_setModified(IsModified_MachineData);
1917 mHWData.backup();
1918 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1919
1920 return S_OK;
1921}
1922
1923HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1924{
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 *aVRAMSize = mHWData->mVRAMSize;
1928
1929 return S_OK;
1930}
1931
1932HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1933{
1934 /* check VRAM limits */
1935 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1936 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1937 return setError(E_INVALIDARG,
1938 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1939 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1940
1941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1942
1943 HRESULT rc = i_checkStateDependency(MutableStateDep);
1944 if (FAILED(rc)) return rc;
1945
1946 i_setModified(IsModified_MachineData);
1947 mHWData.backup();
1948 mHWData->mVRAMSize = aVRAMSize;
1949
1950 return S_OK;
1951}
1952
1953/** @todo this method should not be public */
1954HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1955{
1956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1959
1960 return S_OK;
1961}
1962
1963/**
1964 * Set the memory balloon size.
1965 *
1966 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1967 * we have to make sure that we never call IGuest from here.
1968 */
1969HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1970{
1971 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1972#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1973 /* check limits */
1974 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1975 return setError(E_INVALIDARG,
1976 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1977 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1978
1979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1980
1981 i_setModified(IsModified_MachineData);
1982 mHWData.backup();
1983 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1984
1985 return S_OK;
1986#else
1987 NOREF(aMemoryBalloonSize);
1988 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1989#endif
1990}
1991
1992HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1993{
1994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
1996 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1997 return S_OK;
1998}
1999
2000HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2001{
2002#ifdef VBOX_WITH_PAGE_SHARING
2003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2004
2005 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2006 i_setModified(IsModified_MachineData);
2007 mHWData.backup();
2008 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2009 return S_OK;
2010#else
2011 NOREF(aPageFusionEnabled);
2012 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2013#endif
2014}
2015
2016HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2017{
2018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2019
2020 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2021
2022 return S_OK;
2023}
2024
2025HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2026{
2027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2028
2029 HRESULT rc = i_checkStateDependency(MutableStateDep);
2030 if (FAILED(rc)) return rc;
2031
2032 /** @todo check validity! */
2033
2034 i_setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2037
2038 return S_OK;
2039}
2040
2041
2042HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2043{
2044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2047
2048 return S_OK;
2049}
2050
2051HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2052{
2053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2054
2055 HRESULT rc = i_checkStateDependency(MutableStateDep);
2056 if (FAILED(rc)) return rc;
2057
2058 /** @todo check validity! */
2059 i_setModified(IsModified_MachineData);
2060 mHWData.backup();
2061 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2062
2063 return S_OK;
2064}
2065
2066HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2067{
2068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2069
2070 *aMonitorCount = mHWData->mMonitorCount;
2071
2072 return S_OK;
2073}
2074
2075HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2076{
2077 /* make sure monitor count is a sensible number */
2078 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2079 return setError(E_INVALIDARG,
2080 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2081 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2082
2083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 HRESULT rc = i_checkStateDependency(MutableStateDep);
2086 if (FAILED(rc)) return rc;
2087
2088 i_setModified(IsModified_MachineData);
2089 mHWData.backup();
2090 mHWData->mMonitorCount = aMonitorCount;
2091
2092 return S_OK;
2093}
2094
2095HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2096{
2097 /* mBIOSSettings is constant during life time, no need to lock */
2098 aBIOSSettings = mBIOSSettings;
2099
2100 return S_OK;
2101}
2102
2103HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2104{
2105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2106
2107 switch (aProperty)
2108 {
2109 case CPUPropertyType_PAE:
2110 *aValue = mHWData->mPAEEnabled;
2111 break;
2112
2113 case CPUPropertyType_Synthetic:
2114 *aValue = mHWData->mSyntheticCpu;
2115 break;
2116
2117 case CPUPropertyType_LongMode:
2118 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2119 *aValue = TRUE;
2120 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2121 *aValue = FALSE;
2122#if HC_ARCH_BITS == 64
2123 else
2124 *aValue = TRUE;
2125#else
2126 else
2127 {
2128 *aValue = FALSE;
2129
2130 ComPtr<IGuestOSType> ptrGuestOSType;
2131 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2132 if (SUCCEEDED(hrc2))
2133 {
2134 BOOL fIs64Bit = FALSE;
2135 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2136 if (SUCCEEDED(hrc2) && fIs64Bit)
2137 {
2138 ComObjPtr<Host> ptrHost = mParent->i_host();
2139 alock.release();
2140
2141 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2142 if (FAILED(hrc2))
2143 *aValue = FALSE;
2144 }
2145 }
2146 }
2147#endif
2148 break;
2149
2150 case CPUPropertyType_TripleFaultReset:
2151 *aValue = mHWData->mTripleFaultReset;
2152 break;
2153
2154 default:
2155 return E_INVALIDARG;
2156 }
2157 return S_OK;
2158}
2159
2160HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2161{
2162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2163
2164 HRESULT rc = i_checkStateDependency(MutableStateDep);
2165 if (FAILED(rc)) return rc;
2166
2167 switch (aProperty)
2168 {
2169 case CPUPropertyType_PAE:
2170 i_setModified(IsModified_MachineData);
2171 mHWData.backup();
2172 mHWData->mPAEEnabled = !!aValue;
2173 break;
2174
2175 case CPUPropertyType_Synthetic:
2176 i_setModified(IsModified_MachineData);
2177 mHWData.backup();
2178 mHWData->mSyntheticCpu = !!aValue;
2179 break;
2180
2181 case CPUPropertyType_LongMode:
2182 i_setModified(IsModified_MachineData);
2183 mHWData.backup();
2184 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2185 break;
2186
2187 case CPUPropertyType_TripleFaultReset:
2188 i_setModified(IsModified_MachineData);
2189 mHWData.backup();
2190 mHWData->mTripleFaultReset = !!aValue;
2191 break;
2192
2193 default:
2194 return E_INVALIDARG;
2195 }
2196 return S_OK;
2197}
2198
2199HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2200{
2201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2202
2203 switch(aId)
2204 {
2205 case 0x0:
2206 case 0x1:
2207 case 0x2:
2208 case 0x3:
2209 case 0x4:
2210 case 0x5:
2211 case 0x6:
2212 case 0x7:
2213 case 0x8:
2214 case 0x9:
2215 case 0xA:
2216 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2217 return E_INVALIDARG;
2218
2219 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2220 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2221 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2222 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2223 break;
2224
2225 case 0x80000000:
2226 case 0x80000001:
2227 case 0x80000002:
2228 case 0x80000003:
2229 case 0x80000004:
2230 case 0x80000005:
2231 case 0x80000006:
2232 case 0x80000007:
2233 case 0x80000008:
2234 case 0x80000009:
2235 case 0x8000000A:
2236 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2237 return E_INVALIDARG;
2238
2239 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2240 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2241 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2242 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2243 break;
2244
2245 default:
2246 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2247 }
2248 return S_OK;
2249}
2250
2251
2252HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2253{
2254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2255
2256 HRESULT rc = i_checkStateDependency(MutableStateDep);
2257 if (FAILED(rc)) return rc;
2258
2259 switch(aId)
2260 {
2261 case 0x0:
2262 case 0x1:
2263 case 0x2:
2264 case 0x3:
2265 case 0x4:
2266 case 0x5:
2267 case 0x6:
2268 case 0x7:
2269 case 0x8:
2270 case 0x9:
2271 case 0xA:
2272 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2273 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2274 i_setModified(IsModified_MachineData);
2275 mHWData.backup();
2276 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2277 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2278 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2279 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2280 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2281 break;
2282
2283 case 0x80000000:
2284 case 0x80000001:
2285 case 0x80000002:
2286 case 0x80000003:
2287 case 0x80000004:
2288 case 0x80000005:
2289 case 0x80000006:
2290 case 0x80000007:
2291 case 0x80000008:
2292 case 0x80000009:
2293 case 0x8000000A:
2294 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2295 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2299 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2300 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2301 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2302 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2303 break;
2304
2305 default:
2306 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2307 }
2308 return S_OK;
2309}
2310
2311HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2312{
2313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2314
2315 HRESULT rc = i_checkStateDependency(MutableStateDep);
2316 if (FAILED(rc)) return rc;
2317
2318 switch(aId)
2319 {
2320 case 0x0:
2321 case 0x1:
2322 case 0x2:
2323 case 0x3:
2324 case 0x4:
2325 case 0x5:
2326 case 0x6:
2327 case 0x7:
2328 case 0x8:
2329 case 0x9:
2330 case 0xA:
2331 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2332 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 /* Invalidate leaf. */
2336 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2337 break;
2338
2339 case 0x80000000:
2340 case 0x80000001:
2341 case 0x80000002:
2342 case 0x80000003:
2343 case 0x80000004:
2344 case 0x80000005:
2345 case 0x80000006:
2346 case 0x80000007:
2347 case 0x80000008:
2348 case 0x80000009:
2349 case 0x8000000A:
2350 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2351 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2352 i_setModified(IsModified_MachineData);
2353 mHWData.backup();
2354 /* Invalidate leaf. */
2355 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2356 break;
2357
2358 default:
2359 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2360 }
2361 return S_OK;
2362}
2363
2364HRESULT Machine::removeAllCPUIDLeaves()
2365{
2366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2367
2368 HRESULT rc = i_checkStateDependency(MutableStateDep);
2369 if (FAILED(rc)) return rc;
2370
2371 i_setModified(IsModified_MachineData);
2372 mHWData.backup();
2373
2374 /* Invalidate all standard leafs. */
2375 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2376 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2377
2378 /* Invalidate all extended leafs. */
2379 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2380 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2381
2382 return S_OK;
2383}
2384HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2385{
2386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2387
2388 switch(aProperty)
2389 {
2390 case HWVirtExPropertyType_Enabled:
2391 *aValue = mHWData->mHWVirtExEnabled;
2392 break;
2393
2394 case HWVirtExPropertyType_VPID:
2395 *aValue = mHWData->mHWVirtExVPIDEnabled;
2396 break;
2397
2398 case HWVirtExPropertyType_NestedPaging:
2399 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2400 break;
2401
2402 case HWVirtExPropertyType_UnrestrictedExecution:
2403 *aValue = mHWData->mHWVirtExUXEnabled;
2404 break;
2405
2406 case HWVirtExPropertyType_LargePages:
2407 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2408#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2409 *aValue = FALSE;
2410#endif
2411 break;
2412
2413 case HWVirtExPropertyType_Force:
2414 *aValue = mHWData->mHWVirtExForceEnabled;
2415 break;
2416
2417 default:
2418 return E_INVALIDARG;
2419 }
2420 return S_OK;
2421}
2422
2423HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2424{
2425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2426
2427 HRESULT rc = i_checkStateDependency(MutableStateDep);
2428 if (FAILED(rc)) return rc;
2429
2430 switch(aProperty)
2431 {
2432 case HWVirtExPropertyType_Enabled:
2433 i_setModified(IsModified_MachineData);
2434 mHWData.backup();
2435 mHWData->mHWVirtExEnabled = !!aValue;
2436 break;
2437
2438 case HWVirtExPropertyType_VPID:
2439 i_setModified(IsModified_MachineData);
2440 mHWData.backup();
2441 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2442 break;
2443
2444 case HWVirtExPropertyType_NestedPaging:
2445 i_setModified(IsModified_MachineData);
2446 mHWData.backup();
2447 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2448 break;
2449
2450 case HWVirtExPropertyType_UnrestrictedExecution:
2451 i_setModified(IsModified_MachineData);
2452 mHWData.backup();
2453 mHWData->mHWVirtExUXEnabled = !!aValue;
2454 break;
2455
2456 case HWVirtExPropertyType_LargePages:
2457 i_setModified(IsModified_MachineData);
2458 mHWData.backup();
2459 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2460 break;
2461
2462 case HWVirtExPropertyType_Force:
2463 i_setModified(IsModified_MachineData);
2464 mHWData.backup();
2465 mHWData->mHWVirtExForceEnabled = !!aValue;
2466 break;
2467
2468 default:
2469 return E_INVALIDARG;
2470 }
2471
2472 return S_OK;
2473}
2474
2475HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2476{
2477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2480
2481 return S_OK;
2482}
2483
2484HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2485{
2486 /* @todo (r=dmik):
2487 * 1. Allow to change the name of the snapshot folder containing snapshots
2488 * 2. Rename the folder on disk instead of just changing the property
2489 * value (to be smart and not to leave garbage). Note that it cannot be
2490 * done here because the change may be rolled back. Thus, the right
2491 * place is #saveSettings().
2492 */
2493
2494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 HRESULT rc = i_checkStateDependency(MutableStateDep);
2497 if (FAILED(rc)) return rc;
2498
2499 if (!mData->mCurrentSnapshot.isNull())
2500 return setError(E_FAIL,
2501 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2502
2503 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2504
2505 if (strSnapshotFolder.isEmpty())
2506 strSnapshotFolder = "Snapshots";
2507 int vrc = i_calculateFullPath(strSnapshotFolder,
2508 strSnapshotFolder);
2509 if (RT_FAILURE(vrc))
2510 return setError(E_FAIL,
2511 tr("Invalid snapshot folder '%s' (%Rrc)"),
2512 strSnapshotFolder.c_str(), vrc);
2513
2514 i_setModified(IsModified_MachineData);
2515 mUserData.backup();
2516
2517 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2518
2519 return S_OK;
2520}
2521
2522HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2523{
2524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2525
2526 aMediumAttachments.resize(mMediaData->mAttachments.size());
2527 size_t i = 0;
2528 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2529 it != mMediaData->mAttachments.end(); ++it, ++i)
2530 aMediumAttachments[i] = *it;
2531
2532 return S_OK;
2533}
2534
2535HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2536{
2537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2538
2539 Assert(!!mVRDEServer);
2540
2541 aVRDEServer = mVRDEServer;
2542
2543 return S_OK;
2544}
2545
2546HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2547{
2548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 aAudioAdapter = mAudioAdapter;
2551
2552 return S_OK;
2553}
2554
2555HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2556{
2557#ifdef VBOX_WITH_VUSB
2558 clearError();
2559 MultiResult rc(S_OK);
2560
2561# ifdef VBOX_WITH_USB
2562 rc = mParent->i_host()->i_checkUSBProxyService();
2563 if (FAILED(rc)) return rc;
2564# endif
2565
2566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568 USBControllerList data = *mUSBControllers.data();
2569 aUSBControllers.resize(data.size());
2570 size_t i = 0;
2571 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2572 aUSBControllers[i] = *it;
2573
2574 return S_OK;
2575#else
2576 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2577 * extended error info to indicate that USB is simply not available
2578 * (w/o treating it as a failure), for example, as in OSE */
2579 NOREF(aUSBControllers);
2580 ReturnComNotImplemented();
2581#endif /* VBOX_WITH_VUSB */
2582}
2583
2584HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2585{
2586#ifdef VBOX_WITH_VUSB
2587 clearError();
2588 MultiResult rc(S_OK);
2589
2590# ifdef VBOX_WITH_USB
2591 rc = mParent->i_host()->i_checkUSBProxyService();
2592 if (FAILED(rc)) return rc;
2593# endif
2594
2595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597 aUSBDeviceFilters = mUSBDeviceFilters;
2598 return rc;
2599#else
2600 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2601 * extended error info to indicate that USB is simply not available
2602 * (w/o treating it as a failure), for example, as in OSE */
2603 NOREF(aUSBDeviceFilters);
2604 ReturnComNotImplemented();
2605#endif /* VBOX_WITH_VUSB */
2606}
2607
2608HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2609{
2610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 aSettingsFilePath = mData->m_strConfigFileFull;
2613
2614 return S_OK;
2615}
2616
2617HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2618{
2619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2620
2621 HRESULT rc = i_checkStateDependency(MutableStateDep);
2622 if (FAILED(rc)) return rc;
2623
2624 if (!mData->pMachineConfigFile->fileExists())
2625 // this is a new machine, and no config file exists yet:
2626 *aSettingsModified = TRUE;
2627 else
2628 *aSettingsModified = (mData->flModifications != 0);
2629
2630 return S_OK;
2631}
2632
2633HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2634{
2635
2636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2637
2638 *aSessionState = mData->mSession.mState;
2639
2640 return S_OK;
2641}
2642
2643HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2644{
2645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2646
2647 aSessionType = mData->mSession.mType;
2648
2649 return S_OK;
2650}
2651
2652HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2653{
2654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 *aSessionPID = mData->mSession.mPID;
2657
2658 return S_OK;
2659}
2660
2661HRESULT Machine::getState(MachineState_T *aState)
2662{
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 *aState = mData->mMachineState;
2666
2667 return S_OK;
2668}
2669
2670HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2671{
2672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2673
2674 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2680{
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 aStateFilePath = mSSData->strStateFilePath;
2684
2685 return S_OK;
2686}
2687
2688HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2689{
2690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 i_getLogFolder(aLogFolder);
2693
2694 return S_OK;
2695}
2696
2697HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2698{
2699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2700
2701 aCurrentSnapshot = mData->mCurrentSnapshot;
2702
2703 return S_OK;
2704}
2705
2706HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2707{
2708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2709
2710 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2711 ? 0
2712 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2713
2714 return S_OK;
2715}
2716
2717HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2718{
2719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2720
2721 /* Note: for machines with no snapshots, we always return FALSE
2722 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2723 * reasons :) */
2724
2725 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2726 ? FALSE
2727 : mData->mCurrentStateModified;
2728
2729 return S_OK;
2730}
2731
2732HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2733{
2734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 aSharedFolders.resize(mHWData->mSharedFolders.size());
2737 size_t i = 0;
2738 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2739 it != mHWData->mSharedFolders.end(); ++i, ++it)
2740 aSharedFolders[i] = *it;
2741
2742 return S_OK;
2743}
2744
2745HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2746{
2747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2748
2749 *aClipboardMode = mHWData->mClipboardMode;
2750
2751 return S_OK;
2752}
2753
2754HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2755{
2756 HRESULT rc = S_OK;
2757
2758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 alock.release();
2761 rc = i_onClipboardModeChange(aClipboardMode);
2762 alock.acquire();
2763 if (FAILED(rc)) return rc;
2764
2765 i_setModified(IsModified_MachineData);
2766 mHWData.backup();
2767 mHWData->mClipboardMode = aClipboardMode;
2768
2769 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2770 if (Global::IsOnline(mData->mMachineState))
2771 i_saveSettings(NULL);
2772
2773 return S_OK;
2774}
2775
2776HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2777{
2778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2779
2780 *aDnDMode = mHWData->mDnDMode;
2781
2782 return S_OK;
2783}
2784
2785HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2786{
2787 HRESULT rc = S_OK;
2788
2789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 alock.release();
2792 rc = i_onDnDModeChange(aDnDMode);
2793
2794 alock.acquire();
2795 if (FAILED(rc)) return rc;
2796
2797 i_setModified(IsModified_MachineData);
2798 mHWData.backup();
2799 mHWData->mDnDMode = aDnDMode;
2800
2801 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2802 if (Global::IsOnline(mData->mMachineState))
2803 i_saveSettings(NULL);
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 try
2813 {
2814 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2815 }
2816 catch (...)
2817 {
2818 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2819 }
2820
2821 return S_OK;
2822}
2823
2824HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2825{
2826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 HRESULT rc = i_checkStateDependency(MutableStateDep);
2829 if (FAILED(rc)) return rc;
2830
2831 i_setModified(IsModified_MachineData);
2832 mHWData.backup();
2833 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2834 return rc;
2835}
2836
2837HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2838{
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840 StorageControllerList data = *mStorageControllers.data();
2841 size_t i = 0;
2842 aStorageControllers.resize(data.size());
2843 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2844 aStorageControllers[i] = *it;
2845 return S_OK;
2846}
2847
2848HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2849{
2850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 *aEnabled = mUserData->s.fTeleporterEnabled;
2853
2854 return S_OK;
2855}
2856
2857HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2858{
2859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 /* Only allow it to be set to true when PoweredOff or Aborted.
2862 (Clearing it is always permitted.) */
2863 if ( aTeleporterEnabled
2864 && mData->mRegistered
2865 && ( !i_isSessionMachine()
2866 || ( mData->mMachineState != MachineState_PoweredOff
2867 && mData->mMachineState != MachineState_Teleported
2868 && mData->mMachineState != MachineState_Aborted
2869 )
2870 )
2871 )
2872 return setError(VBOX_E_INVALID_VM_STATE,
2873 tr("The machine is not powered off (state is %s)"),
2874 Global::stringifyMachineState(mData->mMachineState));
2875
2876 i_setModified(IsModified_MachineData);
2877 mUserData.backup();
2878 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2879
2880 return S_OK;
2881}
2882
2883HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2884{
2885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2886
2887 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2888
2889 return S_OK;
2890}
2891
2892HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2893{
2894 if (aTeleporterPort >= _64K)
2895 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2896
2897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 HRESULT rc = i_checkStateDependency(MutableStateDep);
2900 if (FAILED(rc)) return rc;
2901
2902 i_setModified(IsModified_MachineData);
2903 mUserData.backup();
2904 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2905
2906 return S_OK;
2907}
2908
2909HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2910{
2911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2912
2913 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2914
2915 return S_OK;
2916}
2917
2918HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2919{
2920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 HRESULT rc = i_checkStateDependency(MutableStateDep);
2923 if (FAILED(rc)) return rc;
2924
2925 i_setModified(IsModified_MachineData);
2926 mUserData.backup();
2927 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2928
2929 return S_OK;
2930}
2931
2932HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2933{
2934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2935 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2936
2937 return S_OK;
2938}
2939
2940HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2941{
2942 /*
2943 * Hash the password first.
2944 */
2945 com::Utf8Str aT = aTeleporterPassword;
2946
2947 if (!aT.isEmpty())
2948 {
2949 if (VBoxIsPasswordHashed(&aT))
2950 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2951 VBoxHashPassword(&aT);
2952 }
2953
2954 /*
2955 * Do the update.
2956 */
2957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2958 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2959 if (SUCCEEDED(hrc))
2960 {
2961 i_setModified(IsModified_MachineData);
2962 mUserData.backup();
2963 mUserData->s.strTeleporterPassword = aT;
2964 }
2965
2966 return hrc;
2967}
2968
2969HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2970{
2971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2972
2973 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2974 return S_OK;
2975}
2976
2977HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2978{
2979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 /* @todo deal with running state change. */
2982 HRESULT rc = i_checkStateDependency(MutableStateDep);
2983 if (FAILED(rc)) return rc;
2984
2985 i_setModified(IsModified_MachineData);
2986 mUserData.backup();
2987 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2988 return S_OK;
2989}
2990
2991HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2992{
2993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2994
2995 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2996 return S_OK;
2997}
2998
2999HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3000{
3001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 /* @todo deal with running state change. */
3004 HRESULT rc = i_checkStateDependency(MutableStateDep);
3005 if (FAILED(rc)) return rc;
3006
3007 i_setModified(IsModified_MachineData);
3008 mUserData.backup();
3009 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3010 return S_OK;
3011}
3012
3013HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3014{
3015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3016
3017 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3018 return S_OK;
3019}
3020
3021HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3022{
3023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 /* @todo deal with running state change. */
3026 HRESULT rc = i_checkStateDependency(MutableStateDep);
3027 if (FAILED(rc)) return rc;
3028
3029 i_setModified(IsModified_MachineData);
3030 mUserData.backup();
3031 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3032 return S_OK;
3033}
3034
3035HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3036{
3037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3038
3039 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3040
3041 return S_OK;
3042}
3043
3044HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3045{
3046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3047
3048 /* @todo deal with running state change. */
3049 HRESULT rc = i_checkStateDependency(MutableStateDep);
3050 if (FAILED(rc)) return rc;
3051
3052 i_setModified(IsModified_MachineData);
3053 mUserData.backup();
3054 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3055
3056 return S_OK;
3057}
3058
3059HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3060{
3061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3064 return S_OK;
3065}
3066
3067HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3068{
3069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3070
3071 /* @todo deal with running state change. */
3072 HRESULT rc = i_checkStateDependency(MutableStateDep);
3073 if (FAILED(rc)) return rc;
3074
3075 i_setModified(IsModified_MachineData);
3076 mUserData.backup();
3077 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3078 return S_OK;
3079}
3080
3081HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3082{
3083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
3085 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3086
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3091{
3092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 /* Only allow it to be set to true when PoweredOff or Aborted.
3095 (Clearing it is always permitted.) */
3096 if ( aRTCUseUTC
3097 && mData->mRegistered
3098 && ( !i_isSessionMachine()
3099 || ( mData->mMachineState != MachineState_PoweredOff
3100 && mData->mMachineState != MachineState_Teleported
3101 && mData->mMachineState != MachineState_Aborted
3102 )
3103 )
3104 )
3105 return setError(VBOX_E_INVALID_VM_STATE,
3106 tr("The machine is not powered off (state is %s)"),
3107 Global::stringifyMachineState(mData->mMachineState));
3108
3109 i_setModified(IsModified_MachineData);
3110 mUserData.backup();
3111 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3112
3113 return S_OK;
3114}
3115
3116HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3117{
3118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3119
3120 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3121
3122 return S_OK;
3123}
3124
3125HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3126{
3127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3128
3129 HRESULT rc = i_checkStateDependency(MutableStateDep);
3130 if (FAILED(rc)) return rc;
3131
3132 i_setModified(IsModified_MachineData);
3133 mHWData.backup();
3134 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3135
3136 return S_OK;
3137}
3138
3139HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3140{
3141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3142
3143 *aIOCacheSize = mHWData->mIOCacheSize;
3144
3145 return S_OK;
3146}
3147
3148HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3149{
3150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3151
3152 HRESULT rc = i_checkStateDependency(MutableStateDep);
3153 if (FAILED(rc)) return rc;
3154
3155 i_setModified(IsModified_MachineData);
3156 mHWData.backup();
3157 mHWData->mIOCacheSize = aIOCacheSize;
3158
3159 return S_OK;
3160}
3161
3162
3163/**
3164 * @note Locks objects!
3165 */
3166HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3167 LockType_T aLockType)
3168
3169{
3170 /* check the session state */
3171 SessionState_T state;
3172 HRESULT rc = aSession->COMGETTER(State)(&state);
3173 if (FAILED(rc)) return rc;
3174
3175 if (state != SessionState_Unlocked)
3176 return setError(VBOX_E_INVALID_OBJECT_STATE,
3177 tr("The given session is busy"));
3178
3179 // get the client's IInternalSessionControl interface
3180 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3181 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3182 E_INVALIDARG);
3183
3184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3185
3186 if (!mData->mRegistered)
3187 return setError(E_UNEXPECTED,
3188 tr("The machine '%s' is not registered"),
3189 mUserData->s.strName.c_str());
3190
3191 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3192
3193 SessionState_T oldState = mData->mSession.mState;
3194 /* Hack: in case the session is closing and there is a progress object
3195 * which allows waiting for the session to be closed, take the opportunity
3196 * and do a limited wait (max. 1 second). This helps a lot when the system
3197 * is busy and thus session closing can take a little while. */
3198 if ( mData->mSession.mState == SessionState_Unlocking
3199 && mData->mSession.mProgress)
3200 {
3201 alock.release();
3202 mData->mSession.mProgress->WaitForCompletion(1000);
3203 alock.acquire();
3204 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3205 }
3206
3207 // try again now
3208 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3209 // (i.e. session machine exists)
3210 && (aLockType == LockType_Shared) // caller wants a shared link to the
3211 // existing session that holds the write lock:
3212 )
3213 {
3214 // OK, share the session... we are now dealing with three processes:
3215 // 1) VBoxSVC (where this code runs);
3216 // 2) process C: the caller's client process (who wants a shared session);
3217 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3218
3219 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3220 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3221 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3222 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3223 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3224
3225 /*
3226 * Release the lock before calling the client process. It's safe here
3227 * since the only thing to do after we get the lock again is to add
3228 * the remote control to the list (which doesn't directly influence
3229 * anything).
3230 */
3231 alock.release();
3232
3233 // get the console of the session holding the write lock (this is a remote call)
3234 ComPtr<IConsole> pConsoleW;
3235 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3236 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3237 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3238 if (FAILED(rc))
3239 // the failure may occur w/o any error info (from RPC), so provide one
3240 return setError(VBOX_E_VM_ERROR,
3241 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3242
3243 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3244
3245 // share the session machine and W's console with the caller's session
3246 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3247 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3248 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3249
3250 if (FAILED(rc))
3251 // the failure may occur w/o any error info (from RPC), so provide one
3252 return setError(VBOX_E_VM_ERROR,
3253 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3254 alock.acquire();
3255
3256 // need to revalidate the state after acquiring the lock again
3257 if (mData->mSession.mState != SessionState_Locked)
3258 {
3259 pSessionControl->Uninitialize();
3260 return setError(VBOX_E_INVALID_SESSION_STATE,
3261 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3262 mUserData->s.strName.c_str());
3263 }
3264
3265 // add the caller's session to the list
3266 mData->mSession.mRemoteControls.push_back(pSessionControl);
3267 }
3268 else if ( mData->mSession.mState == SessionState_Locked
3269 || mData->mSession.mState == SessionState_Unlocking
3270 )
3271 {
3272 // sharing not permitted, or machine still unlocking:
3273 return setError(VBOX_E_INVALID_OBJECT_STATE,
3274 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3275 mUserData->s.strName.c_str());
3276 }
3277 else
3278 {
3279 // machine is not locked: then write-lock the machine (create the session machine)
3280
3281 // must not be busy
3282 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3283
3284 // get the caller's session PID
3285 RTPROCESS pid = NIL_RTPROCESS;
3286 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3287 pSessionControl->GetPID((ULONG*)&pid);
3288 Assert(pid != NIL_RTPROCESS);
3289
3290 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3291
3292 if (fLaunchingVMProcess)
3293 {
3294 if (mData->mSession.mPID == NIL_RTPROCESS)
3295 {
3296 // two or more clients racing for a lock, the one which set the
3297 // session state to Spawning will win, the others will get an
3298 // error as we can't decide here if waiting a little would help
3299 // (only for shared locks this would avoid an error)
3300 return setError(VBOX_E_INVALID_OBJECT_STATE,
3301 tr("The machine '%s' already has a lock request pending"),
3302 mUserData->s.strName.c_str());
3303 }
3304
3305 // this machine is awaiting for a spawning session to be opened:
3306 // then the calling process must be the one that got started by
3307 // LaunchVMProcess()
3308
3309 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3310 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3311
3312#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3313 /* Hardened windows builds spawns three processes when a VM is
3314 launched, the 3rd one is the one that will end up here. */
3315 RTPROCESS ppid;
3316 int rc = RTProcQueryParent(pid, &ppid);
3317 if (RT_SUCCESS(rc))
3318 rc = RTProcQueryParent(ppid, &ppid);
3319 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3320 || rc == VERR_ACCESS_DENIED)
3321 {
3322 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3323 mData->mSession.mPID = pid;
3324 }
3325#endif
3326
3327 if (mData->mSession.mPID != pid)
3328 return setError(E_ACCESSDENIED,
3329 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3330 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3331 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3332 }
3333
3334 // create the mutable SessionMachine from the current machine
3335 ComObjPtr<SessionMachine> sessionMachine;
3336 sessionMachine.createObject();
3337 rc = sessionMachine->init(this);
3338 AssertComRC(rc);
3339
3340 /* NOTE: doing return from this function after this point but
3341 * before the end is forbidden since it may call SessionMachine::uninit()
3342 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3343 * lock while still holding the Machine lock in alock so that a deadlock
3344 * is possible due to the wrong lock order. */
3345
3346 if (SUCCEEDED(rc))
3347 {
3348 /*
3349 * Set the session state to Spawning to protect against subsequent
3350 * attempts to open a session and to unregister the machine after
3351 * we release the lock.
3352 */
3353 SessionState_T origState = mData->mSession.mState;
3354 mData->mSession.mState = SessionState_Spawning;
3355
3356#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3357 /* Get the client token ID to be passed to the client process */
3358 Utf8Str strTokenId;
3359 sessionMachine->i_getTokenId(strTokenId);
3360 Assert(!strTokenId.isEmpty());
3361#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3362 /* Get the client token to be passed to the client process */
3363 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3364 /* The token is now "owned" by pToken, fix refcount */
3365 if (!pToken.isNull())
3366 pToken->Release();
3367#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3368
3369 /*
3370 * Release the lock before calling the client process -- it will call
3371 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3372 * because the state is Spawning, so that LaunchVMProcess() and
3373 * LockMachine() calls will fail. This method, called before we
3374 * acquire the lock again, will fail because of the wrong PID.
3375 *
3376 * Note that mData->mSession.mRemoteControls accessed outside
3377 * the lock may not be modified when state is Spawning, so it's safe.
3378 */
3379 alock.release();
3380
3381 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3382#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3383 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3384#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3385 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3386 /* Now the token is owned by the client process. */
3387 pToken.setNull();
3388#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3389 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3390
3391 /* The failure may occur w/o any error info (from RPC), so provide one */
3392 if (FAILED(rc))
3393 setError(VBOX_E_VM_ERROR,
3394 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3395
3396 if ( SUCCEEDED(rc)
3397 && fLaunchingVMProcess
3398 )
3399 {
3400 /* complete the remote session initialization */
3401
3402 /* get the console from the direct session */
3403 ComPtr<IConsole> console;
3404 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3405 ComAssertComRC(rc);
3406
3407 if (SUCCEEDED(rc) && !console)
3408 {
3409 ComAssert(!!console);
3410 rc = E_FAIL;
3411 }
3412
3413 /* assign machine & console to the remote session */
3414 if (SUCCEEDED(rc))
3415 {
3416 /*
3417 * after LaunchVMProcess(), the first and the only
3418 * entry in remoteControls is that remote session
3419 */
3420 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3421 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3422 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3423
3424 /* The failure may occur w/o any error info (from RPC), so provide one */
3425 if (FAILED(rc))
3426 setError(VBOX_E_VM_ERROR,
3427 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3428 }
3429
3430 if (FAILED(rc))
3431 pSessionControl->Uninitialize();
3432 }
3433
3434 /* acquire the lock again */
3435 alock.acquire();
3436
3437 /* Restore the session state */
3438 mData->mSession.mState = origState;
3439 }
3440
3441 // finalize spawning anyway (this is why we don't return on errors above)
3442 if (fLaunchingVMProcess)
3443 {
3444 /* Note that the progress object is finalized later */
3445 /** @todo Consider checking mData->mSession.mProgress for cancellation
3446 * around here. */
3447
3448 /* We don't reset mSession.mPID here because it is necessary for
3449 * SessionMachine::uninit() to reap the child process later. */
3450
3451 if (FAILED(rc))
3452 {
3453 /* Close the remote session, remove the remote control from the list
3454 * and reset session state to Closed (@note keep the code in sync
3455 * with the relevant part in checkForSpawnFailure()). */
3456
3457 Assert(mData->mSession.mRemoteControls.size() == 1);
3458 if (mData->mSession.mRemoteControls.size() == 1)
3459 {
3460 ErrorInfoKeeper eik;
3461 mData->mSession.mRemoteControls.front()->Uninitialize();
3462 }
3463
3464 mData->mSession.mRemoteControls.clear();
3465 mData->mSession.mState = SessionState_Unlocked;
3466 }
3467 }
3468 else
3469 {
3470 /* memorize PID of the directly opened session */
3471 if (SUCCEEDED(rc))
3472 mData->mSession.mPID = pid;
3473 }
3474
3475 if (SUCCEEDED(rc))
3476 {
3477 /* memorize the direct session control and cache IUnknown for it */
3478 mData->mSession.mDirectControl = pSessionControl;
3479 mData->mSession.mState = SessionState_Locked;
3480 /* associate the SessionMachine with this Machine */
3481 mData->mSession.mMachine = sessionMachine;
3482
3483 /* request an IUnknown pointer early from the remote party for later
3484 * identity checks (it will be internally cached within mDirectControl
3485 * at least on XPCOM) */
3486 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3487 NOREF(unk);
3488 }
3489
3490 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3491 * would break the lock order */
3492 alock.release();
3493
3494 /* uninitialize the created session machine on failure */
3495 if (FAILED(rc))
3496 sessionMachine->uninit();
3497
3498 }
3499
3500 if (SUCCEEDED(rc))
3501 {
3502 /*
3503 * tell the client watcher thread to update the set of
3504 * machines that have open sessions
3505 */
3506 mParent->i_updateClientWatcher();
3507
3508 if (oldState != SessionState_Locked)
3509 /* fire an event */
3510 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3511 }
3512
3513 return rc;
3514}
3515
3516/**
3517 * @note Locks objects!
3518 */
3519HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3520 const com::Utf8Str &aType,
3521 const com::Utf8Str &aEnvironment,
3522 ComPtr<IProgress> &aProgress)
3523{
3524 Utf8Str strFrontend(aType);
3525 /* "emergencystop" doesn't need the session, so skip the checks/interface
3526 * retrieval. This code doesn't quite fit in here, but introducing a
3527 * special API method would be even more effort, and would require explicit
3528 * support by every API client. It's better to hide the feature a bit. */
3529 if (strFrontend != "emergencystop")
3530 CheckComArgNotNull(aSession);
3531
3532 HRESULT rc = S_OK;
3533 if (strFrontend.isEmpty())
3534 {
3535 Bstr bstrFrontend;
3536 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3537 if (FAILED(rc))
3538 return rc;
3539 strFrontend = bstrFrontend;
3540 if (strFrontend.isEmpty())
3541 {
3542 ComPtr<ISystemProperties> systemProperties;
3543 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3544 if (FAILED(rc))
3545 return rc;
3546 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3547 if (FAILED(rc))
3548 return rc;
3549 strFrontend = bstrFrontend;
3550 }
3551 /* paranoia - emergencystop is not a valid default */
3552 if (strFrontend == "emergencystop")
3553 strFrontend = Utf8Str::Empty;
3554 }
3555 /* default frontend: Qt GUI */
3556 if (strFrontend.isEmpty())
3557 strFrontend = "GUI/Qt";
3558
3559 if (strFrontend != "emergencystop")
3560 {
3561 /* check the session state */
3562 SessionState_T state;
3563 rc = aSession->COMGETTER(State)(&state);
3564 if (FAILED(rc))
3565 return rc;
3566
3567 if (state != SessionState_Unlocked)
3568 return setError(VBOX_E_INVALID_OBJECT_STATE,
3569 tr("The given session is busy"));
3570
3571 /* get the IInternalSessionControl interface */
3572 ComPtr<IInternalSessionControl> control(aSession);
3573 ComAssertMsgRet(!control.isNull(),
3574 ("No IInternalSessionControl interface"),
3575 E_INVALIDARG);
3576
3577 /* get the teleporter enable state for the progress object init. */
3578 BOOL fTeleporterEnabled;
3579 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3580 if (FAILED(rc))
3581 return rc;
3582
3583 /* create a progress object */
3584 ComObjPtr<ProgressProxy> progress;
3585 progress.createObject();
3586 rc = progress->init(mParent,
3587 static_cast<IMachine*>(this),
3588 Bstr(tr("Starting VM")).raw(),
3589 TRUE /* aCancelable */,
3590 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3591 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3592 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3593 2 /* uFirstOperationWeight */,
3594 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3595
3596 if (SUCCEEDED(rc))
3597 {
3598 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3599 if (SUCCEEDED(rc))
3600 {
3601 aProgress = progress;
3602
3603 /* signal the client watcher thread */
3604 mParent->i_updateClientWatcher();
3605
3606 /* fire an event */
3607 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3608 }
3609 }
3610 }
3611 else
3612 {
3613 /* no progress object - either instant success or failure */
3614 aProgress = NULL;
3615
3616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3617
3618 if (mData->mSession.mState != SessionState_Locked)
3619 return setError(VBOX_E_INVALID_OBJECT_STATE,
3620 tr("The machine '%s' is not locked by a session"),
3621 mUserData->s.strName.c_str());
3622
3623 /* must have a VM process associated - do not kill normal API clients
3624 * with an open session */
3625 if (!Global::IsOnline(mData->mMachineState))
3626 return setError(VBOX_E_INVALID_OBJECT_STATE,
3627 tr("The machine '%s' does not have a VM process"),
3628 mUserData->s.strName.c_str());
3629
3630 /* forcibly terminate the VM process */
3631 if (mData->mSession.mPID != NIL_RTPROCESS)
3632 RTProcTerminate(mData->mSession.mPID);
3633
3634 /* signal the client watcher thread, as most likely the client has
3635 * been terminated */
3636 mParent->i_updateClientWatcher();
3637 }
3638
3639 return rc;
3640}
3641
3642HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3643{
3644 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3645 return setError(E_INVALIDARG,
3646 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3647 aPosition, SchemaDefs::MaxBootPosition);
3648
3649 if (aDevice == DeviceType_USB)
3650 return setError(E_NOTIMPL,
3651 tr("Booting from USB device is currently not supported"));
3652
3653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3654
3655 HRESULT rc = i_checkStateDependency(MutableStateDep);
3656 if (FAILED(rc)) return rc;
3657
3658 i_setModified(IsModified_MachineData);
3659 mHWData.backup();
3660 mHWData->mBootOrder[aPosition - 1] = aDevice;
3661
3662 return S_OK;
3663}
3664
3665HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3666{
3667 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3668 return setError(E_INVALIDARG,
3669 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3670 aPosition, SchemaDefs::MaxBootPosition);
3671
3672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3673
3674 *aDevice = mHWData->mBootOrder[aPosition - 1];
3675
3676 return S_OK;
3677}
3678
3679HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3680 LONG aControllerPort,
3681 LONG aDevice,
3682 DeviceType_T aType,
3683 const ComPtr<IMedium> &aMedium)
3684{
3685 IMedium *aM = aMedium;
3686 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3687 aName.c_str(), aControllerPort, aDevice, aType, aM));
3688
3689 // request the host lock first, since might be calling Host methods for getting host drives;
3690 // next, protect the media tree all the while we're in here, as well as our member variables
3691 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3692 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3693
3694 HRESULT rc = i_checkStateDependency(MutableStateDep);
3695 if (FAILED(rc)) return rc;
3696
3697 /// @todo NEWMEDIA implicit machine registration
3698 if (!mData->mRegistered)
3699 return setError(VBOX_E_INVALID_OBJECT_STATE,
3700 tr("Cannot attach storage devices to an unregistered machine"));
3701
3702 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3703
3704 /* Check for an existing controller. */
3705 ComObjPtr<StorageController> ctl;
3706 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3707 if (FAILED(rc)) return rc;
3708
3709 StorageControllerType_T ctrlType;
3710 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3711 if (FAILED(rc))
3712 return setError(E_FAIL,
3713 tr("Could not get type of controller '%s'"),
3714 aName.c_str());
3715
3716 bool fSilent = false;
3717 Utf8Str strReconfig;
3718
3719 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3720 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3721 if ( mData->mMachineState == MachineState_Paused
3722 && strReconfig == "1")
3723 fSilent = true;
3724
3725 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3726 bool fHotplug = false;
3727 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3728 fHotplug = true;
3729
3730 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3731 return setError(VBOX_E_INVALID_VM_STATE,
3732 tr("Controller '%s' does not support hotplugging"),
3733 aName.c_str());
3734
3735 // check that the port and device are not out of range
3736 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3737 if (FAILED(rc)) return rc;
3738
3739 /* check if the device slot is already busy */
3740 MediumAttachment *pAttachTemp;
3741 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3742 Bstr(aName).raw(),
3743 aControllerPort,
3744 aDevice)))
3745 {
3746 Medium *pMedium = pAttachTemp->i_getMedium();
3747 if (pMedium)
3748 {
3749 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3750 return setError(VBOX_E_OBJECT_IN_USE,
3751 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3752 pMedium->i_getLocationFull().c_str(),
3753 aControllerPort,
3754 aDevice,
3755 aName.c_str());
3756 }
3757 else
3758 return setError(VBOX_E_OBJECT_IN_USE,
3759 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3760 aControllerPort, aDevice, aName.c_str());
3761 }
3762
3763 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3764 if (aMedium && medium.isNull())
3765 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3766
3767 AutoCaller mediumCaller(medium);
3768 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3769
3770 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3771
3772 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3773 && !medium.isNull()
3774 )
3775 return setError(VBOX_E_OBJECT_IN_USE,
3776 tr("Medium '%s' is already attached to this virtual machine"),
3777 medium->i_getLocationFull().c_str());
3778
3779 if (!medium.isNull())
3780 {
3781 MediumType_T mtype = medium->i_getType();
3782 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3783 // For DVDs it's not written to the config file, so needs no global config
3784 // version bump. For floppies it's a new attribute "type", which is ignored
3785 // by older VirtualBox version, so needs no global config version bump either.
3786 // For hard disks this type is not accepted.
3787 if (mtype == MediumType_MultiAttach)
3788 {
3789 // This type is new with VirtualBox 4.0 and therefore requires settings
3790 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3791 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3792 // two reasons: The medium type is a property of the media registry tree, which
3793 // can reside in the global config file (for pre-4.0 media); we would therefore
3794 // possibly need to bump the global config version. We don't want to do that though
3795 // because that might make downgrading to pre-4.0 impossible.
3796 // As a result, we can only use these two new types if the medium is NOT in the
3797 // global registry:
3798 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3799 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3800 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3801 )
3802 return setError(VBOX_E_INVALID_OBJECT_STATE,
3803 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3804 "to machines that were created with VirtualBox 4.0 or later"),
3805 medium->i_getLocationFull().c_str());
3806 }
3807 }
3808
3809 bool fIndirect = false;
3810 if (!medium.isNull())
3811 fIndirect = medium->i_isReadOnly();
3812 bool associate = true;
3813
3814 do
3815 {
3816 if ( aType == DeviceType_HardDisk
3817 && mMediaData.isBackedUp())
3818 {
3819 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3820
3821 /* check if the medium was attached to the VM before we started
3822 * changing attachments in which case the attachment just needs to
3823 * be restored */
3824 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3825 {
3826 AssertReturn(!fIndirect, E_FAIL);
3827
3828 /* see if it's the same bus/channel/device */
3829 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3830 {
3831 /* the simplest case: restore the whole attachment
3832 * and return, nothing else to do */
3833 mMediaData->mAttachments.push_back(pAttachTemp);
3834
3835 /* Reattach the medium to the VM. */
3836 if (fHotplug || fSilent)
3837 {
3838 mediumLock.release();
3839 treeLock.release();
3840 alock.release();
3841
3842 MediumLockList *pMediumLockList(new MediumLockList());
3843
3844 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3845 true /* fMediumLockWrite */,
3846 NULL,
3847 *pMediumLockList);
3848 alock.acquire();
3849 if (FAILED(rc))
3850 delete pMediumLockList;
3851 else
3852 {
3853 mData->mSession.mLockedMedia.Unlock();
3854 alock.release();
3855 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3856 mData->mSession.mLockedMedia.Lock();
3857 alock.acquire();
3858 }
3859 alock.release();
3860
3861 if (SUCCEEDED(rc))
3862 {
3863 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3864 /* Remove lock list in case of error. */
3865 if (FAILED(rc))
3866 {
3867 mData->mSession.mLockedMedia.Unlock();
3868 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3869 mData->mSession.mLockedMedia.Lock();
3870 }
3871 }
3872 }
3873
3874 return S_OK;
3875 }
3876
3877 /* bus/channel/device differ; we need a new attachment object,
3878 * but don't try to associate it again */
3879 associate = false;
3880 break;
3881 }
3882 }
3883
3884 /* go further only if the attachment is to be indirect */
3885 if (!fIndirect)
3886 break;
3887
3888 /* perform the so called smart attachment logic for indirect
3889 * attachments. Note that smart attachment is only applicable to base
3890 * hard disks. */
3891
3892 if (medium->i_getParent().isNull())
3893 {
3894 /* first, investigate the backup copy of the current hard disk
3895 * attachments to make it possible to re-attach existing diffs to
3896 * another device slot w/o losing their contents */
3897 if (mMediaData.isBackedUp())
3898 {
3899 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3900
3901 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3902 uint32_t foundLevel = 0;
3903
3904 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3905 {
3906 uint32_t level = 0;
3907 MediumAttachment *pAttach = *it;
3908 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3909 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3910 if (pMedium.isNull())
3911 continue;
3912
3913 if (pMedium->i_getBase(&level) == medium)
3914 {
3915 /* skip the hard disk if its currently attached (we
3916 * cannot attach the same hard disk twice) */
3917 if (i_findAttachment(mMediaData->mAttachments,
3918 pMedium))
3919 continue;
3920
3921 /* matched device, channel and bus (i.e. attached to the
3922 * same place) will win and immediately stop the search;
3923 * otherwise the attachment that has the youngest
3924 * descendant of medium will be used
3925 */
3926 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3927 {
3928 /* the simplest case: restore the whole attachment
3929 * and return, nothing else to do */
3930 mMediaData->mAttachments.push_back(*it);
3931
3932 /* Reattach the medium to the VM. */
3933 if (fHotplug || fSilent)
3934 {
3935 mediumLock.release();
3936 treeLock.release();
3937 alock.release();
3938
3939 MediumLockList *pMediumLockList(new MediumLockList());
3940
3941 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3942 true /* fMediumLockWrite */,
3943 NULL,
3944 *pMediumLockList);
3945 alock.acquire();
3946 if (FAILED(rc))
3947 delete pMediumLockList;
3948 else
3949 {
3950 mData->mSession.mLockedMedia.Unlock();
3951 alock.release();
3952 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3953 mData->mSession.mLockedMedia.Lock();
3954 alock.acquire();
3955 }
3956 alock.release();
3957
3958 if (SUCCEEDED(rc))
3959 {
3960 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3961 /* Remove lock list in case of error. */
3962 if (FAILED(rc))
3963 {
3964 mData->mSession.mLockedMedia.Unlock();
3965 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3966 mData->mSession.mLockedMedia.Lock();
3967 }
3968 }
3969 }
3970
3971 return S_OK;
3972 }
3973 else if ( foundIt == oldAtts.end()
3974 || level > foundLevel /* prefer younger */
3975 )
3976 {
3977 foundIt = it;
3978 foundLevel = level;
3979 }
3980 }
3981 }
3982
3983 if (foundIt != oldAtts.end())
3984 {
3985 /* use the previously attached hard disk */
3986 medium = (*foundIt)->i_getMedium();
3987 mediumCaller.attach(medium);
3988 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3989 mediumLock.attach(medium);
3990 /* not implicit, doesn't require association with this VM */
3991 fIndirect = false;
3992 associate = false;
3993 /* go right to the MediumAttachment creation */
3994 break;
3995 }
3996 }
3997
3998 /* must give up the medium lock and medium tree lock as below we
3999 * go over snapshots, which needs a lock with higher lock order. */
4000 mediumLock.release();
4001 treeLock.release();
4002
4003 /* then, search through snapshots for the best diff in the given
4004 * hard disk's chain to base the new diff on */
4005
4006 ComObjPtr<Medium> base;
4007 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4008 while (snap)
4009 {
4010 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4011
4012 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4013
4014 MediumAttachment *pAttachFound = NULL;
4015 uint32_t foundLevel = 0;
4016
4017 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4018 {
4019 MediumAttachment *pAttach = *it;
4020 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4021 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4022 if (pMedium.isNull())
4023 continue;
4024
4025 uint32_t level = 0;
4026 if (pMedium->i_getBase(&level) == medium)
4027 {
4028 /* matched device, channel and bus (i.e. attached to the
4029 * same place) will win and immediately stop the search;
4030 * otherwise the attachment that has the youngest
4031 * descendant of medium will be used
4032 */
4033 if ( pAttach->i_getDevice() == aDevice
4034 && pAttach->i_getPort() == aControllerPort
4035 && pAttach->i_getControllerName() == aName
4036 )
4037 {
4038 pAttachFound = pAttach;
4039 break;
4040 }
4041 else if ( !pAttachFound
4042 || level > foundLevel /* prefer younger */
4043 )
4044 {
4045 pAttachFound = pAttach;
4046 foundLevel = level;
4047 }
4048 }
4049 }
4050
4051 if (pAttachFound)
4052 {
4053 base = pAttachFound->i_getMedium();
4054 break;
4055 }
4056
4057 snap = snap->i_getParent();
4058 }
4059
4060 /* re-lock medium tree and the medium, as we need it below */
4061 treeLock.acquire();
4062 mediumLock.acquire();
4063
4064 /* found a suitable diff, use it as a base */
4065 if (!base.isNull())
4066 {
4067 medium = base;
4068 mediumCaller.attach(medium);
4069 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4070 mediumLock.attach(medium);
4071 }
4072 }
4073
4074 Utf8Str strFullSnapshotFolder;
4075 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4076
4077 ComObjPtr<Medium> diff;
4078 diff.createObject();
4079 // store this diff in the same registry as the parent
4080 Guid uuidRegistryParent;
4081 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4082 {
4083 // parent image has no registry: this can happen if we're attaching a new immutable
4084 // image that has not yet been attached (medium then points to the base and we're
4085 // creating the diff image for the immutable, and the parent is not yet registered);
4086 // put the parent in the machine registry then
4087 mediumLock.release();
4088 treeLock.release();
4089 alock.release();
4090 i_addMediumToRegistry(medium);
4091 alock.acquire();
4092 treeLock.acquire();
4093 mediumLock.acquire();
4094 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4095 }
4096 rc = diff->init(mParent,
4097 medium->i_getPreferredDiffFormat(),
4098 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4099 uuidRegistryParent,
4100 DeviceType_HardDisk);
4101 if (FAILED(rc)) return rc;
4102
4103 /* Apply the normal locking logic to the entire chain. */
4104 MediumLockList *pMediumLockList(new MediumLockList());
4105 mediumLock.release();
4106 treeLock.release();
4107 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4108 true /* fMediumLockWrite */,
4109 medium,
4110 *pMediumLockList);
4111 treeLock.acquire();
4112 mediumLock.acquire();
4113 if (SUCCEEDED(rc))
4114 {
4115 mediumLock.release();
4116 treeLock.release();
4117 rc = pMediumLockList->Lock();
4118 treeLock.acquire();
4119 mediumLock.acquire();
4120 if (FAILED(rc))
4121 setError(rc,
4122 tr("Could not lock medium when creating diff '%s'"),
4123 diff->i_getLocationFull().c_str());
4124 else
4125 {
4126 /* will release the lock before the potentially lengthy
4127 * operation, so protect with the special state */
4128 MachineState_T oldState = mData->mMachineState;
4129 i_setMachineState(MachineState_SettingUp);
4130
4131 mediumLock.release();
4132 treeLock.release();
4133 alock.release();
4134
4135 rc = medium->i_createDiffStorage(diff,
4136 MediumVariant_Standard,
4137 pMediumLockList,
4138 NULL /* aProgress */,
4139 true /* aWait */);
4140
4141 alock.acquire();
4142 treeLock.acquire();
4143 mediumLock.acquire();
4144
4145 i_setMachineState(oldState);
4146 }
4147 }
4148
4149 /* Unlock the media and free the associated memory. */
4150 delete pMediumLockList;
4151
4152 if (FAILED(rc)) return rc;
4153
4154 /* use the created diff for the actual attachment */
4155 medium = diff;
4156 mediumCaller.attach(medium);
4157 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4158 mediumLock.attach(medium);
4159 }
4160 while (0);
4161
4162 ComObjPtr<MediumAttachment> attachment;
4163 attachment.createObject();
4164 rc = attachment->init(this,
4165 medium,
4166 aName,
4167 aControllerPort,
4168 aDevice,
4169 aType,
4170 fIndirect,
4171 false /* fPassthrough */,
4172 false /* fTempEject */,
4173 false /* fNonRotational */,
4174 false /* fDiscard */,
4175 fHotplug /* fHotPluggable */,
4176 Utf8Str::Empty);
4177 if (FAILED(rc)) return rc;
4178
4179 if (associate && !medium.isNull())
4180 {
4181 // as the last step, associate the medium to the VM
4182 rc = medium->i_addBackReference(mData->mUuid);
4183 // here we can fail because of Deleting, or being in process of creating a Diff
4184 if (FAILED(rc)) return rc;
4185
4186 mediumLock.release();
4187 treeLock.release();
4188 alock.release();
4189 i_addMediumToRegistry(medium);
4190 alock.acquire();
4191 treeLock.acquire();
4192 mediumLock.acquire();
4193 }
4194
4195 /* success: finally remember the attachment */
4196 i_setModified(IsModified_Storage);
4197 mMediaData.backup();
4198 mMediaData->mAttachments.push_back(attachment);
4199
4200 mediumLock.release();
4201 treeLock.release();
4202 alock.release();
4203
4204 if (fHotplug || fSilent)
4205 {
4206 if (!medium.isNull())
4207 {
4208 MediumLockList *pMediumLockList(new MediumLockList());
4209
4210 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4211 true /* fMediumLockWrite */,
4212 NULL,
4213 *pMediumLockList);
4214 alock.acquire();
4215 if (FAILED(rc))
4216 delete pMediumLockList;
4217 else
4218 {
4219 mData->mSession.mLockedMedia.Unlock();
4220 alock.release();
4221 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4222 mData->mSession.mLockedMedia.Lock();
4223 alock.acquire();
4224 }
4225 alock.release();
4226 }
4227
4228 if (SUCCEEDED(rc))
4229 {
4230 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4231 /* Remove lock list in case of error. */
4232 if (FAILED(rc))
4233 {
4234 mData->mSession.mLockedMedia.Unlock();
4235 mData->mSession.mLockedMedia.Remove(attachment);
4236 mData->mSession.mLockedMedia.Lock();
4237 }
4238 }
4239 }
4240
4241 mParent->i_saveModifiedRegistries();
4242
4243 return rc;
4244}
4245
4246HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4247 LONG aDevice)
4248{
4249 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4250 aName.c_str(), aControllerPort, aDevice));
4251
4252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4253
4254 HRESULT rc = i_checkStateDependency(MutableStateDep);
4255 if (FAILED(rc)) return rc;
4256
4257 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4258
4259 /* Check for an existing controller. */
4260 ComObjPtr<StorageController> ctl;
4261 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4262 if (FAILED(rc)) return rc;
4263
4264 StorageControllerType_T ctrlType;
4265 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4266 if (FAILED(rc))
4267 return setError(E_FAIL,
4268 tr("Could not get type of controller '%s'"),
4269 aName.c_str());
4270
4271 bool fSilent = false;
4272 Utf8Str strReconfig;
4273
4274 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4275 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4276 if ( mData->mMachineState == MachineState_Paused
4277 && strReconfig == "1")
4278 fSilent = true;
4279
4280 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4281 bool fHotplug = false;
4282 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4283 fHotplug = true;
4284
4285 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4286 return setError(VBOX_E_INVALID_VM_STATE,
4287 tr("Controller '%s' does not support hotplugging"),
4288 aName.c_str());
4289
4290 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4291 Bstr(aName).raw(),
4292 aControllerPort,
4293 aDevice);
4294 if (!pAttach)
4295 return setError(VBOX_E_OBJECT_NOT_FOUND,
4296 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4297 aDevice, aControllerPort, aName.c_str());
4298
4299 if (fHotplug && !pAttach->i_getHotPluggable())
4300 return setError(VBOX_E_NOT_SUPPORTED,
4301 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4302 aDevice, aControllerPort, aName.c_str());
4303
4304 /*
4305 * The VM has to detach the device before we delete any implicit diffs.
4306 * If this fails we can roll back without loosing data.
4307 */
4308 if (fHotplug || fSilent)
4309 {
4310 alock.release();
4311 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4312 alock.acquire();
4313 }
4314 if (FAILED(rc)) return rc;
4315
4316 /* If we are here everything went well and we can delete the implicit now. */
4317 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4318
4319 alock.release();
4320
4321 mParent->i_saveModifiedRegistries();
4322
4323 return rc;
4324}
4325
4326HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4327 LONG aDevice, BOOL aPassthrough)
4328{
4329 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4330 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4331
4332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4333
4334 HRESULT rc = i_checkStateDependency(MutableStateDep);
4335 if (FAILED(rc)) return rc;
4336
4337 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4338
4339 if (Global::IsOnlineOrTransient(mData->mMachineState))
4340 return setError(VBOX_E_INVALID_VM_STATE,
4341 tr("Invalid machine state: %s"),
4342 Global::stringifyMachineState(mData->mMachineState));
4343
4344 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4345 Bstr(aName).raw(),
4346 aControllerPort,
4347 aDevice);
4348 if (!pAttach)
4349 return setError(VBOX_E_OBJECT_NOT_FOUND,
4350 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4351 aDevice, aControllerPort, aName.c_str());
4352
4353
4354 i_setModified(IsModified_Storage);
4355 mMediaData.backup();
4356
4357 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4358
4359 if (pAttach->i_getType() != DeviceType_DVD)
4360 return setError(E_INVALIDARG,
4361 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4362 aDevice, aControllerPort, aName.c_str());
4363 pAttach->i_updatePassthrough(!!aPassthrough);
4364
4365 return S_OK;
4366}
4367
4368HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4369 LONG aDevice, BOOL aTemporaryEject)
4370{
4371
4372 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4373 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4374
4375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4376
4377 HRESULT rc = i_checkStateDependency(MutableStateDep);
4378 if (FAILED(rc)) return rc;
4379
4380 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4381 Bstr(aName).raw(),
4382 aControllerPort,
4383 aDevice);
4384 if (!pAttach)
4385 return setError(VBOX_E_OBJECT_NOT_FOUND,
4386 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4387 aDevice, aControllerPort, aName.c_str());
4388
4389
4390 i_setModified(IsModified_Storage);
4391 mMediaData.backup();
4392
4393 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4394
4395 if (pAttach->i_getType() != DeviceType_DVD)
4396 return setError(E_INVALIDARG,
4397 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4398 aDevice, aControllerPort, aName.c_str());
4399 pAttach->i_updateTempEject(!!aTemporaryEject);
4400
4401 return S_OK;
4402}
4403
4404HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4405 LONG aDevice, BOOL aNonRotational)
4406{
4407
4408 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4409 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4410
4411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4412
4413 HRESULT rc = i_checkStateDependency(MutableStateDep);
4414 if (FAILED(rc)) return rc;
4415
4416 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4417
4418 if (Global::IsOnlineOrTransient(mData->mMachineState))
4419 return setError(VBOX_E_INVALID_VM_STATE,
4420 tr("Invalid machine state: %s"),
4421 Global::stringifyMachineState(mData->mMachineState));
4422
4423 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4424 Bstr(aName).raw(),
4425 aControllerPort,
4426 aDevice);
4427 if (!pAttach)
4428 return setError(VBOX_E_OBJECT_NOT_FOUND,
4429 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4430 aDevice, aControllerPort, aName.c_str());
4431
4432
4433 i_setModified(IsModified_Storage);
4434 mMediaData.backup();
4435
4436 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4437
4438 if (pAttach->i_getType() != DeviceType_HardDisk)
4439 return setError(E_INVALIDARG,
4440 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"),
4441 aDevice, aControllerPort, aName.c_str());
4442 pAttach->i_updateNonRotational(!!aNonRotational);
4443
4444 return S_OK;
4445}
4446
4447HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4448 LONG aDevice, BOOL aDiscard)
4449{
4450
4451 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4452 aName.c_str(), aControllerPort, aDevice, aDiscard));
4453
4454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4455
4456 HRESULT rc = i_checkStateDependency(MutableStateDep);
4457 if (FAILED(rc)) return rc;
4458
4459 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4460
4461 if (Global::IsOnlineOrTransient(mData->mMachineState))
4462 return setError(VBOX_E_INVALID_VM_STATE,
4463 tr("Invalid machine state: %s"),
4464 Global::stringifyMachineState(mData->mMachineState));
4465
4466 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4467 Bstr(aName).raw(),
4468 aControllerPort,
4469 aDevice);
4470 if (!pAttach)
4471 return setError(VBOX_E_OBJECT_NOT_FOUND,
4472 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4473 aDevice, aControllerPort, aName.c_str());
4474
4475
4476 i_setModified(IsModified_Storage);
4477 mMediaData.backup();
4478
4479 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4480
4481 if (pAttach->i_getType() != DeviceType_HardDisk)
4482 return setError(E_INVALIDARG,
4483 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"),
4484 aDevice, aControllerPort, aName.c_str());
4485 pAttach->i_updateDiscard(!!aDiscard);
4486
4487 return S_OK;
4488}
4489
4490HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4491 LONG aDevice, BOOL aHotPluggable)
4492{
4493 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4494 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4495
4496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4497
4498 HRESULT rc = i_checkStateDependency(MutableStateDep);
4499 if (FAILED(rc)) return rc;
4500
4501 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4502
4503 if (Global::IsOnlineOrTransient(mData->mMachineState))
4504 return setError(VBOX_E_INVALID_VM_STATE,
4505 tr("Invalid machine state: %s"),
4506 Global::stringifyMachineState(mData->mMachineState));
4507
4508 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4509 Bstr(aName).raw(),
4510 aControllerPort,
4511 aDevice);
4512 if (!pAttach)
4513 return setError(VBOX_E_OBJECT_NOT_FOUND,
4514 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4515 aDevice, aControllerPort, aName.c_str());
4516
4517 /* Check for an existing controller. */
4518 ComObjPtr<StorageController> ctl;
4519 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4520 if (FAILED(rc)) return rc;
4521
4522 StorageControllerType_T ctrlType;
4523 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4524 if (FAILED(rc))
4525 return setError(E_FAIL,
4526 tr("Could not get type of controller '%s'"),
4527 aName.c_str());
4528
4529 if (!i_isControllerHotplugCapable(ctrlType))
4530 return setError(VBOX_E_NOT_SUPPORTED,
4531 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4532 aName.c_str());
4533
4534 i_setModified(IsModified_Storage);
4535 mMediaData.backup();
4536
4537 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4538
4539 if (pAttach->i_getType() == DeviceType_Floppy)
4540 return setError(E_INVALIDARG,
4541 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"),
4542 aDevice, aControllerPort, aName.c_str());
4543 pAttach->i_updateHotPluggable(!!aHotPluggable);
4544
4545 return S_OK;
4546}
4547
4548HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4549 LONG aDevice)
4550{
4551 int rc = S_OK;
4552 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4553 aName.c_str(), aControllerPort, aDevice));
4554
4555 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4556
4557 return rc;
4558}
4559
4560HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4561 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4562{
4563 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4564 aName.c_str(), aControllerPort, aDevice));
4565
4566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4567
4568 HRESULT rc = i_checkStateDependency(MutableStateDep);
4569 if (FAILED(rc)) return rc;
4570
4571 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4572
4573 if (Global::IsOnlineOrTransient(mData->mMachineState))
4574 return setError(VBOX_E_INVALID_VM_STATE,
4575 tr("Invalid machine state: %s"),
4576 Global::stringifyMachineState(mData->mMachineState));
4577
4578 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4579 Bstr(aName).raw(),
4580 aControllerPort,
4581 aDevice);
4582 if (!pAttach)
4583 return setError(VBOX_E_OBJECT_NOT_FOUND,
4584 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4585 aDevice, aControllerPort, aName.c_str());
4586
4587
4588 i_setModified(IsModified_Storage);
4589 mMediaData.backup();
4590
4591 IBandwidthGroup *iB = aBandwidthGroup;
4592 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4593 if (aBandwidthGroup && group.isNull())
4594 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4595
4596 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4597
4598 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4599 if (strBandwidthGroupOld.isNotEmpty())
4600 {
4601 /* Get the bandwidth group object and release it - this must not fail. */
4602 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4603 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4604 Assert(SUCCEEDED(rc));
4605
4606 pBandwidthGroupOld->i_release();
4607 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4608 }
4609
4610 if (!group.isNull())
4611 {
4612 group->i_reference();
4613 pAttach->i_updateBandwidthGroup(group->i_getName());
4614 }
4615
4616 return S_OK;
4617}
4618
4619HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4620 LONG aControllerPort,
4621 LONG aDevice,
4622 DeviceType_T aType)
4623{
4624 HRESULT rc = S_OK;
4625
4626 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4627 aName.c_str(), aControllerPort, aDevice, aType));
4628
4629 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4630
4631 return rc;
4632}
4633
4634
4635HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4636 LONG aControllerPort,
4637 LONG aDevice,
4638 BOOL aForce)
4639{
4640 int rc = S_OK;
4641 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4642 aName.c_str(), aControllerPort, aForce));
4643
4644 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4645
4646 return rc;
4647}
4648
4649HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4650 LONG aControllerPort,
4651 LONG aDevice,
4652 const ComPtr<IMedium> &aMedium,
4653 BOOL aForce)
4654{
4655 int rc = S_OK;
4656 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4657 aName.c_str(), aControllerPort, aDevice, aForce));
4658
4659 // request the host lock first, since might be calling Host methods for getting host drives;
4660 // next, protect the media tree all the while we're in here, as well as our member variables
4661 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4662 this->lockHandle(),
4663 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4664
4665 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4666 Bstr(aName).raw(),
4667 aControllerPort,
4668 aDevice);
4669 if (pAttach.isNull())
4670 return setError(VBOX_E_OBJECT_NOT_FOUND,
4671 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4672 aDevice, aControllerPort, aName.c_str());
4673
4674 /* Remember previously mounted medium. The medium before taking the
4675 * backup is not necessarily the same thing. */
4676 ComObjPtr<Medium> oldmedium;
4677 oldmedium = pAttach->i_getMedium();
4678
4679 IMedium *iM = aMedium;
4680 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4681 if (aMedium && pMedium.isNull())
4682 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4683
4684 AutoCaller mediumCaller(pMedium);
4685 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4686
4687 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4688 if (pMedium)
4689 {
4690 DeviceType_T mediumType = pAttach->i_getType();
4691 switch (mediumType)
4692 {
4693 case DeviceType_DVD:
4694 case DeviceType_Floppy:
4695 break;
4696
4697 default:
4698 return setError(VBOX_E_INVALID_OBJECT_STATE,
4699 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4700 aControllerPort,
4701 aDevice,
4702 aName.c_str());
4703 }
4704 }
4705
4706 i_setModified(IsModified_Storage);
4707 mMediaData.backup();
4708
4709 {
4710 // The backup operation makes the pAttach reference point to the
4711 // old settings. Re-get the correct reference.
4712 pAttach = i_findAttachment(mMediaData->mAttachments,
4713 Bstr(aName).raw(),
4714 aControllerPort,
4715 aDevice);
4716 if (!oldmedium.isNull())
4717 oldmedium->i_removeBackReference(mData->mUuid);
4718 if (!pMedium.isNull())
4719 {
4720 pMedium->i_addBackReference(mData->mUuid);
4721
4722 mediumLock.release();
4723 multiLock.release();
4724 i_addMediumToRegistry(pMedium);
4725 multiLock.acquire();
4726 mediumLock.acquire();
4727 }
4728
4729 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4730 pAttach->i_updateMedium(pMedium);
4731 }
4732
4733 i_setModified(IsModified_Storage);
4734
4735 mediumLock.release();
4736 multiLock.release();
4737 rc = i_onMediumChange(pAttach, aForce);
4738 multiLock.acquire();
4739 mediumLock.acquire();
4740
4741 /* On error roll back this change only. */
4742 if (FAILED(rc))
4743 {
4744 if (!pMedium.isNull())
4745 pMedium->i_removeBackReference(mData->mUuid);
4746 pAttach = i_findAttachment(mMediaData->mAttachments,
4747 Bstr(aName).raw(),
4748 aControllerPort,
4749 aDevice);
4750 /* If the attachment is gone in the meantime, bail out. */
4751 if (pAttach.isNull())
4752 return rc;
4753 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4754 if (!oldmedium.isNull())
4755 oldmedium->i_addBackReference(mData->mUuid);
4756 pAttach->i_updateMedium(oldmedium);
4757 }
4758
4759 mediumLock.release();
4760 multiLock.release();
4761
4762 mParent->i_saveModifiedRegistries();
4763
4764 return rc;
4765}
4766HRESULT Machine::getMedium(const com::Utf8Str &aName,
4767 LONG aControllerPort,
4768 LONG aDevice,
4769 ComPtr<IMedium> &aMedium)
4770{
4771 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4772 aName.c_str(), aControllerPort, aDevice));
4773
4774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4775
4776 aMedium = NULL;
4777
4778 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4779 Bstr(aName).raw(),
4780 aControllerPort,
4781 aDevice);
4782 if (pAttach.isNull())
4783 return setError(VBOX_E_OBJECT_NOT_FOUND,
4784 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4785 aDevice, aControllerPort, aName.c_str());
4786
4787 aMedium = pAttach->i_getMedium();
4788
4789 return S_OK;
4790}
4791
4792HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4793{
4794
4795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4796
4797 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4798
4799 return S_OK;
4800}
4801
4802HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4803{
4804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4805
4806 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4807
4808 return S_OK;
4809}
4810
4811HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4812{
4813 /* Do not assert if slot is out of range, just return the advertised
4814 status. testdriver/vbox.py triggers this in logVmInfo. */
4815 if (aSlot >= mNetworkAdapters.size())
4816 return setError(E_INVALIDARG,
4817 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4818 aSlot, mNetworkAdapters.size());
4819
4820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4821
4822 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4823
4824 return S_OK;
4825}
4826
4827HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4828{
4829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4830
4831 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4832 size_t i = 0;
4833 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4834 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4835 ++it, ++i)
4836 aKeys[i] = it->first;
4837
4838 return S_OK;
4839}
4840
4841 /**
4842 * @note Locks this object for reading.
4843 */
4844HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4845 com::Utf8Str &aValue)
4846{
4847 /* start with nothing found */
4848 aValue = "";
4849
4850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4851
4852 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4853 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4854 // found:
4855 aValue = it->second; // source is a Utf8Str
4856
4857 /* return the result to caller (may be empty) */
4858 return S_OK;
4859}
4860
4861 /**
4862 * @note Locks mParent for writing + this object for writing.
4863 */
4864HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4865{
4866 Utf8Str strOldValue; // empty
4867
4868 // locking note: we only hold the read lock briefly to look up the old value,
4869 // then release it and call the onExtraCanChange callbacks. There is a small
4870 // chance of a race insofar as the callback might be called twice if two callers
4871 // change the same key at the same time, but that's a much better solution
4872 // than the deadlock we had here before. The actual changing of the extradata
4873 // is then performed under the write lock and race-free.
4874
4875 // look up the old value first; if nothing has changed then we need not do anything
4876 {
4877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4878 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4879 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4880 strOldValue = it->second;
4881 }
4882
4883 bool fChanged;
4884 if ((fChanged = (strOldValue != aValue)))
4885 {
4886 // ask for permission from all listeners outside the locks;
4887 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4888 // lock to copy the list of callbacks to invoke
4889 Bstr error;
4890 Bstr bstrValue(aValue);
4891
4892 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4893 {
4894 const char *sep = error.isEmpty() ? "" : ": ";
4895 CBSTR err = error.raw();
4896 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4897 sep, err));
4898 return setError(E_ACCESSDENIED,
4899 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4900 aKey.c_str(),
4901 aValue.c_str(),
4902 sep,
4903 err);
4904 }
4905
4906 // data is changing and change not vetoed: then write it out under the lock
4907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4908
4909 if (i_isSnapshotMachine())
4910 {
4911 HRESULT rc = i_checkStateDependency(MutableStateDep);
4912 if (FAILED(rc)) return rc;
4913 }
4914
4915 if (aValue.isEmpty())
4916 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4917 else
4918 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4919 // creates a new key if needed
4920
4921 bool fNeedsGlobalSaveSettings = false;
4922 // This saving of settings is tricky: there is no "old state" for the
4923 // extradata items at all (unlike all other settings), so the old/new
4924 // settings comparison would give a wrong result!
4925 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4926
4927 if (fNeedsGlobalSaveSettings)
4928 {
4929 // save the global settings; for that we should hold only the VirtualBox lock
4930 alock.release();
4931 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4932 mParent->i_saveSettings();
4933 }
4934 }
4935
4936 // fire notification outside the lock
4937 if (fChanged)
4938 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4939
4940 return S_OK;
4941}
4942
4943HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4944{
4945 aProgress = NULL;
4946 NOREF(aSettingsFilePath);
4947 ReturnComNotImplemented();
4948}
4949
4950HRESULT Machine::saveSettings()
4951{
4952 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4953
4954 /* when there was auto-conversion, we want to save the file even if
4955 * the VM is saved */
4956 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4957 if (FAILED(rc)) return rc;
4958
4959 /* the settings file path may never be null */
4960 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4961
4962 /* save all VM data excluding snapshots */
4963 bool fNeedsGlobalSaveSettings = false;
4964 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4965 mlock.release();
4966
4967 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4968 {
4969 // save the global settings; for that we should hold only the VirtualBox lock
4970 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4971 rc = mParent->i_saveSettings();
4972 }
4973
4974 return rc;
4975}
4976
4977
4978HRESULT Machine::discardSettings()
4979{
4980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4981
4982 HRESULT rc = i_checkStateDependency(MutableStateDep);
4983 if (FAILED(rc)) return rc;
4984
4985 /*
4986 * during this rollback, the session will be notified if data has
4987 * been actually changed
4988 */
4989 i_rollback(true /* aNotify */);
4990
4991 return S_OK;
4992}
4993
4994/** @note Locks objects! */
4995HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4996 std::vector<ComPtr<IMedium> > &aMedia)
4997{
4998 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4999 AutoLimitedCaller autoCaller(this);
5000 AssertComRCReturnRC(autoCaller.rc());
5001
5002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5003
5004 Guid id(i_getId());
5005
5006 if (mData->mSession.mState != SessionState_Unlocked)
5007 return setError(VBOX_E_INVALID_OBJECT_STATE,
5008 tr("Cannot unregister the machine '%s' while it is locked"),
5009 mUserData->s.strName.c_str());
5010
5011 // wait for state dependents to drop to zero
5012 i_ensureNoStateDependencies();
5013
5014 if (!mData->mAccessible)
5015 {
5016 // inaccessible maschines can only be unregistered; uninitialize ourselves
5017 // here because currently there may be no unregistered that are inaccessible
5018 // (this state combination is not supported). Note releasing the caller and
5019 // leaving the lock before calling uninit()
5020 alock.release();
5021 autoCaller.release();
5022
5023 uninit();
5024
5025 mParent->i_unregisterMachine(this, id);
5026 // calls VirtualBox::i_saveSettings()
5027
5028 return S_OK;
5029 }
5030
5031 HRESULT rc = S_OK;
5032
5033 // discard saved state
5034 if (mData->mMachineState == MachineState_Saved)
5035 {
5036 // add the saved state file to the list of files the caller should delete
5037 Assert(!mSSData->strStateFilePath.isEmpty());
5038 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5039
5040 mSSData->strStateFilePath.setNull();
5041
5042 // unconditionally set the machine state to powered off, we now
5043 // know no session has locked the machine
5044 mData->mMachineState = MachineState_PoweredOff;
5045 }
5046
5047 size_t cSnapshots = 0;
5048 if (mData->mFirstSnapshot)
5049 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5050 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5051 // fail now before we start detaching media
5052 return setError(VBOX_E_INVALID_OBJECT_STATE,
5053 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5054 mUserData->s.strName.c_str(), cSnapshots);
5055
5056 // This list collects the medium objects from all medium attachments
5057 // which we will detach from the machine and its snapshots, in a specific
5058 // order which allows for closing all media without getting "media in use"
5059 // errors, simply by going through the list from the front to the back:
5060 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5061 // and must be closed before the parent media from the snapshots, or closing the parents
5062 // will fail because they still have children);
5063 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5064 // the root ("first") snapshot of the machine.
5065 MediaList llMedia;
5066
5067 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5068 && mMediaData->mAttachments.size()
5069 )
5070 {
5071 // we have media attachments: detach them all and add the Medium objects to our list
5072 if (aCleanupMode != CleanupMode_UnregisterOnly)
5073 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5074 else
5075 return setError(VBOX_E_INVALID_OBJECT_STATE,
5076 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5077 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5078 }
5079
5080 if (cSnapshots)
5081 {
5082 // autoCleanup must be true here, or we would have failed above
5083
5084 // add the media from the medium attachments of the snapshots to llMedia
5085 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5086 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5087 // into the children first
5088
5089 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5090 MachineState_T oldState = mData->mMachineState;
5091 mData->mMachineState = MachineState_DeletingSnapshot;
5092
5093 // make a copy of the first snapshot so the refcount does not drop to 0
5094 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5095 // because of the AutoCaller voodoo)
5096 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5097
5098 // GO!
5099 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5100
5101 mData->mMachineState = oldState;
5102 }
5103
5104 if (FAILED(rc))
5105 {
5106 i_rollbackMedia();
5107 return rc;
5108 }
5109
5110 // commit all the media changes made above
5111 i_commitMedia();
5112
5113 mData->mRegistered = false;
5114
5115 // machine lock no longer needed
5116 alock.release();
5117
5118 // return media to caller
5119 size_t i = 0;
5120 aMedia.resize(llMedia.size());
5121 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5122 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5123
5124 mParent->i_unregisterMachine(this, id);
5125 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5126
5127 return S_OK;
5128}
5129
5130struct Machine::DeleteTask
5131{
5132 ComObjPtr<Machine> pMachine;
5133 RTCList<ComPtr<IMedium> > llMediums;
5134 StringsList llFilesToDelete;
5135 ComObjPtr<Progress> pProgress;
5136};
5137
5138HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5139{
5140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5141
5142 HRESULT rc = i_checkStateDependency(MutableStateDep);
5143 if (FAILED(rc)) return rc;
5144
5145 if (mData->mRegistered)
5146 return setError(VBOX_E_INVALID_VM_STATE,
5147 tr("Cannot delete settings of a registered machine"));
5148
5149 DeleteTask *pTask = new DeleteTask;
5150 pTask->pMachine = this;
5151
5152 // collect files to delete
5153 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5154
5155 for (size_t i = 0; i < aMedia.size(); ++i)
5156 {
5157 IMedium *pIMedium(aMedia[i]);
5158 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5159 if (pMedium.isNull())
5160 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5161 SafeArray<BSTR> ids;
5162 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5163 if (FAILED(rc)) return rc;
5164 /* At this point the medium should not have any back references
5165 * anymore. If it has it is attached to another VM and *must* not
5166 * deleted. */
5167 if (ids.size() < 1)
5168 pTask->llMediums.append(pMedium);
5169 }
5170 if (mData->pMachineConfigFile->fileExists())
5171 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5172
5173 pTask->pProgress.createObject();
5174 pTask->pProgress->init(i_getVirtualBox(),
5175 static_cast<IMachine*>(this) /* aInitiator */,
5176 Bstr(tr("Deleting files")).raw(),
5177 true /* fCancellable */,
5178 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5179 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5180
5181 int vrc = RTThreadCreate(NULL,
5182 Machine::deleteThread,
5183 (void*)pTask,
5184 0,
5185 RTTHREADTYPE_MAIN_WORKER,
5186 0,
5187 "MachineDelete");
5188
5189 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5190
5191 if (RT_FAILURE(vrc))
5192 {
5193 delete pTask;
5194 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5195 }
5196
5197 LogFlowFuncLeave();
5198
5199 return S_OK;
5200}
5201
5202/**
5203 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5204 * calls Machine::deleteTaskWorker() on the actual machine object.
5205 * @param Thread
5206 * @param pvUser
5207 * @return
5208 */
5209/*static*/
5210DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5211{
5212 LogFlowFuncEnter();
5213
5214 DeleteTask *pTask = (DeleteTask*)pvUser;
5215 Assert(pTask);
5216 Assert(pTask->pMachine);
5217 Assert(pTask->pProgress);
5218
5219 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5220 pTask->pProgress->i_notifyComplete(rc);
5221
5222 delete pTask;
5223
5224 LogFlowFuncLeave();
5225
5226 NOREF(Thread);
5227
5228 return VINF_SUCCESS;
5229}
5230
5231/**
5232 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5233 * @param task
5234 * @return
5235 */
5236HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5237{
5238 AutoCaller autoCaller(this);
5239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5240
5241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5242
5243 HRESULT rc = S_OK;
5244
5245 try
5246 {
5247 ULONG uLogHistoryCount = 3;
5248 ComPtr<ISystemProperties> systemProperties;
5249 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5250 if (FAILED(rc)) throw rc;
5251
5252 if (!systemProperties.isNull())
5253 {
5254 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5255 if (FAILED(rc)) throw rc;
5256 }
5257
5258 MachineState_T oldState = mData->mMachineState;
5259 i_setMachineState(MachineState_SettingUp);
5260 alock.release();
5261 for (size_t i = 0; i < task.llMediums.size(); ++i)
5262 {
5263 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5264 {
5265 AutoCaller mac(pMedium);
5266 if (FAILED(mac.rc())) throw mac.rc();
5267 Utf8Str strLocation = pMedium->i_getLocationFull();
5268 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5269 if (FAILED(rc)) throw rc;
5270 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5271 }
5272 if (pMedium->i_isMediumFormatFile())
5273 {
5274 ComPtr<IProgress> pProgress2;
5275 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5276 if (FAILED(rc)) throw rc;
5277 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5278 if (FAILED(rc)) throw rc;
5279 /* Check the result of the asynchronous process. */
5280 LONG iRc;
5281 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5282 if (FAILED(rc)) throw rc;
5283 /* If the thread of the progress object has an error, then
5284 * retrieve the error info from there, or it'll be lost. */
5285 if (FAILED(iRc))
5286 throw setError(ProgressErrorInfo(pProgress2));
5287 }
5288
5289 /* Close the medium, deliberately without checking the return
5290 * code, and without leaving any trace in the error info, as
5291 * a failure here is a very minor issue, which shouldn't happen
5292 * as above we even managed to delete the medium. */
5293 {
5294 ErrorInfoKeeper eik;
5295 pMedium->Close();
5296 }
5297 }
5298 i_setMachineState(oldState);
5299 alock.acquire();
5300
5301 // delete the files pushed on the task list by Machine::Delete()
5302 // (this includes saved states of the machine and snapshots and
5303 // medium storage files from the IMedium list passed in, and the
5304 // machine XML file)
5305 StringsList::const_iterator it = task.llFilesToDelete.begin();
5306 while (it != task.llFilesToDelete.end())
5307 {
5308 const Utf8Str &strFile = *it;
5309 LogFunc(("Deleting file %s\n", strFile.c_str()));
5310 int vrc = RTFileDelete(strFile.c_str());
5311 if (RT_FAILURE(vrc))
5312 throw setError(VBOX_E_IPRT_ERROR,
5313 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5314
5315 ++it;
5316 if (it == task.llFilesToDelete.end())
5317 {
5318 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5319 if (FAILED(rc)) throw rc;
5320 break;
5321 }
5322
5323 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5324 if (FAILED(rc)) throw rc;
5325 }
5326
5327 /* delete the settings only when the file actually exists */
5328 if (mData->pMachineConfigFile->fileExists())
5329 {
5330 /* Delete any backup or uncommitted XML files. Ignore failures.
5331 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5332 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5333 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5334 RTFileDelete(otherXml.c_str());
5335 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5336 RTFileDelete(otherXml.c_str());
5337
5338 /* delete the Logs folder, nothing important should be left
5339 * there (we don't check for errors because the user might have
5340 * some private files there that we don't want to delete) */
5341 Utf8Str logFolder;
5342 getLogFolder(logFolder);
5343 Assert(logFolder.length());
5344 if (RTDirExists(logFolder.c_str()))
5345 {
5346 /* Delete all VBox.log[.N] files from the Logs folder
5347 * (this must be in sync with the rotation logic in
5348 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5349 * files that may have been created by the GUI. */
5350 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5351 logFolder.c_str(), RTPATH_DELIMITER);
5352 RTFileDelete(log.c_str());
5353 log = Utf8StrFmt("%s%cVBox.png",
5354 logFolder.c_str(), RTPATH_DELIMITER);
5355 RTFileDelete(log.c_str());
5356 for (int i = uLogHistoryCount; i > 0; i--)
5357 {
5358 log = Utf8StrFmt("%s%cVBox.log.%d",
5359 logFolder.c_str(), RTPATH_DELIMITER, i);
5360 RTFileDelete(log.c_str());
5361 log = Utf8StrFmt("%s%cVBox.png.%d",
5362 logFolder.c_str(), RTPATH_DELIMITER, i);
5363 RTFileDelete(log.c_str());
5364 }
5365#if defined(RT_OS_WINDOWS)
5366 log = Utf8StrFmt("%s%cVBoxStartup.log",
5367 logFolder.c_str(), RTPATH_DELIMITER);
5368 RTFileDelete(log.c_str());
5369#endif
5370
5371 RTDirRemove(logFolder.c_str());
5372 }
5373
5374 /* delete the Snapshots folder, nothing important should be left
5375 * there (we don't check for errors because the user might have
5376 * some private files there that we don't want to delete) */
5377 Utf8Str strFullSnapshotFolder;
5378 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5379 Assert(!strFullSnapshotFolder.isEmpty());
5380 if (RTDirExists(strFullSnapshotFolder.c_str()))
5381 RTDirRemove(strFullSnapshotFolder.c_str());
5382
5383 // delete the directory that contains the settings file, but only
5384 // if it matches the VM name
5385 Utf8Str settingsDir;
5386 if (i_isInOwnDir(&settingsDir))
5387 RTDirRemove(settingsDir.c_str());
5388 }
5389
5390 alock.release();
5391
5392 mParent->i_saveModifiedRegistries();
5393 }
5394 catch (HRESULT aRC) { rc = aRC; }
5395
5396 return rc;
5397}
5398
5399HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5400{
5401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5402
5403 ComObjPtr<Snapshot> pSnapshot;
5404 HRESULT rc;
5405
5406 if (aNameOrId.isEmpty())
5407 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5408 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5409 else
5410 {
5411 Guid uuid(aNameOrId);
5412 if (uuid.isValid())
5413 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5414 else
5415 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5416 }
5417 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5418
5419 return rc;
5420}
5421
5422HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5423{
5424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5425
5426 HRESULT rc = i_checkStateDependency(MutableStateDep);
5427 if (FAILED(rc)) return rc;
5428
5429 ComObjPtr<SharedFolder> sharedFolder;
5430 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5431 if (SUCCEEDED(rc))
5432 return setError(VBOX_E_OBJECT_IN_USE,
5433 tr("Shared folder named '%s' already exists"),
5434 aName.c_str());
5435
5436 sharedFolder.createObject();
5437 rc = sharedFolder->init(i_getMachine(),
5438 aName,
5439 aHostPath,
5440 !!aWritable,
5441 !!aAutomount,
5442 true /* fFailOnError */);
5443 if (FAILED(rc)) return rc;
5444
5445 i_setModified(IsModified_SharedFolders);
5446 mHWData.backup();
5447 mHWData->mSharedFolders.push_back(sharedFolder);
5448
5449 /* inform the direct session if any */
5450 alock.release();
5451 i_onSharedFolderChange();
5452
5453 return S_OK;
5454}
5455
5456HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5457{
5458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5459
5460 HRESULT rc = i_checkStateDependency(MutableStateDep);
5461 if (FAILED(rc)) return rc;
5462
5463 ComObjPtr<SharedFolder> sharedFolder;
5464 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5465 if (FAILED(rc)) return rc;
5466
5467 i_setModified(IsModified_SharedFolders);
5468 mHWData.backup();
5469 mHWData->mSharedFolders.remove(sharedFolder);
5470
5471 /* inform the direct session if any */
5472 alock.release();
5473 i_onSharedFolderChange();
5474
5475 return S_OK;
5476}
5477
5478HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5479{
5480 /* start with No */
5481 *aCanShow = FALSE;
5482
5483 ComPtr<IInternalSessionControl> directControl;
5484 {
5485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5486
5487 if (mData->mSession.mState != SessionState_Locked)
5488 return setError(VBOX_E_INVALID_VM_STATE,
5489 tr("Machine is not locked for session (session state: %s)"),
5490 Global::stringifySessionState(mData->mSession.mState));
5491
5492 directControl = mData->mSession.mDirectControl;
5493 }
5494
5495 /* ignore calls made after #OnSessionEnd() is called */
5496 if (!directControl)
5497 return S_OK;
5498
5499 LONG64 dummy;
5500 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5501}
5502
5503HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5504{
5505 ComPtr<IInternalSessionControl> directControl;
5506 {
5507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5508
5509 if (mData->mSession.mState != SessionState_Locked)
5510 return setError(E_FAIL,
5511 tr("Machine is not locked for session (session state: %s)"),
5512 Global::stringifySessionState(mData->mSession.mState));
5513
5514 directControl = mData->mSession.mDirectControl;
5515 }
5516
5517 /* ignore calls made after #OnSessionEnd() is called */
5518 if (!directControl)
5519 return S_OK;
5520
5521 BOOL dummy;
5522 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5523}
5524
5525#ifdef VBOX_WITH_GUEST_PROPS
5526/**
5527 * Look up a guest property in VBoxSVC's internal structures.
5528 */
5529HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5530 com::Utf8Str &aValue,
5531 LONG64 *aTimestamp,
5532 com::Utf8Str &aFlags) const
5533{
5534 using namespace guestProp;
5535
5536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5537 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5538
5539 if (it != mHWData->mGuestProperties.end())
5540 {
5541 char szFlags[MAX_FLAGS_LEN + 1];
5542 aValue = it->second.strValue;
5543 *aTimestamp = it->second.mTimestamp;
5544 writeFlags(it->second.mFlags, szFlags);
5545 aFlags = Utf8Str(szFlags);
5546 }
5547
5548 return S_OK;
5549}
5550
5551/**
5552 * Query the VM that a guest property belongs to for the property.
5553 * @returns E_ACCESSDENIED if the VM process is not available or not
5554 * currently handling queries and the lookup should then be done in
5555 * VBoxSVC.
5556 */
5557HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5558 com::Utf8Str &aValue,
5559 LONG64 *aTimestamp,
5560 com::Utf8Str &aFlags) const
5561{
5562 HRESULT rc = S_OK;
5563 BSTR bValue = NULL;
5564 BSTR bFlags = NULL;
5565
5566 ComPtr<IInternalSessionControl> directControl;
5567 directControl = mData->mSession.mDirectControl;
5568
5569 /* fail if we were called after #OnSessionEnd() is called. This is a
5570 * silly race condition. */
5571
5572 /** @todo This code is bothering API clients (like python script clients) with
5573 * the AccessGuestProperty call, creating unncessary IPC. Need to
5574 * have a way of figuring out which kind of direct session it is... */
5575 if (!directControl)
5576 rc = E_ACCESSDENIED;
5577 else
5578 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5579 0 /* accessMode */,
5580 &bValue, aTimestamp, &bFlags);
5581
5582 aValue = bValue;
5583 aFlags = bFlags;
5584
5585 return rc;
5586}
5587#endif // VBOX_WITH_GUEST_PROPS
5588
5589HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5590 com::Utf8Str &aValue,
5591 LONG64 *aTimestamp,
5592 com::Utf8Str &aFlags)
5593{
5594#ifndef VBOX_WITH_GUEST_PROPS
5595 ReturnComNotImplemented();
5596#else // VBOX_WITH_GUEST_PROPS
5597
5598 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5599
5600 if (rc == E_ACCESSDENIED)
5601 /* The VM is not running or the service is not (yet) accessible */
5602 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5603 return rc;
5604#endif // VBOX_WITH_GUEST_PROPS
5605}
5606
5607HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5608{
5609 LONG64 dummyTimestamp;
5610 com::Utf8Str dummyFlags;
5611 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5612 return rc;
5613
5614}
5615HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5616{
5617 com::Utf8Str dummyFlags;
5618 com::Utf8Str dummyValue;
5619 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5620 return rc;
5621}
5622
5623#ifdef VBOX_WITH_GUEST_PROPS
5624/**
5625 * Set a guest property in VBoxSVC's internal structures.
5626 */
5627HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5628 const com::Utf8Str &aFlags, bool fDelete)
5629{
5630 using namespace guestProp;
5631
5632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5633 HRESULT rc = S_OK;
5634
5635 rc = i_checkStateDependency(MutableStateDep);
5636 if (FAILED(rc)) return rc;
5637
5638 try
5639 {
5640 uint32_t fFlags = NILFLAG;
5641 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5642 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5643
5644 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5645 if (it == mHWData->mGuestProperties.end())
5646 {
5647 if (!fDelete)
5648 {
5649 i_setModified(IsModified_MachineData);
5650 mHWData.backupEx();
5651
5652 RTTIMESPEC time;
5653 HWData::GuestProperty prop;
5654 prop.strValue = Bstr(aValue).raw();
5655 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5656 prop.mFlags = fFlags;
5657 mHWData->mGuestProperties[aName] = prop;
5658 }
5659 }
5660 else
5661 {
5662 if (it->second.mFlags & (RDONLYHOST))
5663 {
5664 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5665 }
5666 else
5667 {
5668 i_setModified(IsModified_MachineData);
5669 mHWData.backupEx();
5670
5671 /* The backupEx() operation invalidates our iterator,
5672 * so get a new one. */
5673 it = mHWData->mGuestProperties.find(aName);
5674 Assert(it != mHWData->mGuestProperties.end());
5675
5676 if (!fDelete)
5677 {
5678 RTTIMESPEC time;
5679 it->second.strValue = aValue;
5680 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5681 it->second.mFlags = fFlags;
5682 }
5683 else
5684 mHWData->mGuestProperties.erase(it);
5685 }
5686 }
5687
5688 if ( SUCCEEDED(rc)
5689 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5690 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5691 RTSTR_MAX,
5692 aName.c_str(),
5693 RTSTR_MAX,
5694 NULL)
5695 )
5696 )
5697 {
5698 alock.release();
5699
5700 mParent->i_onGuestPropertyChange(mData->mUuid,
5701 Bstr(aName).raw(),
5702 Bstr(aValue).raw(),
5703 Bstr(aFlags).raw());
5704 }
5705 }
5706 catch (std::bad_alloc &)
5707 {
5708 rc = E_OUTOFMEMORY;
5709 }
5710
5711 return rc;
5712}
5713
5714/**
5715 * Set a property on the VM that that property belongs to.
5716 * @returns E_ACCESSDENIED if the VM process is not available or not
5717 * currently handling queries and the setting should then be done in
5718 * VBoxSVC.
5719 */
5720HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5721 const com::Utf8Str &aFlags, bool fDelete)
5722{
5723 HRESULT rc;
5724
5725 try
5726 {
5727 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5728
5729 BSTR dummy = NULL; /* will not be changed (setter) */
5730 LONG64 dummy64;
5731 if (!directControl)
5732 rc = E_ACCESSDENIED;
5733 else
5734 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5735 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5736 fDelete? 2: 1 /* accessMode */,
5737 &dummy, &dummy64, &dummy);
5738 }
5739 catch (std::bad_alloc &)
5740 {
5741 rc = E_OUTOFMEMORY;
5742 }
5743
5744 return rc;
5745}
5746#endif // VBOX_WITH_GUEST_PROPS
5747
5748HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5749 const com::Utf8Str &aFlags)
5750{
5751#ifndef VBOX_WITH_GUEST_PROPS
5752 ReturnComNotImplemented();
5753#else // VBOX_WITH_GUEST_PROPS
5754 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5755 if (rc == E_ACCESSDENIED)
5756 /* The VM is not running or the service is not (yet) accessible */
5757 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5758 return rc;
5759#endif // VBOX_WITH_GUEST_PROPS
5760}
5761
5762HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5763{
5764 return setGuestProperty(aProperty, aValue, "");
5765}
5766
5767HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5768{
5769#ifndef VBOX_WITH_GUEST_PROPS
5770 ReturnComNotImplemented();
5771#else // VBOX_WITH_GUEST_PROPS
5772 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5773 if (rc == E_ACCESSDENIED)
5774 /* The VM is not running or the service is not (yet) accessible */
5775 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5776 return rc;
5777#endif // VBOX_WITH_GUEST_PROPS
5778}
5779
5780#ifdef VBOX_WITH_GUEST_PROPS
5781/**
5782 * Enumerate the guest properties in VBoxSVC's internal structures.
5783 */
5784HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5785 std::vector<com::Utf8Str> &aNames,
5786 std::vector<com::Utf8Str> &aValues,
5787 std::vector<LONG64> &aTimestamps,
5788 std::vector<com::Utf8Str> &aFlags)
5789{
5790 using namespace guestProp;
5791
5792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5793 Utf8Str strPatterns(aPatterns);
5794
5795 HWData::GuestPropertyMap propMap;
5796
5797 /*
5798 * Look for matching patterns and build up a list.
5799 */
5800 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5801 while (it != mHWData->mGuestProperties.end())
5802 {
5803 if ( strPatterns.isEmpty()
5804 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5805 RTSTR_MAX,
5806 it->first.c_str(),
5807 RTSTR_MAX,
5808 NULL)
5809 )
5810 propMap.insert(*it);
5811 it++;
5812 }
5813
5814 alock.release();
5815
5816 /*
5817 * And build up the arrays for returning the property information.
5818 */
5819 size_t cEntries = propMap.size();
5820
5821 aNames.resize(cEntries);
5822 aValues.resize(cEntries);
5823 aTimestamps.resize(cEntries);
5824 aFlags.resize(cEntries);
5825
5826 char szFlags[MAX_FLAGS_LEN + 1];
5827 size_t i= 0;
5828 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5829 {
5830 aNames[i] = it->first;
5831 aValues[i] = it->second.strValue;
5832 aTimestamps[i] = it->second.mTimestamp;
5833 writeFlags(it->second.mFlags, szFlags);
5834 aFlags[i] = Utf8Str(szFlags);
5835 }
5836
5837 return S_OK;
5838}
5839
5840/**
5841 * Enumerate the properties managed by a VM.
5842 * @returns E_ACCESSDENIED if the VM process is not available or not
5843 * currently handling queries and the setting should then be done in
5844 * VBoxSVC.
5845 */
5846HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5847 std::vector<com::Utf8Str> &aNames,
5848 std::vector<com::Utf8Str> &aValues,
5849 std::vector<LONG64> &aTimestamps,
5850 std::vector<com::Utf8Str> &aFlags)
5851{
5852 HRESULT rc;
5853 ComPtr<IInternalSessionControl> directControl;
5854 directControl = mData->mSession.mDirectControl;
5855
5856
5857 com::SafeArray<BSTR> bNames;
5858 com::SafeArray<BSTR> bValues;
5859 com::SafeArray<LONG64> bTimestamps;
5860 com::SafeArray<BSTR> bFlags;
5861
5862 if (!directControl)
5863 rc = E_ACCESSDENIED;
5864 else
5865 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5866 ComSafeArrayAsOutParam(bNames),
5867 ComSafeArrayAsOutParam(bValues),
5868 ComSafeArrayAsOutParam(bTimestamps),
5869 ComSafeArrayAsOutParam(bFlags));
5870 size_t i;
5871 aNames.resize(bNames.size());
5872 for (i = 0; i < bNames.size(); ++i)
5873 aNames[i] = Utf8Str(bNames[i]);
5874 aValues.resize(bValues.size());
5875 for (i = 0; i < bValues.size(); ++i)
5876 aValues[i] = Utf8Str(bValues[i]);
5877 aTimestamps.resize(bTimestamps.size());
5878 for (i = 0; i < bTimestamps.size(); ++i)
5879 aTimestamps[i] = bTimestamps[i];
5880 aFlags.resize(bFlags.size());
5881 for (i = 0; i < bFlags.size(); ++i)
5882 aFlags[i] = Utf8Str(bFlags[i]);
5883
5884 return rc;
5885}
5886#endif // VBOX_WITH_GUEST_PROPS
5887HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5888 std::vector<com::Utf8Str> &aNames,
5889 std::vector<com::Utf8Str> &aValues,
5890 std::vector<LONG64> &aTimestamps,
5891 std::vector<com::Utf8Str> &aFlags)
5892{
5893#ifndef VBOX_WITH_GUEST_PROPS
5894 ReturnComNotImplemented();
5895#else // VBOX_WITH_GUEST_PROPS
5896
5897 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5898
5899 if (rc == E_ACCESSDENIED)
5900 /* The VM is not running or the service is not (yet) accessible */
5901 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5902 return rc;
5903#endif // VBOX_WITH_GUEST_PROPS
5904}
5905
5906HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5907 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5908{
5909 MediaData::AttachmentList atts;
5910
5911 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5912 if (FAILED(rc)) return rc;
5913
5914 size_t i = 0;
5915 aMediumAttachments.resize(atts.size());
5916 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5917 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5918
5919 return S_OK;
5920}
5921
5922HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5923 LONG aControllerPort,
5924 LONG aDevice,
5925 ComPtr<IMediumAttachment> &aAttachment)
5926{
5927 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5928 aName.c_str(), aControllerPort, aDevice));
5929
5930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5931
5932 aAttachment = NULL;
5933
5934 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5935 Bstr(aName).raw(),
5936 aControllerPort,
5937 aDevice);
5938 if (pAttach.isNull())
5939 return setError(VBOX_E_OBJECT_NOT_FOUND,
5940 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5941 aDevice, aControllerPort, aName.c_str());
5942
5943 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5944
5945 return S_OK;
5946}
5947
5948
5949HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5950 StorageBus_T aConnectionType,
5951 ComPtr<IStorageController> &aController)
5952{
5953 if ( (aConnectionType <= StorageBus_Null)
5954 || (aConnectionType > StorageBus_USB))
5955 return setError(E_INVALIDARG,
5956 tr("Invalid connection type: %d"),
5957 aConnectionType);
5958
5959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5960
5961 HRESULT rc = i_checkStateDependency(MutableStateDep);
5962 if (FAILED(rc)) return rc;
5963
5964 /* try to find one with the name first. */
5965 ComObjPtr<StorageController> ctrl;
5966
5967 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5968 if (SUCCEEDED(rc))
5969 return setError(VBOX_E_OBJECT_IN_USE,
5970 tr("Storage controller named '%s' already exists"),
5971 aName.c_str());
5972
5973 ctrl.createObject();
5974
5975 /* get a new instance number for the storage controller */
5976 ULONG ulInstance = 0;
5977 bool fBootable = true;
5978 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5979 it != mStorageControllers->end();
5980 ++it)
5981 {
5982 if ((*it)->i_getStorageBus() == aConnectionType)
5983 {
5984 ULONG ulCurInst = (*it)->i_getInstance();
5985
5986 if (ulCurInst >= ulInstance)
5987 ulInstance = ulCurInst + 1;
5988
5989 /* Only one controller of each type can be marked as bootable. */
5990 if ((*it)->i_getBootable())
5991 fBootable = false;
5992 }
5993 }
5994
5995 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5996 if (FAILED(rc)) return rc;
5997
5998 i_setModified(IsModified_Storage);
5999 mStorageControllers.backup();
6000 mStorageControllers->push_back(ctrl);
6001
6002 ctrl.queryInterfaceTo(aController.asOutParam());
6003
6004 /* inform the direct session if any */
6005 alock.release();
6006 i_onStorageControllerChange();
6007
6008 return S_OK;
6009}
6010
6011HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6012 ComPtr<IStorageController> &aStorageController)
6013{
6014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6015
6016 ComObjPtr<StorageController> ctrl;
6017
6018 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6019 if (SUCCEEDED(rc))
6020 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6021
6022 return rc;
6023}
6024
6025HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6026 ComPtr<IStorageController> &aStorageController)
6027{
6028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6029
6030 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6031 it != mStorageControllers->end();
6032 ++it)
6033 {
6034 if ((*it)->i_getInstance() == aInstance)
6035 {
6036 (*it).queryInterfaceTo(aStorageController.asOutParam());
6037 return S_OK;
6038 }
6039 }
6040
6041 return setError(VBOX_E_OBJECT_NOT_FOUND,
6042 tr("Could not find a storage controller with instance number '%lu'"),
6043 aInstance);
6044}
6045
6046HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6047{
6048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6049
6050 HRESULT rc = i_checkStateDependency(MutableStateDep);
6051 if (FAILED(rc)) return rc;
6052
6053 ComObjPtr<StorageController> ctrl;
6054
6055 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6056 if (SUCCEEDED(rc))
6057 {
6058 /* Ensure that only one controller of each type is marked as bootable. */
6059 if (aBootable == TRUE)
6060 {
6061 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6062 it != mStorageControllers->end();
6063 ++it)
6064 {
6065 ComObjPtr<StorageController> aCtrl = (*it);
6066
6067 if ( (aCtrl->i_getName() != aName)
6068 && aCtrl->i_getBootable() == TRUE
6069 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6070 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6071 {
6072 aCtrl->i_setBootable(FALSE);
6073 break;
6074 }
6075 }
6076 }
6077
6078 if (SUCCEEDED(rc))
6079 {
6080 ctrl->i_setBootable(aBootable);
6081 i_setModified(IsModified_Storage);
6082 }
6083 }
6084
6085 if (SUCCEEDED(rc))
6086 {
6087 /* inform the direct session if any */
6088 alock.release();
6089 i_onStorageControllerChange();
6090 }
6091
6092 return rc;
6093}
6094
6095HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6096{
6097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6098
6099 HRESULT rc = i_checkStateDependency(MutableStateDep);
6100 if (FAILED(rc)) return rc;
6101
6102 ComObjPtr<StorageController> ctrl;
6103 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6104 if (FAILED(rc)) return rc;
6105
6106 {
6107 /* find all attached devices to the appropriate storage controller and detach them all */
6108 // make a temporary list because detachDevice invalidates iterators into
6109 // mMediaData->mAttachments
6110 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6111
6112 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6113 it != llAttachments2.end();
6114 ++it)
6115 {
6116 MediumAttachment *pAttachTemp = *it;
6117
6118 AutoCaller localAutoCaller(pAttachTemp);
6119 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6120
6121 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6122
6123 if (pAttachTemp->i_getControllerName() == aName)
6124 {
6125 rc = i_detachDevice(pAttachTemp, alock, NULL);
6126 if (FAILED(rc)) return rc;
6127 }
6128 }
6129 }
6130
6131 /* We can remove it now. */
6132 i_setModified(IsModified_Storage);
6133 mStorageControllers.backup();
6134
6135 ctrl->i_unshare();
6136
6137 mStorageControllers->remove(ctrl);
6138
6139 /* inform the direct session if any */
6140 alock.release();
6141 i_onStorageControllerChange();
6142
6143 return S_OK;
6144}
6145
6146HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6147 ComPtr<IUSBController> &aController)
6148{
6149 if ( (aType <= USBControllerType_Null)
6150 || (aType >= USBControllerType_Last))
6151 return setError(E_INVALIDARG,
6152 tr("Invalid USB controller type: %d"),
6153 aType);
6154
6155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6156
6157 HRESULT rc = i_checkStateDependency(MutableStateDep);
6158 if (FAILED(rc)) return rc;
6159
6160 /* try to find one with the same type first. */
6161 ComObjPtr<USBController> ctrl;
6162
6163 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6164 if (SUCCEEDED(rc))
6165 return setError(VBOX_E_OBJECT_IN_USE,
6166 tr("USB controller named '%s' already exists"),
6167 aName.c_str());
6168
6169 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6170 ULONG maxInstances;
6171 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6172 if (FAILED(rc))
6173 return rc;
6174
6175 ULONG cInstances = i_getUSBControllerCountByType(aType);
6176 if (cInstances >= maxInstances)
6177 return setError(E_INVALIDARG,
6178 tr("Too many USB controllers of this type"));
6179
6180 ctrl.createObject();
6181
6182 rc = ctrl->init(this, aName, aType);
6183 if (FAILED(rc)) return rc;
6184
6185 i_setModified(IsModified_USB);
6186 mUSBControllers.backup();
6187 mUSBControllers->push_back(ctrl);
6188
6189 ctrl.queryInterfaceTo(aController.asOutParam());
6190
6191 /* inform the direct session if any */
6192 alock.release();
6193 i_onUSBControllerChange();
6194
6195 return S_OK;
6196}
6197
6198HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6199{
6200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6201
6202 ComObjPtr<USBController> ctrl;
6203
6204 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6205 if (SUCCEEDED(rc))
6206 ctrl.queryInterfaceTo(aController.asOutParam());
6207
6208 return rc;
6209}
6210
6211HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6212 ULONG *aControllers)
6213{
6214 if ( (aType <= USBControllerType_Null)
6215 || (aType >= USBControllerType_Last))
6216 return setError(E_INVALIDARG,
6217 tr("Invalid USB controller type: %d"),
6218 aType);
6219
6220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6221
6222 ComObjPtr<USBController> ctrl;
6223
6224 *aControllers = i_getUSBControllerCountByType(aType);
6225
6226 return S_OK;
6227}
6228
6229HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6230{
6231
6232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6233
6234 HRESULT rc = i_checkStateDependency(MutableStateDep);
6235 if (FAILED(rc)) return rc;
6236
6237 ComObjPtr<USBController> ctrl;
6238 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6239 if (FAILED(rc)) return rc;
6240
6241 i_setModified(IsModified_USB);
6242 mUSBControllers.backup();
6243
6244 ctrl->i_unshare();
6245
6246 mUSBControllers->remove(ctrl);
6247
6248 /* inform the direct session if any */
6249 alock.release();
6250 i_onUSBControllerChange();
6251
6252 return S_OK;
6253}
6254
6255HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6256 ULONG *aOriginX,
6257 ULONG *aOriginY,
6258 ULONG *aWidth,
6259 ULONG *aHeight,
6260 BOOL *aEnabled)
6261{
6262 uint32_t u32OriginX= 0;
6263 uint32_t u32OriginY= 0;
6264 uint32_t u32Width = 0;
6265 uint32_t u32Height = 0;
6266 uint16_t u16Flags = 0;
6267
6268 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6269 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6270 if (RT_FAILURE(vrc))
6271 {
6272#ifdef RT_OS_WINDOWS
6273 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6274 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6275 * So just assign fEnable to TRUE again.
6276 * The right fix would be to change GUI API wrappers to make sure that parameters
6277 * are changed only if API succeeds.
6278 */
6279 *aEnabled = TRUE;
6280#endif
6281 return setError(VBOX_E_IPRT_ERROR,
6282 tr("Saved guest size is not available (%Rrc)"),
6283 vrc);
6284 }
6285
6286 *aOriginX = u32OriginX;
6287 *aOriginY = u32OriginY;
6288 *aWidth = u32Width;
6289 *aHeight = u32Height;
6290 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6291
6292 return S_OK;
6293}
6294
6295HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6296{
6297 if (aScreenId != 0)
6298 return E_NOTIMPL;
6299
6300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6301
6302 uint8_t *pu8Data = NULL;
6303 uint32_t cbData = 0;
6304 uint32_t u32Width = 0;
6305 uint32_t u32Height = 0;
6306
6307 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6308
6309 if (RT_FAILURE(vrc))
6310 return setError(VBOX_E_IPRT_ERROR,
6311 tr("Saved screenshot data is not available (%Rrc)"),
6312 vrc);
6313
6314 *aSize = cbData;
6315 *aWidth = u32Width;
6316 *aHeight = u32Height;
6317
6318 freeSavedDisplayScreenshot(pu8Data);
6319
6320 return S_OK;
6321}
6322
6323HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6324 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6325{
6326 if (aScreenId != 0)
6327 return E_NOTIMPL;
6328
6329 if ( aBitmapFormat != BitmapFormat_BGR0
6330 && aBitmapFormat != BitmapFormat_BGRA
6331 && aBitmapFormat != BitmapFormat_RGBA
6332 && aBitmapFormat != BitmapFormat_PNG)
6333 return setError(E_NOTIMPL,
6334 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6335
6336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6337
6338 uint8_t *pu8Data = NULL;
6339 uint32_t cbData = 0;
6340 uint32_t u32Width = 0;
6341 uint32_t u32Height = 0;
6342
6343 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6344
6345 if (RT_FAILURE(vrc))
6346 return setError(VBOX_E_IPRT_ERROR,
6347 tr("Saved thumbnail data is not available (%Rrc)"),
6348 vrc);
6349
6350 HRESULT hr = S_OK;
6351
6352 *aWidth = u32Width;
6353 *aHeight = u32Height;
6354
6355 if (cbData > 0)
6356 {
6357 /* Convert pixels to the format expected by the API caller. */
6358 if (aBitmapFormat == BitmapFormat_BGR0)
6359 {
6360 /* [0] B, [1] G, [2] R, [3] 0. */
6361 aData.resize(cbData);
6362 memcpy(&aData.front(), pu8Data, cbData);
6363 }
6364 else if (aBitmapFormat == BitmapFormat_BGRA)
6365 {
6366 /* [0] B, [1] G, [2] R, [3] A. */
6367 aData.resize(cbData);
6368 for (uint32_t i = 0; i < cbData; i += 4)
6369 {
6370 aData[i] = pu8Data[i];
6371 aData[i + 1] = pu8Data[i + 1];
6372 aData[i + 2] = pu8Data[i + 2];
6373 aData[i + 3] = 0xff;
6374 }
6375 }
6376 else if (aBitmapFormat == BitmapFormat_RGBA)
6377 {
6378 /* [0] R, [1] G, [2] B, [3] A. */
6379 aData.resize(cbData);
6380 for (uint32_t i = 0; i < cbData; i += 4)
6381 {
6382 aData[i] = pu8Data[i + 2];
6383 aData[i + 1] = pu8Data[i + 1];
6384 aData[i + 2] = pu8Data[i];
6385 aData[i + 3] = 0xff;
6386 }
6387 }
6388 else if (aBitmapFormat == BitmapFormat_PNG)
6389 {
6390 uint8_t *pu8PNG = NULL;
6391 uint32_t cbPNG = 0;
6392 uint32_t cxPNG = 0;
6393 uint32_t cyPNG = 0;
6394
6395 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6396
6397 if (RT_SUCCESS(vrc))
6398 {
6399 aData.resize(cbPNG);
6400 if (cbPNG)
6401 memcpy(&aData.front(), pu8PNG, cbPNG);
6402 }
6403 else
6404 hr = setError(VBOX_E_IPRT_ERROR,
6405 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6406 vrc);
6407
6408 RTMemFree(pu8PNG);
6409 }
6410 }
6411
6412 freeSavedDisplayScreenshot(pu8Data);
6413
6414 return hr;
6415}
6416
6417HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6418{
6419 if (aScreenId != 0)
6420 return E_NOTIMPL;
6421
6422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6423
6424 uint8_t *pu8Data = NULL;
6425 uint32_t cbData = 0;
6426 uint32_t u32Width = 0;
6427 uint32_t u32Height = 0;
6428
6429 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6430
6431 if (RT_FAILURE(vrc))
6432 return setError(VBOX_E_IPRT_ERROR,
6433 tr("Saved screenshot data is not available (%Rrc)"),
6434 vrc);
6435
6436 *aSize = cbData;
6437 *aWidth = u32Width;
6438 *aHeight = u32Height;
6439
6440 freeSavedDisplayScreenshot(pu8Data);
6441
6442 return S_OK;
6443}
6444
6445HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6446{
6447 if (aScreenId != 0)
6448 return E_NOTIMPL;
6449
6450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6451
6452 uint8_t *pu8Data = NULL;
6453 uint32_t cbData = 0;
6454 uint32_t u32Width = 0;
6455 uint32_t u32Height = 0;
6456
6457 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6458
6459 if (RT_FAILURE(vrc))
6460 return setError(VBOX_E_IPRT_ERROR,
6461 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6462 vrc);
6463
6464 *aWidth = u32Width;
6465 *aHeight = u32Height;
6466
6467 aData.resize(cbData);
6468 if (cbData)
6469 memcpy(&aData.front(), pu8Data, cbData);
6470
6471 freeSavedDisplayScreenshot(pu8Data);
6472
6473 return S_OK;
6474}
6475
6476HRESULT Machine::hotPlugCPU(ULONG aCpu)
6477{
6478 HRESULT rc = S_OK;
6479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6480
6481 if (!mHWData->mCPUHotPlugEnabled)
6482 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6483
6484 if (aCpu >= mHWData->mCPUCount)
6485 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6486
6487 if (mHWData->mCPUAttached[aCpu])
6488 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6489
6490 alock.release();
6491 rc = i_onCPUChange(aCpu, false);
6492 alock.acquire();
6493 if (FAILED(rc)) return rc;
6494
6495 i_setModified(IsModified_MachineData);
6496 mHWData.backup();
6497 mHWData->mCPUAttached[aCpu] = true;
6498
6499 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6500 if (Global::IsOnline(mData->mMachineState))
6501 i_saveSettings(NULL);
6502
6503 return S_OK;
6504}
6505
6506HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6507{
6508 HRESULT rc = S_OK;
6509
6510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 if (!mHWData->mCPUHotPlugEnabled)
6513 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6514
6515 if (aCpu >= SchemaDefs::MaxCPUCount)
6516 return setError(E_INVALIDARG,
6517 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6518 SchemaDefs::MaxCPUCount);
6519
6520 if (!mHWData->mCPUAttached[aCpu])
6521 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6522
6523 /* CPU 0 can't be detached */
6524 if (aCpu == 0)
6525 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6526
6527 alock.release();
6528 rc = i_onCPUChange(aCpu, true);
6529 alock.acquire();
6530 if (FAILED(rc)) return rc;
6531
6532 i_setModified(IsModified_MachineData);
6533 mHWData.backup();
6534 mHWData->mCPUAttached[aCpu] = false;
6535
6536 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6537 if (Global::IsOnline(mData->mMachineState))
6538 i_saveSettings(NULL);
6539
6540 return S_OK;
6541}
6542
6543HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6544{
6545 *aAttached = false;
6546
6547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6548
6549 /* If hotplug is enabled the CPU is always enabled. */
6550 if (!mHWData->mCPUHotPlugEnabled)
6551 {
6552 if (aCpu < mHWData->mCPUCount)
6553 *aAttached = true;
6554 }
6555 else
6556 {
6557 if (aCpu < SchemaDefs::MaxCPUCount)
6558 *aAttached = mHWData->mCPUAttached[aCpu];
6559 }
6560
6561 return S_OK;
6562}
6563
6564HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6565{
6566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6567
6568 Utf8Str log = i_queryLogFilename(aIdx);
6569 if (!RTFileExists(log.c_str()))
6570 log.setNull();
6571 aFilename = log;
6572
6573 return S_OK;
6574}
6575
6576HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6577{
6578 if (aSize < 0)
6579 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6580
6581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6582
6583 HRESULT rc = S_OK;
6584 Utf8Str log = i_queryLogFilename(aIdx);
6585
6586 /* do not unnecessarily hold the lock while doing something which does
6587 * not need the lock and potentially takes a long time. */
6588 alock.release();
6589
6590 /* Limit the chunk size to 32K for now, as that gives better performance
6591 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6592 * One byte expands to approx. 25 bytes of breathtaking XML. */
6593 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6594 aData.resize(cbData);
6595
6596 RTFILE LogFile;
6597 int vrc = RTFileOpen(&LogFile, log.c_str(),
6598 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6599 if (RT_SUCCESS(vrc))
6600 {
6601 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6602 if (RT_SUCCESS(vrc))
6603 aData.resize(cbData);
6604 else
6605 rc = setError(VBOX_E_IPRT_ERROR,
6606 tr("Could not read log file '%s' (%Rrc)"),
6607 log.c_str(), vrc);
6608 RTFileClose(LogFile);
6609 }
6610 else
6611 rc = setError(VBOX_E_IPRT_ERROR,
6612 tr("Could not open log file '%s' (%Rrc)"),
6613 log.c_str(), vrc);
6614
6615 if (FAILED(rc))
6616 aData.resize(0);
6617
6618 return rc;
6619}
6620
6621
6622/**
6623 * Currently this method doesn't attach device to the running VM,
6624 * just makes sure it's plugged on next VM start.
6625 */
6626HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6627{
6628 // lock scope
6629 {
6630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6631
6632 HRESULT rc = i_checkStateDependency(MutableStateDep);
6633 if (FAILED(rc)) return rc;
6634
6635 ChipsetType_T aChipset = ChipsetType_PIIX3;
6636 COMGETTER(ChipsetType)(&aChipset);
6637
6638 if (aChipset != ChipsetType_ICH9)
6639 {
6640 return setError(E_INVALIDARG,
6641 tr("Host PCI attachment only supported with ICH9 chipset"));
6642 }
6643
6644 // check if device with this host PCI address already attached
6645 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6646 it != mHWData->mPCIDeviceAssignments.end();
6647 ++it)
6648 {
6649 LONG iHostAddress = -1;
6650 ComPtr<PCIDeviceAttachment> pAttach;
6651 pAttach = *it;
6652 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6653 if (iHostAddress == aHostAddress)
6654 return setError(E_INVALIDARG,
6655 tr("Device with host PCI address already attached to this VM"));
6656 }
6657
6658 ComObjPtr<PCIDeviceAttachment> pda;
6659 char name[32];
6660
6661 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6662 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6663 Bstr bname(name);
6664 pda.createObject();
6665 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6666 i_setModified(IsModified_MachineData);
6667 mHWData.backup();
6668 mHWData->mPCIDeviceAssignments.push_back(pda);
6669 }
6670
6671 return S_OK;
6672}
6673
6674/**
6675 * Currently this method doesn't detach device from the running VM,
6676 * just makes sure it's not plugged on next VM start.
6677 */
6678HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6679{
6680 ComObjPtr<PCIDeviceAttachment> pAttach;
6681 bool fRemoved = false;
6682 HRESULT rc;
6683
6684 // lock scope
6685 {
6686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6687
6688 rc = i_checkStateDependency(MutableStateDep);
6689 if (FAILED(rc)) return rc;
6690
6691 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6692 it != mHWData->mPCIDeviceAssignments.end();
6693 ++it)
6694 {
6695 LONG iHostAddress = -1;
6696 pAttach = *it;
6697 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6698 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6699 {
6700 i_setModified(IsModified_MachineData);
6701 mHWData.backup();
6702 mHWData->mPCIDeviceAssignments.remove(pAttach);
6703 fRemoved = true;
6704 break;
6705 }
6706 }
6707 }
6708
6709
6710 /* Fire event outside of the lock */
6711 if (fRemoved)
6712 {
6713 Assert(!pAttach.isNull());
6714 ComPtr<IEventSource> es;
6715 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6716 Assert(SUCCEEDED(rc));
6717 Bstr mid;
6718 rc = this->COMGETTER(Id)(mid.asOutParam());
6719 Assert(SUCCEEDED(rc));
6720 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6721 }
6722
6723 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6724 tr("No host PCI device %08x attached"),
6725 aHostAddress
6726 );
6727}
6728
6729HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6730{
6731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6732
6733 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6734
6735 size_t i = 0;
6736 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6737 it != mHWData->mPCIDeviceAssignments.end();
6738 ++i, ++it)
6739 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6740
6741 return S_OK;
6742}
6743
6744HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6745{
6746 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6747
6748 return S_OK;
6749}
6750
6751HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6752{
6753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6754
6755 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6756
6757 return S_OK;
6758}
6759
6760HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6761{
6762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6763 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6764 if (SUCCEEDED(hrc))
6765 {
6766 hrc = mHWData.backupEx();
6767 if (SUCCEEDED(hrc))
6768 {
6769 i_setModified(IsModified_MachineData);
6770 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6771 }
6772 }
6773 return hrc;
6774}
6775
6776HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6777{
6778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6779 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6780 return S_OK;
6781}
6782
6783HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6784{
6785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6786 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6787 if (SUCCEEDED(hrc))
6788 {
6789 hrc = mHWData.backupEx();
6790 if (SUCCEEDED(hrc))
6791 {
6792 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6793 if (SUCCEEDED(hrc))
6794 i_setModified(IsModified_MachineData);
6795 }
6796 }
6797 return hrc;
6798}
6799
6800HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6801{
6802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6803
6804 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6805
6806 return S_OK;
6807}
6808
6809HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6810{
6811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6812 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6813 if (SUCCEEDED(hrc))
6814 {
6815 hrc = mHWData.backupEx();
6816 if (SUCCEEDED(hrc))
6817 {
6818 i_setModified(IsModified_MachineData);
6819 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6820 }
6821 }
6822 return hrc;
6823}
6824
6825HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6826{
6827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6828
6829 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6830
6831 return S_OK;
6832}
6833
6834HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6835{
6836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6839 if ( SUCCEEDED(hrc)
6840 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6841 {
6842 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6843 int vrc;
6844
6845 if (aAutostartEnabled)
6846 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6847 else
6848 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6849
6850 if (RT_SUCCESS(vrc))
6851 {
6852 hrc = mHWData.backupEx();
6853 if (SUCCEEDED(hrc))
6854 {
6855 i_setModified(IsModified_MachineData);
6856 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6857 }
6858 }
6859 else if (vrc == VERR_NOT_SUPPORTED)
6860 hrc = setError(VBOX_E_NOT_SUPPORTED,
6861 tr("The VM autostart feature is not supported on this platform"));
6862 else if (vrc == VERR_PATH_NOT_FOUND)
6863 hrc = setError(E_FAIL,
6864 tr("The path to the autostart database is not set"));
6865 else
6866 hrc = setError(E_UNEXPECTED,
6867 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6868 aAutostartEnabled ? "Adding" : "Removing",
6869 mUserData->s.strName.c_str(), vrc);
6870 }
6871 return hrc;
6872}
6873
6874HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6875{
6876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6877
6878 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6879
6880 return S_OK;
6881}
6882
6883HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6884{
6885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6886 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6887 if (SUCCEEDED(hrc))
6888 {
6889 hrc = mHWData.backupEx();
6890 if (SUCCEEDED(hrc))
6891 {
6892 i_setModified(IsModified_MachineData);
6893 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6894 }
6895 }
6896 return hrc;
6897}
6898
6899HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6900{
6901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6902
6903 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6904
6905 return S_OK;
6906}
6907
6908HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6909{
6910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6911 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6912 if ( SUCCEEDED(hrc)
6913 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6914 {
6915 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6916 int vrc;
6917
6918 if (aAutostopType != AutostopType_Disabled)
6919 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6920 else
6921 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6922
6923 if (RT_SUCCESS(vrc))
6924 {
6925 hrc = mHWData.backupEx();
6926 if (SUCCEEDED(hrc))
6927 {
6928 i_setModified(IsModified_MachineData);
6929 mHWData->mAutostart.enmAutostopType = aAutostopType;
6930 }
6931 }
6932 else if (vrc == VERR_NOT_SUPPORTED)
6933 hrc = setError(VBOX_E_NOT_SUPPORTED,
6934 tr("The VM autostop feature is not supported on this platform"));
6935 else if (vrc == VERR_PATH_NOT_FOUND)
6936 hrc = setError(E_FAIL,
6937 tr("The path to the autostart database is not set"));
6938 else
6939 hrc = setError(E_UNEXPECTED,
6940 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6941 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6942 mUserData->s.strName.c_str(), vrc);
6943 }
6944 return hrc;
6945}
6946
6947HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6948{
6949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6950
6951 aDefaultFrontend = mHWData->mDefaultFrontend;
6952
6953 return S_OK;
6954}
6955
6956HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6957{
6958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6959 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6960 if (SUCCEEDED(hrc))
6961 {
6962 hrc = mHWData.backupEx();
6963 if (SUCCEEDED(hrc))
6964 {
6965 i_setModified(IsModified_MachineData);
6966 mHWData->mDefaultFrontend = aDefaultFrontend;
6967 }
6968 }
6969 return hrc;
6970}
6971
6972HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6973{
6974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6975 size_t cbIcon = mUserData->mIcon.size();
6976 aIcon.resize(cbIcon);
6977 if (cbIcon)
6978 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
6979 return S_OK;
6980}
6981
6982HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6983{
6984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6985 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6986 if (SUCCEEDED(hrc))
6987 {
6988 i_setModified(IsModified_MachineData);
6989 mUserData.backup();
6990 size_t cbIcon = aIcon.size();
6991 mUserData->mIcon.resize(cbIcon);
6992 if (cbIcon)
6993 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
6994 }
6995 return hrc;
6996}
6997
6998HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6999{
7000#ifdef VBOX_WITH_USB
7001 *aUSBProxyAvailable = true;
7002#else
7003 *aUSBProxyAvailable = false;
7004#endif
7005 return S_OK;
7006}
7007
7008HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7009 ComPtr<IProgress> &aProgress)
7010{
7011 ComObjPtr<Progress> pP;
7012 Progress *ppP = pP;
7013 IProgress *iP = static_cast<IProgress *>(ppP);
7014 IProgress **pProgress = &iP;
7015
7016 IMachine *pTarget = aTarget;
7017
7018 /* Convert the options. */
7019 RTCList<CloneOptions_T> optList;
7020 if (aOptions.size())
7021 for (size_t i = 0; i < aOptions.size(); ++i)
7022 optList.append(aOptions[i]);
7023
7024 if (optList.contains(CloneOptions_Link))
7025 {
7026 if (!i_isSnapshotMachine())
7027 return setError(E_INVALIDARG,
7028 tr("Linked clone can only be created from a snapshot"));
7029 if (aMode != CloneMode_MachineState)
7030 return setError(E_INVALIDARG,
7031 tr("Linked clone can only be created for a single machine state"));
7032 }
7033 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7034
7035 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7036
7037 HRESULT rc = pWorker->start(pProgress);
7038
7039 pP = static_cast<Progress *>(*pProgress);
7040 pP.queryInterfaceTo(aProgress.asOutParam());
7041
7042 return rc;
7043
7044}
7045
7046// public methods for internal purposes
7047/////////////////////////////////////////////////////////////////////////////
7048
7049/**
7050 * Adds the given IsModified_* flag to the dirty flags of the machine.
7051 * This must be called either during i_loadSettings or under the machine write lock.
7052 * @param fl
7053 */
7054void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7055{
7056 mData->flModifications |= fl;
7057 if (fAllowStateModification && i_isStateModificationAllowed())
7058 mData->mCurrentStateModified = true;
7059}
7060
7061/**
7062 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7063 * care of the write locking.
7064 *
7065 * @param fModifications The flag to add.
7066 */
7067void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7068{
7069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7070 i_setModified(fModification, fAllowStateModification);
7071}
7072
7073/**
7074 * Saves the registry entry of this machine to the given configuration node.
7075 *
7076 * @param aEntryNode Node to save the registry entry to.
7077 *
7078 * @note locks this object for reading.
7079 */
7080HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7081{
7082 AutoLimitedCaller autoCaller(this);
7083 AssertComRCReturnRC(autoCaller.rc());
7084
7085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7086
7087 data.uuid = mData->mUuid;
7088 data.strSettingsFile = mData->m_strConfigFile;
7089
7090 return S_OK;
7091}
7092
7093/**
7094 * Calculates the absolute path of the given path taking the directory of the
7095 * machine settings file as the current directory.
7096 *
7097 * @param aPath Path to calculate the absolute path for.
7098 * @param aResult Where to put the result (used only on success, can be the
7099 * same Utf8Str instance as passed in @a aPath).
7100 * @return IPRT result.
7101 *
7102 * @note Locks this object for reading.
7103 */
7104int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7105{
7106 AutoCaller autoCaller(this);
7107 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7108
7109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7110
7111 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7112
7113 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7114
7115 strSettingsDir.stripFilename();
7116 char folder[RTPATH_MAX];
7117 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7118 if (RT_SUCCESS(vrc))
7119 aResult = folder;
7120
7121 return vrc;
7122}
7123
7124/**
7125 * Copies strSource to strTarget, making it relative to the machine folder
7126 * if it is a subdirectory thereof, or simply copying it otherwise.
7127 *
7128 * @param strSource Path to evaluate and copy.
7129 * @param strTarget Buffer to receive target path.
7130 *
7131 * @note Locks this object for reading.
7132 */
7133void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7134 Utf8Str &strTarget)
7135{
7136 AutoCaller autoCaller(this);
7137 AssertComRCReturn(autoCaller.rc(), (void)0);
7138
7139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7140
7141 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7142 // use strTarget as a temporary buffer to hold the machine settings dir
7143 strTarget = mData->m_strConfigFileFull;
7144 strTarget.stripFilename();
7145 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7146 {
7147 // is relative: then append what's left
7148 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7149 // for empty paths (only possible for subdirs) use "." to avoid
7150 // triggering default settings for not present config attributes.
7151 if (strTarget.isEmpty())
7152 strTarget = ".";
7153 }
7154 else
7155 // is not relative: then overwrite
7156 strTarget = strSource;
7157}
7158
7159/**
7160 * Returns the full path to the machine's log folder in the
7161 * \a aLogFolder argument.
7162 */
7163void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7164{
7165 AutoCaller autoCaller(this);
7166 AssertComRCReturnVoid(autoCaller.rc());
7167
7168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7169
7170 char szTmp[RTPATH_MAX];
7171 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7172 if (RT_SUCCESS(vrc))
7173 {
7174 if (szTmp[0] && !mUserData.isNull())
7175 {
7176 char szTmp2[RTPATH_MAX];
7177 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7178 if (RT_SUCCESS(vrc))
7179 aLogFolder = BstrFmt("%s%c%s",
7180 szTmp2,
7181 RTPATH_DELIMITER,
7182 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7183 }
7184 else
7185 vrc = VERR_PATH_IS_RELATIVE;
7186 }
7187
7188 if (RT_FAILURE(vrc))
7189 {
7190 // fallback if VBOX_USER_LOGHOME is not set or invalid
7191 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7192 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7193 aLogFolder.append(RTPATH_DELIMITER);
7194 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7195 }
7196}
7197
7198/**
7199 * Returns the full path to the machine's log file for an given index.
7200 */
7201Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7202 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7203{
7204 Utf8Str logFolder;
7205 getLogFolder(logFolder);
7206 Assert(logFolder.length());
7207 Utf8Str log;
7208 if (idx == 0)
7209 log = Utf8StrFmt("%s%cVBox.log",
7210 logFolder.c_str(), RTPATH_DELIMITER);
7211 else
7212 log = Utf8StrFmt("%s%cVBox.log.%d",
7213 logFolder.c_str(), RTPATH_DELIMITER, idx);
7214 return log;
7215}
7216
7217/**
7218 * Returns the full path to the machine's (hardened) startup log file.
7219 */
7220Utf8Str Machine::i_getStartupLogFilename(void)
7221{
7222 Utf8Str strFilename;
7223 getLogFolder(strFilename);
7224 Assert(strFilename.length());
7225 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7226 return strFilename;
7227}
7228
7229
7230/**
7231 * Composes a unique saved state filename based on the current system time. The filename is
7232 * granular to the second so this will work so long as no more than one snapshot is taken on
7233 * a machine per second.
7234 *
7235 * Before version 4.1, we used this formula for saved state files:
7236 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7237 * which no longer works because saved state files can now be shared between the saved state of the
7238 * "saved" machine and an online snapshot, and the following would cause problems:
7239 * 1) save machine
7240 * 2) create online snapshot from that machine state --> reusing saved state file
7241 * 3) save machine again --> filename would be reused, breaking the online snapshot
7242 *
7243 * So instead we now use a timestamp.
7244 *
7245 * @param str
7246 */
7247
7248void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7249{
7250 AutoCaller autoCaller(this);
7251 AssertComRCReturnVoid(autoCaller.rc());
7252
7253 {
7254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7255 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7256 }
7257
7258 RTTIMESPEC ts;
7259 RTTimeNow(&ts);
7260 RTTIME time;
7261 RTTimeExplode(&time, &ts);
7262
7263 strStateFilePath += RTPATH_DELIMITER;
7264 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7265 time.i32Year, time.u8Month, time.u8MonthDay,
7266 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7267}
7268
7269/**
7270 * Returns the full path to the default video capture file.
7271 */
7272void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7273{
7274 AutoCaller autoCaller(this);
7275 AssertComRCReturnVoid(autoCaller.rc());
7276
7277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7278
7279 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7280 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7281 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7282}
7283
7284/**
7285 * Returns whether at least one USB controller is present for the VM.
7286 */
7287bool Machine::i_isUSBControllerPresent()
7288{
7289 AutoCaller autoCaller(this);
7290 AssertComRCReturn(autoCaller.rc(), false);
7291
7292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7293
7294 return (mUSBControllers->size() > 0);
7295}
7296
7297/**
7298 * @note Locks this object for writing, calls the client process
7299 * (inside the lock).
7300 */
7301HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7302 const Utf8Str &strFrontend,
7303 const Utf8Str &strEnvironment,
7304 ProgressProxy *aProgress)
7305{
7306 LogFlowThisFuncEnter();
7307
7308 AssertReturn(aControl, E_FAIL);
7309 AssertReturn(aProgress, E_FAIL);
7310 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7311
7312 AutoCaller autoCaller(this);
7313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7314
7315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7316
7317 if (!mData->mRegistered)
7318 return setError(E_UNEXPECTED,
7319 tr("The machine '%s' is not registered"),
7320 mUserData->s.strName.c_str());
7321
7322 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7323
7324 if ( mData->mSession.mState == SessionState_Locked
7325 || mData->mSession.mState == SessionState_Spawning
7326 || mData->mSession.mState == SessionState_Unlocking)
7327 return setError(VBOX_E_INVALID_OBJECT_STATE,
7328 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7329 mUserData->s.strName.c_str());
7330
7331 /* may not be busy */
7332 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7333
7334 /* get the path to the executable */
7335 char szPath[RTPATH_MAX];
7336 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7337 size_t cchBufLeft = strlen(szPath);
7338 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7339 szPath[cchBufLeft] = 0;
7340 char *pszNamePart = szPath + cchBufLeft;
7341 cchBufLeft = sizeof(szPath) - cchBufLeft;
7342
7343 int vrc = VINF_SUCCESS;
7344 RTPROCESS pid = NIL_RTPROCESS;
7345
7346 RTENV env = RTENV_DEFAULT;
7347
7348 if (!strEnvironment.isEmpty())
7349 {
7350 char *newEnvStr = NULL;
7351
7352 do
7353 {
7354 /* clone the current environment */
7355 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7356 AssertRCBreakStmt(vrc2, vrc = vrc2);
7357
7358 newEnvStr = RTStrDup(strEnvironment.c_str());
7359 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7360
7361 /* put new variables to the environment
7362 * (ignore empty variable names here since RTEnv API
7363 * intentionally doesn't do that) */
7364 char *var = newEnvStr;
7365 for (char *p = newEnvStr; *p; ++p)
7366 {
7367 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7368 {
7369 *p = '\0';
7370 if (*var)
7371 {
7372 char *val = strchr(var, '=');
7373 if (val)
7374 {
7375 *val++ = '\0';
7376 vrc2 = RTEnvSetEx(env, var, val);
7377 }
7378 else
7379 vrc2 = RTEnvUnsetEx(env, var);
7380 if (RT_FAILURE(vrc2))
7381 break;
7382 }
7383 var = p + 1;
7384 }
7385 }
7386 if (RT_SUCCESS(vrc2) && *var)
7387 vrc2 = RTEnvPutEx(env, var);
7388
7389 AssertRCBreakStmt(vrc2, vrc = vrc2);
7390 }
7391 while (0);
7392
7393 if (newEnvStr != NULL)
7394 RTStrFree(newEnvStr);
7395 }
7396
7397 /* Hardened startup logging */
7398#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7399 Utf8Str strSupStartLogArg("--sup-startup-log=");
7400 {
7401 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7402 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7403 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7404 {
7405 Utf8Str strStartupLogDir = strStartupLogFile;
7406 strStartupLogDir.stripFilename();
7407 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7408 file without stripping the file. */
7409 }
7410 strSupStartLogArg.append(strStartupLogFile);
7411 }
7412 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7413#else
7414 const char *pszSupStartupLogArg = NULL;
7415#endif
7416
7417
7418#ifdef VBOX_WITH_QTGUI
7419 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7420 {
7421# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7422 /* Modify the base path so that we don't need to use ".." below. */
7423 RTPathStripTrailingSlash(szPath);
7424 RTPathStripFilename(szPath);
7425 cchBufLeft = strlen(szPath);
7426 pszNamePart = szPath + cchBufLeft;
7427 cchBufLeft = sizeof(szPath) - cchBufLeft;
7428
7429# define OSX_APP_NAME "VirtualBoxVM"
7430# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7431
7432 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7433 if ( strAppOverride.contains(".")
7434 || strAppOverride.contains("/")
7435 || strAppOverride.contains("\\")
7436 || strAppOverride.contains(":"))
7437 strAppOverride.setNull();
7438 Utf8Str strAppPath;
7439 if (!strAppOverride.isEmpty())
7440 {
7441 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7442 Utf8Str strFullPath(szPath);
7443 strFullPath.append(strAppPath);
7444 /* there is a race, but people using this deserve the failure */
7445 if (!RTFileExists(strFullPath.c_str()))
7446 strAppOverride.setNull();
7447 }
7448 if (strAppOverride.isEmpty())
7449 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7450 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7451 strcpy(pszNamePart, strAppPath.c_str());
7452# else
7453 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7454 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7455 strcpy(pszNamePart, s_szVirtualBox_exe);
7456# endif
7457
7458 Utf8Str idStr = mData->mUuid.toString();
7459 const char *apszArgs[] =
7460 {
7461 szPath,
7462 "--comment", mUserData->s.strName.c_str(),
7463 "--startvm", idStr.c_str(),
7464 "--no-startvm-errormsgbox",
7465 pszSupStartupLogArg,
7466 NULL
7467 };
7468 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7469 }
7470#else /* !VBOX_WITH_QTGUI */
7471 if (0)
7472 ;
7473#endif /* VBOX_WITH_QTGUI */
7474
7475 else
7476
7477#ifdef VBOX_WITH_VBOXSDL
7478 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7479 {
7480 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7481 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7482 strcpy(pszNamePart, s_szVBoxSDL_exe);
7483
7484 Utf8Str idStr = mData->mUuid.toString();
7485 const char *apszArgs[] =
7486 {
7487 szPath,
7488 "--comment", mUserData->s.strName.c_str(),
7489 "--startvm", idStr.c_str(),
7490 pszSupStartupLogArg,
7491 NULL
7492 };
7493 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7494 }
7495#else /* !VBOX_WITH_VBOXSDL */
7496 if (0)
7497 ;
7498#endif /* !VBOX_WITH_VBOXSDL */
7499
7500 else
7501
7502#ifdef VBOX_WITH_HEADLESS
7503 if ( strFrontend == "headless"
7504 || strFrontend == "capture"
7505 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7506 )
7507 {
7508 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7509 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7510 * and a VM works even if the server has not been installed.
7511 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7512 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7513 * differently in 4.0 and 3.x.
7514 */
7515 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7516 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7517 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7518
7519 Utf8Str idStr = mData->mUuid.toString();
7520 const char *apszArgs[] =
7521 {
7522 szPath,
7523 "--comment", mUserData->s.strName.c_str(),
7524 "--startvm", idStr.c_str(),
7525 "--vrde", "config",
7526 0, /* For "--capture". */
7527 0, /* For "--sup-startup-log". */
7528 0
7529 };
7530 unsigned iArg = 7;
7531 if (strFrontend == "capture")
7532 apszArgs[iArg++] = "--capture";
7533 apszArgs[iArg++] = pszSupStartupLogArg;
7534
7535# ifdef RT_OS_WINDOWS
7536 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7537# else
7538 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7539# endif
7540 }
7541#else /* !VBOX_WITH_HEADLESS */
7542 if (0)
7543 ;
7544#endif /* !VBOX_WITH_HEADLESS */
7545 else
7546 {
7547 RTEnvDestroy(env);
7548 return setError(E_INVALIDARG,
7549 tr("Invalid frontend name: '%s'"),
7550 strFrontend.c_str());
7551 }
7552
7553 RTEnvDestroy(env);
7554
7555 if (RT_FAILURE(vrc))
7556 return setError(VBOX_E_IPRT_ERROR,
7557 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7558 mUserData->s.strName.c_str(), vrc);
7559
7560 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7561
7562 /*
7563 * Note that we don't release the lock here before calling the client,
7564 * because it doesn't need to call us back if called with a NULL argument.
7565 * Releasing the lock here is dangerous because we didn't prepare the
7566 * launch data yet, but the client we've just started may happen to be
7567 * too fast and call LockMachine() that will fail (because of PID, etc.),
7568 * so that the Machine will never get out of the Spawning session state.
7569 */
7570
7571 /* inform the session that it will be a remote one */
7572 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7573#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7574 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7575#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7576 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7577#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7578 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7579
7580 if (FAILED(rc))
7581 {
7582 /* restore the session state */
7583 mData->mSession.mState = SessionState_Unlocked;
7584 alock.release();
7585 mParent->i_addProcessToReap(pid);
7586 /* The failure may occur w/o any error info (from RPC), so provide one */
7587 return setError(VBOX_E_VM_ERROR,
7588 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7589 }
7590
7591 /* attach launch data to the machine */
7592 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7593 mData->mSession.mRemoteControls.push_back(aControl);
7594 mData->mSession.mProgress = aProgress;
7595 mData->mSession.mPID = pid;
7596 mData->mSession.mState = SessionState_Spawning;
7597 mData->mSession.mType = strFrontend;
7598
7599 alock.release();
7600 mParent->i_addProcessToReap(pid);
7601
7602 LogFlowThisFuncLeave();
7603 return S_OK;
7604}
7605
7606/**
7607 * Returns @c true if the given session machine instance has an open direct
7608 * session (and optionally also for direct sessions which are closing) and
7609 * returns the session control machine instance if so.
7610 *
7611 * Note that when the method returns @c false, the arguments remain unchanged.
7612 *
7613 * @param aMachine Session machine object.
7614 * @param aControl Direct session control object (optional).
7615 * @param aAllowClosing If true then additionally a session which is currently
7616 * being closed will also be allowed.
7617 *
7618 * @note locks this object for reading.
7619 */
7620bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7621 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7622 bool aAllowClosing /*= false*/)
7623{
7624 AutoLimitedCaller autoCaller(this);
7625 AssertComRCReturn(autoCaller.rc(), false);
7626
7627 /* just return false for inaccessible machines */
7628 if (getObjectState().getState() != ObjectState::Ready)
7629 return false;
7630
7631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7632
7633 if ( mData->mSession.mState == SessionState_Locked
7634 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7635 )
7636 {
7637 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7638
7639 aMachine = mData->mSession.mMachine;
7640
7641 if (aControl != NULL)
7642 *aControl = mData->mSession.mDirectControl;
7643
7644 return true;
7645 }
7646
7647 return false;
7648}
7649
7650/**
7651 * Returns @c true if the given machine has an spawning direct session.
7652 *
7653 * @note locks this object for reading.
7654 */
7655bool Machine::i_isSessionSpawning()
7656{
7657 AutoLimitedCaller autoCaller(this);
7658 AssertComRCReturn(autoCaller.rc(), false);
7659
7660 /* just return false for inaccessible machines */
7661 if (getObjectState().getState() != ObjectState::Ready)
7662 return false;
7663
7664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7665
7666 if (mData->mSession.mState == SessionState_Spawning)
7667 return true;
7668
7669 return false;
7670}
7671
7672/**
7673 * Called from the client watcher thread to check for unexpected client process
7674 * death during Session_Spawning state (e.g. before it successfully opened a
7675 * direct session).
7676 *
7677 * On Win32 and on OS/2, this method is called only when we've got the
7678 * direct client's process termination notification, so it always returns @c
7679 * true.
7680 *
7681 * On other platforms, this method returns @c true if the client process is
7682 * terminated and @c false if it's still alive.
7683 *
7684 * @note Locks this object for writing.
7685 */
7686bool Machine::i_checkForSpawnFailure()
7687{
7688 AutoCaller autoCaller(this);
7689 if (!autoCaller.isOk())
7690 {
7691 /* nothing to do */
7692 LogFlowThisFunc(("Already uninitialized!\n"));
7693 return true;
7694 }
7695
7696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7697
7698 if (mData->mSession.mState != SessionState_Spawning)
7699 {
7700 /* nothing to do */
7701 LogFlowThisFunc(("Not spawning any more!\n"));
7702 return true;
7703 }
7704
7705 HRESULT rc = S_OK;
7706
7707 /* PID not yet initialized, skip check. */
7708 if (mData->mSession.mPID == NIL_RTPROCESS)
7709 return false;
7710
7711 RTPROCSTATUS status;
7712 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7713
7714 if (vrc != VERR_PROCESS_RUNNING)
7715 {
7716 Utf8Str strExtraInfo;
7717
7718#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7719 /* If the startup logfile exists and is of non-zero length, tell the
7720 user to look there for more details to encourage them to attach it
7721 when reporting startup issues. */
7722 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7723 uint64_t cbStartupLogFile = 0;
7724 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7725 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7726 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7727#endif
7728
7729 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7730 rc = setError(E_FAIL,
7731 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7732 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7733 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7734 rc = setError(E_FAIL,
7735 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7736 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7737 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7738 rc = setError(E_FAIL,
7739 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7740 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7741 else
7742 rc = setError(E_FAIL,
7743 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7744 i_getName().c_str(), vrc, strExtraInfo.c_str());
7745 }
7746
7747 if (FAILED(rc))
7748 {
7749 /* Close the remote session, remove the remote control from the list
7750 * and reset session state to Closed (@note keep the code in sync with
7751 * the relevant part in LockMachine()). */
7752
7753 Assert(mData->mSession.mRemoteControls.size() == 1);
7754 if (mData->mSession.mRemoteControls.size() == 1)
7755 {
7756 ErrorInfoKeeper eik;
7757 mData->mSession.mRemoteControls.front()->Uninitialize();
7758 }
7759
7760 mData->mSession.mRemoteControls.clear();
7761 mData->mSession.mState = SessionState_Unlocked;
7762
7763 /* finalize the progress after setting the state */
7764 if (!mData->mSession.mProgress.isNull())
7765 {
7766 mData->mSession.mProgress->notifyComplete(rc);
7767 mData->mSession.mProgress.setNull();
7768 }
7769
7770 mData->mSession.mPID = NIL_RTPROCESS;
7771
7772 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7773 return true;
7774 }
7775
7776 return false;
7777}
7778
7779/**
7780 * Checks whether the machine can be registered. If so, commits and saves
7781 * all settings.
7782 *
7783 * @note Must be called from mParent's write lock. Locks this object and
7784 * children for writing.
7785 */
7786HRESULT Machine::i_prepareRegister()
7787{
7788 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7789
7790 AutoLimitedCaller autoCaller(this);
7791 AssertComRCReturnRC(autoCaller.rc());
7792
7793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7794
7795 /* wait for state dependents to drop to zero */
7796 i_ensureNoStateDependencies();
7797
7798 if (!mData->mAccessible)
7799 return setError(VBOX_E_INVALID_OBJECT_STATE,
7800 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7801 mUserData->s.strName.c_str(),
7802 mData->mUuid.toString().c_str());
7803
7804 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7805
7806 if (mData->mRegistered)
7807 return setError(VBOX_E_INVALID_OBJECT_STATE,
7808 tr("The machine '%s' with UUID {%s} is already registered"),
7809 mUserData->s.strName.c_str(),
7810 mData->mUuid.toString().c_str());
7811
7812 HRESULT rc = S_OK;
7813
7814 // Ensure the settings are saved. If we are going to be registered and
7815 // no config file exists yet, create it by calling i_saveSettings() too.
7816 if ( (mData->flModifications)
7817 || (!mData->pMachineConfigFile->fileExists())
7818 )
7819 {
7820 rc = i_saveSettings(NULL);
7821 // no need to check whether VirtualBox.xml needs saving too since
7822 // we can't have a machine XML file rename pending
7823 if (FAILED(rc)) return rc;
7824 }
7825
7826 /* more config checking goes here */
7827
7828 if (SUCCEEDED(rc))
7829 {
7830 /* we may have had implicit modifications we want to fix on success */
7831 i_commit();
7832
7833 mData->mRegistered = true;
7834 }
7835 else
7836 {
7837 /* we may have had implicit modifications we want to cancel on failure*/
7838 i_rollback(false /* aNotify */);
7839 }
7840
7841 return rc;
7842}
7843
7844/**
7845 * Increases the number of objects dependent on the machine state or on the
7846 * registered state. Guarantees that these two states will not change at least
7847 * until #releaseStateDependency() is called.
7848 *
7849 * Depending on the @a aDepType value, additional state checks may be made.
7850 * These checks will set extended error info on failure. See
7851 * #checkStateDependency() for more info.
7852 *
7853 * If this method returns a failure, the dependency is not added and the caller
7854 * is not allowed to rely on any particular machine state or registration state
7855 * value and may return the failed result code to the upper level.
7856 *
7857 * @param aDepType Dependency type to add.
7858 * @param aState Current machine state (NULL if not interested).
7859 * @param aRegistered Current registered state (NULL if not interested).
7860 *
7861 * @note Locks this object for writing.
7862 */
7863HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7864 MachineState_T *aState /* = NULL */,
7865 BOOL *aRegistered /* = NULL */)
7866{
7867 AutoCaller autoCaller(this);
7868 AssertComRCReturnRC(autoCaller.rc());
7869
7870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7871
7872 HRESULT rc = i_checkStateDependency(aDepType);
7873 if (FAILED(rc)) return rc;
7874
7875 {
7876 if (mData->mMachineStateChangePending != 0)
7877 {
7878 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7879 * drop to zero so don't add more. It may make sense to wait a bit
7880 * and retry before reporting an error (since the pending state
7881 * transition should be really quick) but let's just assert for
7882 * now to see if it ever happens on practice. */
7883
7884 AssertFailed();
7885
7886 return setError(E_ACCESSDENIED,
7887 tr("Machine state change is in progress. Please retry the operation later."));
7888 }
7889
7890 ++mData->mMachineStateDeps;
7891 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7892 }
7893
7894 if (aState)
7895 *aState = mData->mMachineState;
7896 if (aRegistered)
7897 *aRegistered = mData->mRegistered;
7898
7899 return S_OK;
7900}
7901
7902/**
7903 * Decreases the number of objects dependent on the machine state.
7904 * Must always complete the #addStateDependency() call after the state
7905 * dependency is no more necessary.
7906 */
7907void Machine::i_releaseStateDependency()
7908{
7909 AutoCaller autoCaller(this);
7910 AssertComRCReturnVoid(autoCaller.rc());
7911
7912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7913
7914 /* releaseStateDependency() w/o addStateDependency()? */
7915 AssertReturnVoid(mData->mMachineStateDeps != 0);
7916 -- mData->mMachineStateDeps;
7917
7918 if (mData->mMachineStateDeps == 0)
7919 {
7920 /* inform i_ensureNoStateDependencies() that there are no more deps */
7921 if (mData->mMachineStateChangePending != 0)
7922 {
7923 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7924 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7925 }
7926 }
7927}
7928
7929Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7930{
7931 /* start with nothing found */
7932 Utf8Str strResult("");
7933
7934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7935
7936 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7937 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7938 // found:
7939 strResult = it->second; // source is a Utf8Str
7940
7941 return strResult;
7942}
7943
7944// protected methods
7945/////////////////////////////////////////////////////////////////////////////
7946
7947/**
7948 * Performs machine state checks based on the @a aDepType value. If a check
7949 * fails, this method will set extended error info, otherwise it will return
7950 * S_OK. It is supposed, that on failure, the caller will immediately return
7951 * the return value of this method to the upper level.
7952 *
7953 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7954 *
7955 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7956 * current state of this machine object allows to change settings of the
7957 * machine (i.e. the machine is not registered, or registered but not running
7958 * and not saved). It is useful to call this method from Machine setters
7959 * before performing any change.
7960 *
7961 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7962 * as for MutableStateDep except that if the machine is saved, S_OK is also
7963 * returned. This is useful in setters which allow changing machine
7964 * properties when it is in the saved state.
7965 *
7966 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7967 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7968 * Aborted).
7969 *
7970 * @param aDepType Dependency type to check.
7971 *
7972 * @note Non Machine based classes should use #addStateDependency() and
7973 * #releaseStateDependency() methods or the smart AutoStateDependency
7974 * template.
7975 *
7976 * @note This method must be called from under this object's read or write
7977 * lock.
7978 */
7979HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7980{
7981 switch (aDepType)
7982 {
7983 case AnyStateDep:
7984 {
7985 break;
7986 }
7987 case MutableStateDep:
7988 {
7989 if ( mData->mRegistered
7990 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7991 Paused should actually be included here... (Live Migration) */
7992 || ( mData->mMachineState != MachineState_Paused
7993 && mData->mMachineState != MachineState_Running
7994 && mData->mMachineState != MachineState_Aborted
7995 && mData->mMachineState != MachineState_Teleported
7996 && mData->mMachineState != MachineState_PoweredOff
7997 )
7998 )
7999 )
8000 return setError(VBOX_E_INVALID_VM_STATE,
8001 tr("The machine is not mutable (state is %s)"),
8002 Global::stringifyMachineState(mData->mMachineState));
8003 break;
8004 }
8005 case MutableOrSavedStateDep:
8006 {
8007 if ( mData->mRegistered
8008 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8009 Paused should actually be included here... (Live Migration) */
8010 || ( mData->mMachineState != MachineState_Paused
8011 && mData->mMachineState != MachineState_Running
8012 && mData->mMachineState != MachineState_Aborted
8013 && mData->mMachineState != MachineState_Teleported
8014 && mData->mMachineState != MachineState_Saved
8015 && mData->mMachineState != MachineState_PoweredOff
8016 )
8017 )
8018 )
8019 return setError(VBOX_E_INVALID_VM_STATE,
8020 tr("The machine is not mutable (state is %s)"),
8021 Global::stringifyMachineState(mData->mMachineState));
8022 break;
8023 }
8024 case OfflineStateDep:
8025 {
8026 if ( mData->mRegistered
8027 && ( !i_isSessionMachine()
8028 || ( mData->mMachineState != MachineState_PoweredOff
8029 && mData->mMachineState != MachineState_Saved
8030 && mData->mMachineState != MachineState_Aborted
8031 && mData->mMachineState != MachineState_Teleported
8032 )
8033 )
8034 )
8035 return setError(VBOX_E_INVALID_VM_STATE,
8036 tr("The machine is not offline (state is %s)"),
8037 Global::stringifyMachineState(mData->mMachineState));
8038 break;
8039 }
8040 }
8041
8042 return S_OK;
8043}
8044
8045/**
8046 * Helper to initialize all associated child objects and allocate data
8047 * structures.
8048 *
8049 * This method must be called as a part of the object's initialization procedure
8050 * (usually done in the #init() method).
8051 *
8052 * @note Must be called only from #init() or from #registeredInit().
8053 */
8054HRESULT Machine::initDataAndChildObjects()
8055{
8056 AutoCaller autoCaller(this);
8057 AssertComRCReturnRC(autoCaller.rc());
8058 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8059 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8060
8061 AssertReturn(!mData->mAccessible, E_FAIL);
8062
8063 /* allocate data structures */
8064 mSSData.allocate();
8065 mUserData.allocate();
8066 mHWData.allocate();
8067 mMediaData.allocate();
8068 mStorageControllers.allocate();
8069 mUSBControllers.allocate();
8070
8071 /* initialize mOSTypeId */
8072 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8073
8074 /* create associated BIOS settings object */
8075 unconst(mBIOSSettings).createObject();
8076 mBIOSSettings->init(this);
8077
8078 /* create an associated VRDE object (default is disabled) */
8079 unconst(mVRDEServer).createObject();
8080 mVRDEServer->init(this);
8081
8082 /* create associated serial port objects */
8083 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8084 {
8085 unconst(mSerialPorts[slot]).createObject();
8086 mSerialPorts[slot]->init(this, slot);
8087 }
8088
8089 /* create associated parallel port objects */
8090 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8091 {
8092 unconst(mParallelPorts[slot]).createObject();
8093 mParallelPorts[slot]->init(this, slot);
8094 }
8095
8096 /* create the audio adapter object (always present, default is disabled) */
8097 unconst(mAudioAdapter).createObject();
8098 mAudioAdapter->init(this);
8099
8100 /* create the USB device filters object (always present) */
8101 unconst(mUSBDeviceFilters).createObject();
8102 mUSBDeviceFilters->init(this);
8103
8104 /* create associated network adapter objects */
8105 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8106 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8107 {
8108 unconst(mNetworkAdapters[slot]).createObject();
8109 mNetworkAdapters[slot]->init(this, slot);
8110 }
8111
8112 /* create the bandwidth control */
8113 unconst(mBandwidthControl).createObject();
8114 mBandwidthControl->init(this);
8115
8116 return S_OK;
8117}
8118
8119/**
8120 * Helper to uninitialize all associated child objects and to free all data
8121 * structures.
8122 *
8123 * This method must be called as a part of the object's uninitialization
8124 * procedure (usually done in the #uninit() method).
8125 *
8126 * @note Must be called only from #uninit() or from #registeredInit().
8127 */
8128void Machine::uninitDataAndChildObjects()
8129{
8130 AutoCaller autoCaller(this);
8131 AssertComRCReturnVoid(autoCaller.rc());
8132 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8133 || getObjectState().getState() == ObjectState::Limited);
8134
8135 /* tell all our other child objects we've been uninitialized */
8136 if (mBandwidthControl)
8137 {
8138 mBandwidthControl->uninit();
8139 unconst(mBandwidthControl).setNull();
8140 }
8141
8142 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8143 {
8144 if (mNetworkAdapters[slot])
8145 {
8146 mNetworkAdapters[slot]->uninit();
8147 unconst(mNetworkAdapters[slot]).setNull();
8148 }
8149 }
8150
8151 if (mUSBDeviceFilters)
8152 {
8153 mUSBDeviceFilters->uninit();
8154 unconst(mUSBDeviceFilters).setNull();
8155 }
8156
8157 if (mAudioAdapter)
8158 {
8159 mAudioAdapter->uninit();
8160 unconst(mAudioAdapter).setNull();
8161 }
8162
8163 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8164 {
8165 if (mParallelPorts[slot])
8166 {
8167 mParallelPorts[slot]->uninit();
8168 unconst(mParallelPorts[slot]).setNull();
8169 }
8170 }
8171
8172 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8173 {
8174 if (mSerialPorts[slot])
8175 {
8176 mSerialPorts[slot]->uninit();
8177 unconst(mSerialPorts[slot]).setNull();
8178 }
8179 }
8180
8181 if (mVRDEServer)
8182 {
8183 mVRDEServer->uninit();
8184 unconst(mVRDEServer).setNull();
8185 }
8186
8187 if (mBIOSSettings)
8188 {
8189 mBIOSSettings->uninit();
8190 unconst(mBIOSSettings).setNull();
8191 }
8192
8193 /* Deassociate media (only when a real Machine or a SnapshotMachine
8194 * instance is uninitialized; SessionMachine instances refer to real
8195 * Machine media). This is necessary for a clean re-initialization of
8196 * the VM after successfully re-checking the accessibility state. Note
8197 * that in case of normal Machine or SnapshotMachine uninitialization (as
8198 * a result of unregistering or deleting the snapshot), outdated media
8199 * attachments will already be uninitialized and deleted, so this
8200 * code will not affect them. */
8201 if ( !!mMediaData
8202 && (!i_isSessionMachine())
8203 )
8204 {
8205 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8206 it != mMediaData->mAttachments.end();
8207 ++it)
8208 {
8209 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8210 if (pMedium.isNull())
8211 continue;
8212 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8213 AssertComRC(rc);
8214 }
8215 }
8216
8217 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8218 {
8219 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8220 if (mData->mFirstSnapshot)
8221 {
8222 // snapshots tree is protected by machine write lock; strictly
8223 // this isn't necessary here since we're deleting the entire
8224 // machine, but otherwise we assert in Snapshot::uninit()
8225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8226 mData->mFirstSnapshot->uninit();
8227 mData->mFirstSnapshot.setNull();
8228 }
8229
8230 mData->mCurrentSnapshot.setNull();
8231 }
8232
8233 /* free data structures (the essential mData structure is not freed here
8234 * since it may be still in use) */
8235 mMediaData.free();
8236 mStorageControllers.free();
8237 mUSBControllers.free();
8238 mHWData.free();
8239 mUserData.free();
8240 mSSData.free();
8241}
8242
8243/**
8244 * Returns a pointer to the Machine object for this machine that acts like a
8245 * parent for complex machine data objects such as shared folders, etc.
8246 *
8247 * For primary Machine objects and for SnapshotMachine objects, returns this
8248 * object's pointer itself. For SessionMachine objects, returns the peer
8249 * (primary) machine pointer.
8250 */
8251Machine* Machine::i_getMachine()
8252{
8253 if (i_isSessionMachine())
8254 return (Machine*)mPeer;
8255 return this;
8256}
8257
8258/**
8259 * Makes sure that there are no machine state dependents. If necessary, waits
8260 * for the number of dependents to drop to zero.
8261 *
8262 * Make sure this method is called from under this object's write lock to
8263 * guarantee that no new dependents may be added when this method returns
8264 * control to the caller.
8265 *
8266 * @note Locks this object for writing. The lock will be released while waiting
8267 * (if necessary).
8268 *
8269 * @warning To be used only in methods that change the machine state!
8270 */
8271void Machine::i_ensureNoStateDependencies()
8272{
8273 AssertReturnVoid(isWriteLockOnCurrentThread());
8274
8275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8276
8277 /* Wait for all state dependents if necessary */
8278 if (mData->mMachineStateDeps != 0)
8279 {
8280 /* lazy semaphore creation */
8281 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8282 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8283
8284 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8285 mData->mMachineStateDeps));
8286
8287 ++mData->mMachineStateChangePending;
8288
8289 /* reset the semaphore before waiting, the last dependent will signal
8290 * it */
8291 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8292
8293 alock.release();
8294
8295 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8296
8297 alock.acquire();
8298
8299 -- mData->mMachineStateChangePending;
8300 }
8301}
8302
8303/**
8304 * Changes the machine state and informs callbacks.
8305 *
8306 * This method is not intended to fail so it either returns S_OK or asserts (and
8307 * returns a failure).
8308 *
8309 * @note Locks this object for writing.
8310 */
8311HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8312{
8313 LogFlowThisFuncEnter();
8314 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8315
8316 AutoCaller autoCaller(this);
8317 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8318
8319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8320
8321 /* wait for state dependents to drop to zero */
8322 i_ensureNoStateDependencies();
8323
8324 MachineState_T const enmOldState = mData->mMachineState;
8325 if (enmOldState != aMachineState)
8326 {
8327 mData->mMachineState = aMachineState;
8328 RTTimeNow(&mData->mLastStateChange);
8329
8330#ifdef VBOX_WITH_DTRACE_R3_MAIN
8331 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8332#endif
8333 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8334 }
8335
8336 LogFlowThisFuncLeave();
8337 return S_OK;
8338}
8339
8340/**
8341 * Searches for a shared folder with the given logical name
8342 * in the collection of shared folders.
8343 *
8344 * @param aName logical name of the shared folder
8345 * @param aSharedFolder where to return the found object
8346 * @param aSetError whether to set the error info if the folder is
8347 * not found
8348 * @return
8349 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8350 *
8351 * @note
8352 * must be called from under the object's lock!
8353 */
8354HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8355 ComObjPtr<SharedFolder> &aSharedFolder,
8356 bool aSetError /* = false */)
8357{
8358 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8359 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8360 it != mHWData->mSharedFolders.end();
8361 ++it)
8362 {
8363 SharedFolder *pSF = *it;
8364 AutoCaller autoCaller(pSF);
8365 if (pSF->i_getName() == aName)
8366 {
8367 aSharedFolder = pSF;
8368 rc = S_OK;
8369 break;
8370 }
8371 }
8372
8373 if (aSetError && FAILED(rc))
8374 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8375
8376 return rc;
8377}
8378
8379/**
8380 * Initializes all machine instance data from the given settings structures
8381 * from XML. The exception is the machine UUID which needs special handling
8382 * depending on the caller's use case, so the caller needs to set that herself.
8383 *
8384 * This gets called in several contexts during machine initialization:
8385 *
8386 * -- When machine XML exists on disk already and needs to be loaded into memory,
8387 * for example, from registeredInit() to load all registered machines on
8388 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8389 * attached to the machine should be part of some media registry already.
8390 *
8391 * -- During OVF import, when a machine config has been constructed from an
8392 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8393 * ensure that the media listed as attachments in the config (which have
8394 * been imported from the OVF) receive the correct registry ID.
8395 *
8396 * -- During VM cloning.
8397 *
8398 * @param config Machine settings from XML.
8399 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8400 * for each attached medium in the config.
8401 * @return
8402 */
8403HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8404 const Guid *puuidRegistry)
8405{
8406 // copy name, description, OS type, teleporter, UTC etc.
8407 mUserData->s = config.machineUserData;
8408
8409 // Decode the Icon overide data from config userdata and set onto Machine.
8410 #define DECODE_STR_MAX _1M
8411 const char* pszStr = config.machineUserData.ovIcon.c_str();
8412 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8413 if (cbOut > DECODE_STR_MAX)
8414 return setError(E_FAIL,
8415 tr("Icon Data too long.'%d' > '%d'"),
8416 cbOut,
8417 DECODE_STR_MAX);
8418 mUserData->mIcon.resize(cbOut);
8419 int vrc = VINF_SUCCESS;
8420 if (cbOut)
8421 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8422 if (RT_FAILURE(vrc))
8423 {
8424 mUserData->mIcon.resize(0);
8425 return setError(E_FAIL,
8426 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8427 pszStr,
8428 vrc);
8429 }
8430
8431 // look up the object by Id to check it is valid
8432 ComPtr<IGuestOSType> guestOSType;
8433 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8434 guestOSType.asOutParam());
8435 if (FAILED(rc)) return rc;
8436
8437 // stateFile (optional)
8438 if (config.strStateFile.isEmpty())
8439 mSSData->strStateFilePath.setNull();
8440 else
8441 {
8442 Utf8Str stateFilePathFull(config.strStateFile);
8443 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8444 if (RT_FAILURE(vrc))
8445 return setError(E_FAIL,
8446 tr("Invalid saved state file path '%s' (%Rrc)"),
8447 config.strStateFile.c_str(),
8448 vrc);
8449 mSSData->strStateFilePath = stateFilePathFull;
8450 }
8451
8452 // snapshot folder needs special processing so set it again
8453 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8454 if (FAILED(rc)) return rc;
8455
8456 /* Copy the extra data items (Not in any case config is already the same as
8457 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8458 * make sure the extra data map is copied). */
8459 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8460
8461 /* currentStateModified (optional, default is true) */
8462 mData->mCurrentStateModified = config.fCurrentStateModified;
8463
8464 mData->mLastStateChange = config.timeLastStateChange;
8465
8466 /*
8467 * note: all mUserData members must be assigned prior this point because
8468 * we need to commit changes in order to let mUserData be shared by all
8469 * snapshot machine instances.
8470 */
8471 mUserData.commitCopy();
8472
8473 // machine registry, if present (must be loaded before snapshots)
8474 if (config.canHaveOwnMediaRegistry())
8475 {
8476 // determine machine folder
8477 Utf8Str strMachineFolder = i_getSettingsFileFull();
8478 strMachineFolder.stripFilename();
8479 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8480 config.mediaRegistry,
8481 strMachineFolder);
8482 if (FAILED(rc)) return rc;
8483 }
8484
8485 /* Snapshot node (optional) */
8486 size_t cRootSnapshots;
8487 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8488 {
8489 // there must be only one root snapshot
8490 Assert(cRootSnapshots == 1);
8491
8492 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8493
8494 rc = i_loadSnapshot(snap,
8495 config.uuidCurrentSnapshot,
8496 NULL); // no parent == first snapshot
8497 if (FAILED(rc)) return rc;
8498 }
8499
8500 // hardware data
8501 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8502 if (FAILED(rc)) return rc;
8503
8504 // load storage controllers
8505 rc = i_loadStorageControllers(config.storageMachine,
8506 puuidRegistry,
8507 NULL /* puuidSnapshot */);
8508 if (FAILED(rc)) return rc;
8509
8510 /*
8511 * NOTE: the assignment below must be the last thing to do,
8512 * otherwise it will be not possible to change the settings
8513 * somewhere in the code above because all setters will be
8514 * blocked by i_checkStateDependency(MutableStateDep).
8515 */
8516
8517 /* set the machine state to Aborted or Saved when appropriate */
8518 if (config.fAborted)
8519 {
8520 mSSData->strStateFilePath.setNull();
8521
8522 /* no need to use i_setMachineState() during init() */
8523 mData->mMachineState = MachineState_Aborted;
8524 }
8525 else if (!mSSData->strStateFilePath.isEmpty())
8526 {
8527 /* no need to use i_setMachineState() during init() */
8528 mData->mMachineState = MachineState_Saved;
8529 }
8530
8531 // after loading settings, we are no longer different from the XML on disk
8532 mData->flModifications = 0;
8533
8534 return S_OK;
8535}
8536
8537/**
8538 * Recursively loads all snapshots starting from the given.
8539 *
8540 * @param aNode <Snapshot> node.
8541 * @param aCurSnapshotId Current snapshot ID from the settings file.
8542 * @param aParentSnapshot Parent snapshot.
8543 */
8544HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8545 const Guid &aCurSnapshotId,
8546 Snapshot *aParentSnapshot)
8547{
8548 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8549 AssertReturn(!i_isSessionMachine(), E_FAIL);
8550
8551 HRESULT rc = S_OK;
8552
8553 Utf8Str strStateFile;
8554 if (!data.strStateFile.isEmpty())
8555 {
8556 /* optional */
8557 strStateFile = data.strStateFile;
8558 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8559 if (RT_FAILURE(vrc))
8560 return setError(E_FAIL,
8561 tr("Invalid saved state file path '%s' (%Rrc)"),
8562 strStateFile.c_str(),
8563 vrc);
8564 }
8565
8566 /* create a snapshot machine object */
8567 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8568 pSnapshotMachine.createObject();
8569 rc = pSnapshotMachine->initFromSettings(this,
8570 data.hardware,
8571 &data.debugging,
8572 &data.autostart,
8573 data.storage,
8574 data.uuid.ref(),
8575 strStateFile);
8576 if (FAILED(rc)) return rc;
8577
8578 /* create a snapshot object */
8579 ComObjPtr<Snapshot> pSnapshot;
8580 pSnapshot.createObject();
8581 /* initialize the snapshot */
8582 rc = pSnapshot->init(mParent, // VirtualBox object
8583 data.uuid,
8584 data.strName,
8585 data.strDescription,
8586 data.timestamp,
8587 pSnapshotMachine,
8588 aParentSnapshot);
8589 if (FAILED(rc)) return rc;
8590
8591 /* memorize the first snapshot if necessary */
8592 if (!mData->mFirstSnapshot)
8593 mData->mFirstSnapshot = pSnapshot;
8594
8595 /* memorize the current snapshot when appropriate */
8596 if ( !mData->mCurrentSnapshot
8597 && pSnapshot->i_getId() == aCurSnapshotId
8598 )
8599 mData->mCurrentSnapshot = pSnapshot;
8600
8601 // now create the children
8602 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8603 it != data.llChildSnapshots.end();
8604 ++it)
8605 {
8606 const settings::Snapshot &childData = *it;
8607 // recurse
8608 rc = i_loadSnapshot(childData,
8609 aCurSnapshotId,
8610 pSnapshot); // parent = the one we created above
8611 if (FAILED(rc)) return rc;
8612 }
8613
8614 return rc;
8615}
8616
8617/**
8618 * Loads settings into mHWData.
8619 *
8620 * @param data Reference to the hardware settings.
8621 * @param pDbg Pointer to the debugging settings.
8622 * @param pAutostart Pointer to the autostart settings.
8623 */
8624HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8625 const settings::Autostart *pAutostart)
8626{
8627 AssertReturn(!i_isSessionMachine(), E_FAIL);
8628
8629 HRESULT rc = S_OK;
8630
8631 try
8632 {
8633 /* The hardware version attribute (optional). */
8634 mHWData->mHWVersion = data.strVersion;
8635 mHWData->mHardwareUUID = data.uuid;
8636
8637 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8638 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8639 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8640 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8641 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8642 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8643 mHWData->mPAEEnabled = data.fPAE;
8644 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8645 mHWData->mLongMode = data.enmLongMode;
8646 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8647 mHWData->mCPUCount = data.cCPUs;
8648 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8649 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8650
8651 // cpu
8652 if (mHWData->mCPUHotPlugEnabled)
8653 {
8654 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8655 it != data.llCpus.end();
8656 ++it)
8657 {
8658 const settings::Cpu &cpu = *it;
8659
8660 mHWData->mCPUAttached[cpu.ulId] = true;
8661 }
8662 }
8663
8664 // cpuid leafs
8665 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8666 it != data.llCpuIdLeafs.end();
8667 ++it)
8668 {
8669 const settings::CpuIdLeaf &leaf = *it;
8670
8671 switch (leaf.ulId)
8672 {
8673 case 0x0:
8674 case 0x1:
8675 case 0x2:
8676 case 0x3:
8677 case 0x4:
8678 case 0x5:
8679 case 0x6:
8680 case 0x7:
8681 case 0x8:
8682 case 0x9:
8683 case 0xA:
8684 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8685 break;
8686
8687 case 0x80000000:
8688 case 0x80000001:
8689 case 0x80000002:
8690 case 0x80000003:
8691 case 0x80000004:
8692 case 0x80000005:
8693 case 0x80000006:
8694 case 0x80000007:
8695 case 0x80000008:
8696 case 0x80000009:
8697 case 0x8000000A:
8698 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8699 break;
8700
8701 default:
8702 /* just ignore */
8703 break;
8704 }
8705 }
8706
8707 mHWData->mMemorySize = data.ulMemorySizeMB;
8708 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8709
8710 // boot order
8711 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8712 {
8713 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8714 if (it == data.mapBootOrder.end())
8715 mHWData->mBootOrder[i] = DeviceType_Null;
8716 else
8717 mHWData->mBootOrder[i] = it->second;
8718 }
8719
8720 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8721 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8722 mHWData->mMonitorCount = data.cMonitors;
8723 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8724 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8725 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8726 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8727 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8728 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8729 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8730 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8731 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8732 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8733 if (!data.strVideoCaptureFile.isEmpty())
8734 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8735 else
8736 mHWData->mVideoCaptureFile.setNull();
8737 mHWData->mFirmwareType = data.firmwareType;
8738 mHWData->mPointingHIDType = data.pointingHIDType;
8739 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8740 mHWData->mChipsetType = data.chipsetType;
8741 mHWData->mParavirtProvider = data.paravirtProvider;
8742 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8743 mHWData->mHPETEnabled = data.fHPETEnabled;
8744
8745 /* VRDEServer */
8746 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8747 if (FAILED(rc)) return rc;
8748
8749 /* BIOS */
8750 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8751 if (FAILED(rc)) return rc;
8752
8753 // Bandwidth control (must come before network adapters)
8754 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8755 if (FAILED(rc)) return rc;
8756
8757 /* Shared folders */
8758 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8759 it != data.usbSettings.llUSBControllers.end();
8760 ++it)
8761 {
8762 const settings::USBController &settingsCtrl = *it;
8763 ComObjPtr<USBController> newCtrl;
8764
8765 newCtrl.createObject();
8766 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8767 mUSBControllers->push_back(newCtrl);
8768 }
8769
8770 /* USB device filters */
8771 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8772 if (FAILED(rc)) return rc;
8773
8774 // network adapters
8775 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8776 size_t oldCount = mNetworkAdapters.size();
8777 if (newCount > oldCount)
8778 {
8779 mNetworkAdapters.resize(newCount);
8780 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8781 {
8782 unconst(mNetworkAdapters[slot]).createObject();
8783 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8784 }
8785 }
8786 else if (newCount < oldCount)
8787 mNetworkAdapters.resize(newCount);
8788 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8789 it != data.llNetworkAdapters.end();
8790 ++it)
8791 {
8792 const settings::NetworkAdapter &nic = *it;
8793
8794 /* slot unicity is guaranteed by XML Schema */
8795 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8796 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8797 if (FAILED(rc)) return rc;
8798 }
8799
8800 // serial ports
8801 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8802 it != data.llSerialPorts.end();
8803 ++it)
8804 {
8805 const settings::SerialPort &s = *it;
8806
8807 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8808 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8809 if (FAILED(rc)) return rc;
8810 }
8811
8812 // parallel ports (optional)
8813 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8814 it != data.llParallelPorts.end();
8815 ++it)
8816 {
8817 const settings::ParallelPort &p = *it;
8818
8819 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8820 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8821 if (FAILED(rc)) return rc;
8822 }
8823
8824 /* AudioAdapter */
8825 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8826 if (FAILED(rc)) return rc;
8827
8828 /* Shared folders */
8829 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8830 it != data.llSharedFolders.end();
8831 ++it)
8832 {
8833 const settings::SharedFolder &sf = *it;
8834
8835 ComObjPtr<SharedFolder> sharedFolder;
8836 /* Check for double entries. Not allowed! */
8837 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8838 if (SUCCEEDED(rc))
8839 return setError(VBOX_E_OBJECT_IN_USE,
8840 tr("Shared folder named '%s' already exists"),
8841 sf.strName.c_str());
8842
8843 /* Create the new shared folder. Don't break on error. This will be
8844 * reported when the machine starts. */
8845 sharedFolder.createObject();
8846 rc = sharedFolder->init(i_getMachine(),
8847 sf.strName,
8848 sf.strHostPath,
8849 RT_BOOL(sf.fWritable),
8850 RT_BOOL(sf.fAutoMount),
8851 false /* fFailOnError */);
8852 if (FAILED(rc)) return rc;
8853 mHWData->mSharedFolders.push_back(sharedFolder);
8854 }
8855
8856 // Clipboard
8857 mHWData->mClipboardMode = data.clipboardMode;
8858
8859 // drag'n'drop
8860 mHWData->mDnDMode = data.dndMode;
8861
8862 // guest settings
8863 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8864
8865 // IO settings
8866 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8867 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8868
8869 // Host PCI devices
8870 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8871 it != data.pciAttachments.end();
8872 ++it)
8873 {
8874 const settings::HostPCIDeviceAttachment &hpda = *it;
8875 ComObjPtr<PCIDeviceAttachment> pda;
8876
8877 pda.createObject();
8878 pda->i_loadSettings(this, hpda);
8879 mHWData->mPCIDeviceAssignments.push_back(pda);
8880 }
8881
8882 /*
8883 * (The following isn't really real hardware, but it lives in HWData
8884 * for reasons of convenience.)
8885 */
8886
8887#ifdef VBOX_WITH_GUEST_PROPS
8888 /* Guest properties (optional) */
8889 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8890 it != data.llGuestProperties.end();
8891 ++it)
8892 {
8893 const settings::GuestProperty &prop = *it;
8894 uint32_t fFlags = guestProp::NILFLAG;
8895 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8896 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8897 mHWData->mGuestProperties[prop.strName] = property;
8898 }
8899
8900 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8901#endif /* VBOX_WITH_GUEST_PROPS defined */
8902
8903 rc = i_loadDebugging(pDbg);
8904 if (FAILED(rc))
8905 return rc;
8906
8907 mHWData->mAutostart = *pAutostart;
8908
8909 /* default frontend */
8910 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8911 }
8912 catch(std::bad_alloc &)
8913 {
8914 return E_OUTOFMEMORY;
8915 }
8916
8917 AssertComRC(rc);
8918 return rc;
8919}
8920
8921/**
8922 * Called from Machine::loadHardware() to load the debugging settings of the
8923 * machine.
8924 *
8925 * @param pDbg Pointer to the settings.
8926 */
8927HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8928{
8929 mHWData->mDebugging = *pDbg;
8930 /* no more processing currently required, this will probably change. */
8931 return S_OK;
8932}
8933
8934/**
8935 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8936 *
8937 * @param data
8938 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8939 * @param puuidSnapshot
8940 * @return
8941 */
8942HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8943 const Guid *puuidRegistry,
8944 const Guid *puuidSnapshot)
8945{
8946 AssertReturn(!i_isSessionMachine(), E_FAIL);
8947
8948 HRESULT rc = S_OK;
8949
8950 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8951 it != data.llStorageControllers.end();
8952 ++it)
8953 {
8954 const settings::StorageController &ctlData = *it;
8955
8956 ComObjPtr<StorageController> pCtl;
8957 /* Try to find one with the name first. */
8958 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8959 if (SUCCEEDED(rc))
8960 return setError(VBOX_E_OBJECT_IN_USE,
8961 tr("Storage controller named '%s' already exists"),
8962 ctlData.strName.c_str());
8963
8964 pCtl.createObject();
8965 rc = pCtl->init(this,
8966 ctlData.strName,
8967 ctlData.storageBus,
8968 ctlData.ulInstance,
8969 ctlData.fBootable);
8970 if (FAILED(rc)) return rc;
8971
8972 mStorageControllers->push_back(pCtl);
8973
8974 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8975 if (FAILED(rc)) return rc;
8976
8977 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8978 if (FAILED(rc)) return rc;
8979
8980 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8981 if (FAILED(rc)) return rc;
8982
8983 /* Set IDE emulation settings (only for AHCI controller). */
8984 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8985 {
8986 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8987 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8988 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8989 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8990 )
8991 return rc;
8992 }
8993
8994 /* Load the attached devices now. */
8995 rc = i_loadStorageDevices(pCtl,
8996 ctlData,
8997 puuidRegistry,
8998 puuidSnapshot);
8999 if (FAILED(rc)) return rc;
9000 }
9001
9002 return S_OK;
9003}
9004
9005/**
9006 * Called from i_loadStorageControllers for a controller's devices.
9007 *
9008 * @param aStorageController
9009 * @param data
9010 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9011 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9012 * @return
9013 */
9014HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9015 const settings::StorageController &data,
9016 const Guid *puuidRegistry,
9017 const Guid *puuidSnapshot)
9018{
9019 HRESULT rc = S_OK;
9020
9021 /* paranoia: detect duplicate attachments */
9022 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9023 it != data.llAttachedDevices.end();
9024 ++it)
9025 {
9026 const settings::AttachedDevice &ad = *it;
9027
9028 for (settings::AttachedDevicesList::const_iterator it2 = it;
9029 it2 != data.llAttachedDevices.end();
9030 ++it2)
9031 {
9032 if (it == it2)
9033 continue;
9034
9035 const settings::AttachedDevice &ad2 = *it2;
9036
9037 if ( ad.lPort == ad2.lPort
9038 && ad.lDevice == ad2.lDevice)
9039 {
9040 return setError(E_FAIL,
9041 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9042 aStorageController->i_getName().c_str(),
9043 ad.lPort,
9044 ad.lDevice,
9045 mUserData->s.strName.c_str());
9046 }
9047 }
9048 }
9049
9050 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9051 it != data.llAttachedDevices.end();
9052 ++it)
9053 {
9054 const settings::AttachedDevice &dev = *it;
9055 ComObjPtr<Medium> medium;
9056
9057 switch (dev.deviceType)
9058 {
9059 case DeviceType_Floppy:
9060 case DeviceType_DVD:
9061 if (dev.strHostDriveSrc.isNotEmpty())
9062 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9063 false /* fRefresh */, medium);
9064 else
9065 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9066 dev.uuid,
9067 false /* fRefresh */,
9068 false /* aSetError */,
9069 medium);
9070 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9071 // This is not an error. The host drive or UUID might have vanished, so just go
9072 // ahead without this removeable medium attachment
9073 rc = S_OK;
9074 break;
9075
9076 case DeviceType_HardDisk:
9077 {
9078 /* find a hard disk by UUID */
9079 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9080 if (FAILED(rc))
9081 {
9082 if (i_isSnapshotMachine())
9083 {
9084 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9085 // so the user knows that the bad disk is in a snapshot somewhere
9086 com::ErrorInfo info;
9087 return setError(E_FAIL,
9088 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9089 puuidSnapshot->raw(),
9090 info.getText().raw());
9091 }
9092 else
9093 return rc;
9094 }
9095
9096 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9097
9098 if (medium->i_getType() == MediumType_Immutable)
9099 {
9100 if (i_isSnapshotMachine())
9101 return setError(E_FAIL,
9102 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9103 "of the virtual machine '%s' ('%s')"),
9104 medium->i_getLocationFull().c_str(),
9105 dev.uuid.raw(),
9106 puuidSnapshot->raw(),
9107 mUserData->s.strName.c_str(),
9108 mData->m_strConfigFileFull.c_str());
9109
9110 return setError(E_FAIL,
9111 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9112 medium->i_getLocationFull().c_str(),
9113 dev.uuid.raw(),
9114 mUserData->s.strName.c_str(),
9115 mData->m_strConfigFileFull.c_str());
9116 }
9117
9118 if (medium->i_getType() == MediumType_MultiAttach)
9119 {
9120 if (i_isSnapshotMachine())
9121 return setError(E_FAIL,
9122 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9123 "of the virtual machine '%s' ('%s')"),
9124 medium->i_getLocationFull().c_str(),
9125 dev.uuid.raw(),
9126 puuidSnapshot->raw(),
9127 mUserData->s.strName.c_str(),
9128 mData->m_strConfigFileFull.c_str());
9129
9130 return setError(E_FAIL,
9131 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9132 medium->i_getLocationFull().c_str(),
9133 dev.uuid.raw(),
9134 mUserData->s.strName.c_str(),
9135 mData->m_strConfigFileFull.c_str());
9136 }
9137
9138 if ( !i_isSnapshotMachine()
9139 && medium->i_getChildren().size() != 0
9140 )
9141 return setError(E_FAIL,
9142 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9143 "because it has %d differencing child hard disks"),
9144 medium->i_getLocationFull().c_str(),
9145 dev.uuid.raw(),
9146 mUserData->s.strName.c_str(),
9147 mData->m_strConfigFileFull.c_str(),
9148 medium->i_getChildren().size());
9149
9150 if (i_findAttachment(mMediaData->mAttachments,
9151 medium))
9152 return setError(E_FAIL,
9153 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9154 medium->i_getLocationFull().c_str(),
9155 dev.uuid.raw(),
9156 mUserData->s.strName.c_str(),
9157 mData->m_strConfigFileFull.c_str());
9158
9159 break;
9160 }
9161
9162 default:
9163 return setError(E_FAIL,
9164 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9165 medium->i_getLocationFull().c_str(),
9166 mUserData->s.strName.c_str(),
9167 mData->m_strConfigFileFull.c_str());
9168 }
9169
9170 if (FAILED(rc))
9171 break;
9172
9173 /* Bandwidth groups are loaded at this point. */
9174 ComObjPtr<BandwidthGroup> pBwGroup;
9175
9176 if (!dev.strBwGroup.isEmpty())
9177 {
9178 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9179 if (FAILED(rc))
9180 return setError(E_FAIL,
9181 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9182 medium->i_getLocationFull().c_str(),
9183 dev.strBwGroup.c_str(),
9184 mUserData->s.strName.c_str(),
9185 mData->m_strConfigFileFull.c_str());
9186 pBwGroup->i_reference();
9187 }
9188
9189 const Bstr controllerName = aStorageController->i_getName();
9190 ComObjPtr<MediumAttachment> pAttachment;
9191 pAttachment.createObject();
9192 rc = pAttachment->init(this,
9193 medium,
9194 controllerName,
9195 dev.lPort,
9196 dev.lDevice,
9197 dev.deviceType,
9198 false,
9199 dev.fPassThrough,
9200 dev.fTempEject,
9201 dev.fNonRotational,
9202 dev.fDiscard,
9203 dev.fHotPluggable,
9204 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9205 if (FAILED(rc)) break;
9206
9207 /* associate the medium with this machine and snapshot */
9208 if (!medium.isNull())
9209 {
9210 AutoCaller medCaller(medium);
9211 if (FAILED(medCaller.rc())) return medCaller.rc();
9212 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9213
9214 if (i_isSnapshotMachine())
9215 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9216 else
9217 rc = medium->i_addBackReference(mData->mUuid);
9218 /* If the medium->addBackReference fails it sets an appropriate
9219 * error message, so no need to do any guesswork here. */
9220
9221 if (puuidRegistry)
9222 // caller wants registry ID to be set on all attached media (OVF import case)
9223 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9224 }
9225
9226 if (FAILED(rc))
9227 break;
9228
9229 /* back up mMediaData to let registeredInit() properly rollback on failure
9230 * (= limited accessibility) */
9231 i_setModified(IsModified_Storage);
9232 mMediaData.backup();
9233 mMediaData->mAttachments.push_back(pAttachment);
9234 }
9235
9236 return rc;
9237}
9238
9239/**
9240 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9241 *
9242 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9243 * @param aSnapshot where to return the found snapshot
9244 * @param aSetError true to set extended error info on failure
9245 */
9246HRESULT Machine::i_findSnapshotById(const Guid &aId,
9247 ComObjPtr<Snapshot> &aSnapshot,
9248 bool aSetError /* = false */)
9249{
9250 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9251
9252 if (!mData->mFirstSnapshot)
9253 {
9254 if (aSetError)
9255 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9256 return E_FAIL;
9257 }
9258
9259 if (aId.isZero())
9260 aSnapshot = mData->mFirstSnapshot;
9261 else
9262 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9263
9264 if (!aSnapshot)
9265 {
9266 if (aSetError)
9267 return setError(E_FAIL,
9268 tr("Could not find a snapshot with UUID {%s}"),
9269 aId.toString().c_str());
9270 return E_FAIL;
9271 }
9272
9273 return S_OK;
9274}
9275
9276/**
9277 * Returns the snapshot with the given name or fails of no such snapshot.
9278 *
9279 * @param aName snapshot name to find
9280 * @param aSnapshot where to return the found snapshot
9281 * @param aSetError true to set extended error info on failure
9282 */
9283HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9284 ComObjPtr<Snapshot> &aSnapshot,
9285 bool aSetError /* = false */)
9286{
9287 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9288
9289 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9290
9291 if (!mData->mFirstSnapshot)
9292 {
9293 if (aSetError)
9294 return setError(VBOX_E_OBJECT_NOT_FOUND,
9295 tr("This machine does not have any snapshots"));
9296 return VBOX_E_OBJECT_NOT_FOUND;
9297 }
9298
9299 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9300
9301 if (!aSnapshot)
9302 {
9303 if (aSetError)
9304 return setError(VBOX_E_OBJECT_NOT_FOUND,
9305 tr("Could not find a snapshot named '%s'"), strName.c_str());
9306 return VBOX_E_OBJECT_NOT_FOUND;
9307 }
9308
9309 return S_OK;
9310}
9311
9312/**
9313 * Returns a storage controller object with the given name.
9314 *
9315 * @param aName storage controller name to find
9316 * @param aStorageController where to return the found storage controller
9317 * @param aSetError true to set extended error info on failure
9318 */
9319HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9320 ComObjPtr<StorageController> &aStorageController,
9321 bool aSetError /* = false */)
9322{
9323 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9324
9325 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9326 it != mStorageControllers->end();
9327 ++it)
9328 {
9329 if ((*it)->i_getName() == aName)
9330 {
9331 aStorageController = (*it);
9332 return S_OK;
9333 }
9334 }
9335
9336 if (aSetError)
9337 return setError(VBOX_E_OBJECT_NOT_FOUND,
9338 tr("Could not find a storage controller named '%s'"),
9339 aName.c_str());
9340 return VBOX_E_OBJECT_NOT_FOUND;
9341}
9342
9343/**
9344 * Returns a USB controller object with the given name.
9345 *
9346 * @param aName USB controller name to find
9347 * @param aUSBController where to return the found USB controller
9348 * @param aSetError true to set extended error info on failure
9349 */
9350HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9351 ComObjPtr<USBController> &aUSBController,
9352 bool aSetError /* = false */)
9353{
9354 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9355
9356 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9357 it != mUSBControllers->end();
9358 ++it)
9359 {
9360 if ((*it)->i_getName() == aName)
9361 {
9362 aUSBController = (*it);
9363 return S_OK;
9364 }
9365 }
9366
9367 if (aSetError)
9368 return setError(VBOX_E_OBJECT_NOT_FOUND,
9369 tr("Could not find a storage controller named '%s'"),
9370 aName.c_str());
9371 return VBOX_E_OBJECT_NOT_FOUND;
9372}
9373
9374/**
9375 * Returns the number of USB controller instance of the given type.
9376 *
9377 * @param enmType USB controller type.
9378 */
9379ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9380{
9381 ULONG cCtrls = 0;
9382
9383 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9384 it != mUSBControllers->end();
9385 ++it)
9386 {
9387 if ((*it)->i_getControllerType() == enmType)
9388 cCtrls++;
9389 }
9390
9391 return cCtrls;
9392}
9393
9394HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9395 MediaData::AttachmentList &atts)
9396{
9397 AutoCaller autoCaller(this);
9398 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9399
9400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9401
9402 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9403 it != mMediaData->mAttachments.end();
9404 ++it)
9405 {
9406 const ComObjPtr<MediumAttachment> &pAtt = *it;
9407 // should never happen, but deal with NULL pointers in the list.
9408 AssertStmt(!pAtt.isNull(), continue);
9409
9410 // getControllerName() needs caller+read lock
9411 AutoCaller autoAttCaller(pAtt);
9412 if (FAILED(autoAttCaller.rc()))
9413 {
9414 atts.clear();
9415 return autoAttCaller.rc();
9416 }
9417 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9418
9419 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9420 atts.push_back(pAtt);
9421 }
9422
9423 return S_OK;
9424}
9425
9426
9427/**
9428 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9429 * file if the machine name was changed and about creating a new settings file
9430 * if this is a new machine.
9431 *
9432 * @note Must be never called directly but only from #saveSettings().
9433 */
9434HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9435{
9436 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9437
9438 HRESULT rc = S_OK;
9439
9440 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9441
9442 /// @todo need to handle primary group change, too
9443
9444 /* attempt to rename the settings file if machine name is changed */
9445 if ( mUserData->s.fNameSync
9446 && mUserData.isBackedUp()
9447 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9448 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9449 )
9450 {
9451 bool dirRenamed = false;
9452 bool fileRenamed = false;
9453
9454 Utf8Str configFile, newConfigFile;
9455 Utf8Str configFilePrev, newConfigFilePrev;
9456 Utf8Str configDir, newConfigDir;
9457
9458 do
9459 {
9460 int vrc = VINF_SUCCESS;
9461
9462 Utf8Str name = mUserData.backedUpData()->s.strName;
9463 Utf8Str newName = mUserData->s.strName;
9464 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9465 if (group == "/")
9466 group.setNull();
9467 Utf8Str newGroup = mUserData->s.llGroups.front();
9468 if (newGroup == "/")
9469 newGroup.setNull();
9470
9471 configFile = mData->m_strConfigFileFull;
9472
9473 /* first, rename the directory if it matches the group and machine name */
9474 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9475 group.c_str(), RTPATH_DELIMITER, name.c_str());
9476 /** @todo hack, make somehow use of ComposeMachineFilename */
9477 if (mUserData->s.fDirectoryIncludesUUID)
9478 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9479 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9480 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9481 /** @todo hack, make somehow use of ComposeMachineFilename */
9482 if (mUserData->s.fDirectoryIncludesUUID)
9483 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9484 configDir = configFile;
9485 configDir.stripFilename();
9486 newConfigDir = configDir;
9487 if ( configDir.length() >= groupPlusName.length()
9488 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9489 groupPlusName.c_str()))
9490 {
9491 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9492 Utf8Str newConfigBaseDir(newConfigDir);
9493 newConfigDir.append(newGroupPlusName);
9494 /* consistency: use \ if appropriate on the platform */
9495 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9496 /* new dir and old dir cannot be equal here because of 'if'
9497 * above and because name != newName */
9498 Assert(configDir != newConfigDir);
9499 if (!fSettingsFileIsNew)
9500 {
9501 /* perform real rename only if the machine is not new */
9502 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9503 if ( vrc == VERR_FILE_NOT_FOUND
9504 || vrc == VERR_PATH_NOT_FOUND)
9505 {
9506 /* create the parent directory, then retry renaming */
9507 Utf8Str parent(newConfigDir);
9508 parent.stripFilename();
9509 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9510 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9511 }
9512 if (RT_FAILURE(vrc))
9513 {
9514 rc = setError(E_FAIL,
9515 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9516 configDir.c_str(),
9517 newConfigDir.c_str(),
9518 vrc);
9519 break;
9520 }
9521 /* delete subdirectories which are no longer needed */
9522 Utf8Str dir(configDir);
9523 dir.stripFilename();
9524 while (dir != newConfigBaseDir && dir != ".")
9525 {
9526 vrc = RTDirRemove(dir.c_str());
9527 if (RT_FAILURE(vrc))
9528 break;
9529 dir.stripFilename();
9530 }
9531 dirRenamed = true;
9532 }
9533 }
9534
9535 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9536 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9537
9538 /* then try to rename the settings file itself */
9539 if (newConfigFile != configFile)
9540 {
9541 /* get the path to old settings file in renamed directory */
9542 configFile = Utf8StrFmt("%s%c%s",
9543 newConfigDir.c_str(),
9544 RTPATH_DELIMITER,
9545 RTPathFilename(configFile.c_str()));
9546 if (!fSettingsFileIsNew)
9547 {
9548 /* perform real rename only if the machine is not new */
9549 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9550 if (RT_FAILURE(vrc))
9551 {
9552 rc = setError(E_FAIL,
9553 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9554 configFile.c_str(),
9555 newConfigFile.c_str(),
9556 vrc);
9557 break;
9558 }
9559 fileRenamed = true;
9560 configFilePrev = configFile;
9561 configFilePrev += "-prev";
9562 newConfigFilePrev = newConfigFile;
9563 newConfigFilePrev += "-prev";
9564 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9565 }
9566 }
9567
9568 // update m_strConfigFileFull amd mConfigFile
9569 mData->m_strConfigFileFull = newConfigFile;
9570 // compute the relative path too
9571 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9572
9573 // store the old and new so that VirtualBox::i_saveSettings() can update
9574 // the media registry
9575 if ( mData->mRegistered
9576 && (configDir != newConfigDir || configFile != newConfigFile))
9577 {
9578 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9579
9580 if (pfNeedsGlobalSaveSettings)
9581 *pfNeedsGlobalSaveSettings = true;
9582 }
9583
9584 // in the saved state file path, replace the old directory with the new directory
9585 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9586 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9587
9588 // and do the same thing for the saved state file paths of all the online snapshots
9589 if (mData->mFirstSnapshot)
9590 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9591 newConfigDir.c_str());
9592 }
9593 while (0);
9594
9595 if (FAILED(rc))
9596 {
9597 /* silently try to rename everything back */
9598 if (fileRenamed)
9599 {
9600 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9601 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9602 }
9603 if (dirRenamed)
9604 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9605 }
9606
9607 if (FAILED(rc)) return rc;
9608 }
9609
9610 if (fSettingsFileIsNew)
9611 {
9612 /* create a virgin config file */
9613 int vrc = VINF_SUCCESS;
9614
9615 /* ensure the settings directory exists */
9616 Utf8Str path(mData->m_strConfigFileFull);
9617 path.stripFilename();
9618 if (!RTDirExists(path.c_str()))
9619 {
9620 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9621 if (RT_FAILURE(vrc))
9622 {
9623 return setError(E_FAIL,
9624 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9625 path.c_str(),
9626 vrc);
9627 }
9628 }
9629
9630 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9631 path = Utf8Str(mData->m_strConfigFileFull);
9632 RTFILE f = NIL_RTFILE;
9633 vrc = RTFileOpen(&f, path.c_str(),
9634 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9635 if (RT_FAILURE(vrc))
9636 return setError(E_FAIL,
9637 tr("Could not create the settings file '%s' (%Rrc)"),
9638 path.c_str(),
9639 vrc);
9640 RTFileClose(f);
9641 }
9642
9643 return rc;
9644}
9645
9646/**
9647 * Saves and commits machine data, user data and hardware data.
9648 *
9649 * Note that on failure, the data remains uncommitted.
9650 *
9651 * @a aFlags may combine the following flags:
9652 *
9653 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9654 * Used when saving settings after an operation that makes them 100%
9655 * correspond to the settings from the current snapshot.
9656 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9657 * #isReallyModified() returns false. This is necessary for cases when we
9658 * change machine data directly, not through the backup()/commit() mechanism.
9659 * - SaveS_Force: settings will be saved without doing a deep compare of the
9660 * settings structures. This is used when this is called because snapshots
9661 * have changed to avoid the overhead of the deep compare.
9662 *
9663 * @note Must be called from under this object's write lock. Locks children for
9664 * writing.
9665 *
9666 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9667 * initialized to false and that will be set to true by this function if
9668 * the caller must invoke VirtualBox::i_saveSettings() because the global
9669 * settings have changed. This will happen if a machine rename has been
9670 * saved and the global machine and media registries will therefore need
9671 * updating.
9672 */
9673HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9674 int aFlags /*= 0*/)
9675{
9676 LogFlowThisFuncEnter();
9677
9678 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9679
9680 /* make sure child objects are unable to modify the settings while we are
9681 * saving them */
9682 i_ensureNoStateDependencies();
9683
9684 AssertReturn(!i_isSnapshotMachine(),
9685 E_FAIL);
9686
9687 HRESULT rc = S_OK;
9688 bool fNeedsWrite = false;
9689
9690 /* First, prepare to save settings. It will care about renaming the
9691 * settings directory and file if the machine name was changed and about
9692 * creating a new settings file if this is a new machine. */
9693 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9694 if (FAILED(rc)) return rc;
9695
9696 // keep a pointer to the current settings structures
9697 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9698 settings::MachineConfigFile *pNewConfig = NULL;
9699
9700 try
9701 {
9702 // make a fresh one to have everyone write stuff into
9703 pNewConfig = new settings::MachineConfigFile(NULL);
9704 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9705
9706 // now go and copy all the settings data from COM to the settings structures
9707 // (this calles i_saveSettings() on all the COM objects in the machine)
9708 i_copyMachineDataToSettings(*pNewConfig);
9709
9710 if (aFlags & SaveS_ResetCurStateModified)
9711 {
9712 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9713 mData->mCurrentStateModified = FALSE;
9714 fNeedsWrite = true; // always, no need to compare
9715 }
9716 else if (aFlags & SaveS_Force)
9717 {
9718 fNeedsWrite = true; // always, no need to compare
9719 }
9720 else
9721 {
9722 if (!mData->mCurrentStateModified)
9723 {
9724 // do a deep compare of the settings that we just saved with the settings
9725 // previously stored in the config file; this invokes MachineConfigFile::operator==
9726 // which does a deep compare of all the settings, which is expensive but less expensive
9727 // than writing out XML in vain
9728 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9729
9730 // could still be modified if any settings changed
9731 mData->mCurrentStateModified = fAnySettingsChanged;
9732
9733 fNeedsWrite = fAnySettingsChanged;
9734 }
9735 else
9736 fNeedsWrite = true;
9737 }
9738
9739 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9740
9741 if (fNeedsWrite)
9742 // now spit it all out!
9743 pNewConfig->write(mData->m_strConfigFileFull);
9744
9745 mData->pMachineConfigFile = pNewConfig;
9746 delete pOldConfig;
9747 i_commit();
9748
9749 // after saving settings, we are no longer different from the XML on disk
9750 mData->flModifications = 0;
9751 }
9752 catch (HRESULT err)
9753 {
9754 // we assume that error info is set by the thrower
9755 rc = err;
9756
9757 // restore old config
9758 delete pNewConfig;
9759 mData->pMachineConfigFile = pOldConfig;
9760 }
9761 catch (...)
9762 {
9763 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9764 }
9765
9766 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9767 {
9768 /* Fire the data change event, even on failure (since we've already
9769 * committed all data). This is done only for SessionMachines because
9770 * mutable Machine instances are always not registered (i.e. private
9771 * to the client process that creates them) and thus don't need to
9772 * inform callbacks. */
9773 if (i_isSessionMachine())
9774 mParent->i_onMachineDataChange(mData->mUuid);
9775 }
9776
9777 LogFlowThisFunc(("rc=%08X\n", rc));
9778 LogFlowThisFuncLeave();
9779 return rc;
9780}
9781
9782/**
9783 * Implementation for saving the machine settings into the given
9784 * settings::MachineConfigFile instance. This copies machine extradata
9785 * from the previous machine config file in the instance data, if any.
9786 *
9787 * This gets called from two locations:
9788 *
9789 * -- Machine::i_saveSettings(), during the regular XML writing;
9790 *
9791 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9792 * exported to OVF and we write the VirtualBox proprietary XML
9793 * into a <vbox:Machine> tag.
9794 *
9795 * This routine fills all the fields in there, including snapshots, *except*
9796 * for the following:
9797 *
9798 * -- fCurrentStateModified. There is some special logic associated with that.
9799 *
9800 * The caller can then call MachineConfigFile::write() or do something else
9801 * with it.
9802 *
9803 * Caller must hold the machine lock!
9804 *
9805 * This throws XML errors and HRESULT, so the caller must have a catch block!
9806 */
9807void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9808{
9809 // deep copy extradata
9810 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9811
9812 config.uuid = mData->mUuid;
9813
9814 // copy name, description, OS type, teleport, UTC etc.
9815 config.machineUserData = mUserData->s;
9816
9817 // Encode the Icon Override data from Machine and store on config userdata.
9818 std::vector<BYTE> iconByte;
9819 getIcon(iconByte);
9820 ssize_t cbData = iconByte.size();
9821 if (cbData > 0)
9822 {
9823 ssize_t cchOut = RTBase64EncodedLength(cbData);
9824 Utf8Str strIconData;
9825 strIconData.reserve(cchOut+1);
9826 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9827 strIconData.mutableRaw(), strIconData.capacity(),
9828 NULL);
9829 if (RT_FAILURE(vrc))
9830 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9831 strIconData.jolt();
9832 config.machineUserData.ovIcon = strIconData;
9833 }
9834 else
9835 config.machineUserData.ovIcon.setNull();
9836
9837 if ( mData->mMachineState == MachineState_Saved
9838 || mData->mMachineState == MachineState_Restoring
9839 // when deleting a snapshot we may or may not have a saved state in the current state,
9840 // so let's not assert here please
9841 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9842 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9843 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9844 && (!mSSData->strStateFilePath.isEmpty())
9845 )
9846 )
9847 {
9848 Assert(!mSSData->strStateFilePath.isEmpty());
9849 /* try to make the file name relative to the settings file dir */
9850 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9851 }
9852 else
9853 {
9854 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9855 config.strStateFile.setNull();
9856 }
9857
9858 if (mData->mCurrentSnapshot)
9859 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9860 else
9861 config.uuidCurrentSnapshot.clear();
9862
9863 config.timeLastStateChange = mData->mLastStateChange;
9864 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9865 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9866
9867 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9868 if (FAILED(rc)) throw rc;
9869
9870 rc = i_saveStorageControllers(config.storageMachine);
9871 if (FAILED(rc)) throw rc;
9872
9873 // save machine's media registry if this is VirtualBox 4.0 or later
9874 if (config.canHaveOwnMediaRegistry())
9875 {
9876 // determine machine folder
9877 Utf8Str strMachineFolder = i_getSettingsFileFull();
9878 strMachineFolder.stripFilename();
9879 mParent->i_saveMediaRegistry(config.mediaRegistry,
9880 i_getId(), // only media with registry ID == machine UUID
9881 strMachineFolder);
9882 // this throws HRESULT
9883 }
9884
9885 // save snapshots
9886 rc = i_saveAllSnapshots(config);
9887 if (FAILED(rc)) throw rc;
9888}
9889
9890/**
9891 * Saves all snapshots of the machine into the given machine config file. Called
9892 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9893 * @param config
9894 * @return
9895 */
9896HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9897{
9898 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9899
9900 HRESULT rc = S_OK;
9901
9902 try
9903 {
9904 config.llFirstSnapshot.clear();
9905
9906 if (mData->mFirstSnapshot)
9907 {
9908 settings::Snapshot snapNew;
9909 config.llFirstSnapshot.push_back(snapNew);
9910
9911 // get reference to the fresh copy of the snapshot on the list and
9912 // work on that copy directly to avoid excessive copying later
9913 settings::Snapshot &snap = config.llFirstSnapshot.front();
9914
9915 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9916 if (FAILED(rc)) throw rc;
9917 }
9918
9919// if (mType == IsSessionMachine)
9920// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9921
9922 }
9923 catch (HRESULT err)
9924 {
9925 /* we assume that error info is set by the thrower */
9926 rc = err;
9927 }
9928 catch (...)
9929 {
9930 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9931 }
9932
9933 return rc;
9934}
9935
9936/**
9937 * Saves the VM hardware configuration. It is assumed that the
9938 * given node is empty.
9939 *
9940 * @param data Reference to the settings object for the hardware config.
9941 * @param pDbg Pointer to the settings object for the debugging config
9942 * which happens to live in mHWData.
9943 * @param pAutostart Pointer to the settings object for the autostart config
9944 * which happens to live in mHWData.
9945 */
9946HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9947 settings::Autostart *pAutostart)
9948{
9949 HRESULT rc = S_OK;
9950
9951 try
9952 {
9953 /* The hardware version attribute (optional).
9954 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9955 if ( mHWData->mHWVersion == "1"
9956 && mSSData->strStateFilePath.isEmpty()
9957 )
9958 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9959 other point needs to be found where this can be done. */
9960
9961 data.strVersion = mHWData->mHWVersion;
9962 data.uuid = mHWData->mHardwareUUID;
9963
9964 // CPU
9965 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9966 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9967 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9968 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9969 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9970 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9971 data.fPAE = !!mHWData->mPAEEnabled;
9972 data.enmLongMode = mHWData->mLongMode;
9973 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9974 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9975
9976 /* Standard and Extended CPUID leafs. */
9977 data.llCpuIdLeafs.clear();
9978 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9979 {
9980 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9981 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9982 }
9983 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9984 {
9985 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9986 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9987 }
9988
9989 data.cCPUs = mHWData->mCPUCount;
9990 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9991 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9992
9993 data.llCpus.clear();
9994 if (data.fCpuHotPlug)
9995 {
9996 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9997 {
9998 if (mHWData->mCPUAttached[idx])
9999 {
10000 settings::Cpu cpu;
10001 cpu.ulId = idx;
10002 data.llCpus.push_back(cpu);
10003 }
10004 }
10005 }
10006
10007 // memory
10008 data.ulMemorySizeMB = mHWData->mMemorySize;
10009 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10010
10011 // firmware
10012 data.firmwareType = mHWData->mFirmwareType;
10013
10014 // HID
10015 data.pointingHIDType = mHWData->mPointingHIDType;
10016 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10017
10018 // chipset
10019 data.chipsetType = mHWData->mChipsetType;
10020
10021 // paravirt
10022 data.paravirtProvider = mHWData->mParavirtProvider;
10023
10024
10025 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10026
10027 // HPET
10028 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10029
10030 // boot order
10031 data.mapBootOrder.clear();
10032 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10033 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10034
10035 // display
10036 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10037 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10038 data.cMonitors = mHWData->mMonitorCount;
10039 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10040 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10041 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10042 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10043 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10044 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10045 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10046 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10047 {
10048 if (mHWData->maVideoCaptureScreens[i])
10049 ASMBitSet(&data.u64VideoCaptureScreens, i);
10050 else
10051 ASMBitClear(&data.u64VideoCaptureScreens, i);
10052 }
10053 /* store relative video capture file if possible */
10054 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10055
10056 /* VRDEServer settings (optional) */
10057 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10058 if (FAILED(rc)) throw rc;
10059
10060 /* BIOS (required) */
10061 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10062 if (FAILED(rc)) throw rc;
10063
10064 /* USB Controller (required) */
10065 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10066 {
10067 ComObjPtr<USBController> ctrl = *it;
10068 settings::USBController settingsCtrl;
10069
10070 settingsCtrl.strName = ctrl->i_getName();
10071 settingsCtrl.enmType = ctrl->i_getControllerType();
10072
10073 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10074 }
10075
10076 /* USB device filters (required) */
10077 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10078 if (FAILED(rc)) throw rc;
10079
10080 /* Network adapters (required) */
10081 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10082 data.llNetworkAdapters.clear();
10083 /* Write out only the nominal number of network adapters for this
10084 * chipset type. Since Machine::commit() hasn't been called there
10085 * may be extra NIC settings in the vector. */
10086 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10087 {
10088 settings::NetworkAdapter nic;
10089 nic.ulSlot = (uint32_t)slot;
10090 /* paranoia check... must not be NULL, but must not crash either. */
10091 if (mNetworkAdapters[slot])
10092 {
10093 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10094 if (FAILED(rc)) throw rc;
10095
10096 data.llNetworkAdapters.push_back(nic);
10097 }
10098 }
10099
10100 /* Serial ports */
10101 data.llSerialPorts.clear();
10102 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10103 {
10104 settings::SerialPort s;
10105 s.ulSlot = slot;
10106 rc = mSerialPorts[slot]->i_saveSettings(s);
10107 if (FAILED(rc)) return rc;
10108
10109 data.llSerialPorts.push_back(s);
10110 }
10111
10112 /* Parallel ports */
10113 data.llParallelPorts.clear();
10114 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10115 {
10116 settings::ParallelPort p;
10117 p.ulSlot = slot;
10118 rc = mParallelPorts[slot]->i_saveSettings(p);
10119 if (FAILED(rc)) return rc;
10120
10121 data.llParallelPorts.push_back(p);
10122 }
10123
10124 /* Audio adapter */
10125 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10126 if (FAILED(rc)) return rc;
10127
10128 /* Shared folders */
10129 data.llSharedFolders.clear();
10130 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10131 it != mHWData->mSharedFolders.end();
10132 ++it)
10133 {
10134 SharedFolder *pSF = *it;
10135 AutoCaller sfCaller(pSF);
10136 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10137 settings::SharedFolder sf;
10138 sf.strName = pSF->i_getName();
10139 sf.strHostPath = pSF->i_getHostPath();
10140 sf.fWritable = !!pSF->i_isWritable();
10141 sf.fAutoMount = !!pSF->i_isAutoMounted();
10142
10143 data.llSharedFolders.push_back(sf);
10144 }
10145
10146 // clipboard
10147 data.clipboardMode = mHWData->mClipboardMode;
10148
10149 // drag'n'drop
10150 data.dndMode = mHWData->mDnDMode;
10151
10152 /* Guest */
10153 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10154
10155 // IO settings
10156 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10157 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10158
10159 /* BandwidthControl (required) */
10160 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10161 if (FAILED(rc)) throw rc;
10162
10163 /* Host PCI devices */
10164 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10165 it != mHWData->mPCIDeviceAssignments.end();
10166 ++it)
10167 {
10168 ComObjPtr<PCIDeviceAttachment> pda = *it;
10169 settings::HostPCIDeviceAttachment hpda;
10170
10171 rc = pda->i_saveSettings(hpda);
10172 if (FAILED(rc)) throw rc;
10173
10174 data.pciAttachments.push_back(hpda);
10175 }
10176
10177
10178 // guest properties
10179 data.llGuestProperties.clear();
10180#ifdef VBOX_WITH_GUEST_PROPS
10181 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10182 it != mHWData->mGuestProperties.end();
10183 ++it)
10184 {
10185 HWData::GuestProperty property = it->second;
10186
10187 /* Remove transient guest properties at shutdown unless we
10188 * are saving state */
10189 if ( ( mData->mMachineState == MachineState_PoweredOff
10190 || mData->mMachineState == MachineState_Aborted
10191 || mData->mMachineState == MachineState_Teleported)
10192 && ( property.mFlags & guestProp::TRANSIENT
10193 || property.mFlags & guestProp::TRANSRESET))
10194 continue;
10195 settings::GuestProperty prop;
10196 prop.strName = it->first;
10197 prop.strValue = property.strValue;
10198 prop.timestamp = property.mTimestamp;
10199 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10200 guestProp::writeFlags(property.mFlags, szFlags);
10201 prop.strFlags = szFlags;
10202
10203 data.llGuestProperties.push_back(prop);
10204 }
10205
10206 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10207 /* I presume this doesn't require a backup(). */
10208 mData->mGuestPropertiesModified = FALSE;
10209#endif /* VBOX_WITH_GUEST_PROPS defined */
10210
10211 *pDbg = mHWData->mDebugging;
10212 *pAutostart = mHWData->mAutostart;
10213
10214 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10215 }
10216 catch(std::bad_alloc &)
10217 {
10218 return E_OUTOFMEMORY;
10219 }
10220
10221 AssertComRC(rc);
10222 return rc;
10223}
10224
10225/**
10226 * Saves the storage controller configuration.
10227 *
10228 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10229 */
10230HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10231{
10232 data.llStorageControllers.clear();
10233
10234 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10235 it != mStorageControllers->end();
10236 ++it)
10237 {
10238 HRESULT rc;
10239 ComObjPtr<StorageController> pCtl = *it;
10240
10241 settings::StorageController ctl;
10242 ctl.strName = pCtl->i_getName();
10243 ctl.controllerType = pCtl->i_getControllerType();
10244 ctl.storageBus = pCtl->i_getStorageBus();
10245 ctl.ulInstance = pCtl->i_getInstance();
10246 ctl.fBootable = pCtl->i_getBootable();
10247
10248 /* Save the port count. */
10249 ULONG portCount;
10250 rc = pCtl->COMGETTER(PortCount)(&portCount);
10251 ComAssertComRCRet(rc, rc);
10252 ctl.ulPortCount = portCount;
10253
10254 /* Save fUseHostIOCache */
10255 BOOL fUseHostIOCache;
10256 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10257 ComAssertComRCRet(rc, rc);
10258 ctl.fUseHostIOCache = !!fUseHostIOCache;
10259
10260 /* Save IDE emulation settings. */
10261 if (ctl.controllerType == StorageControllerType_IntelAhci)
10262 {
10263 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10264 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10265 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10266 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10267 )
10268 ComAssertComRCRet(rc, rc);
10269 }
10270
10271 /* save the devices now. */
10272 rc = i_saveStorageDevices(pCtl, ctl);
10273 ComAssertComRCRet(rc, rc);
10274
10275 data.llStorageControllers.push_back(ctl);
10276 }
10277
10278 return S_OK;
10279}
10280
10281/**
10282 * Saves the hard disk configuration.
10283 */
10284HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10285 settings::StorageController &data)
10286{
10287 MediaData::AttachmentList atts;
10288
10289 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10290 if (FAILED(rc)) return rc;
10291
10292 data.llAttachedDevices.clear();
10293 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10294 it != atts.end();
10295 ++it)
10296 {
10297 settings::AttachedDevice dev;
10298 IMediumAttachment *iA = *it;
10299 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10300 Medium *pMedium = pAttach->i_getMedium();
10301
10302 dev.deviceType = pAttach->i_getType();
10303 dev.lPort = pAttach->i_getPort();
10304 dev.lDevice = pAttach->i_getDevice();
10305 dev.fPassThrough = pAttach->i_getPassthrough();
10306 dev.fHotPluggable = pAttach->i_getHotPluggable();
10307 if (pMedium)
10308 {
10309 if (pMedium->i_isHostDrive())
10310 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10311 else
10312 dev.uuid = pMedium->i_getId();
10313 dev.fTempEject = pAttach->i_getTempEject();
10314 dev.fNonRotational = pAttach->i_getNonRotational();
10315 dev.fDiscard = pAttach->i_getDiscard();
10316 }
10317
10318 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10319
10320 data.llAttachedDevices.push_back(dev);
10321 }
10322
10323 return S_OK;
10324}
10325
10326/**
10327 * Saves machine state settings as defined by aFlags
10328 * (SaveSTS_* values).
10329 *
10330 * @param aFlags Combination of SaveSTS_* flags.
10331 *
10332 * @note Locks objects for writing.
10333 */
10334HRESULT Machine::i_saveStateSettings(int aFlags)
10335{
10336 if (aFlags == 0)
10337 return S_OK;
10338
10339 AutoCaller autoCaller(this);
10340 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10341
10342 /* This object's write lock is also necessary to serialize file access
10343 * (prevent concurrent reads and writes) */
10344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10345
10346 HRESULT rc = S_OK;
10347
10348 Assert(mData->pMachineConfigFile);
10349
10350 try
10351 {
10352 if (aFlags & SaveSTS_CurStateModified)
10353 mData->pMachineConfigFile->fCurrentStateModified = true;
10354
10355 if (aFlags & SaveSTS_StateFilePath)
10356 {
10357 if (!mSSData->strStateFilePath.isEmpty())
10358 /* try to make the file name relative to the settings file dir */
10359 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10360 else
10361 mData->pMachineConfigFile->strStateFile.setNull();
10362 }
10363
10364 if (aFlags & SaveSTS_StateTimeStamp)
10365 {
10366 Assert( mData->mMachineState != MachineState_Aborted
10367 || mSSData->strStateFilePath.isEmpty());
10368
10369 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10370
10371 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10372//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10373 }
10374
10375 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10376 }
10377 catch (...)
10378 {
10379 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10380 }
10381
10382 return rc;
10383}
10384
10385/**
10386 * Ensures that the given medium is added to a media registry. If this machine
10387 * was created with 4.0 or later, then the machine registry is used. Otherwise
10388 * the global VirtualBox media registry is used.
10389 *
10390 * Caller must NOT hold machine lock, media tree or any medium locks!
10391 *
10392 * @param pMedium
10393 */
10394void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10395{
10396 /* Paranoia checks: do not hold machine or media tree locks. */
10397 AssertReturnVoid(!isWriteLockOnCurrentThread());
10398 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10399
10400 ComObjPtr<Medium> pBase;
10401 {
10402 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10403 pBase = pMedium->i_getBase();
10404 }
10405
10406 /* Paranoia checks: do not hold medium locks. */
10407 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10408 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10409
10410 // decide which medium registry to use now that the medium is attached:
10411 Guid uuid;
10412 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10413 // machine XML is VirtualBox 4.0 or higher:
10414 uuid = i_getId(); // machine UUID
10415 else
10416 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10417
10418 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10419 mParent->i_markRegistryModified(uuid);
10420
10421 /* For more complex hard disk structures it can happen that the base
10422 * medium isn't yet associated with any medium registry. Do that now. */
10423 if (pMedium != pBase)
10424 {
10425 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10426 mParent->i_markRegistryModified(uuid);
10427 }
10428}
10429
10430/**
10431 * Creates differencing hard disks for all normal hard disks attached to this
10432 * machine and a new set of attachments to refer to created disks.
10433 *
10434 * Used when taking a snapshot or when deleting the current state. Gets called
10435 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10436 *
10437 * This method assumes that mMediaData contains the original hard disk attachments
10438 * it needs to create diffs for. On success, these attachments will be replaced
10439 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10440 * called to delete created diffs which will also rollback mMediaData and restore
10441 * whatever was backed up before calling this method.
10442 *
10443 * Attachments with non-normal hard disks are left as is.
10444 *
10445 * If @a aOnline is @c false then the original hard disks that require implicit
10446 * diffs will be locked for reading. Otherwise it is assumed that they are
10447 * already locked for writing (when the VM was started). Note that in the latter
10448 * case it is responsibility of the caller to lock the newly created diffs for
10449 * writing if this method succeeds.
10450 *
10451 * @param aProgress Progress object to run (must contain at least as
10452 * many operations left as the number of hard disks
10453 * attached).
10454 * @param aOnline Whether the VM was online prior to this operation.
10455 *
10456 * @note The progress object is not marked as completed, neither on success nor
10457 * on failure. This is a responsibility of the caller.
10458 *
10459 * @note Locks this object and the media tree for writing.
10460 */
10461HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10462 ULONG aWeight,
10463 bool aOnline)
10464{
10465 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10466
10467 AutoCaller autoCaller(this);
10468 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10469
10470 AutoMultiWriteLock2 alock(this->lockHandle(),
10471 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10472
10473 /* must be in a protective state because we release the lock below */
10474 AssertReturn( mData->mMachineState == MachineState_Saving
10475 || mData->mMachineState == MachineState_LiveSnapshotting
10476 || mData->mMachineState == MachineState_RestoringSnapshot
10477 || mData->mMachineState == MachineState_DeletingSnapshot
10478 , E_FAIL);
10479
10480 HRESULT rc = S_OK;
10481
10482 // use appropriate locked media map (online or offline)
10483 MediumLockListMap lockedMediaOffline;
10484 MediumLockListMap *lockedMediaMap;
10485 if (aOnline)
10486 lockedMediaMap = &mData->mSession.mLockedMedia;
10487 else
10488 lockedMediaMap = &lockedMediaOffline;
10489
10490 try
10491 {
10492 if (!aOnline)
10493 {
10494 /* lock all attached hard disks early to detect "in use"
10495 * situations before creating actual diffs */
10496 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10497 it != mMediaData->mAttachments.end();
10498 ++it)
10499 {
10500 MediumAttachment* pAtt = *it;
10501 if (pAtt->i_getType() == DeviceType_HardDisk)
10502 {
10503 Medium* pMedium = pAtt->i_getMedium();
10504 Assert(pMedium);
10505
10506 MediumLockList *pMediumLockList(new MediumLockList());
10507 alock.release();
10508 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10509 false /* fMediumLockWrite */,
10510 NULL,
10511 *pMediumLockList);
10512 alock.acquire();
10513 if (FAILED(rc))
10514 {
10515 delete pMediumLockList;
10516 throw rc;
10517 }
10518 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10519 if (FAILED(rc))
10520 {
10521 throw setError(rc,
10522 tr("Collecting locking information for all attached media failed"));
10523 }
10524 }
10525 }
10526
10527 /* Now lock all media. If this fails, nothing is locked. */
10528 alock.release();
10529 rc = lockedMediaMap->Lock();
10530 alock.acquire();
10531 if (FAILED(rc))
10532 {
10533 throw setError(rc,
10534 tr("Locking of attached media failed"));
10535 }
10536 }
10537
10538 /* remember the current list (note that we don't use backup() since
10539 * mMediaData may be already backed up) */
10540 MediaData::AttachmentList atts = mMediaData->mAttachments;
10541
10542 /* start from scratch */
10543 mMediaData->mAttachments.clear();
10544
10545 /* go through remembered attachments and create diffs for normal hard
10546 * disks and attach them */
10547 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10548 it != atts.end();
10549 ++it)
10550 {
10551 MediumAttachment* pAtt = *it;
10552
10553 DeviceType_T devType = pAtt->i_getType();
10554 Medium* pMedium = pAtt->i_getMedium();
10555
10556 if ( devType != DeviceType_HardDisk
10557 || pMedium == NULL
10558 || pMedium->i_getType() != MediumType_Normal)
10559 {
10560 /* copy the attachment as is */
10561
10562 /** @todo the progress object created in Console::TakeSnaphot
10563 * only expects operations for hard disks. Later other
10564 * device types need to show up in the progress as well. */
10565 if (devType == DeviceType_HardDisk)
10566 {
10567 if (pMedium == NULL)
10568 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10569 aWeight); // weight
10570 else
10571 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10572 pMedium->i_getBase()->i_getName().c_str()).raw(),
10573 aWeight); // weight
10574 }
10575
10576 mMediaData->mAttachments.push_back(pAtt);
10577 continue;
10578 }
10579
10580 /* need a diff */
10581 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10582 pMedium->i_getBase()->i_getName().c_str()).raw(),
10583 aWeight); // weight
10584
10585 Utf8Str strFullSnapshotFolder;
10586 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10587
10588 ComObjPtr<Medium> diff;
10589 diff.createObject();
10590 // store the diff in the same registry as the parent
10591 // (this cannot fail here because we can't create implicit diffs for
10592 // unregistered images)
10593 Guid uuidRegistryParent;
10594 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10595 Assert(fInRegistry); NOREF(fInRegistry);
10596 rc = diff->init(mParent,
10597 pMedium->i_getPreferredDiffFormat(),
10598 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10599 uuidRegistryParent,
10600 DeviceType_HardDisk);
10601 if (FAILED(rc)) throw rc;
10602
10603 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10604 * the push_back? Looks like we're going to release medium with the
10605 * wrong kind of lock (general issue with if we fail anywhere at all)
10606 * and an orphaned VDI in the snapshots folder. */
10607
10608 /* update the appropriate lock list */
10609 MediumLockList *pMediumLockList;
10610 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10611 AssertComRCThrowRC(rc);
10612 if (aOnline)
10613 {
10614 alock.release();
10615 /* The currently attached medium will be read-only, change
10616 * the lock type to read. */
10617 rc = pMediumLockList->Update(pMedium, false);
10618 alock.acquire();
10619 AssertComRCThrowRC(rc);
10620 }
10621
10622 /* release the locks before the potentially lengthy operation */
10623 alock.release();
10624 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10625 pMediumLockList,
10626 NULL /* aProgress */,
10627 true /* aWait */);
10628 alock.acquire();
10629 if (FAILED(rc)) throw rc;
10630
10631 /* actual lock list update is done in Medium::commitMedia */
10632
10633 rc = diff->i_addBackReference(mData->mUuid);
10634 AssertComRCThrowRC(rc);
10635
10636 /* add a new attachment */
10637 ComObjPtr<MediumAttachment> attachment;
10638 attachment.createObject();
10639 rc = attachment->init(this,
10640 diff,
10641 pAtt->i_getControllerName(),
10642 pAtt->i_getPort(),
10643 pAtt->i_getDevice(),
10644 DeviceType_HardDisk,
10645 true /* aImplicit */,
10646 false /* aPassthrough */,
10647 false /* aTempEject */,
10648 pAtt->i_getNonRotational(),
10649 pAtt->i_getDiscard(),
10650 pAtt->i_getHotPluggable(),
10651 pAtt->i_getBandwidthGroup());
10652 if (FAILED(rc)) throw rc;
10653
10654 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10655 AssertComRCThrowRC(rc);
10656 mMediaData->mAttachments.push_back(attachment);
10657 }
10658 }
10659 catch (HRESULT aRC) { rc = aRC; }
10660
10661 /* unlock all hard disks we locked when there is no VM */
10662 if (!aOnline)
10663 {
10664 ErrorInfoKeeper eik;
10665
10666 HRESULT rc1 = lockedMediaMap->Clear();
10667 AssertComRC(rc1);
10668 }
10669
10670 return rc;
10671}
10672
10673/**
10674 * Deletes implicit differencing hard disks created either by
10675 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10676 *
10677 * Note that to delete hard disks created by #AttachDevice() this method is
10678 * called from #fixupMedia() when the changes are rolled back.
10679 *
10680 * @note Locks this object and the media tree for writing.
10681 */
10682HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10683{
10684 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10685
10686 AutoCaller autoCaller(this);
10687 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10688
10689 AutoMultiWriteLock2 alock(this->lockHandle(),
10690 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10691
10692 /* We absolutely must have backed up state. */
10693 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10694
10695 /* Check if there are any implicitly created diff images. */
10696 bool fImplicitDiffs = false;
10697 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10698 it != mMediaData->mAttachments.end();
10699 ++it)
10700 {
10701 const ComObjPtr<MediumAttachment> &pAtt = *it;
10702 if (pAtt->i_isImplicit())
10703 {
10704 fImplicitDiffs = true;
10705 break;
10706 }
10707 }
10708 /* If there is nothing to do, leave early. This saves lots of image locking
10709 * effort. It also avoids a MachineStateChanged event without real reason.
10710 * This is important e.g. when loading a VM config, because there should be
10711 * no events. Otherwise API clients can become thoroughly confused for
10712 * inaccessible VMs (the code for loading VM configs uses this method for
10713 * cleanup if the config makes no sense), as they take such events as an
10714 * indication that the VM is alive, and they would force the VM config to
10715 * be reread, leading to an endless loop. */
10716 if (!fImplicitDiffs)
10717 return S_OK;
10718
10719 HRESULT rc = S_OK;
10720 MachineState_T oldState = mData->mMachineState;
10721
10722 /* will release the lock before the potentially lengthy operation,
10723 * so protect with the special state (unless already protected) */
10724 if ( oldState != MachineState_Saving
10725 && oldState != MachineState_LiveSnapshotting
10726 && oldState != MachineState_RestoringSnapshot
10727 && oldState != MachineState_DeletingSnapshot
10728 && oldState != MachineState_DeletingSnapshotOnline
10729 && oldState != MachineState_DeletingSnapshotPaused
10730 )
10731 i_setMachineState(MachineState_SettingUp);
10732
10733 // use appropriate locked media map (online or offline)
10734 MediumLockListMap lockedMediaOffline;
10735 MediumLockListMap *lockedMediaMap;
10736 if (aOnline)
10737 lockedMediaMap = &mData->mSession.mLockedMedia;
10738 else
10739 lockedMediaMap = &lockedMediaOffline;
10740
10741 try
10742 {
10743 if (!aOnline)
10744 {
10745 /* lock all attached hard disks early to detect "in use"
10746 * situations before deleting actual diffs */
10747 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10748 it != mMediaData->mAttachments.end();
10749 ++it)
10750 {
10751 MediumAttachment* pAtt = *it;
10752 if (pAtt->i_getType() == DeviceType_HardDisk)
10753 {
10754 Medium* pMedium = pAtt->i_getMedium();
10755 Assert(pMedium);
10756
10757 MediumLockList *pMediumLockList(new MediumLockList());
10758 alock.release();
10759 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10760 false /* fMediumLockWrite */,
10761 NULL,
10762 *pMediumLockList);
10763 alock.acquire();
10764
10765 if (FAILED(rc))
10766 {
10767 delete pMediumLockList;
10768 throw rc;
10769 }
10770
10771 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10772 if (FAILED(rc))
10773 throw rc;
10774 }
10775 }
10776
10777 if (FAILED(rc))
10778 throw rc;
10779 } // end of offline
10780
10781 /* Lock lists are now up to date and include implicitly created media */
10782
10783 /* Go through remembered attachments and delete all implicitly created
10784 * diffs and fix up the attachment information */
10785 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10786 MediaData::AttachmentList implicitAtts;
10787 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10788 it != mMediaData->mAttachments.end();
10789 ++it)
10790 {
10791 ComObjPtr<MediumAttachment> pAtt = *it;
10792 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10793 if (pMedium.isNull())
10794 continue;
10795
10796 // Implicit attachments go on the list for deletion and back references are removed.
10797 if (pAtt->i_isImplicit())
10798 {
10799 /* Deassociate and mark for deletion */
10800 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10801 rc = pMedium->i_removeBackReference(mData->mUuid);
10802 if (FAILED(rc))
10803 throw rc;
10804 implicitAtts.push_back(pAtt);
10805 continue;
10806 }
10807
10808 /* Was this medium attached before? */
10809 if (!i_findAttachment(oldAtts, pMedium))
10810 {
10811 /* no: de-associate */
10812 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10813 rc = pMedium->i_removeBackReference(mData->mUuid);
10814 if (FAILED(rc))
10815 throw rc;
10816 continue;
10817 }
10818 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10819 }
10820
10821 /* If there are implicit attachments to delete, throw away the lock
10822 * map contents (which will unlock all media) since the medium
10823 * attachments will be rolled back. Below we need to completely
10824 * recreate the lock map anyway since it is infinitely complex to
10825 * do this incrementally (would need reconstructing each attachment
10826 * change, which would be extremely hairy). */
10827 if (implicitAtts.size() != 0)
10828 {
10829 ErrorInfoKeeper eik;
10830
10831 HRESULT rc1 = lockedMediaMap->Clear();
10832 AssertComRC(rc1);
10833 }
10834
10835 /* rollback hard disk changes */
10836 mMediaData.rollback();
10837
10838 MultiResult mrc(S_OK);
10839
10840 // Delete unused implicit diffs.
10841 if (implicitAtts.size() != 0)
10842 {
10843 alock.release();
10844
10845 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10846 {
10847 // Remove medium associated with this attachment.
10848 ComObjPtr<MediumAttachment> pAtt = *it;
10849 Assert(pAtt);
10850 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10851 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10852 Assert(pMedium);
10853
10854 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10855 // continue on delete failure, just collect error messages
10856 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10857 pMedium->i_getLocationFull().c_str() ));
10858 mrc = rc;
10859 }
10860
10861 alock.acquire();
10862
10863 /* if there is a VM recreate media lock map as mentioned above,
10864 * otherwise it is a waste of time and we leave things unlocked */
10865 if (aOnline)
10866 {
10867 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10868 /* must never be NULL, but better safe than sorry */
10869 if (!pMachine.isNull())
10870 {
10871 alock.release();
10872 rc = mData->mSession.mMachine->i_lockMedia();
10873 alock.acquire();
10874 if (FAILED(rc))
10875 throw rc;
10876 }
10877 }
10878 }
10879 }
10880 catch (HRESULT aRC) {rc = aRC;}
10881
10882 if (mData->mMachineState == MachineState_SettingUp)
10883 i_setMachineState(oldState);
10884
10885 /* unlock all hard disks we locked when there is no VM */
10886 if (!aOnline)
10887 {
10888 ErrorInfoKeeper eik;
10889
10890 HRESULT rc1 = lockedMediaMap->Clear();
10891 AssertComRC(rc1);
10892 }
10893
10894 return rc;
10895}
10896
10897
10898/**
10899 * Looks through the given list of media attachments for one with the given parameters
10900 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10901 * can be searched as well if needed.
10902 *
10903 * @param list
10904 * @param aControllerName
10905 * @param aControllerPort
10906 * @param aDevice
10907 * @return
10908 */
10909MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10910 IN_BSTR aControllerName,
10911 LONG aControllerPort,
10912 LONG aDevice)
10913{
10914 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10915 {
10916 MediumAttachment *pAttach = *it;
10917 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10918 return pAttach;
10919 }
10920
10921 return NULL;
10922}
10923
10924/**
10925 * Looks through the given list of media attachments for one with the given parameters
10926 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10927 * can be searched as well if needed.
10928 *
10929 * @param list
10930 * @param aControllerName
10931 * @param aControllerPort
10932 * @param aDevice
10933 * @return
10934 */
10935MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10936 ComObjPtr<Medium> pMedium)
10937{
10938 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10939 {
10940 MediumAttachment *pAttach = *it;
10941 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10942 if (pMediumThis == pMedium)
10943 return pAttach;
10944 }
10945
10946 return NULL;
10947}
10948
10949/**
10950 * Looks through the given list of media attachments for one with the given parameters
10951 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10952 * can be searched as well if needed.
10953 *
10954 * @param list
10955 * @param aControllerName
10956 * @param aControllerPort
10957 * @param aDevice
10958 * @return
10959 */
10960MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10961 Guid &id)
10962{
10963 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10964 {
10965 MediumAttachment *pAttach = *it;
10966 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10967 if (pMediumThis->i_getId() == id)
10968 return pAttach;
10969 }
10970
10971 return NULL;
10972}
10973
10974/**
10975 * Main implementation for Machine::DetachDevice. This also gets called
10976 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10977 *
10978 * @param pAttach Medium attachment to detach.
10979 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10980 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10981 * SnapshotMachine, and this must be its snapshot.
10982 * @return
10983 */
10984HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10985 AutoWriteLock &writeLock,
10986 Snapshot *pSnapshot)
10987{
10988 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10989 DeviceType_T mediumType = pAttach->i_getType();
10990
10991 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10992
10993 if (pAttach->i_isImplicit())
10994 {
10995 /* attempt to implicitly delete the implicitly created diff */
10996
10997 /// @todo move the implicit flag from MediumAttachment to Medium
10998 /// and forbid any hard disk operation when it is implicit. Or maybe
10999 /// a special media state for it to make it even more simple.
11000
11001 Assert(mMediaData.isBackedUp());
11002
11003 /* will release the lock before the potentially lengthy operation, so
11004 * protect with the special state */
11005 MachineState_T oldState = mData->mMachineState;
11006 i_setMachineState(MachineState_SettingUp);
11007
11008 writeLock.release();
11009
11010 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11011 true /*aWait*/);
11012
11013 writeLock.acquire();
11014
11015 i_setMachineState(oldState);
11016
11017 if (FAILED(rc)) return rc;
11018 }
11019
11020 i_setModified(IsModified_Storage);
11021 mMediaData.backup();
11022 mMediaData->mAttachments.remove(pAttach);
11023
11024 if (!oldmedium.isNull())
11025 {
11026 // if this is from a snapshot, do not defer detachment to commitMedia()
11027 if (pSnapshot)
11028 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11029 // else if non-hard disk media, do not defer detachment to commitMedia() either
11030 else if (mediumType != DeviceType_HardDisk)
11031 oldmedium->i_removeBackReference(mData->mUuid);
11032 }
11033
11034 return S_OK;
11035}
11036
11037/**
11038 * Goes thru all media of the given list and
11039 *
11040 * 1) calls i_detachDevice() on each of them for this machine and
11041 * 2) adds all Medium objects found in the process to the given list,
11042 * depending on cleanupMode.
11043 *
11044 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11045 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11046 * media to the list.
11047 *
11048 * This gets called from Machine::Unregister, both for the actual Machine and
11049 * the SnapshotMachine objects that might be found in the snapshots.
11050 *
11051 * Requires caller and locking. The machine lock must be passed in because it
11052 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11053 *
11054 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11055 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11056 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11057 * Full, then all media get added;
11058 * otherwise no media get added.
11059 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11060 * @return
11061 */
11062HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11063 Snapshot *pSnapshot,
11064 CleanupMode_T cleanupMode,
11065 MediaList &llMedia)
11066{
11067 Assert(isWriteLockOnCurrentThread());
11068
11069 HRESULT rc;
11070
11071 // make a temporary list because i_detachDevice invalidates iterators into
11072 // mMediaData->mAttachments
11073 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11074
11075 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11076 {
11077 ComObjPtr<MediumAttachment> &pAttach = *it;
11078 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11079
11080 if (!pMedium.isNull())
11081 {
11082 AutoCaller mac(pMedium);
11083 if (FAILED(mac.rc())) return mac.rc();
11084 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11085 DeviceType_T devType = pMedium->i_getDeviceType();
11086 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11087 && devType == DeviceType_HardDisk)
11088 || (cleanupMode == CleanupMode_Full)
11089 )
11090 {
11091 llMedia.push_back(pMedium);
11092 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11093 /* Not allowed to keep this lock as below we need the parent
11094 * medium lock, and the lock order is parent to child. */
11095 lock.release();
11096 /*
11097 * Search for medias which are not attached to any machine, but
11098 * in the chain to an attached disk. Mediums are only consided
11099 * if they are:
11100 * - have only one child
11101 * - no references to any machines
11102 * - are of normal medium type
11103 */
11104 while (!pParent.isNull())
11105 {
11106 AutoCaller mac1(pParent);
11107 if (FAILED(mac1.rc())) return mac1.rc();
11108 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11109 if (pParent->i_getChildren().size() == 1)
11110 {
11111 if ( pParent->i_getMachineBackRefCount() == 0
11112 && pParent->i_getType() == MediumType_Normal
11113 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11114 llMedia.push_back(pParent);
11115 }
11116 else
11117 break;
11118 pParent = pParent->i_getParent();
11119 }
11120 }
11121 }
11122
11123 // real machine: then we need to use the proper method
11124 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11125
11126 if (FAILED(rc))
11127 return rc;
11128 }
11129
11130 return S_OK;
11131}
11132
11133/**
11134 * Perform deferred hard disk detachments.
11135 *
11136 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11137 * backed up).
11138 *
11139 * If @a aOnline is @c true then this method will also unlock the old hard disks
11140 * for which the new implicit diffs were created and will lock these new diffs for
11141 * writing.
11142 *
11143 * @param aOnline Whether the VM was online prior to this operation.
11144 *
11145 * @note Locks this object for writing!
11146 */
11147void Machine::i_commitMedia(bool aOnline /*= false*/)
11148{
11149 AutoCaller autoCaller(this);
11150 AssertComRCReturnVoid(autoCaller.rc());
11151
11152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11153
11154 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11155
11156 HRESULT rc = S_OK;
11157
11158 /* no attach/detach operations -- nothing to do */
11159 if (!mMediaData.isBackedUp())
11160 return;
11161
11162 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11163 bool fMediaNeedsLocking = false;
11164
11165 /* enumerate new attachments */
11166 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11167 it != mMediaData->mAttachments.end();
11168 ++it)
11169 {
11170 MediumAttachment *pAttach = *it;
11171
11172 pAttach->i_commit();
11173
11174 Medium* pMedium = pAttach->i_getMedium();
11175 bool fImplicit = pAttach->i_isImplicit();
11176
11177 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11178 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11179 fImplicit));
11180
11181 /** @todo convert all this Machine-based voodoo to MediumAttachment
11182 * based commit logic. */
11183 if (fImplicit)
11184 {
11185 /* convert implicit attachment to normal */
11186 pAttach->i_setImplicit(false);
11187
11188 if ( aOnline
11189 && pMedium
11190 && pAttach->i_getType() == DeviceType_HardDisk
11191 )
11192 {
11193 /* update the appropriate lock list */
11194 MediumLockList *pMediumLockList;
11195 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11196 AssertComRC(rc);
11197 if (pMediumLockList)
11198 {
11199 /* unlock if there's a need to change the locking */
11200 if (!fMediaNeedsLocking)
11201 {
11202 rc = mData->mSession.mLockedMedia.Unlock();
11203 AssertComRC(rc);
11204 fMediaNeedsLocking = true;
11205 }
11206 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11207 AssertComRC(rc);
11208 rc = pMediumLockList->Append(pMedium, true);
11209 AssertComRC(rc);
11210 }
11211 }
11212
11213 continue;
11214 }
11215
11216 if (pMedium)
11217 {
11218 /* was this medium attached before? */
11219 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11220 {
11221 MediumAttachment *pOldAttach = *oldIt;
11222 if (pOldAttach->i_getMedium() == pMedium)
11223 {
11224 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11225
11226 /* yes: remove from old to avoid de-association */
11227 oldAtts.erase(oldIt);
11228 break;
11229 }
11230 }
11231 }
11232 }
11233
11234 /* enumerate remaining old attachments and de-associate from the
11235 * current machine state */
11236 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11237 {
11238 MediumAttachment *pAttach = *it;
11239 Medium* pMedium = pAttach->i_getMedium();
11240
11241 /* Detach only hard disks, since DVD/floppy media is detached
11242 * instantly in MountMedium. */
11243 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11244 {
11245 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11246
11247 /* now de-associate from the current machine state */
11248 rc = pMedium->i_removeBackReference(mData->mUuid);
11249 AssertComRC(rc);
11250
11251 if (aOnline)
11252 {
11253 /* unlock since medium is not used anymore */
11254 MediumLockList *pMediumLockList;
11255 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11256 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11257 {
11258 /* this happens for online snapshots, there the attachment
11259 * is changing, but only to a diff image created under
11260 * the old one, so there is no separate lock list */
11261 Assert(!pMediumLockList);
11262 }
11263 else
11264 {
11265 AssertComRC(rc);
11266 if (pMediumLockList)
11267 {
11268 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11269 AssertComRC(rc);
11270 }
11271 }
11272 }
11273 }
11274 }
11275
11276 /* take media locks again so that the locking state is consistent */
11277 if (fMediaNeedsLocking)
11278 {
11279 Assert(aOnline);
11280 rc = mData->mSession.mLockedMedia.Lock();
11281 AssertComRC(rc);
11282 }
11283
11284 /* commit the hard disk changes */
11285 mMediaData.commit();
11286
11287 if (i_isSessionMachine())
11288 {
11289 /*
11290 * Update the parent machine to point to the new owner.
11291 * This is necessary because the stored parent will point to the
11292 * session machine otherwise and cause crashes or errors later
11293 * when the session machine gets invalid.
11294 */
11295 /** @todo Change the MediumAttachment class to behave like any other
11296 * class in this regard by creating peer MediumAttachment
11297 * objects for session machines and share the data with the peer
11298 * machine.
11299 */
11300 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11301 it != mMediaData->mAttachments.end();
11302 ++it)
11303 (*it)->i_updateParentMachine(mPeer);
11304
11305 /* attach new data to the primary machine and reshare it */
11306 mPeer->mMediaData.attach(mMediaData);
11307 }
11308
11309 return;
11310}
11311
11312/**
11313 * Perform deferred deletion of implicitly created diffs.
11314 *
11315 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11316 * backed up).
11317 *
11318 * @note Locks this object for writing!
11319 */
11320void Machine::i_rollbackMedia()
11321{
11322 AutoCaller autoCaller(this);
11323 AssertComRCReturnVoid(autoCaller.rc());
11324
11325 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11326 LogFlowThisFunc(("Entering rollbackMedia\n"));
11327
11328 HRESULT rc = S_OK;
11329
11330 /* no attach/detach operations -- nothing to do */
11331 if (!mMediaData.isBackedUp())
11332 return;
11333
11334 /* enumerate new attachments */
11335 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11336 it != mMediaData->mAttachments.end();
11337 ++it)
11338 {
11339 MediumAttachment *pAttach = *it;
11340 /* Fix up the backrefs for DVD/floppy media. */
11341 if (pAttach->i_getType() != DeviceType_HardDisk)
11342 {
11343 Medium* pMedium = pAttach->i_getMedium();
11344 if (pMedium)
11345 {
11346 rc = pMedium->i_removeBackReference(mData->mUuid);
11347 AssertComRC(rc);
11348 }
11349 }
11350
11351 (*it)->i_rollback();
11352
11353 pAttach = *it;
11354 /* Fix up the backrefs for DVD/floppy media. */
11355 if (pAttach->i_getType() != DeviceType_HardDisk)
11356 {
11357 Medium* pMedium = pAttach->i_getMedium();
11358 if (pMedium)
11359 {
11360 rc = pMedium->i_addBackReference(mData->mUuid);
11361 AssertComRC(rc);
11362 }
11363 }
11364 }
11365
11366 /** @todo convert all this Machine-based voodoo to MediumAttachment
11367 * based rollback logic. */
11368 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11369
11370 return;
11371}
11372
11373/**
11374 * Returns true if the settings file is located in the directory named exactly
11375 * as the machine; this means, among other things, that the machine directory
11376 * should be auto-renamed.
11377 *
11378 * @param aSettingsDir if not NULL, the full machine settings file directory
11379 * name will be assigned there.
11380 *
11381 * @note Doesn't lock anything.
11382 * @note Not thread safe (must be called from this object's lock).
11383 */
11384bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11385{
11386 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11387 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11388 if (aSettingsDir)
11389 *aSettingsDir = strMachineDirName;
11390 strMachineDirName.stripPath(); // vmname
11391 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11392 strConfigFileOnly.stripPath() // vmname.vbox
11393 .stripSuffix(); // vmname
11394 /** @todo hack, make somehow use of ComposeMachineFilename */
11395 if (mUserData->s.fDirectoryIncludesUUID)
11396 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11397
11398 AssertReturn(!strMachineDirName.isEmpty(), false);
11399 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11400
11401 return strMachineDirName == strConfigFileOnly;
11402}
11403
11404/**
11405 * Discards all changes to machine settings.
11406 *
11407 * @param aNotify Whether to notify the direct session about changes or not.
11408 *
11409 * @note Locks objects for writing!
11410 */
11411void Machine::i_rollback(bool aNotify)
11412{
11413 AutoCaller autoCaller(this);
11414 AssertComRCReturn(autoCaller.rc(), (void)0);
11415
11416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11417
11418 if (!mStorageControllers.isNull())
11419 {
11420 if (mStorageControllers.isBackedUp())
11421 {
11422 /* unitialize all new devices (absent in the backed up list). */
11423 StorageControllerList::const_iterator it = mStorageControllers->begin();
11424 StorageControllerList *backedList = mStorageControllers.backedUpData();
11425 while (it != mStorageControllers->end())
11426 {
11427 if ( std::find(backedList->begin(), backedList->end(), *it)
11428 == backedList->end()
11429 )
11430 {
11431 (*it)->uninit();
11432 }
11433 ++it;
11434 }
11435
11436 /* restore the list */
11437 mStorageControllers.rollback();
11438 }
11439
11440 /* rollback any changes to devices after restoring the list */
11441 if (mData->flModifications & IsModified_Storage)
11442 {
11443 StorageControllerList::const_iterator it = mStorageControllers->begin();
11444 while (it != mStorageControllers->end())
11445 {
11446 (*it)->i_rollback();
11447 ++it;
11448 }
11449 }
11450 }
11451
11452 if (!mUSBControllers.isNull())
11453 {
11454 if (mUSBControllers.isBackedUp())
11455 {
11456 /* unitialize all new devices (absent in the backed up list). */
11457 USBControllerList::const_iterator it = mUSBControllers->begin();
11458 USBControllerList *backedList = mUSBControllers.backedUpData();
11459 while (it != mUSBControllers->end())
11460 {
11461 if ( std::find(backedList->begin(), backedList->end(), *it)
11462 == backedList->end()
11463 )
11464 {
11465 (*it)->uninit();
11466 }
11467 ++it;
11468 }
11469
11470 /* restore the list */
11471 mUSBControllers.rollback();
11472 }
11473
11474 /* rollback any changes to devices after restoring the list */
11475 if (mData->flModifications & IsModified_USB)
11476 {
11477 USBControllerList::const_iterator it = mUSBControllers->begin();
11478 while (it != mUSBControllers->end())
11479 {
11480 (*it)->i_rollback();
11481 ++it;
11482 }
11483 }
11484 }
11485
11486 mUserData.rollback();
11487
11488 mHWData.rollback();
11489
11490 if (mData->flModifications & IsModified_Storage)
11491 i_rollbackMedia();
11492
11493 if (mBIOSSettings)
11494 mBIOSSettings->i_rollback();
11495
11496 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11497 mVRDEServer->i_rollback();
11498
11499 if (mAudioAdapter)
11500 mAudioAdapter->i_rollback();
11501
11502 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11503 mUSBDeviceFilters->i_rollback();
11504
11505 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11506 mBandwidthControl->i_rollback();
11507
11508 if (!mHWData.isNull())
11509 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11510 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11511 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11512 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11513
11514 if (mData->flModifications & IsModified_NetworkAdapters)
11515 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11516 if ( mNetworkAdapters[slot]
11517 && mNetworkAdapters[slot]->i_isModified())
11518 {
11519 mNetworkAdapters[slot]->i_rollback();
11520 networkAdapters[slot] = mNetworkAdapters[slot];
11521 }
11522
11523 if (mData->flModifications & IsModified_SerialPorts)
11524 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11525 if ( mSerialPorts[slot]
11526 && mSerialPorts[slot]->i_isModified())
11527 {
11528 mSerialPorts[slot]->i_rollback();
11529 serialPorts[slot] = mSerialPorts[slot];
11530 }
11531
11532 if (mData->flModifications & IsModified_ParallelPorts)
11533 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11534 if ( mParallelPorts[slot]
11535 && mParallelPorts[slot]->i_isModified())
11536 {
11537 mParallelPorts[slot]->i_rollback();
11538 parallelPorts[slot] = mParallelPorts[slot];
11539 }
11540
11541 if (aNotify)
11542 {
11543 /* inform the direct session about changes */
11544
11545 ComObjPtr<Machine> that = this;
11546 uint32_t flModifications = mData->flModifications;
11547 alock.release();
11548
11549 if (flModifications & IsModified_SharedFolders)
11550 that->i_onSharedFolderChange();
11551
11552 if (flModifications & IsModified_VRDEServer)
11553 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11554 if (flModifications & IsModified_USB)
11555 that->i_onUSBControllerChange();
11556
11557 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11558 if (networkAdapters[slot])
11559 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11560 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11561 if (serialPorts[slot])
11562 that->i_onSerialPortChange(serialPorts[slot]);
11563 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11564 if (parallelPorts[slot])
11565 that->i_onParallelPortChange(parallelPorts[slot]);
11566
11567 if (flModifications & IsModified_Storage)
11568 that->i_onStorageControllerChange();
11569
11570#if 0
11571 if (flModifications & IsModified_BandwidthControl)
11572 that->onBandwidthControlChange();
11573#endif
11574 }
11575}
11576
11577/**
11578 * Commits all the changes to machine settings.
11579 *
11580 * Note that this operation is supposed to never fail.
11581 *
11582 * @note Locks this object and children for writing.
11583 */
11584void Machine::i_commit()
11585{
11586 AutoCaller autoCaller(this);
11587 AssertComRCReturnVoid(autoCaller.rc());
11588
11589 AutoCaller peerCaller(mPeer);
11590 AssertComRCReturnVoid(peerCaller.rc());
11591
11592 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11593
11594 /*
11595 * use safe commit to ensure Snapshot machines (that share mUserData)
11596 * will still refer to a valid memory location
11597 */
11598 mUserData.commitCopy();
11599
11600 mHWData.commit();
11601
11602 if (mMediaData.isBackedUp())
11603 i_commitMedia(Global::IsOnline(mData->mMachineState));
11604
11605 mBIOSSettings->i_commit();
11606 mVRDEServer->i_commit();
11607 mAudioAdapter->i_commit();
11608 mUSBDeviceFilters->i_commit();
11609 mBandwidthControl->i_commit();
11610
11611 /* Since mNetworkAdapters is a list which might have been changed (resized)
11612 * without using the Backupable<> template we need to handle the copying
11613 * of the list entries manually, including the creation of peers for the
11614 * new objects. */
11615 bool commitNetworkAdapters = false;
11616 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11617 if (mPeer)
11618 {
11619 /* commit everything, even the ones which will go away */
11620 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11621 mNetworkAdapters[slot]->i_commit();
11622 /* copy over the new entries, creating a peer and uninit the original */
11623 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11624 for (size_t slot = 0; slot < newSize; slot++)
11625 {
11626 /* look if this adapter has a peer device */
11627 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11628 if (!peer)
11629 {
11630 /* no peer means the adapter is a newly created one;
11631 * create a peer owning data this data share it with */
11632 peer.createObject();
11633 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11634 }
11635 mPeer->mNetworkAdapters[slot] = peer;
11636 }
11637 /* uninit any no longer needed network adapters */
11638 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11639 mNetworkAdapters[slot]->uninit();
11640 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11641 {
11642 if (mPeer->mNetworkAdapters[slot])
11643 mPeer->mNetworkAdapters[slot]->uninit();
11644 }
11645 /* Keep the original network adapter count until this point, so that
11646 * discarding a chipset type change will not lose settings. */
11647 mNetworkAdapters.resize(newSize);
11648 mPeer->mNetworkAdapters.resize(newSize);
11649 }
11650 else
11651 {
11652 /* we have no peer (our parent is the newly created machine);
11653 * just commit changes to the network adapters */
11654 commitNetworkAdapters = true;
11655 }
11656 if (commitNetworkAdapters)
11657 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11658 mNetworkAdapters[slot]->i_commit();
11659
11660 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11661 mSerialPorts[slot]->i_commit();
11662 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11663 mParallelPorts[slot]->i_commit();
11664
11665 bool commitStorageControllers = false;
11666
11667 if (mStorageControllers.isBackedUp())
11668 {
11669 mStorageControllers.commit();
11670
11671 if (mPeer)
11672 {
11673 /* Commit all changes to new controllers (this will reshare data with
11674 * peers for those who have peers) */
11675 StorageControllerList *newList = new StorageControllerList();
11676 StorageControllerList::const_iterator it = mStorageControllers->begin();
11677 while (it != mStorageControllers->end())
11678 {
11679 (*it)->i_commit();
11680
11681 /* look if this controller has a peer device */
11682 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11683 if (!peer)
11684 {
11685 /* no peer means the device is a newly created one;
11686 * create a peer owning data this device share it with */
11687 peer.createObject();
11688 peer->init(mPeer, *it, true /* aReshare */);
11689 }
11690 else
11691 {
11692 /* remove peer from the old list */
11693 mPeer->mStorageControllers->remove(peer);
11694 }
11695 /* and add it to the new list */
11696 newList->push_back(peer);
11697
11698 ++it;
11699 }
11700
11701 /* uninit old peer's controllers that are left */
11702 it = mPeer->mStorageControllers->begin();
11703 while (it != mPeer->mStorageControllers->end())
11704 {
11705 (*it)->uninit();
11706 ++it;
11707 }
11708
11709 /* attach new list of controllers to our peer */
11710 mPeer->mStorageControllers.attach(newList);
11711 }
11712 else
11713 {
11714 /* we have no peer (our parent is the newly created machine);
11715 * just commit changes to devices */
11716 commitStorageControllers = true;
11717 }
11718 }
11719 else
11720 {
11721 /* the list of controllers itself is not changed,
11722 * just commit changes to controllers themselves */
11723 commitStorageControllers = true;
11724 }
11725
11726 if (commitStorageControllers)
11727 {
11728 StorageControllerList::const_iterator it = mStorageControllers->begin();
11729 while (it != mStorageControllers->end())
11730 {
11731 (*it)->i_commit();
11732 ++it;
11733 }
11734 }
11735
11736 bool commitUSBControllers = false;
11737
11738 if (mUSBControllers.isBackedUp())
11739 {
11740 mUSBControllers.commit();
11741
11742 if (mPeer)
11743 {
11744 /* Commit all changes to new controllers (this will reshare data with
11745 * peers for those who have peers) */
11746 USBControllerList *newList = new USBControllerList();
11747 USBControllerList::const_iterator it = mUSBControllers->begin();
11748 while (it != mUSBControllers->end())
11749 {
11750 (*it)->i_commit();
11751
11752 /* look if this controller has a peer device */
11753 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11754 if (!peer)
11755 {
11756 /* no peer means the device is a newly created one;
11757 * create a peer owning data this device share it with */
11758 peer.createObject();
11759 peer->init(mPeer, *it, true /* aReshare */);
11760 }
11761 else
11762 {
11763 /* remove peer from the old list */
11764 mPeer->mUSBControllers->remove(peer);
11765 }
11766 /* and add it to the new list */
11767 newList->push_back(peer);
11768
11769 ++it;
11770 }
11771
11772 /* uninit old peer's controllers that are left */
11773 it = mPeer->mUSBControllers->begin();
11774 while (it != mPeer->mUSBControllers->end())
11775 {
11776 (*it)->uninit();
11777 ++it;
11778 }
11779
11780 /* attach new list of controllers to our peer */
11781 mPeer->mUSBControllers.attach(newList);
11782 }
11783 else
11784 {
11785 /* we have no peer (our parent is the newly created machine);
11786 * just commit changes to devices */
11787 commitUSBControllers = true;
11788 }
11789 }
11790 else
11791 {
11792 /* the list of controllers itself is not changed,
11793 * just commit changes to controllers themselves */
11794 commitUSBControllers = true;
11795 }
11796
11797 if (commitUSBControllers)
11798 {
11799 USBControllerList::const_iterator it = mUSBControllers->begin();
11800 while (it != mUSBControllers->end())
11801 {
11802 (*it)->i_commit();
11803 ++it;
11804 }
11805 }
11806
11807 if (i_isSessionMachine())
11808 {
11809 /* attach new data to the primary machine and reshare it */
11810 mPeer->mUserData.attach(mUserData);
11811 mPeer->mHWData.attach(mHWData);
11812 /* mMediaData is reshared by fixupMedia */
11813 // mPeer->mMediaData.attach(mMediaData);
11814 Assert(mPeer->mMediaData.data() == mMediaData.data());
11815 }
11816}
11817
11818/**
11819 * Copies all the hardware data from the given machine.
11820 *
11821 * Currently, only called when the VM is being restored from a snapshot. In
11822 * particular, this implies that the VM is not running during this method's
11823 * call.
11824 *
11825 * @note This method must be called from under this object's lock.
11826 *
11827 * @note This method doesn't call #commit(), so all data remains backed up and
11828 * unsaved.
11829 */
11830void Machine::i_copyFrom(Machine *aThat)
11831{
11832 AssertReturnVoid(!i_isSnapshotMachine());
11833 AssertReturnVoid(aThat->i_isSnapshotMachine());
11834
11835 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11836
11837 mHWData.assignCopy(aThat->mHWData);
11838
11839 // create copies of all shared folders (mHWData after attaching a copy
11840 // contains just references to original objects)
11841 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11842 it != mHWData->mSharedFolders.end();
11843 ++it)
11844 {
11845 ComObjPtr<SharedFolder> folder;
11846 folder.createObject();
11847 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11848 AssertComRC(rc);
11849 *it = folder;
11850 }
11851
11852 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11853 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11854 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11855 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11856 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11857
11858 /* create private copies of all controllers */
11859 mStorageControllers.backup();
11860 mStorageControllers->clear();
11861 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11862 it != aThat->mStorageControllers->end();
11863 ++it)
11864 {
11865 ComObjPtr<StorageController> ctrl;
11866 ctrl.createObject();
11867 ctrl->initCopy(this, *it);
11868 mStorageControllers->push_back(ctrl);
11869 }
11870
11871 /* create private copies of all USB controllers */
11872 mUSBControllers.backup();
11873 mUSBControllers->clear();
11874 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11875 it != aThat->mUSBControllers->end();
11876 ++it)
11877 {
11878 ComObjPtr<USBController> ctrl;
11879 ctrl.createObject();
11880 ctrl->initCopy(this, *it);
11881 mUSBControllers->push_back(ctrl);
11882 }
11883
11884 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11885 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11886 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11887 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11888 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11889 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11890 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11891}
11892
11893/**
11894 * Returns whether the given storage controller is hotplug capable.
11895 *
11896 * @returns true if the controller supports hotplugging
11897 * false otherwise.
11898 * @param enmCtrlType The controller type to check for.
11899 */
11900bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11901{
11902 ComPtr<ISystemProperties> systemProperties;
11903 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11904 if (FAILED(rc))
11905 return false;
11906
11907 BOOL aHotplugCapable = FALSE;
11908 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11909
11910 return RT_BOOL(aHotplugCapable);
11911}
11912
11913#ifdef VBOX_WITH_RESOURCE_USAGE_API
11914
11915void Machine::i_getDiskList(MediaList &list)
11916{
11917 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11918 it != mMediaData->mAttachments.end();
11919 ++it)
11920 {
11921 MediumAttachment* pAttach = *it;
11922 /* just in case */
11923 AssertStmt(pAttach, continue);
11924
11925 AutoCaller localAutoCallerA(pAttach);
11926 if (FAILED(localAutoCallerA.rc())) continue;
11927
11928 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11929
11930 if (pAttach->i_getType() == DeviceType_HardDisk)
11931 list.push_back(pAttach->i_getMedium());
11932 }
11933}
11934
11935void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11936{
11937 AssertReturnVoid(isWriteLockOnCurrentThread());
11938 AssertPtrReturnVoid(aCollector);
11939
11940 pm::CollectorHAL *hal = aCollector->getHAL();
11941 /* Create sub metrics */
11942 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11943 "Percentage of processor time spent in user mode by the VM process.");
11944 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11945 "Percentage of processor time spent in kernel mode by the VM process.");
11946 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11947 "Size of resident portion of VM process in memory.");
11948 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11949 "Actual size of all VM disks combined.");
11950 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11951 "Network receive rate.");
11952 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11953 "Network transmit rate.");
11954 /* Create and register base metrics */
11955 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11956 cpuLoadUser, cpuLoadKernel);
11957 aCollector->registerBaseMetric(cpuLoad);
11958 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11959 ramUsageUsed);
11960 aCollector->registerBaseMetric(ramUsage);
11961 MediaList disks;
11962 i_getDiskList(disks);
11963 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11964 diskUsageUsed);
11965 aCollector->registerBaseMetric(diskUsage);
11966
11967 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11968 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11969 new pm::AggregateAvg()));
11970 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11971 new pm::AggregateMin()));
11972 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11973 new pm::AggregateMax()));
11974 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11975 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11976 new pm::AggregateAvg()));
11977 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11978 new pm::AggregateMin()));
11979 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11980 new pm::AggregateMax()));
11981
11982 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11983 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11984 new pm::AggregateAvg()));
11985 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11986 new pm::AggregateMin()));
11987 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11988 new pm::AggregateMax()));
11989
11990 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11991 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11992 new pm::AggregateAvg()));
11993 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11994 new pm::AggregateMin()));
11995 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11996 new pm::AggregateMax()));
11997
11998
11999 /* Guest metrics collector */
12000 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12001 aCollector->registerGuest(mCollectorGuest);
12002 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12003 this, __PRETTY_FUNCTION__, mCollectorGuest));
12004
12005 /* Create sub metrics */
12006 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12007 "Percentage of processor time spent in user mode as seen by the guest.");
12008 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12009 "Percentage of processor time spent in kernel mode as seen by the guest.");
12010 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12011 "Percentage of processor time spent idling as seen by the guest.");
12012
12013 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12014 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12015 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12016 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12017 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12018 pm::SubMetric *guestMemCache = new pm::SubMetric(
12019 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12020
12021 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12022 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12023
12024 /* Create and register base metrics */
12025 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12026 machineNetRx, machineNetTx);
12027 aCollector->registerBaseMetric(machineNetRate);
12028
12029 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12030 guestLoadUser, guestLoadKernel, guestLoadIdle);
12031 aCollector->registerBaseMetric(guestCpuLoad);
12032
12033 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12034 guestMemTotal, guestMemFree,
12035 guestMemBalloon, guestMemShared,
12036 guestMemCache, guestPagedTotal);
12037 aCollector->registerBaseMetric(guestCpuMem);
12038
12039 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12040 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12041 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12042 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12043
12044 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12045 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12046 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12047 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12048
12049 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12050 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12051 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12052 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12053
12054 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12055 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12056 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12057 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12058
12059 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12060 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12061 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12062 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12063
12064 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12065 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12066 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12067 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12068
12069 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12071 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12073
12074 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12075 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12076 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12077 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12078
12079 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12080 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12081 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12082 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12083
12084 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12085 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12086 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12087 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12088
12089 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12090 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12091 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12092 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12093}
12094
12095void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12096{
12097 AssertReturnVoid(isWriteLockOnCurrentThread());
12098
12099 if (aCollector)
12100 {
12101 aCollector->unregisterMetricsFor(aMachine);
12102 aCollector->unregisterBaseMetricsFor(aMachine);
12103 }
12104}
12105
12106#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12107
12108
12109////////////////////////////////////////////////////////////////////////////////
12110
12111DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12112
12113HRESULT SessionMachine::FinalConstruct()
12114{
12115 LogFlowThisFunc(("\n"));
12116
12117 mClientToken = NULL;
12118
12119 return BaseFinalConstruct();
12120}
12121
12122void SessionMachine::FinalRelease()
12123{
12124 LogFlowThisFunc(("\n"));
12125
12126 Assert(!mClientToken);
12127 /* paranoia, should not hang around any more */
12128 if (mClientToken)
12129 {
12130 delete mClientToken;
12131 mClientToken = NULL;
12132 }
12133
12134 uninit(Uninit::Unexpected);
12135
12136 BaseFinalRelease();
12137}
12138
12139/**
12140 * @note Must be called only by Machine::LockMachine() from its own write lock.
12141 */
12142HRESULT SessionMachine::init(Machine *aMachine)
12143{
12144 LogFlowThisFuncEnter();
12145 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12146
12147 AssertReturn(aMachine, E_INVALIDARG);
12148
12149 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12150
12151 /* Enclose the state transition NotReady->InInit->Ready */
12152 AutoInitSpan autoInitSpan(this);
12153 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12154
12155 HRESULT rc = S_OK;
12156
12157 /* create the machine client token */
12158 try
12159 {
12160 mClientToken = new ClientToken(aMachine, this);
12161 if (!mClientToken->isReady())
12162 {
12163 delete mClientToken;
12164 mClientToken = NULL;
12165 rc = E_FAIL;
12166 }
12167 }
12168 catch (std::bad_alloc &)
12169 {
12170 rc = E_OUTOFMEMORY;
12171 }
12172 if (FAILED(rc))
12173 return rc;
12174
12175 /* memorize the peer Machine */
12176 unconst(mPeer) = aMachine;
12177 /* share the parent pointer */
12178 unconst(mParent) = aMachine->mParent;
12179
12180 /* take the pointers to data to share */
12181 mData.share(aMachine->mData);
12182 mSSData.share(aMachine->mSSData);
12183
12184 mUserData.share(aMachine->mUserData);
12185 mHWData.share(aMachine->mHWData);
12186 mMediaData.share(aMachine->mMediaData);
12187
12188 mStorageControllers.allocate();
12189 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12190 it != aMachine->mStorageControllers->end();
12191 ++it)
12192 {
12193 ComObjPtr<StorageController> ctl;
12194 ctl.createObject();
12195 ctl->init(this, *it);
12196 mStorageControllers->push_back(ctl);
12197 }
12198
12199 mUSBControllers.allocate();
12200 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12201 it != aMachine->mUSBControllers->end();
12202 ++it)
12203 {
12204 ComObjPtr<USBController> ctl;
12205 ctl.createObject();
12206 ctl->init(this, *it);
12207 mUSBControllers->push_back(ctl);
12208 }
12209
12210 unconst(mBIOSSettings).createObject();
12211 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12212 /* create another VRDEServer object that will be mutable */
12213 unconst(mVRDEServer).createObject();
12214 mVRDEServer->init(this, aMachine->mVRDEServer);
12215 /* create another audio adapter object that will be mutable */
12216 unconst(mAudioAdapter).createObject();
12217 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12218 /* create a list of serial ports that will be mutable */
12219 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12220 {
12221 unconst(mSerialPorts[slot]).createObject();
12222 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12223 }
12224 /* create a list of parallel ports that will be mutable */
12225 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12226 {
12227 unconst(mParallelPorts[slot]).createObject();
12228 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12229 }
12230
12231 /* create another USB device filters object that will be mutable */
12232 unconst(mUSBDeviceFilters).createObject();
12233 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12234
12235 /* create a list of network adapters that will be mutable */
12236 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12237 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12238 {
12239 unconst(mNetworkAdapters[slot]).createObject();
12240 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12241 }
12242
12243 /* create another bandwidth control object that will be mutable */
12244 unconst(mBandwidthControl).createObject();
12245 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12246
12247 /* default is to delete saved state on Saved -> PoweredOff transition */
12248 mRemoveSavedState = true;
12249
12250 /* Confirm a successful initialization when it's the case */
12251 autoInitSpan.setSucceeded();
12252
12253 miNATNetworksStarted = 0;
12254
12255 LogFlowThisFuncLeave();
12256 return rc;
12257}
12258
12259/**
12260 * Uninitializes this session object. If the reason is other than
12261 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12262 * or the client watcher code.
12263 *
12264 * @param aReason uninitialization reason
12265 *
12266 * @note Locks mParent + this object for writing.
12267 */
12268void SessionMachine::uninit(Uninit::Reason aReason)
12269{
12270 LogFlowThisFuncEnter();
12271 LogFlowThisFunc(("reason=%d\n", aReason));
12272
12273 /*
12274 * Strongly reference ourselves to prevent this object deletion after
12275 * mData->mSession.mMachine.setNull() below (which can release the last
12276 * reference and call the destructor). Important: this must be done before
12277 * accessing any members (and before AutoUninitSpan that does it as well).
12278 * This self reference will be released as the very last step on return.
12279 */
12280 ComObjPtr<SessionMachine> selfRef = this;
12281
12282 /* Enclose the state transition Ready->InUninit->NotReady */
12283 AutoUninitSpan autoUninitSpan(this);
12284 if (autoUninitSpan.uninitDone())
12285 {
12286 LogFlowThisFunc(("Already uninitialized\n"));
12287 LogFlowThisFuncLeave();
12288 return;
12289 }
12290
12291 if (autoUninitSpan.initFailed())
12292 {
12293 /* We've been called by init() because it's failed. It's not really
12294 * necessary (nor it's safe) to perform the regular uninit sequence
12295 * below, the following is enough.
12296 */
12297 LogFlowThisFunc(("Initialization failed.\n"));
12298 /* destroy the machine client token */
12299 if (mClientToken)
12300 {
12301 delete mClientToken;
12302 mClientToken = NULL;
12303 }
12304 uninitDataAndChildObjects();
12305 mData.free();
12306 unconst(mParent) = NULL;
12307 unconst(mPeer) = NULL;
12308 LogFlowThisFuncLeave();
12309 return;
12310 }
12311
12312 MachineState_T lastState;
12313 {
12314 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12315 lastState = mData->mMachineState;
12316 }
12317 NOREF(lastState);
12318
12319#ifdef VBOX_WITH_USB
12320 // release all captured USB devices, but do this before requesting the locks below
12321 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12322 {
12323 /* Console::captureUSBDevices() is called in the VM process only after
12324 * setting the machine state to Starting or Restoring.
12325 * Console::detachAllUSBDevices() will be called upon successful
12326 * termination. So, we need to release USB devices only if there was
12327 * an abnormal termination of a running VM.
12328 *
12329 * This is identical to SessionMachine::DetachAllUSBDevices except
12330 * for the aAbnormal argument. */
12331 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12332 AssertComRC(rc);
12333 NOREF(rc);
12334
12335 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12336 if (service)
12337 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12338 }
12339#endif /* VBOX_WITH_USB */
12340
12341 // we need to lock this object in uninit() because the lock is shared
12342 // with mPeer (as well as data we modify below). mParent lock is needed
12343 // by several calls to it, and USB needs host lock.
12344 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12345
12346#ifdef VBOX_WITH_RESOURCE_USAGE_API
12347 /*
12348 * It is safe to call Machine::i_unregisterMetrics() here because
12349 * PerformanceCollector::samplerCallback no longer accesses guest methods
12350 * holding the lock.
12351 */
12352 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12353 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12354 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12355 this, __PRETTY_FUNCTION__, mCollectorGuest));
12356 if (mCollectorGuest)
12357 {
12358 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12359 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12360 mCollectorGuest = NULL;
12361 }
12362#endif
12363
12364 if (aReason == Uninit::Abnormal)
12365 {
12366 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12367 Global::IsOnlineOrTransient(lastState)));
12368
12369 /* reset the state to Aborted */
12370 if (mData->mMachineState != MachineState_Aborted)
12371 i_setMachineState(MachineState_Aborted);
12372 }
12373
12374 // any machine settings modified?
12375 if (mData->flModifications)
12376 {
12377 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12378 i_rollback(false /* aNotify */);
12379 }
12380
12381 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12382 || !mConsoleTaskData.mSnapshot);
12383 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12384 {
12385 LogWarningThisFunc(("canceling failed save state request!\n"));
12386 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12387 }
12388 else if (!mConsoleTaskData.mSnapshot.isNull())
12389 {
12390 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12391
12392 /* delete all differencing hard disks created (this will also attach
12393 * their parents back by rolling back mMediaData) */
12394 i_rollbackMedia();
12395
12396 // delete the saved state file (it might have been already created)
12397 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12398 // think it's still in use
12399 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12400 mConsoleTaskData.mSnapshot->uninit();
12401 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12402 }
12403
12404 mData->mSession.mPID = NIL_RTPROCESS;
12405
12406 if (aReason == Uninit::Unexpected)
12407 {
12408 /* Uninitialization didn't come from #checkForDeath(), so tell the
12409 * client watcher thread to update the set of machines that have open
12410 * sessions. */
12411 mParent->i_updateClientWatcher();
12412 }
12413
12414 /* uninitialize all remote controls */
12415 if (mData->mSession.mRemoteControls.size())
12416 {
12417 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12418 mData->mSession.mRemoteControls.size()));
12419
12420 Data::Session::RemoteControlList::iterator it =
12421 mData->mSession.mRemoteControls.begin();
12422 while (it != mData->mSession.mRemoteControls.end())
12423 {
12424 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12425 HRESULT rc = (*it)->Uninitialize();
12426 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12427 if (FAILED(rc))
12428 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12429 ++it;
12430 }
12431 mData->mSession.mRemoteControls.clear();
12432 }
12433
12434 /* Remove all references to the NAT network service. The service will stop
12435 * if all references (also from other VMs) are removed. */
12436 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12437 {
12438 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12439 {
12440 NetworkAttachmentType_T type;
12441 HRESULT hrc;
12442
12443 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12444 if ( SUCCEEDED(hrc)
12445 && type == NetworkAttachmentType_NATNetwork)
12446 {
12447 Bstr name;
12448 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12449 if (SUCCEEDED(hrc))
12450 {
12451 multilock.release();
12452 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12453 mUserData->s.strName.c_str(), name.raw()));
12454 mParent->i_natNetworkRefDec(name.raw());
12455 multilock.acquire();
12456 }
12457 }
12458 }
12459 }
12460
12461 /*
12462 * An expected uninitialization can come only from #checkForDeath().
12463 * Otherwise it means that something's gone really wrong (for example,
12464 * the Session implementation has released the VirtualBox reference
12465 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12466 * etc). However, it's also possible, that the client releases the IPC
12467 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12468 * but the VirtualBox release event comes first to the server process.
12469 * This case is practically possible, so we should not assert on an
12470 * unexpected uninit, just log a warning.
12471 */
12472
12473 if ((aReason == Uninit::Unexpected))
12474 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12475
12476 if (aReason != Uninit::Normal)
12477 {
12478 mData->mSession.mDirectControl.setNull();
12479 }
12480 else
12481 {
12482 /* this must be null here (see #OnSessionEnd()) */
12483 Assert(mData->mSession.mDirectControl.isNull());
12484 Assert(mData->mSession.mState == SessionState_Unlocking);
12485 Assert(!mData->mSession.mProgress.isNull());
12486 }
12487 if (mData->mSession.mProgress)
12488 {
12489 if (aReason == Uninit::Normal)
12490 mData->mSession.mProgress->i_notifyComplete(S_OK);
12491 else
12492 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12493 COM_IIDOF(ISession),
12494 getComponentName(),
12495 tr("The VM session was aborted"));
12496 mData->mSession.mProgress.setNull();
12497 }
12498
12499 /* remove the association between the peer machine and this session machine */
12500 Assert( (SessionMachine*)mData->mSession.mMachine == this
12501 || aReason == Uninit::Unexpected);
12502
12503 /* reset the rest of session data */
12504 mData->mSession.mMachine.setNull();
12505 mData->mSession.mState = SessionState_Unlocked;
12506 mData->mSession.mType.setNull();
12507
12508 /* destroy the machine client token before leaving the exclusive lock */
12509 if (mClientToken)
12510 {
12511 delete mClientToken;
12512 mClientToken = NULL;
12513 }
12514
12515 /* fire an event */
12516 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12517
12518 uninitDataAndChildObjects();
12519
12520 /* free the essential data structure last */
12521 mData.free();
12522
12523 /* release the exclusive lock before setting the below two to NULL */
12524 multilock.release();
12525
12526 unconst(mParent) = NULL;
12527 unconst(mPeer) = NULL;
12528
12529 LogFlowThisFuncLeave();
12530}
12531
12532// util::Lockable interface
12533////////////////////////////////////////////////////////////////////////////////
12534
12535/**
12536 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12537 * with the primary Machine instance (mPeer).
12538 */
12539RWLockHandle *SessionMachine::lockHandle() const
12540{
12541 AssertReturn(mPeer != NULL, NULL);
12542 return mPeer->lockHandle();
12543}
12544
12545// IInternalMachineControl methods
12546////////////////////////////////////////////////////////////////////////////////
12547
12548/**
12549 * Passes collected guest statistics to performance collector object
12550 */
12551HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12552 ULONG aCpuKernel, ULONG aCpuIdle,
12553 ULONG aMemTotal, ULONG aMemFree,
12554 ULONG aMemBalloon, ULONG aMemShared,
12555 ULONG aMemCache, ULONG aPageTotal,
12556 ULONG aAllocVMM, ULONG aFreeVMM,
12557 ULONG aBalloonedVMM, ULONG aSharedVMM,
12558 ULONG aVmNetRx, ULONG aVmNetTx)
12559{
12560#ifdef VBOX_WITH_RESOURCE_USAGE_API
12561 if (mCollectorGuest)
12562 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12563 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12564 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12565 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12566
12567 return S_OK;
12568#else
12569 NOREF(aValidStats);
12570 NOREF(aCpuUser);
12571 NOREF(aCpuKernel);
12572 NOREF(aCpuIdle);
12573 NOREF(aMemTotal);
12574 NOREF(aMemFree);
12575 NOREF(aMemBalloon);
12576 NOREF(aMemShared);
12577 NOREF(aMemCache);
12578 NOREF(aPageTotal);
12579 NOREF(aAllocVMM);
12580 NOREF(aFreeVMM);
12581 NOREF(aBalloonedVMM);
12582 NOREF(aSharedVMM);
12583 NOREF(aVmNetRx);
12584 NOREF(aVmNetTx);
12585 return E_NOTIMPL;
12586#endif
12587}
12588
12589/**
12590 * @note Locks this object for writing.
12591 */
12592HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12593{
12594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12595
12596 mRemoveSavedState = RT_BOOL(aRemove);
12597
12598 return S_OK;
12599}
12600
12601/**
12602 * @note Locks the same as #i_setMachineState() does.
12603 */
12604HRESULT SessionMachine::updateState(MachineState_T aState)
12605{
12606 return i_setMachineState(aState);
12607}
12608
12609/**
12610 * @note Locks this object for writing.
12611 */
12612HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12613{
12614 IProgress* pProgress(aProgress);
12615
12616 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12617
12618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12619
12620 if (mData->mSession.mState != SessionState_Locked)
12621 return VBOX_E_INVALID_OBJECT_STATE;
12622
12623 if (!mData->mSession.mProgress.isNull())
12624 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12625
12626 /* If we didn't reference the NAT network service yet, add a reference to
12627 * force a start */
12628 if (miNATNetworksStarted < 1)
12629 {
12630 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12631 {
12632 NetworkAttachmentType_T type;
12633 HRESULT hrc;
12634 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12635 if ( SUCCEEDED(hrc)
12636 && type == NetworkAttachmentType_NATNetwork)
12637 {
12638 Bstr name;
12639 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12640 if (SUCCEEDED(hrc))
12641 {
12642 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12643 mUserData->s.strName.c_str(), name.raw()));
12644 mPeer->lockHandle()->unlockWrite();
12645 mParent->i_natNetworkRefInc(name.raw());
12646#ifdef RT_LOCK_STRICT
12647 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12648#else
12649 mPeer->lockHandle()->lockWrite();
12650#endif
12651 }
12652 }
12653 }
12654 miNATNetworksStarted++;
12655 }
12656
12657 LogFlowThisFunc(("returns S_OK.\n"));
12658 return S_OK;
12659}
12660
12661/**
12662 * @note Locks this object for writing.
12663 */
12664HRESULT SessionMachine::endPowerUp(LONG aResult)
12665{
12666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12667
12668 if (mData->mSession.mState != SessionState_Locked)
12669 return VBOX_E_INVALID_OBJECT_STATE;
12670
12671 /* Finalize the LaunchVMProcess progress object. */
12672 if (mData->mSession.mProgress)
12673 {
12674 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12675 mData->mSession.mProgress.setNull();
12676 }
12677
12678 if (SUCCEEDED((HRESULT)aResult))
12679 {
12680#ifdef VBOX_WITH_RESOURCE_USAGE_API
12681 /* The VM has been powered up successfully, so it makes sense
12682 * now to offer the performance metrics for a running machine
12683 * object. Doing it earlier wouldn't be safe. */
12684 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12685 mData->mSession.mPID);
12686#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12687 }
12688
12689 return S_OK;
12690}
12691
12692/**
12693 * @note Locks this object for writing.
12694 */
12695HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12696{
12697 LogFlowThisFuncEnter();
12698
12699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12700
12701 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12702 E_FAIL);
12703
12704 /* create a progress object to track operation completion */
12705 ComObjPtr<Progress> pProgress;
12706 pProgress.createObject();
12707 pProgress->init(i_getVirtualBox(),
12708 static_cast<IMachine *>(this) /* aInitiator */,
12709 Bstr(tr("Stopping the virtual machine")).raw(),
12710 FALSE /* aCancelable */);
12711
12712 /* fill in the console task data */
12713 mConsoleTaskData.mLastState = mData->mMachineState;
12714 mConsoleTaskData.mProgress = pProgress;
12715
12716 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12717 i_setMachineState(MachineState_Stopping);
12718
12719 pProgress.queryInterfaceTo(aProgress.asOutParam());
12720
12721 return S_OK;
12722}
12723
12724/**
12725 * @note Locks this object for writing.
12726 */
12727HRESULT SessionMachine::endPoweringDown(LONG aResult,
12728 const com::Utf8Str &aErrMsg)
12729{
12730 LogFlowThisFuncEnter();
12731
12732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12733
12734 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12735 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12736 && mConsoleTaskData.mLastState != MachineState_Null,
12737 E_FAIL);
12738
12739 /*
12740 * On failure, set the state to the state we had when BeginPoweringDown()
12741 * was called (this is expected by Console::PowerDown() and the associated
12742 * task). On success the VM process already changed the state to
12743 * MachineState_PoweredOff, so no need to do anything.
12744 */
12745 if (FAILED(aResult))
12746 i_setMachineState(mConsoleTaskData.mLastState);
12747
12748 /* notify the progress object about operation completion */
12749 Assert(mConsoleTaskData.mProgress);
12750 if (SUCCEEDED(aResult))
12751 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12752 else
12753 {
12754 if (aErrMsg.length())
12755 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12756 COM_IIDOF(ISession),
12757 getComponentName(),
12758 aErrMsg.c_str());
12759 else
12760 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12761 }
12762
12763 /* clear out the temporary saved state data */
12764 mConsoleTaskData.mLastState = MachineState_Null;
12765 mConsoleTaskData.mProgress.setNull();
12766
12767 LogFlowThisFuncLeave();
12768 return S_OK;
12769}
12770
12771
12772/**
12773 * Goes through the USB filters of the given machine to see if the given
12774 * device matches any filter or not.
12775 *
12776 * @note Locks the same as USBController::hasMatchingFilter() does.
12777 */
12778HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12779 BOOL *aMatched,
12780 ULONG *aMaskedInterfaces)
12781{
12782 LogFlowThisFunc(("\n"));
12783
12784#ifdef VBOX_WITH_USB
12785 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12786#else
12787 NOREF(aDevice);
12788 NOREF(aMaskedInterfaces);
12789 *aMatched = FALSE;
12790#endif
12791
12792 return S_OK;
12793}
12794
12795/**
12796 * @note Locks the same as Host::captureUSBDevice() does.
12797 */
12798HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
12799{
12800 LogFlowThisFunc(("\n"));
12801
12802#ifdef VBOX_WITH_USB
12803 /* if captureDeviceForVM() fails, it must have set extended error info */
12804 clearError();
12805 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12806 if (FAILED(rc)) return rc;
12807
12808 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12809 AssertReturn(service, E_FAIL);
12810 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
12811#else
12812 NOREF(aId);
12813 return E_NOTIMPL;
12814#endif
12815}
12816
12817/**
12818 * @note Locks the same as Host::detachUSBDevice() does.
12819 */
12820HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12821 BOOL aDone)
12822{
12823 LogFlowThisFunc(("\n"));
12824
12825#ifdef VBOX_WITH_USB
12826 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12827 AssertReturn(service, E_FAIL);
12828 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12829#else
12830 NOREF(aId);
12831 NOREF(aDone);
12832 return E_NOTIMPL;
12833#endif
12834}
12835
12836/**
12837 * Inserts all machine filters to the USB proxy service and then calls
12838 * Host::autoCaptureUSBDevices().
12839 *
12840 * Called by Console from the VM process upon VM startup.
12841 *
12842 * @note Locks what called methods lock.
12843 */
12844HRESULT SessionMachine::autoCaptureUSBDevices()
12845{
12846 LogFlowThisFunc(("\n"));
12847
12848#ifdef VBOX_WITH_USB
12849 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12850 AssertComRC(rc);
12851 NOREF(rc);
12852
12853 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12854 AssertReturn(service, E_FAIL);
12855 return service->autoCaptureDevicesForVM(this);
12856#else
12857 return S_OK;
12858#endif
12859}
12860
12861/**
12862 * Removes all machine filters from the USB proxy service and then calls
12863 * Host::detachAllUSBDevices().
12864 *
12865 * Called by Console from the VM process upon normal VM termination or by
12866 * SessionMachine::uninit() upon abnormal VM termination (from under the
12867 * Machine/SessionMachine lock).
12868 *
12869 * @note Locks what called methods lock.
12870 */
12871HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12872{
12873 LogFlowThisFunc(("\n"));
12874
12875#ifdef VBOX_WITH_USB
12876 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12877 AssertComRC(rc);
12878 NOREF(rc);
12879
12880 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12881 AssertReturn(service, E_FAIL);
12882 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12883#else
12884 NOREF(aDone);
12885 return S_OK;
12886#endif
12887}
12888
12889/**
12890 * @note Locks this object for writing.
12891 */
12892HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12893 ComPtr<IProgress> &aProgress)
12894{
12895 LogFlowThisFuncEnter();
12896
12897 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12898 /*
12899 * We don't assert below because it might happen that a non-direct session
12900 * informs us it is closed right after we've been uninitialized -- it's ok.
12901 */
12902
12903 /* get IInternalSessionControl interface */
12904 ComPtr<IInternalSessionControl> control(aSession);
12905
12906 ComAssertRet(!control.isNull(), E_INVALIDARG);
12907
12908 /* Creating a Progress object requires the VirtualBox lock, and
12909 * thus locking it here is required by the lock order rules. */
12910 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12911
12912 if (control == mData->mSession.mDirectControl)
12913 {
12914 /* The direct session is being normally closed by the client process
12915 * ----------------------------------------------------------------- */
12916
12917 /* go to the closing state (essential for all open*Session() calls and
12918 * for #checkForDeath()) */
12919 Assert(mData->mSession.mState == SessionState_Locked);
12920 mData->mSession.mState = SessionState_Unlocking;
12921
12922 /* set direct control to NULL to release the remote instance */
12923 mData->mSession.mDirectControl.setNull();
12924 LogFlowThisFunc(("Direct control is set to NULL\n"));
12925
12926 if (mData->mSession.mProgress)
12927 {
12928 /* finalize the progress, someone might wait if a frontend
12929 * closes the session before powering on the VM. */
12930 mData->mSession.mProgress->notifyComplete(E_FAIL,
12931 COM_IIDOF(ISession),
12932 getComponentName(),
12933 tr("The VM session was closed before any attempt to power it on"));
12934 mData->mSession.mProgress.setNull();
12935 }
12936
12937 /* Create the progress object the client will use to wait until
12938 * #checkForDeath() is called to uninitialize this session object after
12939 * it releases the IPC semaphore.
12940 * Note! Because we're "reusing" mProgress here, this must be a proxy
12941 * object just like for LaunchVMProcess. */
12942 Assert(mData->mSession.mProgress.isNull());
12943 ComObjPtr<ProgressProxy> progress;
12944 progress.createObject();
12945 ComPtr<IUnknown> pPeer(mPeer);
12946 progress->init(mParent, pPeer,
12947 Bstr(tr("Closing session")).raw(),
12948 FALSE /* aCancelable */);
12949 progress.queryInterfaceTo(aProgress.asOutParam());
12950 mData->mSession.mProgress = progress;
12951 }
12952 else
12953 {
12954 /* the remote session is being normally closed */
12955 Data::Session::RemoteControlList::iterator it =
12956 mData->mSession.mRemoteControls.begin();
12957 while (it != mData->mSession.mRemoteControls.end())
12958 {
12959 if (control == *it)
12960 break;
12961 ++it;
12962 }
12963 BOOL found = it != mData->mSession.mRemoteControls.end();
12964 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12965 E_INVALIDARG);
12966 // This MUST be erase(it), not remove(*it) as the latter triggers a
12967 // very nasty use after free due to the place where the value "lives".
12968 mData->mSession.mRemoteControls.erase(it);
12969 }
12970
12971 /* signal the client watcher thread, because the client is going away */
12972 mParent->i_updateClientWatcher();
12973
12974 LogFlowThisFuncLeave();
12975 return S_OK;
12976}
12977
12978/**
12979 * @note Locks this object for writing.
12980 */
12981HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
12982 com::Utf8Str &aStateFilePath)
12983{
12984 LogFlowThisFuncEnter();
12985
12986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12987
12988 AssertReturn( mData->mMachineState == MachineState_Paused
12989 && mConsoleTaskData.mLastState == MachineState_Null
12990 && mConsoleTaskData.strStateFilePath.isEmpty(),
12991 E_FAIL);
12992
12993 /* create a progress object to track operation completion */
12994 ComObjPtr<Progress> pProgress;
12995 pProgress.createObject();
12996 pProgress->init(i_getVirtualBox(),
12997 static_cast<IMachine *>(this) /* aInitiator */,
12998 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12999 FALSE /* aCancelable */);
13000
13001 /* stateFilePath is null when the machine is not running */
13002 if (mData->mMachineState == MachineState_Paused)
13003 i_composeSavedStateFilename(aStateFilePath);
13004
13005 /* fill in the console task data */
13006 mConsoleTaskData.mLastState = mData->mMachineState;
13007 mConsoleTaskData.strStateFilePath = aStateFilePath;
13008 mConsoleTaskData.mProgress = pProgress;
13009
13010 /* set the state to Saving (this is expected by Console::SaveState()) */
13011 i_setMachineState(MachineState_Saving);
13012
13013 pProgress.queryInterfaceTo(aProgress.asOutParam());
13014
13015 return S_OK;
13016}
13017
13018/**
13019 * @note Locks mParent + this object for writing.
13020 */
13021HRESULT SessionMachine::endSavingState(LONG aResult,
13022 const com::Utf8Str &aErrMsg)
13023{
13024 LogFlowThisFunc(("\n"));
13025
13026 /* endSavingState() need mParent lock */
13027 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13028
13029 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13030 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13031 && mConsoleTaskData.mLastState != MachineState_Null
13032 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13033 E_FAIL);
13034
13035 /*
13036 * On failure, set the state to the state we had when BeginSavingState()
13037 * was called (this is expected by Console::SaveState() and the associated
13038 * task). On success the VM process already changed the state to
13039 * MachineState_Saved, so no need to do anything.
13040 */
13041 if (FAILED(aResult))
13042 i_setMachineState(mConsoleTaskData.mLastState);
13043
13044 return i_endSavingState(aResult, aErrMsg);
13045}
13046
13047/**
13048 * @note Locks this object for writing.
13049 */
13050HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13051{
13052 LogFlowThisFunc(("\n"));
13053
13054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13055
13056 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13057 || mData->mMachineState == MachineState_Teleported
13058 || mData->mMachineState == MachineState_Aborted
13059 , E_FAIL); /** @todo setError. */
13060
13061 com::Utf8Str stateFilePathFull;
13062 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13063 if (RT_FAILURE(vrc))
13064 return setError(VBOX_E_FILE_ERROR,
13065 tr("Invalid saved state file path '%s' (%Rrc)"),
13066 aSavedStateFile.c_str(),
13067 vrc);
13068
13069 mSSData->strStateFilePath = stateFilePathFull;
13070
13071 /* The below i_setMachineState() will detect the state transition and will
13072 * update the settings file */
13073
13074 return i_setMachineState(MachineState_Saved);
13075}
13076
13077HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13078 std::vector<com::Utf8Str> &aValues,
13079 std::vector<LONG64> &aTimestamps,
13080 std::vector<com::Utf8Str> &aFlags)
13081{
13082 LogFlowThisFunc(("\n"));
13083
13084#ifdef VBOX_WITH_GUEST_PROPS
13085 using namespace guestProp;
13086
13087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13088
13089 size_t cEntries = mHWData->mGuestProperties.size();
13090 aNames.resize(cEntries);
13091 aValues.resize(cEntries);
13092 aTimestamps.resize(cEntries);
13093 aFlags.resize(cEntries);
13094
13095 size_t i = 0;
13096 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13097 it != mHWData->mGuestProperties.end();
13098 ++it, ++i)
13099 {
13100 char szFlags[MAX_FLAGS_LEN + 1];
13101 aNames[i] = it->first;
13102 aValues[i] = it->second.strValue;
13103 aTimestamps[i] = it->second.mTimestamp;
13104
13105 /* If it is NULL, keep it NULL. */
13106 if (it->second.mFlags)
13107 {
13108 writeFlags(it->second.mFlags, szFlags);
13109 aFlags[i] = szFlags;
13110 }
13111 else
13112 aFlags[i] = "";
13113 }
13114 return S_OK;
13115#else
13116 ReturnComNotImplemented();
13117#endif
13118}
13119
13120HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13121 const com::Utf8Str &aValue,
13122 LONG64 aTimestamp,
13123 const com::Utf8Str &aFlags)
13124{
13125 LogFlowThisFunc(("\n"));
13126
13127#ifdef VBOX_WITH_GUEST_PROPS
13128 using namespace guestProp;
13129
13130 try
13131 {
13132 /*
13133 * Convert input up front.
13134 */
13135 uint32_t fFlags = NILFLAG;
13136 if (aFlags.length())
13137 {
13138 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13139 AssertRCReturn(vrc, E_INVALIDARG);
13140 }
13141
13142 /*
13143 * Now grab the object lock, validate the state and do the update.
13144 */
13145
13146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13147
13148 switch (mData->mMachineState)
13149 {
13150 case MachineState_Paused:
13151 case MachineState_Running:
13152 case MachineState_Teleporting:
13153 case MachineState_TeleportingPausedVM:
13154 case MachineState_LiveSnapshotting:
13155 case MachineState_DeletingSnapshotOnline:
13156 case MachineState_DeletingSnapshotPaused:
13157 case MachineState_Saving:
13158 case MachineState_Stopping:
13159 break;
13160
13161 default:
13162 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13163 VBOX_E_INVALID_VM_STATE);
13164 }
13165
13166 i_setModified(IsModified_MachineData);
13167 mHWData.backup();
13168
13169 bool fDelete = !aValue.length();
13170 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13171 if (it != mHWData->mGuestProperties.end())
13172 {
13173 if (!fDelete)
13174 {
13175 it->second.strValue = aValue;
13176 it->second.mTimestamp = aTimestamp;
13177 it->second.mFlags = fFlags;
13178 }
13179 else
13180 mHWData->mGuestProperties.erase(it);
13181
13182 mData->mGuestPropertiesModified = TRUE;
13183 }
13184 else if (!fDelete)
13185 {
13186 HWData::GuestProperty prop;
13187 prop.strValue = aValue;
13188 prop.mTimestamp = aTimestamp;
13189 prop.mFlags = fFlags;
13190
13191 mHWData->mGuestProperties[aName] = prop;
13192 mData->mGuestPropertiesModified = TRUE;
13193 }
13194
13195 /*
13196 * Send a callback notification if appropriate
13197 */
13198 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13199 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13200 RTSTR_MAX,
13201 aName.c_str(),
13202 RTSTR_MAX, NULL)
13203 )
13204 {
13205 alock.release();
13206
13207 mParent->i_onGuestPropertyChange(mData->mUuid,
13208 Bstr(aName).raw(),
13209 Bstr(aValue).raw(),
13210 Bstr(aFlags).raw());
13211 }
13212 }
13213 catch (...)
13214 {
13215 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13216 }
13217 return S_OK;
13218#else
13219 ReturnComNotImplemented();
13220#endif
13221}
13222
13223
13224HRESULT SessionMachine::lockMedia()
13225{
13226 AutoMultiWriteLock2 alock(this->lockHandle(),
13227 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13228
13229 AssertReturn( mData->mMachineState == MachineState_Starting
13230 || mData->mMachineState == MachineState_Restoring
13231 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13232
13233 clearError();
13234 alock.release();
13235 return i_lockMedia();
13236}
13237
13238HRESULT SessionMachine::unlockMedia()
13239{
13240 HRESULT hrc = i_unlockMedia();
13241 return hrc;
13242}
13243
13244HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13245 ComPtr<IMediumAttachment> &aNewAttachment)
13246{
13247 // request the host lock first, since might be calling Host methods for getting host drives;
13248 // next, protect the media tree all the while we're in here, as well as our member variables
13249 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13250 this->lockHandle(),
13251 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13252
13253 IMediumAttachment *iAttach = aAttachment;
13254 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13255
13256 Bstr ctrlName;
13257 LONG lPort;
13258 LONG lDevice;
13259 bool fTempEject;
13260 {
13261 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13262
13263 /* Need to query the details first, as the IMediumAttachment reference
13264 * might be to the original settings, which we are going to change. */
13265 ctrlName = pAttach->i_getControllerName();
13266 lPort = pAttach->i_getPort();
13267 lDevice = pAttach->i_getDevice();
13268 fTempEject = pAttach->i_getTempEject();
13269 }
13270
13271 if (!fTempEject)
13272 {
13273 /* Remember previously mounted medium. The medium before taking the
13274 * backup is not necessarily the same thing. */
13275 ComObjPtr<Medium> oldmedium;
13276 oldmedium = pAttach->i_getMedium();
13277
13278 i_setModified(IsModified_Storage);
13279 mMediaData.backup();
13280
13281 // The backup operation makes the pAttach reference point to the
13282 // old settings. Re-get the correct reference.
13283 pAttach = i_findAttachment(mMediaData->mAttachments,
13284 ctrlName.raw(),
13285 lPort,
13286 lDevice);
13287
13288 {
13289 AutoCaller autoAttachCaller(this);
13290 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13291
13292 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13293 if (!oldmedium.isNull())
13294 oldmedium->i_removeBackReference(mData->mUuid);
13295
13296 pAttach->i_updateMedium(NULL);
13297 pAttach->i_updateEjected();
13298 }
13299
13300 i_setModified(IsModified_Storage);
13301 }
13302 else
13303 {
13304 {
13305 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13306 pAttach->i_updateEjected();
13307 }
13308 }
13309
13310 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13311
13312 return S_OK;
13313}
13314
13315// public methods only for internal purposes
13316/////////////////////////////////////////////////////////////////////////////
13317
13318#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13319/**
13320 * Called from the client watcher thread to check for expected or unexpected
13321 * death of the client process that has a direct session to this machine.
13322 *
13323 * On Win32 and on OS/2, this method is called only when we've got the
13324 * mutex (i.e. the client has either died or terminated normally) so it always
13325 * returns @c true (the client is terminated, the session machine is
13326 * uninitialized).
13327 *
13328 * On other platforms, the method returns @c true if the client process has
13329 * terminated normally or abnormally and the session machine was uninitialized,
13330 * and @c false if the client process is still alive.
13331 *
13332 * @note Locks this object for writing.
13333 */
13334bool SessionMachine::i_checkForDeath()
13335{
13336 Uninit::Reason reason;
13337 bool terminated = false;
13338
13339 /* Enclose autoCaller with a block because calling uninit() from under it
13340 * will deadlock. */
13341 {
13342 AutoCaller autoCaller(this);
13343 if (!autoCaller.isOk())
13344 {
13345 /* return true if not ready, to cause the client watcher to exclude
13346 * the corresponding session from watching */
13347 LogFlowThisFunc(("Already uninitialized!\n"));
13348 return true;
13349 }
13350
13351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13352
13353 /* Determine the reason of death: if the session state is Closing here,
13354 * everything is fine. Otherwise it means that the client did not call
13355 * OnSessionEnd() before it released the IPC semaphore. This may happen
13356 * either because the client process has abnormally terminated, or
13357 * because it simply forgot to call ISession::Close() before exiting. We
13358 * threat the latter also as an abnormal termination (see
13359 * Session::uninit() for details). */
13360 reason = mData->mSession.mState == SessionState_Unlocking ?
13361 Uninit::Normal :
13362 Uninit::Abnormal;
13363
13364 if (mClientToken)
13365 terminated = mClientToken->release();
13366 } /* AutoCaller block */
13367
13368 if (terminated)
13369 uninit(reason);
13370
13371 return terminated;
13372}
13373
13374void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13375{
13376 LogFlowThisFunc(("\n"));
13377
13378 strTokenId.setNull();
13379
13380 AutoCaller autoCaller(this);
13381 AssertComRCReturnVoid(autoCaller.rc());
13382
13383 Assert(mClientToken);
13384 if (mClientToken)
13385 mClientToken->getId(strTokenId);
13386}
13387#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13388IToken *SessionMachine::i_getToken()
13389{
13390 LogFlowThisFunc(("\n"));
13391
13392 AutoCaller autoCaller(this);
13393 AssertComRCReturn(autoCaller.rc(), NULL);
13394
13395 Assert(mClientToken);
13396 if (mClientToken)
13397 return mClientToken->getToken();
13398 else
13399 return NULL;
13400}
13401#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13402
13403Machine::ClientToken *SessionMachine::i_getClientToken()
13404{
13405 LogFlowThisFunc(("\n"));
13406
13407 AutoCaller autoCaller(this);
13408 AssertComRCReturn(autoCaller.rc(), NULL);
13409
13410 return mClientToken;
13411}
13412
13413
13414/**
13415 * @note Locks this object for reading.
13416 */
13417HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13418{
13419 LogFlowThisFunc(("\n"));
13420
13421 AutoCaller autoCaller(this);
13422 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13423
13424 ComPtr<IInternalSessionControl> directControl;
13425 {
13426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13427 directControl = mData->mSession.mDirectControl;
13428 }
13429
13430 /* ignore notifications sent after #OnSessionEnd() is called */
13431 if (!directControl)
13432 return S_OK;
13433
13434 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13435}
13436
13437/**
13438 * @note Locks this object for reading.
13439 */
13440HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13441 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13442 IN_BSTR aGuestIp, LONG aGuestPort)
13443{
13444 LogFlowThisFunc(("\n"));
13445
13446 AutoCaller autoCaller(this);
13447 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13448
13449 ComPtr<IInternalSessionControl> directControl;
13450 {
13451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13452 directControl = mData->mSession.mDirectControl;
13453 }
13454
13455 /* ignore notifications sent after #OnSessionEnd() is called */
13456 if (!directControl)
13457 return S_OK;
13458 /*
13459 * instead acting like callback we ask IVirtualBox deliver corresponding event
13460 */
13461
13462 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13463 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13464 return S_OK;
13465}
13466
13467/**
13468 * @note Locks this object for reading.
13469 */
13470HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13471{
13472 LogFlowThisFunc(("\n"));
13473
13474 AutoCaller autoCaller(this);
13475 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13476
13477 ComPtr<IInternalSessionControl> directControl;
13478 {
13479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13480 directControl = mData->mSession.mDirectControl;
13481 }
13482
13483 /* ignore notifications sent after #OnSessionEnd() is called */
13484 if (!directControl)
13485 return S_OK;
13486
13487 return directControl->OnSerialPortChange(serialPort);
13488}
13489
13490/**
13491 * @note Locks this object for reading.
13492 */
13493HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13494{
13495 LogFlowThisFunc(("\n"));
13496
13497 AutoCaller autoCaller(this);
13498 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13499
13500 ComPtr<IInternalSessionControl> directControl;
13501 {
13502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13503 directControl = mData->mSession.mDirectControl;
13504 }
13505
13506 /* ignore notifications sent after #OnSessionEnd() is called */
13507 if (!directControl)
13508 return S_OK;
13509
13510 return directControl->OnParallelPortChange(parallelPort);
13511}
13512
13513/**
13514 * @note Locks this object for reading.
13515 */
13516HRESULT SessionMachine::i_onStorageControllerChange()
13517{
13518 LogFlowThisFunc(("\n"));
13519
13520 AutoCaller autoCaller(this);
13521 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13522
13523 ComPtr<IInternalSessionControl> directControl;
13524 {
13525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13526 directControl = mData->mSession.mDirectControl;
13527 }
13528
13529 /* ignore notifications sent after #OnSessionEnd() is called */
13530 if (!directControl)
13531 return S_OK;
13532
13533 return directControl->OnStorageControllerChange();
13534}
13535
13536/**
13537 * @note Locks this object for reading.
13538 */
13539HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13540{
13541 LogFlowThisFunc(("\n"));
13542
13543 AutoCaller autoCaller(this);
13544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13545
13546 ComPtr<IInternalSessionControl> directControl;
13547 {
13548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13549 directControl = mData->mSession.mDirectControl;
13550 }
13551
13552 /* ignore notifications sent after #OnSessionEnd() is called */
13553 if (!directControl)
13554 return S_OK;
13555
13556 return directControl->OnMediumChange(aAttachment, aForce);
13557}
13558
13559/**
13560 * @note Locks this object for reading.
13561 */
13562HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13563{
13564 LogFlowThisFunc(("\n"));
13565
13566 AutoCaller autoCaller(this);
13567 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13568
13569 ComPtr<IInternalSessionControl> directControl;
13570 {
13571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13572 directControl = mData->mSession.mDirectControl;
13573 }
13574
13575 /* ignore notifications sent after #OnSessionEnd() is called */
13576 if (!directControl)
13577 return S_OK;
13578
13579 return directControl->OnCPUChange(aCPU, aRemove);
13580}
13581
13582HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13583{
13584 LogFlowThisFunc(("\n"));
13585
13586 AutoCaller autoCaller(this);
13587 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13588
13589 ComPtr<IInternalSessionControl> directControl;
13590 {
13591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13592 directControl = mData->mSession.mDirectControl;
13593 }
13594
13595 /* ignore notifications sent after #OnSessionEnd() is called */
13596 if (!directControl)
13597 return S_OK;
13598
13599 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13600}
13601
13602/**
13603 * @note Locks this object for reading.
13604 */
13605HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13606{
13607 LogFlowThisFunc(("\n"));
13608
13609 AutoCaller autoCaller(this);
13610 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13611
13612 ComPtr<IInternalSessionControl> directControl;
13613 {
13614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13615 directControl = mData->mSession.mDirectControl;
13616 }
13617
13618 /* ignore notifications sent after #OnSessionEnd() is called */
13619 if (!directControl)
13620 return S_OK;
13621
13622 return directControl->OnVRDEServerChange(aRestart);
13623}
13624
13625/**
13626 * @note Locks this object for reading.
13627 */
13628HRESULT SessionMachine::i_onVideoCaptureChange()
13629{
13630 LogFlowThisFunc(("\n"));
13631
13632 AutoCaller autoCaller(this);
13633 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13634
13635 ComPtr<IInternalSessionControl> directControl;
13636 {
13637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13638 directControl = mData->mSession.mDirectControl;
13639 }
13640
13641 /* ignore notifications sent after #OnSessionEnd() is called */
13642 if (!directControl)
13643 return S_OK;
13644
13645 return directControl->OnVideoCaptureChange();
13646}
13647
13648/**
13649 * @note Locks this object for reading.
13650 */
13651HRESULT SessionMachine::i_onUSBControllerChange()
13652{
13653 LogFlowThisFunc(("\n"));
13654
13655 AutoCaller autoCaller(this);
13656 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13657
13658 ComPtr<IInternalSessionControl> directControl;
13659 {
13660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13661 directControl = mData->mSession.mDirectControl;
13662 }
13663
13664 /* ignore notifications sent after #OnSessionEnd() is called */
13665 if (!directControl)
13666 return S_OK;
13667
13668 return directControl->OnUSBControllerChange();
13669}
13670
13671/**
13672 * @note Locks this object for reading.
13673 */
13674HRESULT SessionMachine::i_onSharedFolderChange()
13675{
13676 LogFlowThisFunc(("\n"));
13677
13678 AutoCaller autoCaller(this);
13679 AssertComRCReturnRC(autoCaller.rc());
13680
13681 ComPtr<IInternalSessionControl> directControl;
13682 {
13683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13684 directControl = mData->mSession.mDirectControl;
13685 }
13686
13687 /* ignore notifications sent after #OnSessionEnd() is called */
13688 if (!directControl)
13689 return S_OK;
13690
13691 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13692}
13693
13694/**
13695 * @note Locks this object for reading.
13696 */
13697HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13698{
13699 LogFlowThisFunc(("\n"));
13700
13701 AutoCaller autoCaller(this);
13702 AssertComRCReturnRC(autoCaller.rc());
13703
13704 ComPtr<IInternalSessionControl> directControl;
13705 {
13706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13707 directControl = mData->mSession.mDirectControl;
13708 }
13709
13710 /* ignore notifications sent after #OnSessionEnd() is called */
13711 if (!directControl)
13712 return S_OK;
13713
13714 return directControl->OnClipboardModeChange(aClipboardMode);
13715}
13716
13717/**
13718 * @note Locks this object for reading.
13719 */
13720HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13721{
13722 LogFlowThisFunc(("\n"));
13723
13724 AutoCaller autoCaller(this);
13725 AssertComRCReturnRC(autoCaller.rc());
13726
13727 ComPtr<IInternalSessionControl> directControl;
13728 {
13729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13730 directControl = mData->mSession.mDirectControl;
13731 }
13732
13733 /* ignore notifications sent after #OnSessionEnd() is called */
13734 if (!directControl)
13735 return S_OK;
13736
13737 return directControl->OnDnDModeChange(aDnDMode);
13738}
13739
13740/**
13741 * @note Locks this object for reading.
13742 */
13743HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13744{
13745 LogFlowThisFunc(("\n"));
13746
13747 AutoCaller autoCaller(this);
13748 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13749
13750 ComPtr<IInternalSessionControl> directControl;
13751 {
13752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13753 directControl = mData->mSession.mDirectControl;
13754 }
13755
13756 /* ignore notifications sent after #OnSessionEnd() is called */
13757 if (!directControl)
13758 return S_OK;
13759
13760 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13761}
13762
13763/**
13764 * @note Locks this object for reading.
13765 */
13766HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13767{
13768 LogFlowThisFunc(("\n"));
13769
13770 AutoCaller autoCaller(this);
13771 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13772
13773 ComPtr<IInternalSessionControl> directControl;
13774 {
13775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13776 directControl = mData->mSession.mDirectControl;
13777 }
13778
13779 /* ignore notifications sent after #OnSessionEnd() is called */
13780 if (!directControl)
13781 return S_OK;
13782
13783 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13784}
13785
13786/**
13787 * Returns @c true if this machine's USB controller reports it has a matching
13788 * filter for the given USB device and @c false otherwise.
13789 *
13790 * @note locks this object for reading.
13791 */
13792bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13793{
13794 AutoCaller autoCaller(this);
13795 /* silently return if not ready -- this method may be called after the
13796 * direct machine session has been called */
13797 if (!autoCaller.isOk())
13798 return false;
13799
13800#ifdef VBOX_WITH_USB
13801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13802
13803 switch (mData->mMachineState)
13804 {
13805 case MachineState_Starting:
13806 case MachineState_Restoring:
13807 case MachineState_TeleportingIn:
13808 case MachineState_Paused:
13809 case MachineState_Running:
13810 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13811 * elsewhere... */
13812 alock.release();
13813 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13814 default: break;
13815 }
13816#else
13817 NOREF(aDevice);
13818 NOREF(aMaskedIfs);
13819#endif
13820 return false;
13821}
13822
13823/**
13824 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13825 */
13826HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13827 IVirtualBoxErrorInfo *aError,
13828 ULONG aMaskedIfs,
13829 const com::Utf8Str &aCaptureFilename)
13830{
13831 LogFlowThisFunc(("\n"));
13832
13833 AutoCaller autoCaller(this);
13834
13835 /* This notification may happen after the machine object has been
13836 * uninitialized (the session was closed), so don't assert. */
13837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13838
13839 ComPtr<IInternalSessionControl> directControl;
13840 {
13841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13842 directControl = mData->mSession.mDirectControl;
13843 }
13844
13845 /* fail on notifications sent after #OnSessionEnd() is called, it is
13846 * expected by the caller */
13847 if (!directControl)
13848 return E_FAIL;
13849
13850 /* No locks should be held at this point. */
13851 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13852 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13853
13854 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
13855}
13856
13857/**
13858 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13859 */
13860HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13861 IVirtualBoxErrorInfo *aError)
13862{
13863 LogFlowThisFunc(("\n"));
13864
13865 AutoCaller autoCaller(this);
13866
13867 /* This notification may happen after the machine object has been
13868 * uninitialized (the session was closed), so don't assert. */
13869 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13870
13871 ComPtr<IInternalSessionControl> directControl;
13872 {
13873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13874 directControl = mData->mSession.mDirectControl;
13875 }
13876
13877 /* fail on notifications sent after #OnSessionEnd() is called, it is
13878 * expected by the caller */
13879 if (!directControl)
13880 return E_FAIL;
13881
13882 /* No locks should be held at this point. */
13883 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13884 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13885
13886 return directControl->OnUSBDeviceDetach(aId, aError);
13887}
13888
13889// protected methods
13890/////////////////////////////////////////////////////////////////////////////
13891
13892/**
13893 * Helper method to finalize saving the state.
13894 *
13895 * @note Must be called from under this object's lock.
13896 *
13897 * @param aRc S_OK if the snapshot has been taken successfully
13898 * @param aErrMsg human readable error message for failure
13899 *
13900 * @note Locks mParent + this objects for writing.
13901 */
13902HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13903{
13904 LogFlowThisFuncEnter();
13905
13906 AutoCaller autoCaller(this);
13907 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13908
13909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13910
13911 HRESULT rc = S_OK;
13912
13913 if (SUCCEEDED(aRc))
13914 {
13915 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13916
13917 /* save all VM settings */
13918 rc = i_saveSettings(NULL);
13919 // no need to check whether VirtualBox.xml needs saving also since
13920 // we can't have a name change pending at this point
13921 }
13922 else
13923 {
13924 // delete the saved state file (it might have been already created);
13925 // we need not check whether this is shared with a snapshot here because
13926 // we certainly created this saved state file here anew
13927 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13928 }
13929
13930 /* notify the progress object about operation completion */
13931 Assert(mConsoleTaskData.mProgress);
13932 if (SUCCEEDED(aRc))
13933 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13934 else
13935 {
13936 if (aErrMsg.length())
13937 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13938 COM_IIDOF(ISession),
13939 getComponentName(),
13940 aErrMsg.c_str());
13941 else
13942 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13943 }
13944
13945 /* clear out the temporary saved state data */
13946 mConsoleTaskData.mLastState = MachineState_Null;
13947 mConsoleTaskData.strStateFilePath.setNull();
13948 mConsoleTaskData.mProgress.setNull();
13949
13950 LogFlowThisFuncLeave();
13951 return rc;
13952}
13953
13954/**
13955 * Deletes the given file if it is no longer in use by either the current machine state
13956 * (if the machine is "saved") or any of the machine's snapshots.
13957 *
13958 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13959 * but is different for each SnapshotMachine. When calling this, the order of calling this
13960 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13961 * is therefore critical. I know, it's all rather messy.
13962 *
13963 * @param strStateFile
13964 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13965 * the test for whether the saved state file is in use.
13966 */
13967void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13968 Snapshot *pSnapshotToIgnore)
13969{
13970 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13971 if ( (strStateFile.isNotEmpty())
13972 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13973 )
13974 // ... and it must also not be shared with other snapshots
13975 if ( !mData->mFirstSnapshot
13976 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13977 // this checks the SnapshotMachine's state file paths
13978 )
13979 RTFileDelete(strStateFile.c_str());
13980}
13981
13982/**
13983 * Locks the attached media.
13984 *
13985 * All attached hard disks are locked for writing and DVD/floppy are locked for
13986 * reading. Parents of attached hard disks (if any) are locked for reading.
13987 *
13988 * This method also performs accessibility check of all media it locks: if some
13989 * media is inaccessible, the method will return a failure and a bunch of
13990 * extended error info objects per each inaccessible medium.
13991 *
13992 * Note that this method is atomic: if it returns a success, all media are
13993 * locked as described above; on failure no media is locked at all (all
13994 * succeeded individual locks will be undone).
13995 *
13996 * The caller is responsible for doing the necessary state sanity checks.
13997 *
13998 * The locks made by this method must be undone by calling #unlockMedia() when
13999 * no more needed.
14000 */
14001HRESULT SessionMachine::i_lockMedia()
14002{
14003 AutoCaller autoCaller(this);
14004 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14005
14006 AutoMultiWriteLock2 alock(this->lockHandle(),
14007 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14008
14009 /* bail out if trying to lock things with already set up locking */
14010 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14011
14012 MultiResult mrc(S_OK);
14013
14014 /* Collect locking information for all medium objects attached to the VM. */
14015 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14016 it != mMediaData->mAttachments.end();
14017 ++it)
14018 {
14019 MediumAttachment* pAtt = *it;
14020 DeviceType_T devType = pAtt->i_getType();
14021 Medium *pMedium = pAtt->i_getMedium();
14022
14023 MediumLockList *pMediumLockList(new MediumLockList());
14024 // There can be attachments without a medium (floppy/dvd), and thus
14025 // it's impossible to create a medium lock list. It still makes sense
14026 // to have the empty medium lock list in the map in case a medium is
14027 // attached later.
14028 if (pMedium != NULL)
14029 {
14030 MediumType_T mediumType = pMedium->i_getType();
14031 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14032 || mediumType == MediumType_Shareable;
14033 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14034
14035 alock.release();
14036 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14037 !fIsReadOnlyLock /* fMediumLockWrite */,
14038 NULL,
14039 *pMediumLockList);
14040 alock.acquire();
14041 if (FAILED(mrc))
14042 {
14043 delete pMediumLockList;
14044 mData->mSession.mLockedMedia.Clear();
14045 break;
14046 }
14047 }
14048
14049 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14050 if (FAILED(rc))
14051 {
14052 mData->mSession.mLockedMedia.Clear();
14053 mrc = setError(rc,
14054 tr("Collecting locking information for all attached media failed"));
14055 break;
14056 }
14057 }
14058
14059 if (SUCCEEDED(mrc))
14060 {
14061 /* Now lock all media. If this fails, nothing is locked. */
14062 alock.release();
14063 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14064 alock.acquire();
14065 if (FAILED(rc))
14066 {
14067 mrc = setError(rc,
14068 tr("Locking of attached media failed"));
14069 }
14070 }
14071
14072 return mrc;
14073}
14074
14075/**
14076 * Undoes the locks made by by #lockMedia().
14077 */
14078HRESULT SessionMachine::i_unlockMedia()
14079{
14080 AutoCaller autoCaller(this);
14081 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14082
14083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14084
14085 /* we may be holding important error info on the current thread;
14086 * preserve it */
14087 ErrorInfoKeeper eik;
14088
14089 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14090 AssertComRC(rc);
14091 return rc;
14092}
14093
14094/**
14095 * Helper to change the machine state (reimplementation).
14096 *
14097 * @note Locks this object for writing.
14098 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14099 * it can cause crashes in random places due to unexpectedly committing
14100 * the current settings. The caller is responsible for that. The call
14101 * to saveStateSettings is fine, because this method does not commit.
14102 */
14103HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14104{
14105 LogFlowThisFuncEnter();
14106 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14107
14108 AutoCaller autoCaller(this);
14109 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14110
14111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14112
14113 MachineState_T oldMachineState = mData->mMachineState;
14114
14115 AssertMsgReturn(oldMachineState != aMachineState,
14116 ("oldMachineState=%s, aMachineState=%s\n",
14117 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14118 E_FAIL);
14119
14120 HRESULT rc = S_OK;
14121
14122 int stsFlags = 0;
14123 bool deleteSavedState = false;
14124
14125 /* detect some state transitions */
14126
14127 if ( ( oldMachineState == MachineState_Saved
14128 && aMachineState == MachineState_Restoring)
14129 || ( ( oldMachineState == MachineState_PoweredOff
14130 || oldMachineState == MachineState_Teleported
14131 || oldMachineState == MachineState_Aborted
14132 )
14133 && ( aMachineState == MachineState_TeleportingIn
14134 || aMachineState == MachineState_Starting
14135 )
14136 )
14137 )
14138 {
14139 /* The EMT thread is about to start */
14140
14141 /* Nothing to do here for now... */
14142
14143 /// @todo NEWMEDIA don't let mDVDDrive and other children
14144 /// change anything when in the Starting/Restoring state
14145 }
14146 else if ( ( oldMachineState == MachineState_Running
14147 || oldMachineState == MachineState_Paused
14148 || oldMachineState == MachineState_Teleporting
14149 || oldMachineState == MachineState_LiveSnapshotting
14150 || oldMachineState == MachineState_Stuck
14151 || oldMachineState == MachineState_Starting
14152 || oldMachineState == MachineState_Stopping
14153 || oldMachineState == MachineState_Saving
14154 || oldMachineState == MachineState_Restoring
14155 || oldMachineState == MachineState_TeleportingPausedVM
14156 || oldMachineState == MachineState_TeleportingIn
14157 )
14158 && ( aMachineState == MachineState_PoweredOff
14159 || aMachineState == MachineState_Saved
14160 || aMachineState == MachineState_Teleported
14161 || aMachineState == MachineState_Aborted
14162 )
14163 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14164 * snapshot */
14165 && ( mConsoleTaskData.mSnapshot.isNull()
14166 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14167 )
14168 )
14169 {
14170 /* The EMT thread has just stopped, unlock attached media. Note that as
14171 * opposed to locking that is done from Console, we do unlocking here
14172 * because the VM process may have aborted before having a chance to
14173 * properly unlock all media it locked. */
14174
14175 unlockMedia();
14176 }
14177
14178 if (oldMachineState == MachineState_Restoring)
14179 {
14180 if (aMachineState != MachineState_Saved)
14181 {
14182 /*
14183 * delete the saved state file once the machine has finished
14184 * restoring from it (note that Console sets the state from
14185 * Restoring to Saved if the VM couldn't restore successfully,
14186 * to give the user an ability to fix an error and retry --
14187 * we keep the saved state file in this case)
14188 */
14189 deleteSavedState = true;
14190 }
14191 }
14192 else if ( oldMachineState == MachineState_Saved
14193 && ( aMachineState == MachineState_PoweredOff
14194 || aMachineState == MachineState_Aborted
14195 || aMachineState == MachineState_Teleported
14196 )
14197 )
14198 {
14199 /*
14200 * delete the saved state after Console::ForgetSavedState() is called
14201 * or if the VM process (owning a direct VM session) crashed while the
14202 * VM was Saved
14203 */
14204
14205 /// @todo (dmik)
14206 // Not sure that deleting the saved state file just because of the
14207 // client death before it attempted to restore the VM is a good
14208 // thing. But when it crashes we need to go to the Aborted state
14209 // which cannot have the saved state file associated... The only
14210 // way to fix this is to make the Aborted condition not a VM state
14211 // but a bool flag: i.e., when a crash occurs, set it to true and
14212 // change the state to PoweredOff or Saved depending on the
14213 // saved state presence.
14214
14215 deleteSavedState = true;
14216 mData->mCurrentStateModified = TRUE;
14217 stsFlags |= SaveSTS_CurStateModified;
14218 }
14219
14220 if ( aMachineState == MachineState_Starting
14221 || aMachineState == MachineState_Restoring
14222 || aMachineState == MachineState_TeleportingIn
14223 )
14224 {
14225 /* set the current state modified flag to indicate that the current
14226 * state is no more identical to the state in the
14227 * current snapshot */
14228 if (!mData->mCurrentSnapshot.isNull())
14229 {
14230 mData->mCurrentStateModified = TRUE;
14231 stsFlags |= SaveSTS_CurStateModified;
14232 }
14233 }
14234
14235 if (deleteSavedState)
14236 {
14237 if (mRemoveSavedState)
14238 {
14239 Assert(!mSSData->strStateFilePath.isEmpty());
14240
14241 // it is safe to delete the saved state file if ...
14242 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14243 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14244 // ... none of the snapshots share the saved state file
14245 )
14246 RTFileDelete(mSSData->strStateFilePath.c_str());
14247 }
14248
14249 mSSData->strStateFilePath.setNull();
14250 stsFlags |= SaveSTS_StateFilePath;
14251 }
14252
14253 /* redirect to the underlying peer machine */
14254 mPeer->i_setMachineState(aMachineState);
14255
14256 if ( aMachineState == MachineState_PoweredOff
14257 || aMachineState == MachineState_Teleported
14258 || aMachineState == MachineState_Aborted
14259 || aMachineState == MachineState_Saved)
14260 {
14261 /* the machine has stopped execution
14262 * (or the saved state file was adopted) */
14263 stsFlags |= SaveSTS_StateTimeStamp;
14264 }
14265
14266 if ( ( oldMachineState == MachineState_PoweredOff
14267 || oldMachineState == MachineState_Aborted
14268 || oldMachineState == MachineState_Teleported
14269 )
14270 && aMachineState == MachineState_Saved)
14271 {
14272 /* the saved state file was adopted */
14273 Assert(!mSSData->strStateFilePath.isEmpty());
14274 stsFlags |= SaveSTS_StateFilePath;
14275 }
14276
14277#ifdef VBOX_WITH_GUEST_PROPS
14278 if ( aMachineState == MachineState_PoweredOff
14279 || aMachineState == MachineState_Aborted
14280 || aMachineState == MachineState_Teleported)
14281 {
14282 /* Make sure any transient guest properties get removed from the
14283 * property store on shutdown. */
14284 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14285
14286 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14287 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14288 while (it != llGuestProperties.end())
14289 {
14290 const settings::GuestProperty &prop = *it;
14291 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14292 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14293 {
14294 it = llGuestProperties.erase(it);
14295 fNeedsSaving = true;
14296 }
14297 else
14298 {
14299 ++it;
14300 }
14301 }
14302
14303 if (fNeedsSaving)
14304 {
14305 mData->mCurrentStateModified = TRUE;
14306 stsFlags |= SaveSTS_CurStateModified;
14307 }
14308 }
14309#endif /* VBOX_WITH_GUEST_PROPS */
14310
14311 rc = i_saveStateSettings(stsFlags);
14312
14313 if ( ( oldMachineState != MachineState_PoweredOff
14314 && oldMachineState != MachineState_Aborted
14315 && oldMachineState != MachineState_Teleported
14316 )
14317 && ( aMachineState == MachineState_PoweredOff
14318 || aMachineState == MachineState_Aborted
14319 || aMachineState == MachineState_Teleported
14320 )
14321 )
14322 {
14323 /* we've been shut down for any reason */
14324 /* no special action so far */
14325 }
14326
14327 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14328 LogFlowThisFuncLeave();
14329 return rc;
14330}
14331
14332/**
14333 * Sends the current machine state value to the VM process.
14334 *
14335 * @note Locks this object for reading, then calls a client process.
14336 */
14337HRESULT SessionMachine::i_updateMachineStateOnClient()
14338{
14339 AutoCaller autoCaller(this);
14340 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14341
14342 ComPtr<IInternalSessionControl> directControl;
14343 {
14344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14345 AssertReturn(!!mData, E_FAIL);
14346 directControl = mData->mSession.mDirectControl;
14347
14348 /* directControl may be already set to NULL here in #OnSessionEnd()
14349 * called too early by the direct session process while there is still
14350 * some operation (like deleting the snapshot) in progress. The client
14351 * process in this case is waiting inside Session::close() for the
14352 * "end session" process object to complete, while #uninit() called by
14353 * #checkForDeath() on the Watcher thread is waiting for the pending
14354 * operation to complete. For now, we accept this inconsistent behavior
14355 * and simply do nothing here. */
14356
14357 if (mData->mSession.mState == SessionState_Unlocking)
14358 return S_OK;
14359
14360 AssertReturn(!directControl.isNull(), E_FAIL);
14361 }
14362
14363 return directControl->UpdateMachineState(mData->mMachineState);
14364}
14365
14366HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14367{
14368 NOREF(aRemove);
14369 ReturnComNotImplemented();
14370}
14371
14372HRESULT Machine::updateState(MachineState_T aState)
14373{
14374 NOREF(aState);
14375 ReturnComNotImplemented();
14376}
14377
14378HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14379{
14380 NOREF(aProgress);
14381 ReturnComNotImplemented();
14382}
14383
14384HRESULT Machine::endPowerUp(LONG aResult)
14385{
14386 NOREF(aResult);
14387 ReturnComNotImplemented();
14388}
14389
14390HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14391{
14392 NOREF(aProgress);
14393 ReturnComNotImplemented();
14394}
14395
14396HRESULT Machine::endPoweringDown(LONG aResult,
14397 const com::Utf8Str &aErrMsg)
14398{
14399 NOREF(aResult);
14400 NOREF(aErrMsg);
14401 ReturnComNotImplemented();
14402}
14403
14404HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14405 BOOL *aMatched,
14406 ULONG *aMaskedInterfaces)
14407{
14408 NOREF(aDevice);
14409 NOREF(aMatched);
14410 NOREF(aMaskedInterfaces);
14411 ReturnComNotImplemented();
14412
14413}
14414
14415HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14416{
14417 NOREF(aId); NOREF(aCaptureFilename);
14418 ReturnComNotImplemented();
14419}
14420
14421HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14422 BOOL aDone)
14423{
14424 NOREF(aId);
14425 NOREF(aDone);
14426 ReturnComNotImplemented();
14427}
14428
14429HRESULT Machine::autoCaptureUSBDevices()
14430{
14431 ReturnComNotImplemented();
14432}
14433
14434HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14435{
14436 NOREF(aDone);
14437 ReturnComNotImplemented();
14438}
14439
14440HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14441 ComPtr<IProgress> &aProgress)
14442{
14443 NOREF(aSession);
14444 NOREF(aProgress);
14445 ReturnComNotImplemented();
14446}
14447
14448HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14449 com::Utf8Str &aStateFilePath)
14450{
14451 NOREF(aProgress);
14452 NOREF(aStateFilePath);
14453 ReturnComNotImplemented();
14454}
14455
14456HRESULT Machine::endSavingState(LONG aResult,
14457 const com::Utf8Str &aErrMsg)
14458{
14459 NOREF(aResult);
14460 NOREF(aErrMsg);
14461 ReturnComNotImplemented();
14462}
14463
14464HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14465{
14466 NOREF(aSavedStateFile);
14467 ReturnComNotImplemented();
14468}
14469
14470HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14471 const com::Utf8Str &aName,
14472 const com::Utf8Str &aDescription,
14473 const ComPtr<IProgress> &aConsoleProgress,
14474 BOOL aFTakingSnapshotOnline,
14475 com::Utf8Str &aStateFilePath)
14476{
14477 NOREF(aInitiator);
14478 NOREF(aName);
14479 NOREF(aDescription);
14480 NOREF(aConsoleProgress);
14481 NOREF(aFTakingSnapshotOnline);
14482 NOREF(aStateFilePath);
14483 ReturnComNotImplemented();
14484}
14485
14486HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14487{
14488 NOREF(aSuccess);
14489 ReturnComNotImplemented();
14490}
14491
14492HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14493 const com::Guid &aStartId,
14494 const com::Guid &aEndId,
14495 BOOL aDeleteAllChildren,
14496 MachineState_T *aMachineState,
14497 ComPtr<IProgress> &aProgress)
14498{
14499 NOREF(aInitiator);
14500 NOREF(aStartId);
14501 NOREF(aEndId);
14502 NOREF(aDeleteAllChildren);
14503 NOREF(aMachineState);
14504 NOREF(aProgress);
14505 ReturnComNotImplemented();
14506}
14507
14508HRESULT Machine::finishOnlineMergeMedium()
14509{
14510 ReturnComNotImplemented();
14511}
14512
14513HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14514 const ComPtr<ISnapshot> &aSnapshot,
14515 MachineState_T *aMachineState,
14516 ComPtr<IProgress> &aProgress)
14517{
14518 NOREF(aInitiator);
14519 NOREF(aSnapshot);
14520 NOREF(aMachineState);
14521 NOREF(aProgress);
14522 ReturnComNotImplemented();
14523}
14524
14525HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14526 std::vector<com::Utf8Str> &aValues,
14527 std::vector<LONG64> &aTimestamps,
14528 std::vector<com::Utf8Str> &aFlags)
14529{
14530 NOREF(aNames);
14531 NOREF(aValues);
14532 NOREF(aTimestamps);
14533 NOREF(aFlags);
14534 ReturnComNotImplemented();
14535}
14536
14537HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14538 const com::Utf8Str &aValue,
14539 LONG64 aTimestamp,
14540 const com::Utf8Str &aFlags)
14541{
14542 NOREF(aName);
14543 NOREF(aValue);
14544 NOREF(aTimestamp);
14545 NOREF(aFlags);
14546 ReturnComNotImplemented();
14547}
14548
14549HRESULT Machine::lockMedia()
14550{
14551 ReturnComNotImplemented();
14552}
14553
14554HRESULT Machine::unlockMedia()
14555{
14556 ReturnComNotImplemented();
14557}
14558
14559HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14560 ComPtr<IMediumAttachment> &aNewAttachment)
14561{
14562 NOREF(aAttachment);
14563 NOREF(aNewAttachment);
14564 ReturnComNotImplemented();
14565}
14566
14567HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14568 ULONG aCpuUser,
14569 ULONG aCpuKernel,
14570 ULONG aCpuIdle,
14571 ULONG aMemTotal,
14572 ULONG aMemFree,
14573 ULONG aMemBalloon,
14574 ULONG aMemShared,
14575 ULONG aMemCache,
14576 ULONG aPagedTotal,
14577 ULONG aMemAllocTotal,
14578 ULONG aMemFreeTotal,
14579 ULONG aMemBalloonTotal,
14580 ULONG aMemSharedTotal,
14581 ULONG aVmNetRx,
14582 ULONG aVmNetTx)
14583{
14584 NOREF(aValidStats);
14585 NOREF(aCpuUser);
14586 NOREF(aCpuKernel);
14587 NOREF(aCpuIdle);
14588 NOREF(aMemTotal);
14589 NOREF(aMemFree);
14590 NOREF(aMemBalloon);
14591 NOREF(aMemShared);
14592 NOREF(aMemCache);
14593 NOREF(aPagedTotal);
14594 NOREF(aMemAllocTotal);
14595 NOREF(aMemFreeTotal);
14596 NOREF(aMemBalloonTotal);
14597 NOREF(aMemSharedTotal);
14598 NOREF(aVmNetRx);
14599 NOREF(aVmNetTx);
14600 ReturnComNotImplemented();
14601}
Note: See TracBrowser for help on using the repository browser.

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