VirtualBox

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

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

Main/Machine+Settings: Changing extradata for a Machine instance is special, there is no "old" state available for comparing, and thus it must force saving. The settings change optimizes out the useless effort to do a deep comparison of two maps.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 498.5 KB
Line 
1/* $Id: MachineImpl.cpp 52682 2014-09-10 16:43:11Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureMaxTime = 0;
169 mVideoCaptureMaxFileSize = 0;
170 mVideoCaptureEnabled = false;
171 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
172 maVideoCaptureScreens[i] = true;
173
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExUXEnabled = true;
184 mHWVirtExForceEnabled = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mSyntheticCpu = false;
192 mTripleFaultReset = false;
193 mHPETEnabled = false;
194
195 /* default boot order: floppy - DVD - HDD */
196 mBootOrder[0] = DeviceType_Floppy;
197 mBootOrder[1] = DeviceType_DVD;
198 mBootOrder[2] = DeviceType_HardDisk;
199 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
200 mBootOrder[i] = DeviceType_Null;
201
202 mClipboardMode = ClipboardMode_Disabled;
203 mDnDMode = DnDMode_Disabled;
204 mGuestPropertyNotificationPatterns = "";
205
206 mFirmwareType = FirmwareType_BIOS;
207 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
208 mPointingHIDType = PointingHIDType_PS2Mouse;
209 mChipsetType = ChipsetType_PIIX3;
210 mParavirtProvider = ParavirtProvider_Default;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Apply BIOS defaults */
345 mBIOSSettings->i_applyDefaults(aOsType);
346
347 /* Apply network adapters defaults */
348 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
349 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
350
351 /* Apply serial port defaults */
352 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
353 mSerialPorts[slot]->i_applyDefaults(aOsType);
354
355 /* Let the OS type select 64-bit ness. */
356 mHWData->mLongMode = aOsType->i_is64Bit()
357 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
358 }
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param aConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param strName Name for the new machine; this overrides what is specified in config and is used
511 * for the settings file as well.
512 * @param config Machine configuration loaded and parsed from XML.
513 *
514 * @return Success indicator. if not S_OK, the machine object is invalid
515 */
516HRESULT Machine::init(VirtualBox *aParent,
517 const Utf8Str &strName,
518 const settings::MachineConfigFile &config)
519{
520 LogFlowThisFuncEnter();
521
522 /* Enclose the state transition NotReady->InInit->Ready */
523 AutoInitSpan autoInitSpan(this);
524 AssertReturn(autoInitSpan.isOk(), E_FAIL);
525
526 Utf8Str strConfigFile;
527 aParent->i_getDefaultMachineFolder(strConfigFile);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(".vbox");
533
534 HRESULT rc = initImpl(aParent, strConfigFile);
535 if (FAILED(rc)) return rc;
536
537 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
538 if (FAILED(rc)) return rc;
539
540 rc = initDataAndChildObjects();
541
542 if (SUCCEEDED(rc))
543 {
544 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
545 mData->mAccessible = TRUE;
546
547 // create empty machine config for instance data
548 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
549
550 // generate fresh UUID, ignore machine config
551 unconst(mData->mUuid).create();
552
553 rc = i_loadMachineDataFromSettings(config,
554 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
555
556 // override VM name as well, it may be different
557 mUserData->s.strName = strName;
558
559 if (SUCCEEDED(rc))
560 {
561 /* At this point the changing of the current state modification
562 * flag is allowed. */
563 i_allowStateModification();
564
565 /* commit all changes made during the initialization */
566 i_commit();
567 }
568 }
569
570 /* Confirm a successful initialization when it's the case */
571 if (SUCCEEDED(rc))
572 {
573 if (mData->mAccessible)
574 autoInitSpan.setSucceeded();
575 else
576 {
577 /* Ignore all errors from unregistering, they would destroy
578- * the more interesting error information we already have,
579- * pinpointing the issue with the VM config. */
580 ErrorInfoKeeper eik;
581
582 autoInitSpan.setLimited();
583
584 // uninit media from this machine's media registry, or else
585 // reloading the settings will fail
586 mParent->i_unregisterMachineMedia(i_getId());
587 }
588 }
589
590 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
591 "rc=%08X\n",
592 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
593 mData->mRegistered, mData->mAccessible, rc));
594
595 LogFlowThisFuncLeave();
596
597 return rc;
598}
599
600/**
601 * Shared code between the various init() implementations.
602 * @param aParent
603 * @return
604 */
605HRESULT Machine::initImpl(VirtualBox *aParent,
606 const Utf8Str &strConfigFile)
607{
608 LogFlowThisFuncEnter();
609
610 AssertReturn(aParent, E_INVALIDARG);
611 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
612
613 HRESULT rc = S_OK;
614
615 /* share the parent weakly */
616 unconst(mParent) = aParent;
617
618 /* allocate the essential machine data structure (the rest will be
619 * allocated later by initDataAndChildObjects() */
620 mData.allocate();
621
622 /* memorize the config file name (as provided) */
623 mData->m_strConfigFile = strConfigFile;
624
625 /* get the full file name */
626 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
627 if (RT_FAILURE(vrc1))
628 return setError(VBOX_E_FILE_ERROR,
629 tr("Invalid machine settings file name '%s' (%Rrc)"),
630 strConfigFile.c_str(),
631 vrc1);
632
633 LogFlowThisFuncLeave();
634
635 return rc;
636}
637
638/**
639 * Tries to create a machine settings file in the path stored in the machine
640 * instance data. Used when a new machine is created to fail gracefully if
641 * the settings file could not be written (e.g. because machine dir is read-only).
642 * @return
643 */
644HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
645{
646 HRESULT rc = S_OK;
647
648 // when we create a new machine, we must be able to create the settings file
649 RTFILE f = NIL_RTFILE;
650 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
651 if ( RT_SUCCESS(vrc)
652 || vrc == VERR_SHARING_VIOLATION
653 )
654 {
655 if (RT_SUCCESS(vrc))
656 RTFileClose(f);
657 if (!fForceOverwrite)
658 rc = setError(VBOX_E_FILE_ERROR,
659 tr("Machine settings file '%s' already exists"),
660 mData->m_strConfigFileFull.c_str());
661 else
662 {
663 /* try to delete the config file, as otherwise the creation
664 * of a new settings file will fail. */
665 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
666 if (RT_FAILURE(vrc2))
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Could not delete the existing settings file '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(), vrc2);
670 }
671 }
672 else if ( vrc != VERR_FILE_NOT_FOUND
673 && vrc != VERR_PATH_NOT_FOUND
674 )
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Invalid machine settings file name '%s' (%Rrc)"),
677 mData->m_strConfigFileFull.c_str(),
678 vrc);
679 return rc;
680}
681
682/**
683 * Initializes the registered machine by loading the settings file.
684 * This method is separated from #init() in order to make it possible to
685 * retry the operation after VirtualBox startup instead of refusing to
686 * startup the whole VirtualBox server in case if the settings file of some
687 * registered VM is invalid or inaccessible.
688 *
689 * @note Must be always called from this object's write lock
690 * (unless called from #init() that doesn't need any locking).
691 * @note Locks the mUSBController method for writing.
692 * @note Subclasses must not call this method.
693 */
694HRESULT Machine::i_registeredInit()
695{
696 AssertReturn(!i_isSessionMachine(), E_FAIL);
697 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
698 AssertReturn(mData->mUuid.isValid(), E_FAIL);
699 AssertReturn(!mData->mAccessible, E_FAIL);
700
701 HRESULT rc = initDataAndChildObjects();
702
703 if (SUCCEEDED(rc))
704 {
705 /* Temporarily reset the registered flag in order to let setters
706 * potentially called from loadSettings() succeed (isMutable() used in
707 * all setters will return FALSE for a Machine instance if mRegistered
708 * is TRUE). */
709 mData->mRegistered = FALSE;
710
711 try
712 {
713 // load and parse machine XML; this will throw on XML or logic errors
714 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
715
716 if (mData->mUuid != mData->pMachineConfigFile->uuid)
717 throw setError(E_FAIL,
718 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
719 mData->pMachineConfigFile->uuid.raw(),
720 mData->m_strConfigFileFull.c_str(),
721 mData->mUuid.toString().c_str(),
722 mParent->i_settingsFilePath().c_str());
723
724 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
725 NULL /* const Guid *puuidRegistry */);
726 if (FAILED(rc)) throw rc;
727 }
728 catch (HRESULT err)
729 {
730 /* we assume that error info is set by the thrower */
731 rc = err;
732 }
733 catch (...)
734 {
735 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
736 }
737
738 /* Restore the registered flag (even on failure) */
739 mData->mRegistered = TRUE;
740 }
741
742 if (SUCCEEDED(rc))
743 {
744 /* Set mAccessible to TRUE only if we successfully locked and loaded
745 * the settings file */
746 mData->mAccessible = TRUE;
747
748 /* commit all changes made during loading the settings file */
749 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
750 /// @todo r=klaus for some reason the settings loading logic backs up
751 // the settings, and therefore a commit is needed. Should probably be changed.
752 }
753 else
754 {
755 /* If the machine is registered, then, instead of returning a
756 * failure, we mark it as inaccessible and set the result to
757 * success to give it a try later */
758
759 /* fetch the current error info */
760 mData->mAccessError = com::ErrorInfo();
761 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
762 mData->mUuid.raw(),
763 mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it does
831 * VirtualBox::addCaller() for the duration of the
832 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 LogWarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1048 it != mUserData->s.llGroups.end(); ++it, ++i)
1049 aGroups[i] = (*it);
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1055{
1056 StringsList llGroups;
1057 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1058 if (FAILED(rc))
1059 return rc;
1060
1061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 // changing machine groups is possible while the VM is offline
1064 rc = i_checkStateDependency(OfflineStateDep);
1065 if (FAILED(rc)) return rc;
1066
1067 i_setModified(IsModified_MachineData);
1068 mUserData.backup();
1069 mUserData->s.llGroups = llGroups;
1070
1071 return S_OK;
1072}
1073
1074HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1075{
1076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 aOSTypeId = mUserData->s.strOsType;
1079
1080 return S_OK;
1081}
1082
1083HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1084{
1085 /* look up the object by Id to check it is valid */
1086 ComPtr<IGuestOSType> guestOSType;
1087 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1088 if (FAILED(rc)) return rc;
1089
1090 /* when setting, always use the "etalon" value for consistency -- lookup
1091 * by ID is case-insensitive and the input value may have different case */
1092 Bstr osTypeId;
1093 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1094 if (FAILED(rc)) return rc;
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 *aParavirtProvider = mHWData->mParavirtProvider;
1223
1224 return S_OK;
1225}
1226
1227HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 HRESULT rc = i_checkStateDependency(MutableStateDep);
1232 if (FAILED(rc)) return rc;
1233
1234 if (aParavirtProvider != mHWData->mParavirtProvider)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtProvider = aParavirtProvider;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249 switch (mHWData->mParavirtProvider)
1250 {
1251 case ParavirtProvider_None:
1252 case ParavirtProvider_HyperV:
1253 case ParavirtProvider_Minimal:
1254 break;
1255
1256 /* Resolve dynamic provider types to the effective types. */
1257 default:
1258 {
1259 ComPtr<IGuestOSType> ptrGuestOSType;
1260 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1261 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1262
1263 Bstr guestTypeFamilyId;
1264 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1266 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1267
1268 switch (mHWData->mParavirtProvider)
1269 {
1270 case ParavirtProvider_Legacy:
1271 {
1272 if (fOsXGuest)
1273 *aParavirtProvider = ParavirtProvider_Minimal;
1274 else
1275 *aParavirtProvider = ParavirtProvider_None;
1276 break;
1277 }
1278
1279 case ParavirtProvider_Default:
1280 {
1281 if (fOsXGuest)
1282 *aParavirtProvider = ParavirtProvider_Minimal;
1283 else if ( mUserData->s.strOsType == "Windows81"
1284 || mUserData->s.strOsType == "Windows81_64"
1285 || mUserData->s.strOsType == "Windows8"
1286 || mUserData->s.strOsType == "Windows8_64"
1287 || mUserData->s.strOsType == "Windows7"
1288 || mUserData->s.strOsType == "Windows7_64"
1289 || mUserData->s.strOsType == "WindowsVista"
1290 || mUserData->s.strOsType == "WindowsVista_64"
1291 || mUserData->s.strOsType == "Windows2012"
1292 || mUserData->s.strOsType == "Windows2012_64"
1293 || mUserData->s.strOsType == "Windows2008"
1294 || mUserData->s.strOsType == "Windows2008_64")
1295 {
1296 *aParavirtProvider = ParavirtProvider_HyperV;
1297 }
1298 else
1299 *aParavirtProvider = ParavirtProvider_None;
1300 break;
1301 }
1302 }
1303 break;
1304 }
1305 }
1306
1307 Assert( *aParavirtProvider == ParavirtProvider_None
1308 || *aParavirtProvider == ParavirtProvider_Minimal
1309 || *aParavirtProvider == ParavirtProvider_HyperV);
1310 return S_OK;
1311}
1312
1313HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1314{
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 aHardwareVersion = mHWData->mHWVersion;
1318
1319 return S_OK;
1320}
1321
1322HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1323{
1324 /* check known version */
1325 Utf8Str hwVersion = aHardwareVersion;
1326 if ( hwVersion.compare("1") != 0
1327 && hwVersion.compare("2") != 0)
1328 return setError(E_INVALIDARG,
1329 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1330
1331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 HRESULT rc = i_checkStateDependency(MutableStateDep);
1334 if (FAILED(rc)) return rc;
1335
1336 i_setModified(IsModified_MachineData);
1337 mHWData.backup();
1338 mHWData->mHWVersion = aHardwareVersion;
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346
1347 if (!mHWData->mHardwareUUID.isZero())
1348 aHardwareUUID = mHWData->mHardwareUUID;
1349 else
1350 aHardwareUUID = mData->mUuid;
1351
1352 return S_OK;
1353}
1354
1355HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1356{
1357 if (!aHardwareUUID.isValid())
1358 return E_INVALIDARG;
1359
1360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1361
1362 HRESULT rc = i_checkStateDependency(MutableStateDep);
1363 if (FAILED(rc)) return rc;
1364
1365 i_setModified(IsModified_MachineData);
1366 mHWData.backup();
1367 if (aHardwareUUID == mData->mUuid)
1368 mHWData->mHardwareUUID.clear();
1369 else
1370 mHWData->mHardwareUUID = aHardwareUUID;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1376{
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *aMemorySize = mHWData->mMemorySize;
1380
1381 return S_OK;
1382}
1383
1384HRESULT Machine::setMemorySize(ULONG aMemorySize)
1385{
1386 /* check RAM limits */
1387 if ( aMemorySize < MM_RAM_MIN_IN_MB
1388 || aMemorySize > MM_RAM_MAX_IN_MB
1389 )
1390 return setError(E_INVALIDARG,
1391 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1392 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mMemorySize = aMemorySize;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 *aCPUCount = mHWData->mCPUCount;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::setCPUCount(ULONG aCPUCount)
1416{
1417 /* check CPU limits */
1418 if ( aCPUCount < SchemaDefs::MinCPUCount
1419 || aCPUCount > SchemaDefs::MaxCPUCount
1420 )
1421 return setError(E_INVALIDARG,
1422 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1423 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1424
1425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1426
1427 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1428 if (mHWData->mCPUHotPlugEnabled)
1429 {
1430 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1431 {
1432 if (mHWData->mCPUAttached[idx])
1433 return setError(E_INVALIDARG,
1434 tr("There is still a CPU attached to socket %lu."
1435 "Detach the CPU before removing the socket"),
1436 aCPUCount, idx+1);
1437 }
1438 }
1439
1440 HRESULT rc = i_checkStateDependency(MutableStateDep);
1441 if (FAILED(rc)) return rc;
1442
1443 i_setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 mHWData->mCPUCount = aCPUCount;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1460{
1461 HRESULT rc = S_OK;
1462
1463 /* check throttle limits */
1464 if ( aCPUExecutionCap < 1
1465 || aCPUExecutionCap > 100
1466 )
1467 return setError(E_INVALIDARG,
1468 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1469 aCPUExecutionCap, 1, 100);
1470
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 alock.release();
1474 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1475 alock.acquire();
1476 if (FAILED(rc)) return rc;
1477
1478 i_setModified(IsModified_MachineData);
1479 mHWData.backup();
1480 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1481
1482 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1483 if (Global::IsOnline(mData->mMachineState))
1484 i_saveSettings(NULL);
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1490{
1491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1499{
1500 HRESULT rc = S_OK;
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 rc = i_checkStateDependency(MutableStateDep);
1505 if (FAILED(rc)) return rc;
1506
1507 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1508 {
1509 if (aCPUHotPlugEnabled)
1510 {
1511 i_setModified(IsModified_MachineData);
1512 mHWData.backup();
1513
1514 /* Add the amount of CPUs currently attached */
1515 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1516 mHWData->mCPUAttached[i] = true;
1517 }
1518 else
1519 {
1520 /*
1521 * We can disable hotplug only if the amount of maximum CPUs is equal
1522 * to the amount of attached CPUs
1523 */
1524 unsigned cCpusAttached = 0;
1525 unsigned iHighestId = 0;
1526
1527 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1528 {
1529 if (mHWData->mCPUAttached[i])
1530 {
1531 cCpusAttached++;
1532 iHighestId = i;
1533 }
1534 }
1535
1536 if ( (cCpusAttached != mHWData->mCPUCount)
1537 || (iHighestId >= mHWData->mCPUCount))
1538 return setError(E_INVALIDARG,
1539 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 }
1544 }
1545
1546 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1547
1548 return rc;
1549}
1550
1551HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1552{
1553#ifdef VBOX_WITH_USB_CARDREADER
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1557
1558 return S_OK;
1559#else
1560 NOREF(aEmulatedUSBCardReaderEnabled);
1561 return E_NOTIMPL;
1562#endif
1563}
1564
1565HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1566{
1567#ifdef VBOX_WITH_USB_CARDREADER
1568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 HRESULT rc = i_checkStateDependency(MutableStateDep);
1571 if (FAILED(rc)) return rc;
1572
1573 i_setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1576
1577 return S_OK;
1578#else
1579 NOREF(aEmulatedUSBCardReaderEnabled);
1580 return E_NOTIMPL;
1581#endif
1582}
1583
1584HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1585{
1586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 *aHPETEnabled = mHWData->mHPETEnabled;
1589
1590 return S_OK;
1591}
1592
1593HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1594{
1595 HRESULT rc = S_OK;
1596
1597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1598
1599 rc = i_checkStateDependency(MutableStateDep);
1600 if (FAILED(rc)) return rc;
1601
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604
1605 mHWData->mHPETEnabled = aHPETEnabled;
1606
1607 return rc;
1608}
1609
1610HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1611{
1612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1615 return S_OK;
1616}
1617
1618HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1619{
1620 HRESULT rc = S_OK;
1621
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 i_setModified(IsModified_MachineData);
1625 mHWData.backup();
1626 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1627
1628 alock.release();
1629 rc = i_onVideoCaptureChange();
1630 alock.acquire();
1631 if (FAILED(rc))
1632 {
1633 /*
1634 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1635 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1636 * determine if it should start or stop capturing. Therefore we need to manually
1637 * undo change.
1638 */
1639 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1640 return rc;
1641 }
1642
1643 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1644 if (Global::IsOnline(mData->mMachineState))
1645 i_saveSettings(NULL);
1646
1647 return rc;
1648}
1649
1650HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1651{
1652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1653 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1654 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1655 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1656 return S_OK;
1657}
1658
1659HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1660{
1661 SafeArray<BOOL> screens(aVideoCaptureScreens);
1662 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1663 bool fChanged = false;
1664
1665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1666
1667 for (unsigned i = 0; i < screens.size(); ++i)
1668 {
1669 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1670 {
1671 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1672 fChanged = true;
1673 }
1674 }
1675 if (fChanged)
1676 {
1677 alock.release();
1678 HRESULT rc = i_onVideoCaptureChange();
1679 alock.acquire();
1680 if (FAILED(rc)) return rc;
1681 i_setModified(IsModified_MachineData);
1682
1683 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1684 if (Global::IsOnline(mData->mMachineState))
1685 i_saveSettings(NULL);
1686 }
1687
1688 return S_OK;
1689}
1690
1691HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1692{
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694 if (mHWData->mVideoCaptureFile.isEmpty())
1695 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1696 else
1697 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1698 return S_OK;
1699}
1700
1701HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1702{
1703 Utf8Str strFile(aVideoCaptureFile);
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705
1706 if ( Global::IsOnline(mData->mMachineState)
1707 && mHWData->mVideoCaptureEnabled)
1708 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1709
1710 if (!RTPathStartsWithRoot(strFile.c_str()))
1711 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1712
1713 if (!strFile.isEmpty())
1714 {
1715 Utf8Str defaultFile;
1716 i_getDefaultVideoCaptureFile(defaultFile);
1717 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1718 strFile.setNull();
1719 }
1720
1721 i_setModified(IsModified_MachineData);
1722 mHWData.backup();
1723 mHWData->mVideoCaptureFile = strFile;
1724
1725 return S_OK;
1726}
1727
1728HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1729{
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1732 return S_OK;
1733}
1734
1735HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1736{
1737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 if ( Global::IsOnline(mData->mMachineState)
1740 && mHWData->mVideoCaptureEnabled)
1741 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1742
1743 i_setModified(IsModified_MachineData);
1744 mHWData.backup();
1745 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1746
1747 return S_OK;
1748}
1749
1750HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1751{
1752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1753 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1754 return S_OK;
1755}
1756
1757HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1758{
1759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1760
1761 if ( Global::IsOnline(mData->mMachineState)
1762 && mHWData->mVideoCaptureEnabled)
1763 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1764
1765 i_setModified(IsModified_MachineData);
1766 mHWData.backup();
1767 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1768
1769 return S_OK;
1770}
1771
1772HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1773{
1774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1775 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1776 return S_OK;
1777}
1778
1779HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1780{
1781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1782
1783 if ( Global::IsOnline(mData->mMachineState)
1784 && mHWData->mVideoCaptureEnabled)
1785 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1786
1787 i_setModified(IsModified_MachineData);
1788 mHWData.backup();
1789 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1790
1791 return S_OK;
1792}
1793
1794HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1795{
1796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1797 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1798 return S_OK;
1799}
1800
1801HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1802{
1803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1804
1805 if ( Global::IsOnline(mData->mMachineState)
1806 && mHWData->mVideoCaptureEnabled)
1807 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1808
1809 i_setModified(IsModified_MachineData);
1810 mHWData.backup();
1811 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1812
1813 return S_OK;
1814}
1815
1816HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1817{
1818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1819 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1820 return S_OK;
1821}
1822
1823HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1824{
1825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1826
1827 if ( Global::IsOnline(mData->mMachineState)
1828 && mHWData->mVideoCaptureEnabled)
1829 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1830
1831 i_setModified(IsModified_MachineData);
1832 mHWData.backup();
1833 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1834
1835 return S_OK;
1836}
1837
1838HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1839{
1840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1841 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1842 return S_OK;
1843}
1844
1845HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1846{
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 if ( Global::IsOnline(mData->mMachineState)
1850 && mHWData->mVideoCaptureEnabled)
1851 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1852
1853 i_setModified(IsModified_MachineData);
1854 mHWData.backup();
1855 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1856
1857 return S_OK;
1858}
1859
1860HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1865 return S_OK;
1866}
1867
1868HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1869{
1870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1871
1872 if ( Global::IsOnline(mData->mMachineState)
1873 && mHWData->mVideoCaptureEnabled)
1874 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1875
1876 i_setModified(IsModified_MachineData);
1877 mHWData.backup();
1878 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1879
1880 return S_OK;
1881}
1882
1883HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1884{
1885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1886
1887 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1888
1889 return S_OK;
1890}
1891
1892HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1893{
1894 switch (aGraphicsControllerType)
1895 {
1896 case GraphicsControllerType_Null:
1897 case GraphicsControllerType_VBoxVGA:
1898#ifdef VBOX_WITH_VMSVGA
1899 case GraphicsControllerType_VMSVGA:
1900#endif
1901 break;
1902 default:
1903 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1904 }
1905
1906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1907
1908 HRESULT rc = i_checkStateDependency(MutableStateDep);
1909 if (FAILED(rc)) return rc;
1910
1911 i_setModified(IsModified_MachineData);
1912 mHWData.backup();
1913 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1914
1915 return S_OK;
1916}
1917
1918HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1919{
1920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1921
1922 *aVRAMSize = mHWData->mVRAMSize;
1923
1924 return S_OK;
1925}
1926
1927HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1928{
1929 /* check VRAM limits */
1930 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1931 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1932 return setError(E_INVALIDARG,
1933 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1934 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1935
1936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1937
1938 HRESULT rc = i_checkStateDependency(MutableStateDep);
1939 if (FAILED(rc)) return rc;
1940
1941 i_setModified(IsModified_MachineData);
1942 mHWData.backup();
1943 mHWData->mVRAMSize = aVRAMSize;
1944
1945 return S_OK;
1946}
1947
1948/** @todo this method should not be public */
1949HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1950{
1951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1952
1953 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1954
1955 return S_OK;
1956}
1957
1958/**
1959 * Set the memory balloon size.
1960 *
1961 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1962 * we have to make sure that we never call IGuest from here.
1963 */
1964HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1965{
1966 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1967#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1968 /* check limits */
1969 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1970 return setError(E_INVALIDARG,
1971 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1972 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1973
1974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 i_setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1979
1980 return S_OK;
1981#else
1982 NOREF(aMemoryBalloonSize);
1983 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1984#endif
1985}
1986
1987HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1988{
1989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1992 return S_OK;
1993}
1994
1995HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1996{
1997#ifdef VBOX_WITH_PAGE_SHARING
1998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1999
2000 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2001 i_setModified(IsModified_MachineData);
2002 mHWData.backup();
2003 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2004 return S_OK;
2005#else
2006 NOREF(aPageFusionEnabled);
2007 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2008#endif
2009}
2010
2011HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2012{
2013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2014
2015 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2016
2017 return S_OK;
2018}
2019
2020HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2021{
2022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2023
2024 HRESULT rc = i_checkStateDependency(MutableStateDep);
2025 if (FAILED(rc)) return rc;
2026
2027 /** @todo check validity! */
2028
2029 i_setModified(IsModified_MachineData);
2030 mHWData.backup();
2031 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2032
2033 return S_OK;
2034}
2035
2036
2037HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2038{
2039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2040
2041 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2042
2043 return S_OK;
2044}
2045
2046HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2047{
2048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 HRESULT rc = i_checkStateDependency(MutableStateDep);
2051 if (FAILED(rc)) return rc;
2052
2053 /** @todo check validity! */
2054 i_setModified(IsModified_MachineData);
2055 mHWData.backup();
2056 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2057
2058 return S_OK;
2059}
2060
2061HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2062{
2063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 *aMonitorCount = mHWData->mMonitorCount;
2066
2067 return S_OK;
2068}
2069
2070HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2071{
2072 /* make sure monitor count is a sensible number */
2073 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2074 return setError(E_INVALIDARG,
2075 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2076 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2077
2078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2079
2080 HRESULT rc = i_checkStateDependency(MutableStateDep);
2081 if (FAILED(rc)) return rc;
2082
2083 i_setModified(IsModified_MachineData);
2084 mHWData.backup();
2085 mHWData->mMonitorCount = aMonitorCount;
2086
2087 return S_OK;
2088}
2089
2090HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2091{
2092 /* mBIOSSettings is constant during life time, no need to lock */
2093 aBIOSSettings = mBIOSSettings;
2094
2095 return S_OK;
2096}
2097
2098HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2099{
2100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 switch (aProperty)
2103 {
2104 case CPUPropertyType_PAE:
2105 *aValue = mHWData->mPAEEnabled;
2106 break;
2107
2108 case CPUPropertyType_Synthetic:
2109 *aValue = mHWData->mSyntheticCpu;
2110 break;
2111
2112 case CPUPropertyType_LongMode:
2113 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2114 *aValue = TRUE;
2115 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2116 *aValue = FALSE;
2117#if HC_ARCH_BITS == 64
2118 else
2119 *aValue = TRUE;
2120#else
2121 else
2122 {
2123 *aValue = FALSE;
2124
2125 ComPtr<IGuestOSType> ptrGuestOSType;
2126 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2127 if (SUCCEEDED(hrc2))
2128 {
2129 BOOL fIs64Bit = FALSE;
2130 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2131 if (SUCCEEDED(hrc2) && fIs64Bit)
2132 {
2133 ComObjPtr<Host> ptrHost = mParent->i_host();
2134 alock.release();
2135
2136 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2137 if (FAILED(hrc2))
2138 *aValue = FALSE;
2139 }
2140 }
2141 }
2142#endif
2143 break;
2144
2145 case CPUPropertyType_TripleFaultReset:
2146 *aValue = mHWData->mTripleFaultReset;
2147 break;
2148
2149 default:
2150 return E_INVALIDARG;
2151 }
2152 return S_OK;
2153}
2154
2155HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2156{
2157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2158
2159 HRESULT rc = i_checkStateDependency(MutableStateDep);
2160 if (FAILED(rc)) return rc;
2161
2162 switch (aProperty)
2163 {
2164 case CPUPropertyType_PAE:
2165 i_setModified(IsModified_MachineData);
2166 mHWData.backup();
2167 mHWData->mPAEEnabled = !!aValue;
2168 break;
2169
2170 case CPUPropertyType_Synthetic:
2171 i_setModified(IsModified_MachineData);
2172 mHWData.backup();
2173 mHWData->mSyntheticCpu = !!aValue;
2174 break;
2175
2176 case CPUPropertyType_LongMode:
2177 i_setModified(IsModified_MachineData);
2178 mHWData.backup();
2179 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2180 break;
2181
2182 case CPUPropertyType_TripleFaultReset:
2183 i_setModified(IsModified_MachineData);
2184 mHWData.backup();
2185 mHWData->mTripleFaultReset = !!aValue;
2186 break;
2187
2188 default:
2189 return E_INVALIDARG;
2190 }
2191 return S_OK;
2192}
2193
2194HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2195{
2196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2197
2198 switch(aId)
2199 {
2200 case 0x0:
2201 case 0x1:
2202 case 0x2:
2203 case 0x3:
2204 case 0x4:
2205 case 0x5:
2206 case 0x6:
2207 case 0x7:
2208 case 0x8:
2209 case 0x9:
2210 case 0xA:
2211 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2212 return E_INVALIDARG;
2213
2214 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2215 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2216 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2217 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2218 break;
2219
2220 case 0x80000000:
2221 case 0x80000001:
2222 case 0x80000002:
2223 case 0x80000003:
2224 case 0x80000004:
2225 case 0x80000005:
2226 case 0x80000006:
2227 case 0x80000007:
2228 case 0x80000008:
2229 case 0x80000009:
2230 case 0x8000000A:
2231 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2232 return E_INVALIDARG;
2233
2234 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2235 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2236 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2237 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2238 break;
2239
2240 default:
2241 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2242 }
2243 return S_OK;
2244}
2245
2246
2247HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2248{
2249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2250
2251 HRESULT rc = i_checkStateDependency(MutableStateDep);
2252 if (FAILED(rc)) return rc;
2253
2254 switch(aId)
2255 {
2256 case 0x0:
2257 case 0x1:
2258 case 0x2:
2259 case 0x3:
2260 case 0x4:
2261 case 0x5:
2262 case 0x6:
2263 case 0x7:
2264 case 0x8:
2265 case 0x9:
2266 case 0xA:
2267 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2268 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2269 i_setModified(IsModified_MachineData);
2270 mHWData.backup();
2271 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2272 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2273 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2274 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2275 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2276 break;
2277
2278 case 0x80000000:
2279 case 0x80000001:
2280 case 0x80000002:
2281 case 0x80000003:
2282 case 0x80000004:
2283 case 0x80000005:
2284 case 0x80000006:
2285 case 0x80000007:
2286 case 0x80000008:
2287 case 0x80000009:
2288 case 0x8000000A:
2289 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2290 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2291 i_setModified(IsModified_MachineData);
2292 mHWData.backup();
2293 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2294 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2295 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2296 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2297 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2298 break;
2299
2300 default:
2301 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2302 }
2303 return S_OK;
2304}
2305
2306HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2307{
2308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2309
2310 HRESULT rc = i_checkStateDependency(MutableStateDep);
2311 if (FAILED(rc)) return rc;
2312
2313 switch(aId)
2314 {
2315 case 0x0:
2316 case 0x1:
2317 case 0x2:
2318 case 0x3:
2319 case 0x4:
2320 case 0x5:
2321 case 0x6:
2322 case 0x7:
2323 case 0x8:
2324 case 0x9:
2325 case 0xA:
2326 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2327 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2328 i_setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 /* Invalidate leaf. */
2331 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2332 break;
2333
2334 case 0x80000000:
2335 case 0x80000001:
2336 case 0x80000002:
2337 case 0x80000003:
2338 case 0x80000004:
2339 case 0x80000005:
2340 case 0x80000006:
2341 case 0x80000007:
2342 case 0x80000008:
2343 case 0x80000009:
2344 case 0x8000000A:
2345 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2346 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2347 i_setModified(IsModified_MachineData);
2348 mHWData.backup();
2349 /* Invalidate leaf. */
2350 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2351 break;
2352
2353 default:
2354 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2355 }
2356 return S_OK;
2357}
2358
2359HRESULT Machine::removeAllCPUIDLeaves()
2360{
2361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2362
2363 HRESULT rc = i_checkStateDependency(MutableStateDep);
2364 if (FAILED(rc)) return rc;
2365
2366 i_setModified(IsModified_MachineData);
2367 mHWData.backup();
2368
2369 /* Invalidate all standard leafs. */
2370 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2371 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2372
2373 /* Invalidate all extended leafs. */
2374 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2375 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2376
2377 return S_OK;
2378}
2379HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2380{
2381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2382
2383 switch(aProperty)
2384 {
2385 case HWVirtExPropertyType_Enabled:
2386 *aValue = mHWData->mHWVirtExEnabled;
2387 break;
2388
2389 case HWVirtExPropertyType_VPID:
2390 *aValue = mHWData->mHWVirtExVPIDEnabled;
2391 break;
2392
2393 case HWVirtExPropertyType_NestedPaging:
2394 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2395 break;
2396
2397 case HWVirtExPropertyType_UnrestrictedExecution:
2398 *aValue = mHWData->mHWVirtExUXEnabled;
2399 break;
2400
2401 case HWVirtExPropertyType_LargePages:
2402 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2403#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2404 *aValue = FALSE;
2405#endif
2406 break;
2407
2408 case HWVirtExPropertyType_Force:
2409 *aValue = mHWData->mHWVirtExForceEnabled;
2410 break;
2411
2412 default:
2413 return E_INVALIDARG;
2414 }
2415 return S_OK;
2416}
2417
2418HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2419{
2420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2421
2422 HRESULT rc = i_checkStateDependency(MutableStateDep);
2423 if (FAILED(rc)) return rc;
2424
2425 switch(aProperty)
2426 {
2427 case HWVirtExPropertyType_Enabled:
2428 i_setModified(IsModified_MachineData);
2429 mHWData.backup();
2430 mHWData->mHWVirtExEnabled = !!aValue;
2431 break;
2432
2433 case HWVirtExPropertyType_VPID:
2434 i_setModified(IsModified_MachineData);
2435 mHWData.backup();
2436 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2437 break;
2438
2439 case HWVirtExPropertyType_NestedPaging:
2440 i_setModified(IsModified_MachineData);
2441 mHWData.backup();
2442 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2443 break;
2444
2445 case HWVirtExPropertyType_UnrestrictedExecution:
2446 i_setModified(IsModified_MachineData);
2447 mHWData.backup();
2448 mHWData->mHWVirtExUXEnabled = !!aValue;
2449 break;
2450
2451 case HWVirtExPropertyType_LargePages:
2452 i_setModified(IsModified_MachineData);
2453 mHWData.backup();
2454 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2455 break;
2456
2457 case HWVirtExPropertyType_Force:
2458 i_setModified(IsModified_MachineData);
2459 mHWData.backup();
2460 mHWData->mHWVirtExForceEnabled = !!aValue;
2461 break;
2462
2463 default:
2464 return E_INVALIDARG;
2465 }
2466
2467 return S_OK;
2468}
2469
2470HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2471{
2472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2473
2474 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2475
2476 return S_OK;
2477}
2478
2479HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2480{
2481 /* @todo (r=dmik):
2482 * 1. Allow to change the name of the snapshot folder containing snapshots
2483 * 2. Rename the folder on disk instead of just changing the property
2484 * value (to be smart and not to leave garbage). Note that it cannot be
2485 * done here because the change may be rolled back. Thus, the right
2486 * place is #saveSettings().
2487 */
2488
2489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2490
2491 HRESULT rc = i_checkStateDependency(MutableStateDep);
2492 if (FAILED(rc)) return rc;
2493
2494 if (!mData->mCurrentSnapshot.isNull())
2495 return setError(E_FAIL,
2496 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2497
2498 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2499
2500 if (strSnapshotFolder.isEmpty())
2501 strSnapshotFolder = "Snapshots";
2502 int vrc = i_calculateFullPath(strSnapshotFolder,
2503 strSnapshotFolder);
2504 if (RT_FAILURE(vrc))
2505 return setError(E_FAIL,
2506 tr("Invalid snapshot folder '%s' (%Rrc)"),
2507 strSnapshotFolder.c_str(), vrc);
2508
2509 i_setModified(IsModified_MachineData);
2510 mUserData.backup();
2511
2512 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2513
2514 return S_OK;
2515}
2516
2517HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2518{
2519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2520
2521 aMediumAttachments.resize(mMediaData->mAttachments.size());
2522 size_t i = 0;
2523 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2524 it != mMediaData->mAttachments.end(); ++it, ++i)
2525 aMediumAttachments[i] = *it;
2526
2527 return S_OK;
2528}
2529
2530HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2531{
2532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 Assert(!!mVRDEServer);
2535
2536 aVRDEServer = mVRDEServer;
2537
2538 return S_OK;
2539}
2540
2541HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2542{
2543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2544
2545 aAudioAdapter = mAudioAdapter;
2546
2547 return S_OK;
2548}
2549
2550HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2551{
2552#ifdef VBOX_WITH_VUSB
2553 clearError();
2554 MultiResult rc(S_OK);
2555
2556# ifdef VBOX_WITH_USB
2557 rc = mParent->i_host()->i_checkUSBProxyService();
2558 if (FAILED(rc)) return rc;
2559# endif
2560
2561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 USBControllerList data = *mUSBControllers.data();
2564 aUSBControllers.resize(data.size());
2565 size_t i = 0;
2566 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2567 aUSBControllers[i] = *it;
2568
2569 return S_OK;
2570#else
2571 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2572 * extended error info to indicate that USB is simply not available
2573 * (w/o treating it as a failure), for example, as in OSE */
2574 NOREF(aUSBControllers);
2575 ReturnComNotImplemented();
2576#endif /* VBOX_WITH_VUSB */
2577}
2578
2579HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2580{
2581#ifdef VBOX_WITH_VUSB
2582 clearError();
2583 MultiResult rc(S_OK);
2584
2585# ifdef VBOX_WITH_USB
2586 rc = mParent->i_host()->i_checkUSBProxyService();
2587 if (FAILED(rc)) return rc;
2588# endif
2589
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 aUSBDeviceFilters = mUSBDeviceFilters;
2593 return rc;
2594#else
2595 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2596 * extended error info to indicate that USB is simply not available
2597 * (w/o treating it as a failure), for example, as in OSE */
2598 NOREF(aUSBDeviceFilters);
2599 ReturnComNotImplemented();
2600#endif /* VBOX_WITH_VUSB */
2601}
2602
2603HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 aSettingsFilePath = mData->m_strConfigFileFull;
2608
2609 return S_OK;
2610}
2611
2612HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2613{
2614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 HRESULT rc = i_checkStateDependency(MutableStateDep);
2617 if (FAILED(rc)) return rc;
2618
2619 if (!mData->pMachineConfigFile->fileExists())
2620 // this is a new machine, and no config file exists yet:
2621 *aSettingsModified = TRUE;
2622 else
2623 *aSettingsModified = (mData->flModifications != 0);
2624
2625 return S_OK;
2626}
2627
2628HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2629{
2630
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 *aSessionState = mData->mSession.mState;
2634
2635 return S_OK;
2636}
2637
2638HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 aSessionType = mData->mSession.mType;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 *aSessionPID = mData->mSession.mPID;
2652
2653 return S_OK;
2654}
2655
2656HRESULT Machine::getState(MachineState_T *aState)
2657{
2658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2659
2660 *aState = mData->mMachineState;
2661
2662 return S_OK;
2663}
2664
2665HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2666{
2667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2668
2669 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2675{
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 aStateFilePath = mSSData->strStateFilePath;
2679
2680 return S_OK;
2681}
2682
2683HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2684{
2685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2686
2687 i_getLogFolder(aLogFolder);
2688
2689 return S_OK;
2690}
2691
2692HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2693{
2694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 aCurrentSnapshot = mData->mCurrentSnapshot;
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2702{
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2706 ? 0
2707 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2708
2709 return S_OK;
2710}
2711
2712HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2713{
2714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2715
2716 /* Note: for machines with no snapshots, we always return FALSE
2717 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2718 * reasons :) */
2719
2720 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2721 ? FALSE
2722 : mData->mCurrentStateModified;
2723
2724 return S_OK;
2725}
2726
2727HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2728{
2729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2730
2731 aSharedFolders.resize(mHWData->mSharedFolders.size());
2732 size_t i = 0;
2733 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2734 it != mHWData->mSharedFolders.end(); ++i, ++it)
2735 aSharedFolders[i] = *it;
2736
2737 return S_OK;
2738}
2739
2740HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2741{
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 *aClipboardMode = mHWData->mClipboardMode;
2745
2746 return S_OK;
2747}
2748
2749HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2750{
2751 HRESULT rc = S_OK;
2752
2753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 alock.release();
2756 rc = i_onClipboardModeChange(aClipboardMode);
2757 alock.acquire();
2758 if (FAILED(rc)) return rc;
2759
2760 i_setModified(IsModified_MachineData);
2761 mHWData.backup();
2762 mHWData->mClipboardMode = aClipboardMode;
2763
2764 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2765 if (Global::IsOnline(mData->mMachineState))
2766 i_saveSettings(NULL);
2767
2768 return S_OK;
2769}
2770
2771HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2772{
2773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 *aDnDMode = mHWData->mDnDMode;
2776
2777 return S_OK;
2778}
2779
2780HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2781{
2782 HRESULT rc = S_OK;
2783
2784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2785
2786 alock.release();
2787 rc = i_onDnDModeChange(aDnDMode);
2788
2789 alock.acquire();
2790 if (FAILED(rc)) return rc;
2791
2792 i_setModified(IsModified_MachineData);
2793 mHWData.backup();
2794 mHWData->mDnDMode = aDnDMode;
2795
2796 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2797 if (Global::IsOnline(mData->mMachineState))
2798 i_saveSettings(NULL);
2799
2800 return S_OK;
2801}
2802
2803HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2804{
2805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2806
2807 try
2808 {
2809 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2810 }
2811 catch (...)
2812 {
2813 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2814 }
2815
2816 return S_OK;
2817}
2818
2819HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2820{
2821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2822
2823 HRESULT rc = i_checkStateDependency(MutableStateDep);
2824 if (FAILED(rc)) return rc;
2825
2826 i_setModified(IsModified_MachineData);
2827 mHWData.backup();
2828 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2829 return rc;
2830}
2831
2832HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2833{
2834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2835 StorageControllerList data = *mStorageControllers.data();
2836 size_t i = 0;
2837 aStorageControllers.resize(data.size());
2838 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2839 aStorageControllers[i] = *it;
2840 return S_OK;
2841}
2842
2843HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2844{
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 *aEnabled = mUserData->s.fTeleporterEnabled;
2848
2849 return S_OK;
2850}
2851
2852HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2853{
2854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 /* Only allow it to be set to true when PoweredOff or Aborted.
2857 (Clearing it is always permitted.) */
2858 if ( aTeleporterEnabled
2859 && mData->mRegistered
2860 && ( !i_isSessionMachine()
2861 || ( mData->mMachineState != MachineState_PoweredOff
2862 && mData->mMachineState != MachineState_Teleported
2863 && mData->mMachineState != MachineState_Aborted
2864 )
2865 )
2866 )
2867 return setError(VBOX_E_INVALID_VM_STATE,
2868 tr("The machine is not powered off (state is %s)"),
2869 Global::stringifyMachineState(mData->mMachineState));
2870
2871 i_setModified(IsModified_MachineData);
2872 mUserData.backup();
2873 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2874
2875 return S_OK;
2876}
2877
2878HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2879{
2880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2881
2882 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2883
2884 return S_OK;
2885}
2886
2887HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2888{
2889 if (aTeleporterPort >= _64K)
2890 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2891
2892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 HRESULT rc = i_checkStateDependency(MutableStateDep);
2895 if (FAILED(rc)) return rc;
2896
2897 i_setModified(IsModified_MachineData);
2898 mUserData.backup();
2899 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2900
2901 return S_OK;
2902}
2903
2904HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2905{
2906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2914{
2915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 HRESULT rc = i_checkStateDependency(MutableStateDep);
2918 if (FAILED(rc)) return rc;
2919
2920 i_setModified(IsModified_MachineData);
2921 mUserData.backup();
2922 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2923
2924 return S_OK;
2925}
2926
2927HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2928{
2929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2930 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2931
2932 return S_OK;
2933}
2934
2935HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2936{
2937 /*
2938 * Hash the password first.
2939 */
2940 com::Utf8Str aT = aTeleporterPassword;
2941
2942 if (!aT.isEmpty())
2943 {
2944 if (VBoxIsPasswordHashed(&aT))
2945 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2946 VBoxHashPassword(&aT);
2947 }
2948
2949 /*
2950 * Do the update.
2951 */
2952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2953 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2954 if (SUCCEEDED(hrc))
2955 {
2956 i_setModified(IsModified_MachineData);
2957 mUserData.backup();
2958 mUserData->s.strTeleporterPassword = aT;
2959 }
2960
2961 return hrc;
2962}
2963
2964HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2965{
2966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2967
2968 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2969 return S_OK;
2970}
2971
2972HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2973{
2974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2975
2976 /* @todo deal with running state change. */
2977 HRESULT rc = i_checkStateDependency(MutableStateDep);
2978 if (FAILED(rc)) return rc;
2979
2980 i_setModified(IsModified_MachineData);
2981 mUserData.backup();
2982 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2983 return S_OK;
2984}
2985
2986HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2987{
2988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2991 return S_OK;
2992}
2993
2994HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2995{
2996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 /* @todo deal with running state change. */
2999 HRESULT rc = i_checkStateDependency(MutableStateDep);
3000 if (FAILED(rc)) return rc;
3001
3002 i_setModified(IsModified_MachineData);
3003 mUserData.backup();
3004 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3005 return S_OK;
3006}
3007
3008HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3009{
3010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3013 return S_OK;
3014}
3015
3016HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3017{
3018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3019
3020 /* @todo deal with running state change. */
3021 HRESULT rc = i_checkStateDependency(MutableStateDep);
3022 if (FAILED(rc)) return rc;
3023
3024 i_setModified(IsModified_MachineData);
3025 mUserData.backup();
3026 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3027 return S_OK;
3028}
3029
3030HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3031{
3032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3033
3034 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3035
3036 return S_OK;
3037}
3038
3039HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3040{
3041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 /* @todo deal with running state change. */
3044 HRESULT rc = i_checkStateDependency(MutableStateDep);
3045 if (FAILED(rc)) return rc;
3046
3047 i_setModified(IsModified_MachineData);
3048 mUserData.backup();
3049 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3050
3051 return S_OK;
3052}
3053
3054HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3055{
3056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3059 return S_OK;
3060}
3061
3062HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3063{
3064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 /* @todo deal with running state change. */
3067 HRESULT rc = i_checkStateDependency(MutableStateDep);
3068 if (FAILED(rc)) return rc;
3069
3070 i_setModified(IsModified_MachineData);
3071 mUserData.backup();
3072 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3073 return S_OK;
3074}
3075
3076HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3077{
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3081
3082 return S_OK;
3083}
3084
3085HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3086{
3087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3088
3089 /* Only allow it to be set to true when PoweredOff or Aborted.
3090 (Clearing it is always permitted.) */
3091 if ( aRTCUseUTC
3092 && mData->mRegistered
3093 && ( !i_isSessionMachine()
3094 || ( mData->mMachineState != MachineState_PoweredOff
3095 && mData->mMachineState != MachineState_Teleported
3096 && mData->mMachineState != MachineState_Aborted
3097 )
3098 )
3099 )
3100 return setError(VBOX_E_INVALID_VM_STATE,
3101 tr("The machine is not powered off (state is %s)"),
3102 Global::stringifyMachineState(mData->mMachineState));
3103
3104 i_setModified(IsModified_MachineData);
3105 mUserData.backup();
3106 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3107
3108 return S_OK;
3109}
3110
3111HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3112{
3113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3114
3115 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3116
3117 return S_OK;
3118}
3119
3120HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3121{
3122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 HRESULT rc = i_checkStateDependency(MutableStateDep);
3125 if (FAILED(rc)) return rc;
3126
3127 i_setModified(IsModified_MachineData);
3128 mHWData.backup();
3129 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3130
3131 return S_OK;
3132}
3133
3134HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3135{
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 *aIOCacheSize = mHWData->mIOCacheSize;
3139
3140 return S_OK;
3141}
3142
3143HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3144{
3145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3146
3147 HRESULT rc = i_checkStateDependency(MutableStateDep);
3148 if (FAILED(rc)) return rc;
3149
3150 i_setModified(IsModified_MachineData);
3151 mHWData.backup();
3152 mHWData->mIOCacheSize = aIOCacheSize;
3153
3154 return S_OK;
3155}
3156
3157
3158/**
3159 * @note Locks objects!
3160 */
3161HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3162 LockType_T aLockType)
3163
3164{
3165 /* check the session state */
3166 SessionState_T state;
3167 HRESULT rc = aSession->COMGETTER(State)(&state);
3168 if (FAILED(rc)) return rc;
3169
3170 if (state != SessionState_Unlocked)
3171 return setError(VBOX_E_INVALID_OBJECT_STATE,
3172 tr("The given session is busy"));
3173
3174 // get the client's IInternalSessionControl interface
3175 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3176 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3177 E_INVALIDARG);
3178
3179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 if (!mData->mRegistered)
3182 return setError(E_UNEXPECTED,
3183 tr("The machine '%s' is not registered"),
3184 mUserData->s.strName.c_str());
3185
3186 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3187
3188 SessionState_T oldState = mData->mSession.mState;
3189 /* Hack: in case the session is closing and there is a progress object
3190 * which allows waiting for the session to be closed, take the opportunity
3191 * and do a limited wait (max. 1 second). This helps a lot when the system
3192 * is busy and thus session closing can take a little while. */
3193 if ( mData->mSession.mState == SessionState_Unlocking
3194 && mData->mSession.mProgress)
3195 {
3196 alock.release();
3197 mData->mSession.mProgress->WaitForCompletion(1000);
3198 alock.acquire();
3199 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3200 }
3201
3202 // try again now
3203 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3204 // (i.e. session machine exists)
3205 && (aLockType == LockType_Shared) // caller wants a shared link to the
3206 // existing session that holds the write lock:
3207 )
3208 {
3209 // OK, share the session... we are now dealing with three processes:
3210 // 1) VBoxSVC (where this code runs);
3211 // 2) process C: the caller's client process (who wants a shared session);
3212 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3213
3214 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3215 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3216 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3217 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3218 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3219
3220 /*
3221 * Release the lock before calling the client process. It's safe here
3222 * since the only thing to do after we get the lock again is to add
3223 * the remote control to the list (which doesn't directly influence
3224 * anything).
3225 */
3226 alock.release();
3227
3228 // get the console of the session holding the write lock (this is a remote call)
3229 ComPtr<IConsole> pConsoleW;
3230 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3231 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3232 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3233 if (FAILED(rc))
3234 // the failure may occur w/o any error info (from RPC), so provide one
3235 return setError(VBOX_E_VM_ERROR,
3236 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3237
3238 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3239
3240 // share the session machine and W's console with the caller's session
3241 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3242 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3243 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3244
3245 if (FAILED(rc))
3246 // the failure may occur w/o any error info (from RPC), so provide one
3247 return setError(VBOX_E_VM_ERROR,
3248 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3249 alock.acquire();
3250
3251 // need to revalidate the state after acquiring the lock again
3252 if (mData->mSession.mState != SessionState_Locked)
3253 {
3254 pSessionControl->Uninitialize();
3255 return setError(VBOX_E_INVALID_SESSION_STATE,
3256 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3257 mUserData->s.strName.c_str());
3258 }
3259
3260 // add the caller's session to the list
3261 mData->mSession.mRemoteControls.push_back(pSessionControl);
3262 }
3263 else if ( mData->mSession.mState == SessionState_Locked
3264 || mData->mSession.mState == SessionState_Unlocking
3265 )
3266 {
3267 // sharing not permitted, or machine still unlocking:
3268 return setError(VBOX_E_INVALID_OBJECT_STATE,
3269 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3270 mUserData->s.strName.c_str());
3271 }
3272 else
3273 {
3274 // machine is not locked: then write-lock the machine (create the session machine)
3275
3276 // must not be busy
3277 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3278
3279 // get the caller's session PID
3280 RTPROCESS pid = NIL_RTPROCESS;
3281 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3282 pSessionControl->GetPID((ULONG*)&pid);
3283 Assert(pid != NIL_RTPROCESS);
3284
3285 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3286
3287 if (fLaunchingVMProcess)
3288 {
3289 if (mData->mSession.mPID == NIL_RTPROCESS)
3290 {
3291 // two or more clients racing for a lock, the one which set the
3292 // session state to Spawning will win, the others will get an
3293 // error as we can't decide here if waiting a little would help
3294 // (only for shared locks this would avoid an error)
3295 return setError(VBOX_E_INVALID_OBJECT_STATE,
3296 tr("The machine '%s' already has a lock request pending"),
3297 mUserData->s.strName.c_str());
3298 }
3299
3300 // this machine is awaiting for a spawning session to be opened:
3301 // then the calling process must be the one that got started by
3302 // LaunchVMProcess()
3303
3304 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3305 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3306
3307#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3308 /* Hardened windows builds spawns three processes when a VM is
3309 launched, the 3rd one is the one that will end up here. */
3310 RTPROCESS ppid;
3311 int rc = RTProcQueryParent(pid, &ppid);
3312 if (RT_SUCCESS(rc))
3313 rc = RTProcQueryParent(ppid, &ppid);
3314 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3315 || rc == VERR_ACCESS_DENIED)
3316 {
3317 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3318 mData->mSession.mPID = pid;
3319 }
3320#endif
3321
3322 if (mData->mSession.mPID != pid)
3323 return setError(E_ACCESSDENIED,
3324 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3325 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3326 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3327 }
3328
3329 // create the mutable SessionMachine from the current machine
3330 ComObjPtr<SessionMachine> sessionMachine;
3331 sessionMachine.createObject();
3332 rc = sessionMachine->init(this);
3333 AssertComRC(rc);
3334
3335 /* NOTE: doing return from this function after this point but
3336 * before the end is forbidden since it may call SessionMachine::uninit()
3337 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3338 * lock while still holding the Machine lock in alock so that a deadlock
3339 * is possible due to the wrong lock order. */
3340
3341 if (SUCCEEDED(rc))
3342 {
3343 /*
3344 * Set the session state to Spawning to protect against subsequent
3345 * attempts to open a session and to unregister the machine after
3346 * we release the lock.
3347 */
3348 SessionState_T origState = mData->mSession.mState;
3349 mData->mSession.mState = SessionState_Spawning;
3350
3351#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3352 /* Get the client token ID to be passed to the client process */
3353 Utf8Str strTokenId;
3354 sessionMachine->i_getTokenId(strTokenId);
3355 Assert(!strTokenId.isEmpty());
3356#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3357 /* Get the client token to be passed to the client process */
3358 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3359 /* The token is now "owned" by pToken, fix refcount */
3360 if (!pToken.isNull())
3361 pToken->Release();
3362#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3363
3364 /*
3365 * Release the lock before calling the client process -- it will call
3366 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3367 * because the state is Spawning, so that LaunchVMProcess() and
3368 * LockMachine() calls will fail. This method, called before we
3369 * acquire the lock again, will fail because of the wrong PID.
3370 *
3371 * Note that mData->mSession.mRemoteControls accessed outside
3372 * the lock may not be modified when state is Spawning, so it's safe.
3373 */
3374 alock.release();
3375
3376 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3377#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3378 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3379#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3380 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3381 /* Now the token is owned by the client process. */
3382 pToken.setNull();
3383#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3384 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3385
3386 /* The failure may occur w/o any error info (from RPC), so provide one */
3387 if (FAILED(rc))
3388 setError(VBOX_E_VM_ERROR,
3389 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3390
3391 if ( SUCCEEDED(rc)
3392 && fLaunchingVMProcess
3393 )
3394 {
3395 /* complete the remote session initialization */
3396
3397 /* get the console from the direct session */
3398 ComPtr<IConsole> console;
3399 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3400 ComAssertComRC(rc);
3401
3402 if (SUCCEEDED(rc) && !console)
3403 {
3404 ComAssert(!!console);
3405 rc = E_FAIL;
3406 }
3407
3408 /* assign machine & console to the remote session */
3409 if (SUCCEEDED(rc))
3410 {
3411 /*
3412 * after LaunchVMProcess(), the first and the only
3413 * entry in remoteControls is that remote session
3414 */
3415 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3416 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3417 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3418
3419 /* The failure may occur w/o any error info (from RPC), so provide one */
3420 if (FAILED(rc))
3421 setError(VBOX_E_VM_ERROR,
3422 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3423 }
3424
3425 if (FAILED(rc))
3426 pSessionControl->Uninitialize();
3427 }
3428
3429 /* acquire the lock again */
3430 alock.acquire();
3431
3432 /* Restore the session state */
3433 mData->mSession.mState = origState;
3434 }
3435
3436 // finalize spawning anyway (this is why we don't return on errors above)
3437 if (fLaunchingVMProcess)
3438 {
3439 /* Note that the progress object is finalized later */
3440 /** @todo Consider checking mData->mSession.mProgress for cancellation
3441 * around here. */
3442
3443 /* We don't reset mSession.mPID here because it is necessary for
3444 * SessionMachine::uninit() to reap the child process later. */
3445
3446 if (FAILED(rc))
3447 {
3448 /* Close the remote session, remove the remote control from the list
3449 * and reset session state to Closed (@note keep the code in sync
3450 * with the relevant part in checkForSpawnFailure()). */
3451
3452 Assert(mData->mSession.mRemoteControls.size() == 1);
3453 if (mData->mSession.mRemoteControls.size() == 1)
3454 {
3455 ErrorInfoKeeper eik;
3456 mData->mSession.mRemoteControls.front()->Uninitialize();
3457 }
3458
3459 mData->mSession.mRemoteControls.clear();
3460 mData->mSession.mState = SessionState_Unlocked;
3461 }
3462 }
3463 else
3464 {
3465 /* memorize PID of the directly opened session */
3466 if (SUCCEEDED(rc))
3467 mData->mSession.mPID = pid;
3468 }
3469
3470 if (SUCCEEDED(rc))
3471 {
3472 /* memorize the direct session control and cache IUnknown for it */
3473 mData->mSession.mDirectControl = pSessionControl;
3474 mData->mSession.mState = SessionState_Locked;
3475 /* associate the SessionMachine with this Machine */
3476 mData->mSession.mMachine = sessionMachine;
3477
3478 /* request an IUnknown pointer early from the remote party for later
3479 * identity checks (it will be internally cached within mDirectControl
3480 * at least on XPCOM) */
3481 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3482 NOREF(unk);
3483 }
3484
3485 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3486 * would break the lock order */
3487 alock.release();
3488
3489 /* uninitialize the created session machine on failure */
3490 if (FAILED(rc))
3491 sessionMachine->uninit();
3492
3493 }
3494
3495 if (SUCCEEDED(rc))
3496 {
3497 /*
3498 * tell the client watcher thread to update the set of
3499 * machines that have open sessions
3500 */
3501 mParent->i_updateClientWatcher();
3502
3503 if (oldState != SessionState_Locked)
3504 /* fire an event */
3505 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3506 }
3507
3508 return rc;
3509}
3510
3511/**
3512 * @note Locks objects!
3513 */
3514HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3515 const com::Utf8Str &aType,
3516 const com::Utf8Str &aEnvironment,
3517 ComPtr<IProgress> &aProgress)
3518{
3519 Utf8Str strFrontend(aType);
3520 Utf8Str strEnvironment(aEnvironment);
3521 /* "emergencystop" doesn't need the session, so skip the checks/interface
3522 * retrieval. This code doesn't quite fit in here, but introducing a
3523 * special API method would be even more effort, and would require explicit
3524 * support by every API client. It's better to hide the feature a bit. */
3525 if (strFrontend != "emergencystop")
3526 CheckComArgNotNull(aSession);
3527
3528 HRESULT rc = S_OK;
3529 if (strFrontend.isEmpty())
3530 {
3531 Bstr bstrFrontend;
3532 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3533 if (FAILED(rc))
3534 return rc;
3535 strFrontend = bstrFrontend;
3536 if (strFrontend.isEmpty())
3537 {
3538 ComPtr<ISystemProperties> systemProperties;
3539 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3540 if (FAILED(rc))
3541 return rc;
3542 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3543 if (FAILED(rc))
3544 return rc;
3545 strFrontend = bstrFrontend;
3546 }
3547 /* paranoia - emergencystop is not a valid default */
3548 if (strFrontend == "emergencystop")
3549 strFrontend = Utf8Str::Empty;
3550 }
3551 /* default frontend: Qt GUI */
3552 if (strFrontend.isEmpty())
3553 strFrontend = "GUI/Qt";
3554
3555 if (strFrontend != "emergencystop")
3556 {
3557 /* check the session state */
3558 SessionState_T state;
3559 rc = aSession->COMGETTER(State)(&state);
3560 if (FAILED(rc))
3561 return rc;
3562
3563 if (state != SessionState_Unlocked)
3564 return setError(VBOX_E_INVALID_OBJECT_STATE,
3565 tr("The given session is busy"));
3566
3567 /* get the IInternalSessionControl interface */
3568 ComPtr<IInternalSessionControl> control(aSession);
3569 ComAssertMsgRet(!control.isNull(),
3570 ("No IInternalSessionControl interface"),
3571 E_INVALIDARG);
3572
3573 /* get the teleporter enable state for the progress object init. */
3574 BOOL fTeleporterEnabled;
3575 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3576 if (FAILED(rc))
3577 return rc;
3578
3579 /* create a progress object */
3580 ComObjPtr<ProgressProxy> progress;
3581 progress.createObject();
3582 rc = progress->init(mParent,
3583 static_cast<IMachine*>(this),
3584 Bstr(tr("Starting VM")).raw(),
3585 TRUE /* aCancelable */,
3586 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3587 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3588 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3589 2 /* uFirstOperationWeight */,
3590 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3591
3592 if (SUCCEEDED(rc))
3593 {
3594 rc = i_launchVMProcess(control, strFrontend, strEnvironment, progress);
3595 if (SUCCEEDED(rc))
3596 {
3597 aProgress = progress;
3598
3599 /* signal the client watcher thread */
3600 mParent->i_updateClientWatcher();
3601
3602 /* fire an event */
3603 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3604 }
3605 }
3606 }
3607 else
3608 {
3609 /* no progress object - either instant success or failure */
3610 aProgress = NULL;
3611
3612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3613
3614 if (mData->mSession.mState != SessionState_Locked)
3615 return setError(VBOX_E_INVALID_OBJECT_STATE,
3616 tr("The machine '%s' is not locked by a session"),
3617 mUserData->s.strName.c_str());
3618
3619 /* must have a VM process associated - do not kill normal API clients
3620 * with an open session */
3621 if (!Global::IsOnline(mData->mMachineState))
3622 return setError(VBOX_E_INVALID_OBJECT_STATE,
3623 tr("The machine '%s' does not have a VM process"),
3624 mUserData->s.strName.c_str());
3625
3626 /* forcibly terminate the VM process */
3627 if (mData->mSession.mPID != NIL_RTPROCESS)
3628 RTProcTerminate(mData->mSession.mPID);
3629
3630 /* signal the client watcher thread, as most likely the client has
3631 * been terminated */
3632 mParent->i_updateClientWatcher();
3633 }
3634
3635 return rc;
3636}
3637
3638HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3639{
3640 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3641 return setError(E_INVALIDARG,
3642 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3643 aPosition, SchemaDefs::MaxBootPosition);
3644
3645 if (aDevice == DeviceType_USB)
3646 return setError(E_NOTIMPL,
3647 tr("Booting from USB device is currently not supported"));
3648
3649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3650
3651 HRESULT rc = i_checkStateDependency(MutableStateDep);
3652 if (FAILED(rc)) return rc;
3653
3654 i_setModified(IsModified_MachineData);
3655 mHWData.backup();
3656 mHWData->mBootOrder[aPosition - 1] = aDevice;
3657
3658 return S_OK;
3659}
3660
3661HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3662{
3663 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3664 return setError(E_INVALIDARG,
3665 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3666 aPosition, SchemaDefs::MaxBootPosition);
3667
3668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3669
3670 *aDevice = mHWData->mBootOrder[aPosition - 1];
3671
3672 return S_OK;
3673}
3674
3675HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3676 LONG aControllerPort,
3677 LONG aDevice,
3678 DeviceType_T aType,
3679 const ComPtr<IMedium> &aMedium)
3680{
3681 IMedium *aM = aMedium;
3682 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3683 aName.c_str(), aControllerPort, aDevice, aType, aM));
3684
3685 // request the host lock first, since might be calling Host methods for getting host drives;
3686 // next, protect the media tree all the while we're in here, as well as our member variables
3687 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3688 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3689
3690 HRESULT rc = i_checkStateDependency(MutableStateDep);
3691 if (FAILED(rc)) return rc;
3692
3693 /// @todo NEWMEDIA implicit machine registration
3694 if (!mData->mRegistered)
3695 return setError(VBOX_E_INVALID_OBJECT_STATE,
3696 tr("Cannot attach storage devices to an unregistered machine"));
3697
3698 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3699
3700 /* Check for an existing controller. */
3701 ComObjPtr<StorageController> ctl;
3702 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3703 if (FAILED(rc)) return rc;
3704
3705 StorageControllerType_T ctrlType;
3706 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3707 if (FAILED(rc))
3708 return setError(E_FAIL,
3709 tr("Could not get type of controller '%s'"),
3710 aName.c_str());
3711
3712 bool fSilent = false;
3713 Utf8Str strReconfig;
3714
3715 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3716 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3717 if ( mData->mMachineState == MachineState_Paused
3718 && strReconfig == "1")
3719 fSilent = true;
3720
3721 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3722 bool fHotplug = false;
3723 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3724 fHotplug = true;
3725
3726 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3727 return setError(VBOX_E_INVALID_VM_STATE,
3728 tr("Controller '%s' does not support hotplugging"),
3729 aName.c_str());
3730
3731 // check that the port and device are not out of range
3732 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3733 if (FAILED(rc)) return rc;
3734
3735 /* check if the device slot is already busy */
3736 MediumAttachment *pAttachTemp;
3737 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3738 Bstr(aName).raw(),
3739 aControllerPort,
3740 aDevice)))
3741 {
3742 Medium *pMedium = pAttachTemp->i_getMedium();
3743 if (pMedium)
3744 {
3745 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3746 return setError(VBOX_E_OBJECT_IN_USE,
3747 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3748 pMedium->i_getLocationFull().c_str(),
3749 aControllerPort,
3750 aDevice,
3751 aName.c_str());
3752 }
3753 else
3754 return setError(VBOX_E_OBJECT_IN_USE,
3755 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3756 aControllerPort, aDevice, aName.c_str());
3757 }
3758
3759 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3760 if (aMedium && medium.isNull())
3761 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3762
3763 AutoCaller mediumCaller(medium);
3764 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3765
3766 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3767
3768 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3769 && !medium.isNull()
3770 )
3771 return setError(VBOX_E_OBJECT_IN_USE,
3772 tr("Medium '%s' is already attached to this virtual machine"),
3773 medium->i_getLocationFull().c_str());
3774
3775 if (!medium.isNull())
3776 {
3777 MediumType_T mtype = medium->i_getType();
3778 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3779 // For DVDs it's not written to the config file, so needs no global config
3780 // version bump. For floppies it's a new attribute "type", which is ignored
3781 // by older VirtualBox version, so needs no global config version bump either.
3782 // For hard disks this type is not accepted.
3783 if (mtype == MediumType_MultiAttach)
3784 {
3785 // This type is new with VirtualBox 4.0 and therefore requires settings
3786 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3787 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3788 // two reasons: The medium type is a property of the media registry tree, which
3789 // can reside in the global config file (for pre-4.0 media); we would therefore
3790 // possibly need to bump the global config version. We don't want to do that though
3791 // because that might make downgrading to pre-4.0 impossible.
3792 // As a result, we can only use these two new types if the medium is NOT in the
3793 // global registry:
3794 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3795 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3796 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3797 )
3798 return setError(VBOX_E_INVALID_OBJECT_STATE,
3799 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3800 "to machines that were created with VirtualBox 4.0 or later"),
3801 medium->i_getLocationFull().c_str());
3802 }
3803 }
3804
3805 bool fIndirect = false;
3806 if (!medium.isNull())
3807 fIndirect = medium->i_isReadOnly();
3808 bool associate = true;
3809
3810 do
3811 {
3812 if ( aType == DeviceType_HardDisk
3813 && mMediaData.isBackedUp())
3814 {
3815 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3816
3817 /* check if the medium was attached to the VM before we started
3818 * changing attachments in which case the attachment just needs to
3819 * be restored */
3820 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3821 {
3822 AssertReturn(!fIndirect, E_FAIL);
3823
3824 /* see if it's the same bus/channel/device */
3825 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3826 {
3827 /* the simplest case: restore the whole attachment
3828 * and return, nothing else to do */
3829 mMediaData->mAttachments.push_back(pAttachTemp);
3830
3831 /* Reattach the medium to the VM. */
3832 if (fHotplug || fSilent)
3833 {
3834 mediumLock.release();
3835 treeLock.release();
3836 alock.release();
3837
3838 MediumLockList *pMediumLockList(new MediumLockList());
3839
3840 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3841 true /* fMediumLockWrite */,
3842 NULL,
3843 *pMediumLockList);
3844 alock.acquire();
3845 if (FAILED(rc))
3846 delete pMediumLockList;
3847 else
3848 {
3849 mData->mSession.mLockedMedia.Unlock();
3850 alock.release();
3851 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3852 mData->mSession.mLockedMedia.Lock();
3853 alock.acquire();
3854 }
3855 alock.release();
3856
3857 if (SUCCEEDED(rc))
3858 {
3859 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3860 /* Remove lock list in case of error. */
3861 if (FAILED(rc))
3862 {
3863 mData->mSession.mLockedMedia.Unlock();
3864 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3865 mData->mSession.mLockedMedia.Lock();
3866 }
3867 }
3868 }
3869
3870 return S_OK;
3871 }
3872
3873 /* bus/channel/device differ; we need a new attachment object,
3874 * but don't try to associate it again */
3875 associate = false;
3876 break;
3877 }
3878 }
3879
3880 /* go further only if the attachment is to be indirect */
3881 if (!fIndirect)
3882 break;
3883
3884 /* perform the so called smart attachment logic for indirect
3885 * attachments. Note that smart attachment is only applicable to base
3886 * hard disks. */
3887
3888 if (medium->i_getParent().isNull())
3889 {
3890 /* first, investigate the backup copy of the current hard disk
3891 * attachments to make it possible to re-attach existing diffs to
3892 * another device slot w/o losing their contents */
3893 if (mMediaData.isBackedUp())
3894 {
3895 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3896
3897 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3898 uint32_t foundLevel = 0;
3899
3900 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3901 {
3902 uint32_t level = 0;
3903 MediumAttachment *pAttach = *it;
3904 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3905 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3906 if (pMedium.isNull())
3907 continue;
3908
3909 if (pMedium->i_getBase(&level) == medium)
3910 {
3911 /* skip the hard disk if its currently attached (we
3912 * cannot attach the same hard disk twice) */
3913 if (i_findAttachment(mMediaData->mAttachments,
3914 pMedium))
3915 continue;
3916
3917 /* matched device, channel and bus (i.e. attached to the
3918 * same place) will win and immediately stop the search;
3919 * otherwise the attachment that has the youngest
3920 * descendant of medium will be used
3921 */
3922 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3923 {
3924 /* the simplest case: restore the whole attachment
3925 * and return, nothing else to do */
3926 mMediaData->mAttachments.push_back(*it);
3927
3928 /* Reattach the medium to the VM. */
3929 if (fHotplug || fSilent)
3930 {
3931 mediumLock.release();
3932 treeLock.release();
3933 alock.release();
3934
3935 MediumLockList *pMediumLockList(new MediumLockList());
3936
3937 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3938 true /* fMediumLockWrite */,
3939 NULL,
3940 *pMediumLockList);
3941 alock.acquire();
3942 if (FAILED(rc))
3943 delete pMediumLockList;
3944 else
3945 {
3946 mData->mSession.mLockedMedia.Unlock();
3947 alock.release();
3948 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3949 mData->mSession.mLockedMedia.Lock();
3950 alock.acquire();
3951 }
3952 alock.release();
3953
3954 if (SUCCEEDED(rc))
3955 {
3956 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3957 /* Remove lock list in case of error. */
3958 if (FAILED(rc))
3959 {
3960 mData->mSession.mLockedMedia.Unlock();
3961 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3962 mData->mSession.mLockedMedia.Lock();
3963 }
3964 }
3965 }
3966
3967 return S_OK;
3968 }
3969 else if ( foundIt == oldAtts.end()
3970 || level > foundLevel /* prefer younger */
3971 )
3972 {
3973 foundIt = it;
3974 foundLevel = level;
3975 }
3976 }
3977 }
3978
3979 if (foundIt != oldAtts.end())
3980 {
3981 /* use the previously attached hard disk */
3982 medium = (*foundIt)->i_getMedium();
3983 mediumCaller.attach(medium);
3984 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3985 mediumLock.attach(medium);
3986 /* not implicit, doesn't require association with this VM */
3987 fIndirect = false;
3988 associate = false;
3989 /* go right to the MediumAttachment creation */
3990 break;
3991 }
3992 }
3993
3994 /* must give up the medium lock and medium tree lock as below we
3995 * go over snapshots, which needs a lock with higher lock order. */
3996 mediumLock.release();
3997 treeLock.release();
3998
3999 /* then, search through snapshots for the best diff in the given
4000 * hard disk's chain to base the new diff on */
4001
4002 ComObjPtr<Medium> base;
4003 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4004 while (snap)
4005 {
4006 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4007
4008 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4009
4010 MediumAttachment *pAttachFound = NULL;
4011 uint32_t foundLevel = 0;
4012
4013 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4014 {
4015 MediumAttachment *pAttach = *it;
4016 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4017 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4018 if (pMedium.isNull())
4019 continue;
4020
4021 uint32_t level = 0;
4022 if (pMedium->i_getBase(&level) == medium)
4023 {
4024 /* matched device, channel and bus (i.e. attached to the
4025 * same place) will win and immediately stop the search;
4026 * otherwise the attachment that has the youngest
4027 * descendant of medium will be used
4028 */
4029 if ( pAttach->i_getDevice() == aDevice
4030 && pAttach->i_getPort() == aControllerPort
4031 && pAttach->i_getControllerName() == aName
4032 )
4033 {
4034 pAttachFound = pAttach;
4035 break;
4036 }
4037 else if ( !pAttachFound
4038 || level > foundLevel /* prefer younger */
4039 )
4040 {
4041 pAttachFound = pAttach;
4042 foundLevel = level;
4043 }
4044 }
4045 }
4046
4047 if (pAttachFound)
4048 {
4049 base = pAttachFound->i_getMedium();
4050 break;
4051 }
4052
4053 snap = snap->i_getParent();
4054 }
4055
4056 /* re-lock medium tree and the medium, as we need it below */
4057 treeLock.acquire();
4058 mediumLock.acquire();
4059
4060 /* found a suitable diff, use it as a base */
4061 if (!base.isNull())
4062 {
4063 medium = base;
4064 mediumCaller.attach(medium);
4065 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4066 mediumLock.attach(medium);
4067 }
4068 }
4069
4070 Utf8Str strFullSnapshotFolder;
4071 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4072
4073 ComObjPtr<Medium> diff;
4074 diff.createObject();
4075 // store this diff in the same registry as the parent
4076 Guid uuidRegistryParent;
4077 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4078 {
4079 // parent image has no registry: this can happen if we're attaching a new immutable
4080 // image that has not yet been attached (medium then points to the base and we're
4081 // creating the diff image for the immutable, and the parent is not yet registered);
4082 // put the parent in the machine registry then
4083 mediumLock.release();
4084 treeLock.release();
4085 alock.release();
4086 i_addMediumToRegistry(medium);
4087 alock.acquire();
4088 treeLock.acquire();
4089 mediumLock.acquire();
4090 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4091 }
4092 rc = diff->init(mParent,
4093 medium->i_getPreferredDiffFormat(),
4094 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4095 uuidRegistryParent);
4096 if (FAILED(rc)) return rc;
4097
4098 /* Apply the normal locking logic to the entire chain. */
4099 MediumLockList *pMediumLockList(new MediumLockList());
4100 mediumLock.release();
4101 treeLock.release();
4102 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4103 true /* fMediumLockWrite */,
4104 medium,
4105 *pMediumLockList);
4106 treeLock.acquire();
4107 mediumLock.acquire();
4108 if (SUCCEEDED(rc))
4109 {
4110 mediumLock.release();
4111 treeLock.release();
4112 rc = pMediumLockList->Lock();
4113 treeLock.acquire();
4114 mediumLock.acquire();
4115 if (FAILED(rc))
4116 setError(rc,
4117 tr("Could not lock medium when creating diff '%s'"),
4118 diff->i_getLocationFull().c_str());
4119 else
4120 {
4121 /* will release the lock before the potentially lengthy
4122 * operation, so protect with the special state */
4123 MachineState_T oldState = mData->mMachineState;
4124 i_setMachineState(MachineState_SettingUp);
4125
4126 mediumLock.release();
4127 treeLock.release();
4128 alock.release();
4129
4130 rc = medium->i_createDiffStorage(diff,
4131 MediumVariant_Standard,
4132 pMediumLockList,
4133 NULL /* aProgress */,
4134 true /* aWait */);
4135
4136 alock.acquire();
4137 treeLock.acquire();
4138 mediumLock.acquire();
4139
4140 i_setMachineState(oldState);
4141 }
4142 }
4143
4144 /* Unlock the media and free the associated memory. */
4145 delete pMediumLockList;
4146
4147 if (FAILED(rc)) return rc;
4148
4149 /* use the created diff for the actual attachment */
4150 medium = diff;
4151 mediumCaller.attach(medium);
4152 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4153 mediumLock.attach(medium);
4154 }
4155 while (0);
4156
4157 ComObjPtr<MediumAttachment> attachment;
4158 attachment.createObject();
4159 rc = attachment->init(this,
4160 medium,
4161 aName,
4162 aControllerPort,
4163 aDevice,
4164 aType,
4165 fIndirect,
4166 false /* fPassthrough */,
4167 false /* fTempEject */,
4168 false /* fNonRotational */,
4169 false /* fDiscard */,
4170 fHotplug /* fHotPluggable */,
4171 Utf8Str::Empty);
4172 if (FAILED(rc)) return rc;
4173
4174 if (associate && !medium.isNull())
4175 {
4176 // as the last step, associate the medium to the VM
4177 rc = medium->i_addBackReference(mData->mUuid);
4178 // here we can fail because of Deleting, or being in process of creating a Diff
4179 if (FAILED(rc)) return rc;
4180
4181 mediumLock.release();
4182 treeLock.release();
4183 alock.release();
4184 i_addMediumToRegistry(medium);
4185 alock.acquire();
4186 treeLock.acquire();
4187 mediumLock.acquire();
4188 }
4189
4190 /* success: finally remember the attachment */
4191 i_setModified(IsModified_Storage);
4192 mMediaData.backup();
4193 mMediaData->mAttachments.push_back(attachment);
4194
4195 mediumLock.release();
4196 treeLock.release();
4197 alock.release();
4198
4199 if (fHotplug || fSilent)
4200 {
4201 if (!medium.isNull())
4202 {
4203 MediumLockList *pMediumLockList(new MediumLockList());
4204
4205 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4206 true /* fMediumLockWrite */,
4207 NULL,
4208 *pMediumLockList);
4209 alock.acquire();
4210 if (FAILED(rc))
4211 delete pMediumLockList;
4212 else
4213 {
4214 mData->mSession.mLockedMedia.Unlock();
4215 alock.release();
4216 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4217 mData->mSession.mLockedMedia.Lock();
4218 alock.acquire();
4219 }
4220 alock.release();
4221 }
4222
4223 if (SUCCEEDED(rc))
4224 {
4225 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4226 /* Remove lock list in case of error. */
4227 if (FAILED(rc))
4228 {
4229 mData->mSession.mLockedMedia.Unlock();
4230 mData->mSession.mLockedMedia.Remove(attachment);
4231 mData->mSession.mLockedMedia.Lock();
4232 }
4233 }
4234 }
4235
4236 mParent->i_saveModifiedRegistries();
4237
4238 return rc;
4239}
4240
4241HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4242 LONG aDevice)
4243{
4244 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4245 aName.c_str(), aControllerPort, aDevice));
4246
4247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4248
4249 HRESULT rc = i_checkStateDependency(MutableStateDep);
4250 if (FAILED(rc)) return rc;
4251
4252 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4253
4254 /* Check for an existing controller. */
4255 ComObjPtr<StorageController> ctl;
4256 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4257 if (FAILED(rc)) return rc;
4258
4259 StorageControllerType_T ctrlType;
4260 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4261 if (FAILED(rc))
4262 return setError(E_FAIL,
4263 tr("Could not get type of controller '%s'"),
4264 aName.c_str());
4265
4266 bool fSilent = false;
4267 Utf8Str strReconfig;
4268
4269 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4270 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4271 if ( mData->mMachineState == MachineState_Paused
4272 && strReconfig == "1")
4273 fSilent = true;
4274
4275 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4276 bool fHotplug = false;
4277 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4278 fHotplug = true;
4279
4280 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4281 return setError(VBOX_E_INVALID_VM_STATE,
4282 tr("Controller '%s' does not support hotplugging"),
4283 aName.c_str());
4284
4285 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4286 Bstr(aName).raw(),
4287 aControllerPort,
4288 aDevice);
4289 if (!pAttach)
4290 return setError(VBOX_E_OBJECT_NOT_FOUND,
4291 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4292 aDevice, aControllerPort, aName.c_str());
4293
4294 if (fHotplug && !pAttach->i_getHotPluggable())
4295 return setError(VBOX_E_NOT_SUPPORTED,
4296 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4297 aDevice, aControllerPort, aName.c_str());
4298
4299 /*
4300 * The VM has to detach the device before we delete any implicit diffs.
4301 * If this fails we can roll back without loosing data.
4302 */
4303 if (fHotplug || fSilent)
4304 {
4305 alock.release();
4306 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4307 alock.acquire();
4308 }
4309 if (FAILED(rc)) return rc;
4310
4311 /* If we are here everything went well and we can delete the implicit now. */
4312 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4313
4314 alock.release();
4315
4316 mParent->i_saveModifiedRegistries();
4317
4318 return rc;
4319}
4320
4321HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4322 LONG aDevice, BOOL aPassthrough)
4323{
4324 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4325 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4326
4327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4328
4329 HRESULT rc = i_checkStateDependency(MutableStateDep);
4330 if (FAILED(rc)) return rc;
4331
4332 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4333
4334 if (Global::IsOnlineOrTransient(mData->mMachineState))
4335 return setError(VBOX_E_INVALID_VM_STATE,
4336 tr("Invalid machine state: %s"),
4337 Global::stringifyMachineState(mData->mMachineState));
4338
4339 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4340 Bstr(aName).raw(),
4341 aControllerPort,
4342 aDevice);
4343 if (!pAttach)
4344 return setError(VBOX_E_OBJECT_NOT_FOUND,
4345 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4346 aDevice, aControllerPort, aName.c_str());
4347
4348
4349 i_setModified(IsModified_Storage);
4350 mMediaData.backup();
4351
4352 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4353
4354 if (pAttach->i_getType() != DeviceType_DVD)
4355 return setError(E_INVALIDARG,
4356 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4357 aDevice, aControllerPort, aName.c_str());
4358 pAttach->i_updatePassthrough(!!aPassthrough);
4359
4360 return S_OK;
4361}
4362
4363HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4364 LONG aDevice, BOOL aTemporaryEject)
4365{
4366
4367 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4368 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4369
4370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4371
4372 HRESULT rc = i_checkStateDependency(MutableStateDep);
4373 if (FAILED(rc)) return rc;
4374
4375 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4376 Bstr(aName).raw(),
4377 aControllerPort,
4378 aDevice);
4379 if (!pAttach)
4380 return setError(VBOX_E_OBJECT_NOT_FOUND,
4381 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4382 aDevice, aControllerPort, aName.c_str());
4383
4384
4385 i_setModified(IsModified_Storage);
4386 mMediaData.backup();
4387
4388 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4389
4390 if (pAttach->i_getType() != DeviceType_DVD)
4391 return setError(E_INVALIDARG,
4392 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4393 aDevice, aControllerPort, aName.c_str());
4394 pAttach->i_updateTempEject(!!aTemporaryEject);
4395
4396 return S_OK;
4397}
4398
4399HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4400 LONG aDevice, BOOL aNonRotational)
4401{
4402
4403 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4404 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4405
4406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4407
4408 HRESULT rc = i_checkStateDependency(MutableStateDep);
4409 if (FAILED(rc)) return rc;
4410
4411 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4412
4413 if (Global::IsOnlineOrTransient(mData->mMachineState))
4414 return setError(VBOX_E_INVALID_VM_STATE,
4415 tr("Invalid machine state: %s"),
4416 Global::stringifyMachineState(mData->mMachineState));
4417
4418 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4419 Bstr(aName).raw(),
4420 aControllerPort,
4421 aDevice);
4422 if (!pAttach)
4423 return setError(VBOX_E_OBJECT_NOT_FOUND,
4424 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4425 aDevice, aControllerPort, aName.c_str());
4426
4427
4428 i_setModified(IsModified_Storage);
4429 mMediaData.backup();
4430
4431 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4432
4433 if (pAttach->i_getType() != DeviceType_HardDisk)
4434 return setError(E_INVALIDARG,
4435 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"),
4436 aDevice, aControllerPort, aName.c_str());
4437 pAttach->i_updateNonRotational(!!aNonRotational);
4438
4439 return S_OK;
4440}
4441
4442HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4443 LONG aDevice, BOOL aDiscard)
4444{
4445
4446 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4447 aName.c_str(), aControllerPort, aDevice, aDiscard));
4448
4449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4450
4451 HRESULT rc = i_checkStateDependency(MutableStateDep);
4452 if (FAILED(rc)) return rc;
4453
4454 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4455
4456 if (Global::IsOnlineOrTransient(mData->mMachineState))
4457 return setError(VBOX_E_INVALID_VM_STATE,
4458 tr("Invalid machine state: %s"),
4459 Global::stringifyMachineState(mData->mMachineState));
4460
4461 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4462 Bstr(aName).raw(),
4463 aControllerPort,
4464 aDevice);
4465 if (!pAttach)
4466 return setError(VBOX_E_OBJECT_NOT_FOUND,
4467 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4468 aDevice, aControllerPort, aName.c_str());
4469
4470
4471 i_setModified(IsModified_Storage);
4472 mMediaData.backup();
4473
4474 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4475
4476 if (pAttach->i_getType() != DeviceType_HardDisk)
4477 return setError(E_INVALIDARG,
4478 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"),
4479 aDevice, aControllerPort, aName.c_str());
4480 pAttach->i_updateDiscard(!!aDiscard);
4481
4482 return S_OK;
4483}
4484
4485HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4486 LONG aDevice, BOOL aHotPluggable)
4487{
4488 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4489 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4490
4491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4492
4493 HRESULT rc = i_checkStateDependency(MutableStateDep);
4494 if (FAILED(rc)) return rc;
4495
4496 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4497
4498 if (Global::IsOnlineOrTransient(mData->mMachineState))
4499 return setError(VBOX_E_INVALID_VM_STATE,
4500 tr("Invalid machine state: %s"),
4501 Global::stringifyMachineState(mData->mMachineState));
4502
4503 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4504 Bstr(aName).raw(),
4505 aControllerPort,
4506 aDevice);
4507 if (!pAttach)
4508 return setError(VBOX_E_OBJECT_NOT_FOUND,
4509 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4510 aDevice, aControllerPort, aName.c_str());
4511
4512 /* Check for an existing controller. */
4513 ComObjPtr<StorageController> ctl;
4514 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4515 if (FAILED(rc)) return rc;
4516
4517 StorageControllerType_T ctrlType;
4518 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4519 if (FAILED(rc))
4520 return setError(E_FAIL,
4521 tr("Could not get type of controller '%s'"),
4522 aName.c_str());
4523
4524 if (!i_isControllerHotplugCapable(ctrlType))
4525 return setError(VBOX_E_NOT_SUPPORTED,
4526 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4527 aName.c_str());
4528
4529 i_setModified(IsModified_Storage);
4530 mMediaData.backup();
4531
4532 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4533
4534 if (pAttach->i_getType() == DeviceType_Floppy)
4535 return setError(E_INVALIDARG,
4536 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"),
4537 aDevice, aControllerPort, aName.c_str());
4538 pAttach->i_updateHotPluggable(!!aHotPluggable);
4539
4540 return S_OK;
4541}
4542
4543HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4544 LONG aDevice)
4545{
4546 int rc = S_OK;
4547 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4548 aName.c_str(), aControllerPort, aDevice));
4549
4550 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4551
4552 return rc;
4553}
4554
4555HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4556 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4557{
4558 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4559 aName.c_str(), aControllerPort, aDevice));
4560
4561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4562
4563 HRESULT rc = i_checkStateDependency(MutableStateDep);
4564 if (FAILED(rc)) return rc;
4565
4566 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4567
4568 if (Global::IsOnlineOrTransient(mData->mMachineState))
4569 return setError(VBOX_E_INVALID_VM_STATE,
4570 tr("Invalid machine state: %s"),
4571 Global::stringifyMachineState(mData->mMachineState));
4572
4573 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4574 Bstr(aName).raw(),
4575 aControllerPort,
4576 aDevice);
4577 if (!pAttach)
4578 return setError(VBOX_E_OBJECT_NOT_FOUND,
4579 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4580 aDevice, aControllerPort, aName.c_str());
4581
4582
4583 i_setModified(IsModified_Storage);
4584 mMediaData.backup();
4585
4586 IBandwidthGroup *iB = aBandwidthGroup;
4587 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4588 if (aBandwidthGroup && group.isNull())
4589 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4590
4591 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4592
4593 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4594 if (strBandwidthGroupOld.isNotEmpty())
4595 {
4596 /* Get the bandwidth group object and release it - this must not fail. */
4597 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4598 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4599 Assert(SUCCEEDED(rc));
4600
4601 pBandwidthGroupOld->i_release();
4602 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4603 }
4604
4605 if (!group.isNull())
4606 {
4607 group->i_reference();
4608 pAttach->i_updateBandwidthGroup(group->i_getName());
4609 }
4610
4611 return S_OK;
4612}
4613
4614HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4615 LONG aControllerPort,
4616 LONG aDevice,
4617 DeviceType_T aType)
4618{
4619 HRESULT rc = S_OK;
4620
4621 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4622 aName.c_str(), aControllerPort, aDevice, aType));
4623
4624 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4625
4626 return rc;
4627}
4628
4629
4630HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4631 LONG aControllerPort,
4632 LONG aDevice,
4633 BOOL aForce)
4634{
4635 int rc = S_OK;
4636 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4637 aName.c_str(), aControllerPort, aForce));
4638
4639 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4640
4641 return rc;
4642}
4643
4644HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4645 LONG aControllerPort,
4646 LONG aDevice,
4647 const ComPtr<IMedium> &aMedium,
4648 BOOL aForce)
4649{
4650 int rc = S_OK;
4651 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4652 aName.c_str(), aControllerPort, aDevice, aForce));
4653
4654 // request the host lock first, since might be calling Host methods for getting host drives;
4655 // next, protect the media tree all the while we're in here, as well as our member variables
4656 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4657 this->lockHandle(),
4658 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4659
4660 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4661 Bstr(aName).raw(),
4662 aControllerPort,
4663 aDevice);
4664 if (pAttach.isNull())
4665 return setError(VBOX_E_OBJECT_NOT_FOUND,
4666 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4667 aDevice, aControllerPort, aName.c_str());
4668
4669 /* Remember previously mounted medium. The medium before taking the
4670 * backup is not necessarily the same thing. */
4671 ComObjPtr<Medium> oldmedium;
4672 oldmedium = pAttach->i_getMedium();
4673
4674 IMedium *iM = aMedium;
4675 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4676 if (aMedium && pMedium.isNull())
4677 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4678
4679 AutoCaller mediumCaller(pMedium);
4680 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4681
4682 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4683 if (pMedium)
4684 {
4685 DeviceType_T mediumType = pAttach->i_getType();
4686 switch (mediumType)
4687 {
4688 case DeviceType_DVD:
4689 case DeviceType_Floppy:
4690 break;
4691
4692 default:
4693 return setError(VBOX_E_INVALID_OBJECT_STATE,
4694 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4695 aControllerPort,
4696 aDevice,
4697 aName.c_str());
4698 }
4699 }
4700
4701 i_setModified(IsModified_Storage);
4702 mMediaData.backup();
4703
4704 {
4705 // The backup operation makes the pAttach reference point to the
4706 // old settings. Re-get the correct reference.
4707 pAttach = i_findAttachment(mMediaData->mAttachments,
4708 Bstr(aName).raw(),
4709 aControllerPort,
4710 aDevice);
4711 if (!oldmedium.isNull())
4712 oldmedium->i_removeBackReference(mData->mUuid);
4713 if (!pMedium.isNull())
4714 {
4715 pMedium->i_addBackReference(mData->mUuid);
4716
4717 mediumLock.release();
4718 multiLock.release();
4719 i_addMediumToRegistry(pMedium);
4720 multiLock.acquire();
4721 mediumLock.acquire();
4722 }
4723
4724 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4725 pAttach->i_updateMedium(pMedium);
4726 }
4727
4728 i_setModified(IsModified_Storage);
4729
4730 mediumLock.release();
4731 multiLock.release();
4732 rc = i_onMediumChange(pAttach, aForce);
4733 multiLock.acquire();
4734 mediumLock.acquire();
4735
4736 /* On error roll back this change only. */
4737 if (FAILED(rc))
4738 {
4739 if (!pMedium.isNull())
4740 pMedium->i_removeBackReference(mData->mUuid);
4741 pAttach = i_findAttachment(mMediaData->mAttachments,
4742 Bstr(aName).raw(),
4743 aControllerPort,
4744 aDevice);
4745 /* If the attachment is gone in the meantime, bail out. */
4746 if (pAttach.isNull())
4747 return rc;
4748 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4749 if (!oldmedium.isNull())
4750 oldmedium->i_addBackReference(mData->mUuid);
4751 pAttach->i_updateMedium(oldmedium);
4752 }
4753
4754 mediumLock.release();
4755 multiLock.release();
4756
4757 mParent->i_saveModifiedRegistries();
4758
4759 return rc;
4760}
4761HRESULT Machine::getMedium(const com::Utf8Str &aName,
4762 LONG aControllerPort,
4763 LONG aDevice,
4764 ComPtr<IMedium> &aMedium)
4765{
4766 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4767 aName.c_str(), aControllerPort, aDevice));
4768
4769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4770
4771 aMedium = NULL;
4772
4773 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4774 Bstr(aName).raw(),
4775 aControllerPort,
4776 aDevice);
4777 if (pAttach.isNull())
4778 return setError(VBOX_E_OBJECT_NOT_FOUND,
4779 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4780 aDevice, aControllerPort, aName.c_str());
4781
4782 aMedium = pAttach->i_getMedium();
4783
4784 return S_OK;
4785}
4786
4787HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4788{
4789
4790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4791
4792 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4793
4794 return S_OK;
4795}
4796
4797HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4798{
4799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4800
4801 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4802
4803 return S_OK;
4804}
4805
4806HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4807{
4808 /* Do not assert if slot is out of range, just return the advertised
4809 status. testdriver/vbox.py triggers this in logVmInfo. */
4810 if (aSlot >= mNetworkAdapters.size())
4811 return setError(E_INVALIDARG,
4812 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4813 aSlot, mNetworkAdapters.size());
4814
4815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4816
4817 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4818
4819 return S_OK;
4820}
4821
4822HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4823{
4824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4825
4826 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4827 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4828 size_t i = 0;
4829 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4830 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4831 ++it, ++i)
4832 aKeys[i] = it->first;
4833
4834 return S_OK;
4835}
4836
4837 /**
4838 * @note Locks this object for reading.
4839 */
4840HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4841 com::Utf8Str &aValue)
4842{
4843 /* start with nothing found */
4844 aValue = "";
4845
4846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4847
4848 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4849 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4850 // found:
4851 aValue = it->second; // source is a Utf8Str
4852
4853 /* return the result to caller (may be empty) */
4854 return S_OK;
4855}
4856
4857 /**
4858 * @note Locks mParent for writing + this object for writing.
4859 */
4860HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4861{
4862 Utf8Str strOldValue; // empty
4863
4864 // locking note: we only hold the read lock briefly to look up the old value,
4865 // then release it and call the onExtraCanChange callbacks. There is a small
4866 // chance of a race insofar as the callback might be called twice if two callers
4867 // change the same key at the same time, but that's a much better solution
4868 // than the deadlock we had here before. The actual changing of the extradata
4869 // is then performed under the write lock and race-free.
4870
4871 // look up the old value first; if nothing has changed then we need not do anything
4872 {
4873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4874 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4875 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4876 strOldValue = it->second;
4877 }
4878
4879 bool fChanged;
4880 if ((fChanged = (strOldValue != aValue)))
4881 {
4882 // ask for permission from all listeners outside the locks;
4883 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4884 // lock to copy the list of callbacks to invoke
4885 Bstr error;
4886 Bstr bstrValue(aValue);
4887
4888 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4889 {
4890 const char *sep = error.isEmpty() ? "" : ": ";
4891 CBSTR err = error.raw();
4892 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4893 sep, err));
4894 return setError(E_ACCESSDENIED,
4895 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4896 aKey.c_str(),
4897 aValue.c_str(),
4898 sep,
4899 err);
4900 }
4901
4902 // data is changing and change not vetoed: then write it out under the lock
4903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4904
4905 if (i_isSnapshotMachine())
4906 {
4907 HRESULT rc = i_checkStateDependency(MutableStateDep);
4908 if (FAILED(rc)) return rc;
4909 }
4910
4911 if (aValue.isEmpty())
4912 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4913 else
4914 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4915 // creates a new key if needed
4916
4917 bool fNeedsGlobalSaveSettings = false;
4918 // This saving of settings is tricky: there is no "old state" for the
4919 // extradata items at all (unlike all other settings), so the old/new
4920 // settings comparison would give a wrong result!
4921 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4922
4923 if (fNeedsGlobalSaveSettings)
4924 {
4925 // save the global settings; for that we should hold only the VirtualBox lock
4926 alock.release();
4927 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4928 mParent->i_saveSettings();
4929 }
4930 }
4931
4932 // fire notification outside the lock
4933 if (fChanged)
4934 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4935
4936 return S_OK;
4937}
4938
4939HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4940{
4941 aProgress = NULL;
4942 NOREF(aSettingsFilePath);
4943 ReturnComNotImplemented();
4944}
4945
4946HRESULT Machine::saveSettings()
4947{
4948 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4949
4950 /* when there was auto-conversion, we want to save the file even if
4951 * the VM is saved */
4952 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4953 if (FAILED(rc)) return rc;
4954
4955 /* the settings file path may never be null */
4956 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4957
4958 /* save all VM data excluding snapshots */
4959 bool fNeedsGlobalSaveSettings = false;
4960 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4961 mlock.release();
4962
4963 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4964 {
4965 // save the global settings; for that we should hold only the VirtualBox lock
4966 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4967 rc = mParent->i_saveSettings();
4968 }
4969
4970 return rc;
4971}
4972
4973
4974HRESULT Machine::discardSettings()
4975{
4976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4977
4978 HRESULT rc = i_checkStateDependency(MutableStateDep);
4979 if (FAILED(rc)) return rc;
4980
4981 /*
4982 * during this rollback, the session will be notified if data has
4983 * been actually changed
4984 */
4985 i_rollback(true /* aNotify */);
4986
4987 return S_OK;
4988}
4989
4990/** @note Locks objects! */
4991HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4992 std::vector<ComPtr<IMedium> > &aMedia)
4993{
4994 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4995 AutoLimitedCaller autoCaller(this);
4996 AssertComRCReturnRC(autoCaller.rc());
4997
4998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4999
5000 Guid id(i_getId());
5001
5002 if (mData->mSession.mState != SessionState_Unlocked)
5003 return setError(VBOX_E_INVALID_OBJECT_STATE,
5004 tr("Cannot unregister the machine '%s' while it is locked"),
5005 mUserData->s.strName.c_str());
5006
5007 // wait for state dependents to drop to zero
5008 i_ensureNoStateDependencies();
5009
5010 if (!mData->mAccessible)
5011 {
5012 // inaccessible maschines can only be unregistered; uninitialize ourselves
5013 // here because currently there may be no unregistered that are inaccessible
5014 // (this state combination is not supported). Note releasing the caller and
5015 // leaving the lock before calling uninit()
5016 alock.release();
5017 autoCaller.release();
5018
5019 uninit();
5020
5021 mParent->i_unregisterMachine(this, id);
5022 // calls VirtualBox::i_saveSettings()
5023
5024 return S_OK;
5025 }
5026
5027 HRESULT rc = S_OK;
5028
5029 // discard saved state
5030 if (mData->mMachineState == MachineState_Saved)
5031 {
5032 // add the saved state file to the list of files the caller should delete
5033 Assert(!mSSData->strStateFilePath.isEmpty());
5034 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5035
5036 mSSData->strStateFilePath.setNull();
5037
5038 // unconditionally set the machine state to powered off, we now
5039 // know no session has locked the machine
5040 mData->mMachineState = MachineState_PoweredOff;
5041 }
5042
5043 size_t cSnapshots = 0;
5044 if (mData->mFirstSnapshot)
5045 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5046 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5047 // fail now before we start detaching media
5048 return setError(VBOX_E_INVALID_OBJECT_STATE,
5049 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5050 mUserData->s.strName.c_str(), cSnapshots);
5051
5052 // This list collects the medium objects from all medium attachments
5053 // which we will detach from the machine and its snapshots, in a specific
5054 // order which allows for closing all media without getting "media in use"
5055 // errors, simply by going through the list from the front to the back:
5056 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5057 // and must be closed before the parent media from the snapshots, or closing the parents
5058 // will fail because they still have children);
5059 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5060 // the root ("first") snapshot of the machine.
5061 MediaList llMedia;
5062
5063 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5064 && mMediaData->mAttachments.size()
5065 )
5066 {
5067 // we have media attachments: detach them all and add the Medium objects to our list
5068 if (aCleanupMode != CleanupMode_UnregisterOnly)
5069 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5070 else
5071 return setError(VBOX_E_INVALID_OBJECT_STATE,
5072 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5073 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5074 }
5075
5076 if (cSnapshots)
5077 {
5078 // autoCleanup must be true here, or we would have failed above
5079
5080 // add the media from the medium attachments of the snapshots to llMedia
5081 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5082 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5083 // into the children first
5084
5085 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5086 MachineState_T oldState = mData->mMachineState;
5087 mData->mMachineState = MachineState_DeletingSnapshot;
5088
5089 // make a copy of the first snapshot so the refcount does not drop to 0
5090 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5091 // because of the AutoCaller voodoo)
5092 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5093
5094 // GO!
5095 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5096
5097 mData->mMachineState = oldState;
5098 }
5099
5100 if (FAILED(rc))
5101 {
5102 i_rollbackMedia();
5103 return rc;
5104 }
5105
5106 // commit all the media changes made above
5107 i_commitMedia();
5108
5109 mData->mRegistered = false;
5110
5111 // machine lock no longer needed
5112 alock.release();
5113
5114 // return media to caller
5115 size_t i = 0;
5116 aMedia.resize(llMedia.size());
5117 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5118 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5119
5120 mParent->i_unregisterMachine(this, id);
5121 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5122
5123 return S_OK;
5124}
5125
5126struct Machine::DeleteTask
5127{
5128 ComObjPtr<Machine> pMachine;
5129 RTCList<ComPtr<IMedium> > llMediums;
5130 StringsList llFilesToDelete;
5131 ComObjPtr<Progress> pProgress;
5132};
5133
5134HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5135{
5136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5137
5138 HRESULT rc = i_checkStateDependency(MutableStateDep);
5139 if (FAILED(rc)) return rc;
5140
5141 if (mData->mRegistered)
5142 return setError(VBOX_E_INVALID_VM_STATE,
5143 tr("Cannot delete settings of a registered machine"));
5144
5145 DeleteTask *pTask = new DeleteTask;
5146 pTask->pMachine = this;
5147
5148 // collect files to delete
5149 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5150
5151 for (size_t i = 0; i < aMedia.size(); ++i)
5152 {
5153 IMedium *pIMedium(aMedia[i]);
5154 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5155 if (pMedium.isNull())
5156 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5157 SafeArray<BSTR> ids;
5158 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5159 if (FAILED(rc)) return rc;
5160 /* At this point the medium should not have any back references
5161 * anymore. If it has it is attached to another VM and *must* not
5162 * deleted. */
5163 if (ids.size() < 1)
5164 pTask->llMediums.append(pMedium);
5165 }
5166 if (mData->pMachineConfigFile->fileExists())
5167 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5168
5169 pTask->pProgress.createObject();
5170 pTask->pProgress->init(i_getVirtualBox(),
5171 static_cast<IMachine*>(this) /* aInitiator */,
5172 Bstr(tr("Deleting files")).raw(),
5173 true /* fCancellable */,
5174 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5175 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5176
5177 int vrc = RTThreadCreate(NULL,
5178 Machine::deleteThread,
5179 (void*)pTask,
5180 0,
5181 RTTHREADTYPE_MAIN_WORKER,
5182 0,
5183 "MachineDelete");
5184
5185 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5186
5187 if (RT_FAILURE(vrc))
5188 {
5189 delete pTask;
5190 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5191 }
5192
5193 LogFlowFuncLeave();
5194
5195 return S_OK;
5196}
5197
5198/**
5199 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5200 * calls Machine::deleteTaskWorker() on the actual machine object.
5201 * @param Thread
5202 * @param pvUser
5203 * @return
5204 */
5205/*static*/
5206DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5207{
5208 LogFlowFuncEnter();
5209
5210 DeleteTask *pTask = (DeleteTask*)pvUser;
5211 Assert(pTask);
5212 Assert(pTask->pMachine);
5213 Assert(pTask->pProgress);
5214
5215 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5216 pTask->pProgress->i_notifyComplete(rc);
5217
5218 delete pTask;
5219
5220 LogFlowFuncLeave();
5221
5222 NOREF(Thread);
5223
5224 return VINF_SUCCESS;
5225}
5226
5227/**
5228 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5229 * @param task
5230 * @return
5231 */
5232HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5233{
5234 AutoCaller autoCaller(this);
5235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5236
5237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5238
5239 HRESULT rc = S_OK;
5240
5241 try
5242 {
5243 ULONG uLogHistoryCount = 3;
5244 ComPtr<ISystemProperties> systemProperties;
5245 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5246 if (FAILED(rc)) throw rc;
5247
5248 if (!systemProperties.isNull())
5249 {
5250 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5251 if (FAILED(rc)) throw rc;
5252 }
5253
5254 MachineState_T oldState = mData->mMachineState;
5255 i_setMachineState(MachineState_SettingUp);
5256 alock.release();
5257 for (size_t i = 0; i < task.llMediums.size(); ++i)
5258 {
5259 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5260 {
5261 AutoCaller mac(pMedium);
5262 if (FAILED(mac.rc())) throw mac.rc();
5263 Utf8Str strLocation = pMedium->i_getLocationFull();
5264 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5265 if (FAILED(rc)) throw rc;
5266 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5267 }
5268 ComPtr<IProgress> pProgress2;
5269 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5270 if (FAILED(rc)) throw rc;
5271 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5272 if (FAILED(rc)) throw rc;
5273 /* Check the result of the asynchronous process. */
5274 LONG iRc;
5275 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5276 if (FAILED(rc)) throw rc;
5277 /* If the thread of the progress object has an error, then
5278 * retrieve the error info from there, or it'll be lost. */
5279 if (FAILED(iRc))
5280 throw setError(ProgressErrorInfo(pProgress2));
5281
5282 /* Close the medium, deliberately without checking the return
5283- * code, and without leaving any trace in the error info, as
5284- * a failure here is a very minor issue, which shouldn't happen
5285- * as above we even managed to delete the medium. */
5286 {
5287 ErrorInfoKeeper eik;
5288 pMedium->Close();
5289 }
5290 }
5291 i_setMachineState(oldState);
5292 alock.acquire();
5293
5294 // delete the files pushed on the task list by Machine::Delete()
5295 // (this includes saved states of the machine and snapshots and
5296 // medium storage files from the IMedium list passed in, and the
5297 // machine XML file)
5298 StringsList::const_iterator it = task.llFilesToDelete.begin();
5299 while (it != task.llFilesToDelete.end())
5300 {
5301 const Utf8Str &strFile = *it;
5302 LogFunc(("Deleting file %s\n", strFile.c_str()));
5303 int vrc = RTFileDelete(strFile.c_str());
5304 if (RT_FAILURE(vrc))
5305 throw setError(VBOX_E_IPRT_ERROR,
5306 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5307
5308 ++it;
5309 if (it == task.llFilesToDelete.end())
5310 {
5311 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5312 if (FAILED(rc)) throw rc;
5313 break;
5314 }
5315
5316 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5317 if (FAILED(rc)) throw rc;
5318 }
5319
5320 /* delete the settings only when the file actually exists */
5321 if (mData->pMachineConfigFile->fileExists())
5322 {
5323 /* Delete any backup or uncommitted XML files. Ignore failures.
5324 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5325 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5326 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5327 RTFileDelete(otherXml.c_str());
5328 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5329 RTFileDelete(otherXml.c_str());
5330
5331 /* delete the Logs folder, nothing important should be left
5332 * there (we don't check for errors because the user might have
5333 * some private files there that we don't want to delete) */
5334 Utf8Str logFolder;
5335 getLogFolder(logFolder);
5336 Assert(logFolder.length());
5337 if (RTDirExists(logFolder.c_str()))
5338 {
5339 /* Delete all VBox.log[.N] files from the Logs folder
5340 * (this must be in sync with the rotation logic in
5341 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5342 * files that may have been created by the GUI. */
5343 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5344 logFolder.c_str(), RTPATH_DELIMITER);
5345 RTFileDelete(log.c_str());
5346 log = Utf8StrFmt("%s%cVBox.png",
5347 logFolder.c_str(), RTPATH_DELIMITER);
5348 RTFileDelete(log.c_str());
5349 for (int i = uLogHistoryCount; i > 0; i--)
5350 {
5351 log = Utf8StrFmt("%s%cVBox.log.%d",
5352 logFolder.c_str(), RTPATH_DELIMITER, i);
5353 RTFileDelete(log.c_str());
5354 log = Utf8StrFmt("%s%cVBox.png.%d",
5355 logFolder.c_str(), RTPATH_DELIMITER, i);
5356 RTFileDelete(log.c_str());
5357 }
5358
5359 RTDirRemove(logFolder.c_str());
5360 }
5361
5362 /* delete the Snapshots folder, nothing important should be left
5363 * there (we don't check for errors because the user might have
5364 * some private files there that we don't want to delete) */
5365 Utf8Str strFullSnapshotFolder;
5366 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5367 Assert(!strFullSnapshotFolder.isEmpty());
5368 if (RTDirExists(strFullSnapshotFolder.c_str()))
5369 RTDirRemove(strFullSnapshotFolder.c_str());
5370
5371 // delete the directory that contains the settings file, but only
5372 // if it matches the VM name
5373 Utf8Str settingsDir;
5374 if (i_isInOwnDir(&settingsDir))
5375 RTDirRemove(settingsDir.c_str());
5376 }
5377
5378 alock.release();
5379
5380 mParent->i_saveModifiedRegistries();
5381 }
5382 catch (HRESULT aRC) { rc = aRC; }
5383
5384 return rc;
5385}
5386
5387HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5388{
5389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5390
5391 ComObjPtr<Snapshot> pSnapshot;
5392 HRESULT rc;
5393
5394 if (aNameOrId.isEmpty())
5395 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5396 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5397 else
5398 {
5399 Guid uuid(aNameOrId);
5400 if (uuid.isValid())
5401 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5402 else
5403 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5404 }
5405 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5406
5407 return rc;
5408}
5409
5410HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5411{
5412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5413
5414 HRESULT rc = i_checkStateDependency(MutableStateDep);
5415 if (FAILED(rc)) return rc;
5416
5417 ComObjPtr<SharedFolder> sharedFolder;
5418 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5419 if (SUCCEEDED(rc))
5420 return setError(VBOX_E_OBJECT_IN_USE,
5421 tr("Shared folder named '%s' already exists"),
5422 aName.c_str());
5423
5424 sharedFolder.createObject();
5425 rc = sharedFolder->init(i_getMachine(),
5426 aName,
5427 aHostPath,
5428 !!aWritable,
5429 !!aAutomount,
5430 true /* fFailOnError */);
5431 if (FAILED(rc)) return rc;
5432
5433 i_setModified(IsModified_SharedFolders);
5434 mHWData.backup();
5435 mHWData->mSharedFolders.push_back(sharedFolder);
5436
5437 /* inform the direct session if any */
5438 alock.release();
5439 i_onSharedFolderChange();
5440
5441 return S_OK;
5442}
5443
5444HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5445{
5446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5447
5448 HRESULT rc = i_checkStateDependency(MutableStateDep);
5449 if (FAILED(rc)) return rc;
5450
5451 ComObjPtr<SharedFolder> sharedFolder;
5452 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5453 if (FAILED(rc)) return rc;
5454
5455 i_setModified(IsModified_SharedFolders);
5456 mHWData.backup();
5457 mHWData->mSharedFolders.remove(sharedFolder);
5458
5459 /* inform the direct session if any */
5460 alock.release();
5461 i_onSharedFolderChange();
5462
5463 return S_OK;
5464}
5465
5466HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5467{
5468 /* start with No */
5469 *aCanShow = FALSE;
5470
5471 ComPtr<IInternalSessionControl> directControl;
5472 {
5473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5474
5475 if (mData->mSession.mState != SessionState_Locked)
5476 return setError(VBOX_E_INVALID_VM_STATE,
5477 tr("Machine is not locked for session (session state: %s)"),
5478 Global::stringifySessionState(mData->mSession.mState));
5479
5480 directControl = mData->mSession.mDirectControl;
5481 }
5482
5483 /* ignore calls made after #OnSessionEnd() is called */
5484 if (!directControl)
5485 return S_OK;
5486
5487 LONG64 dummy;
5488 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5489}
5490
5491HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5492{
5493 ComPtr<IInternalSessionControl> directControl;
5494 {
5495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5496
5497 if (mData->mSession.mState != SessionState_Locked)
5498 return setError(E_FAIL,
5499 tr("Machine is not locked for session (session state: %s)"),
5500 Global::stringifySessionState(mData->mSession.mState));
5501
5502 directControl = mData->mSession.mDirectControl;
5503 }
5504
5505 /* ignore calls made after #OnSessionEnd() is called */
5506 if (!directControl)
5507 return S_OK;
5508
5509 BOOL dummy;
5510 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5511}
5512
5513#ifdef VBOX_WITH_GUEST_PROPS
5514/**
5515 * Look up a guest property in VBoxSVC's internal structures.
5516 */
5517HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5518 com::Utf8Str &aValue,
5519 LONG64 *aTimestamp,
5520 com::Utf8Str &aFlags) const
5521{
5522 using namespace guestProp;
5523
5524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5525 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5526
5527 if (it != mHWData->mGuestProperties.end())
5528 {
5529 char szFlags[MAX_FLAGS_LEN + 1];
5530 aValue = it->second.strValue;
5531 *aTimestamp = it->second.mTimestamp;
5532 writeFlags(it->second.mFlags, szFlags);
5533 aFlags = Utf8Str(szFlags);
5534 }
5535
5536 return S_OK;
5537}
5538
5539/**
5540 * Query the VM that a guest property belongs to for the property.
5541 * @returns E_ACCESSDENIED if the VM process is not available or not
5542 * currently handling queries and the lookup should then be done in
5543 * VBoxSVC.
5544 */
5545HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5546 com::Utf8Str &aValue,
5547 LONG64 *aTimestamp,
5548 com::Utf8Str &aFlags) const
5549{
5550 HRESULT rc = S_OK;
5551 BSTR bValue = NULL;
5552 BSTR bFlags = NULL;
5553
5554 ComPtr<IInternalSessionControl> directControl;
5555 directControl = mData->mSession.mDirectControl;
5556
5557 /* fail if we were called after #OnSessionEnd() is called. This is a
5558 * silly race condition. */
5559
5560 /** @todo This code is bothering API clients (like python script clients) with
5561 * the AccessGuestProperty call, creating unncessary IPC. Need to
5562 * have a way of figuring out which kind of direct session it is... */
5563 if (!directControl)
5564 rc = E_ACCESSDENIED;
5565 else
5566 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5567 0 /* accessMode */,
5568 &bValue, aTimestamp, &bFlags);
5569
5570 aValue = bValue;
5571 aFlags = bFlags;
5572
5573 return rc;
5574}
5575#endif // VBOX_WITH_GUEST_PROPS
5576
5577HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5578 com::Utf8Str &aValue,
5579 LONG64 *aTimestamp,
5580 com::Utf8Str &aFlags)
5581{
5582#ifndef VBOX_WITH_GUEST_PROPS
5583 ReturnComNotImplemented();
5584#else // VBOX_WITH_GUEST_PROPS
5585
5586 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5587
5588 if (rc == E_ACCESSDENIED)
5589 /* The VM is not running or the service is not (yet) accessible */
5590 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5591 return rc;
5592#endif // VBOX_WITH_GUEST_PROPS
5593}
5594
5595HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5596{
5597 LONG64 dummyTimestamp;
5598 com::Utf8Str dummyFlags;
5599 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5600 return rc;
5601
5602}
5603HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5604{
5605 com::Utf8Str dummyFlags;
5606 com::Utf8Str dummyValue;
5607 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5608 return rc;
5609}
5610
5611#ifdef VBOX_WITH_GUEST_PROPS
5612/**
5613 * Set a guest property in VBoxSVC's internal structures.
5614 */
5615HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5616 const com::Utf8Str &aFlags, bool fDelete)
5617{
5618 using namespace guestProp;
5619
5620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5621 HRESULT rc = S_OK;
5622
5623 rc = i_checkStateDependency(MutableStateDep);
5624 if (FAILED(rc)) return rc;
5625
5626 try
5627 {
5628 uint32_t fFlags = NILFLAG;
5629 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5630 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5631
5632 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5633 if (it == mHWData->mGuestProperties.end())
5634 {
5635 if (!fDelete)
5636 {
5637 i_setModified(IsModified_MachineData);
5638 mHWData.backupEx();
5639
5640 RTTIMESPEC time;
5641 HWData::GuestProperty prop;
5642 prop.strValue = Bstr(aValue).raw();
5643 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5644 prop.mFlags = fFlags;
5645 mHWData->mGuestProperties[aName] = prop;
5646 }
5647 }
5648 else
5649 {
5650 if (it->second.mFlags & (RDONLYHOST))
5651 {
5652 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5653 }
5654 else
5655 {
5656 i_setModified(IsModified_MachineData);
5657 mHWData.backupEx();
5658
5659 /* The backupEx() operation invalidates our iterator,
5660 * so get a new one. */
5661 it = mHWData->mGuestProperties.find(aName);
5662 Assert(it != mHWData->mGuestProperties.end());
5663
5664 if (!fDelete)
5665 {
5666 RTTIMESPEC time;
5667 it->second.strValue = aValue;
5668 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5669 it->second.mFlags = fFlags;
5670 }
5671 else
5672 mHWData->mGuestProperties.erase(it);
5673 }
5674 }
5675
5676 if ( SUCCEEDED(rc)
5677 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5678 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5679 RTSTR_MAX,
5680 aName.c_str(),
5681 RTSTR_MAX,
5682 NULL)
5683 )
5684 )
5685 {
5686 alock.release();
5687
5688 mParent->i_onGuestPropertyChange(mData->mUuid,
5689 Bstr(aName).raw(),
5690 Bstr(aValue).raw(),
5691 Bstr(aFlags).raw());
5692 }
5693 }
5694 catch (std::bad_alloc &)
5695 {
5696 rc = E_OUTOFMEMORY;
5697 }
5698
5699 return rc;
5700}
5701
5702/**
5703 * Set a property on the VM that that property belongs to.
5704 * @returns E_ACCESSDENIED if the VM process is not available or not
5705 * currently handling queries and the setting should then be done in
5706 * VBoxSVC.
5707 */
5708HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5709 const com::Utf8Str &aFlags, bool fDelete)
5710{
5711 HRESULT rc;
5712
5713 try
5714 {
5715 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5716
5717 BSTR dummy = NULL; /* will not be changed (setter) */
5718 LONG64 dummy64;
5719 if (!directControl)
5720 rc = E_ACCESSDENIED;
5721 else
5722 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5723 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5724 fDelete? 2: 1 /* accessMode */,
5725 &dummy, &dummy64, &dummy);
5726 }
5727 catch (std::bad_alloc &)
5728 {
5729 rc = E_OUTOFMEMORY;
5730 }
5731
5732 return rc;
5733}
5734#endif // VBOX_WITH_GUEST_PROPS
5735
5736HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5737 const com::Utf8Str &aFlags)
5738{
5739#ifndef VBOX_WITH_GUEST_PROPS
5740 ReturnComNotImplemented();
5741#else // VBOX_WITH_GUEST_PROPS
5742 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5743 if (rc == E_ACCESSDENIED)
5744 /* The VM is not running or the service is not (yet) accessible */
5745 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5746 return rc;
5747#endif // VBOX_WITH_GUEST_PROPS
5748}
5749
5750HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5751{
5752 return setGuestProperty(aProperty, aValue, "");
5753}
5754
5755HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5756{
5757#ifndef VBOX_WITH_GUEST_PROPS
5758 ReturnComNotImplemented();
5759#else // VBOX_WITH_GUEST_PROPS
5760 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5761 if (rc == E_ACCESSDENIED)
5762 /* The VM is not running or the service is not (yet) accessible */
5763 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5764 return rc;
5765#endif // VBOX_WITH_GUEST_PROPS
5766}
5767
5768#ifdef VBOX_WITH_GUEST_PROPS
5769/**
5770 * Enumerate the guest properties in VBoxSVC's internal structures.
5771 */
5772HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5773 std::vector<com::Utf8Str> &aNames,
5774 std::vector<com::Utf8Str> &aValues,
5775 std::vector<LONG64> &aTimestamps,
5776 std::vector<com::Utf8Str> &aFlags)
5777{
5778 using namespace guestProp;
5779
5780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5781 Utf8Str strPatterns(aPatterns);
5782
5783 HWData::GuestPropertyMap propMap;
5784
5785 /*
5786 * Look for matching patterns and build up a list.
5787 */
5788 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5789 while (it != mHWData->mGuestProperties.end())
5790 {
5791 if ( strPatterns.isEmpty()
5792 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5793 RTSTR_MAX,
5794 it->first.c_str(),
5795 RTSTR_MAX,
5796 NULL)
5797 )
5798 propMap.insert(*it);
5799 it++;
5800 }
5801
5802 alock.release();
5803
5804 /*
5805 * And build up the arrays for returning the property information.
5806 */
5807 size_t cEntries = propMap.size();
5808
5809 aNames.resize(cEntries);
5810 aValues.resize(cEntries);
5811 aTimestamps.resize(cEntries);
5812 aFlags.resize(cEntries);
5813
5814 char szFlags[MAX_FLAGS_LEN + 1];
5815 size_t i= 0;
5816 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5817 {
5818 aNames[i] = it->first;
5819 aValues[i] = it->second.strValue;
5820 aTimestamps[i] = it->second.mTimestamp;
5821 writeFlags(it->second.mFlags, szFlags);
5822 aFlags[i] = Utf8Str(szFlags);
5823 }
5824
5825 return S_OK;
5826}
5827
5828/**
5829 * Enumerate the properties managed by a VM.
5830 * @returns E_ACCESSDENIED if the VM process is not available or not
5831 * currently handling queries and the setting should then be done in
5832 * VBoxSVC.
5833 */
5834HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5835 std::vector<com::Utf8Str> &aNames,
5836 std::vector<com::Utf8Str> &aValues,
5837 std::vector<LONG64> &aTimestamps,
5838 std::vector<com::Utf8Str> &aFlags)
5839{
5840 HRESULT rc;
5841 ComPtr<IInternalSessionControl> directControl;
5842 directControl = mData->mSession.mDirectControl;
5843
5844
5845 com::SafeArray<BSTR> bNames;
5846 com::SafeArray<BSTR> bValues;
5847 com::SafeArray<LONG64> bTimestamps;
5848 com::SafeArray<BSTR> bFlags;
5849
5850 if (!directControl)
5851 rc = E_ACCESSDENIED;
5852 else
5853 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5854 ComSafeArrayAsOutParam(bNames),
5855 ComSafeArrayAsOutParam(bValues),
5856 ComSafeArrayAsOutParam(bTimestamps),
5857 ComSafeArrayAsOutParam(bFlags));
5858 size_t i;
5859 aNames.resize(bNames.size());
5860 for (i = 0; i < bNames.size(); ++i)
5861 aNames[i] = Utf8Str(bNames[i]);
5862 aValues.resize(bValues.size());
5863 for (i = 0; i < bValues.size(); ++i)
5864 aValues[i] = Utf8Str(bValues[i]);
5865 aTimestamps.resize(bTimestamps.size());
5866 for (i = 0; i < bTimestamps.size(); ++i)
5867 aTimestamps[i] = bTimestamps[i];
5868 aFlags.resize(bFlags.size());
5869 for (i = 0; i < bFlags.size(); ++i)
5870 aFlags[i] = Utf8Str(bFlags[i]);
5871
5872 return rc;
5873}
5874#endif // VBOX_WITH_GUEST_PROPS
5875HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5876 std::vector<com::Utf8Str> &aNames,
5877 std::vector<com::Utf8Str> &aValues,
5878 std::vector<LONG64> &aTimestamps,
5879 std::vector<com::Utf8Str> &aFlags)
5880{
5881#ifndef VBOX_WITH_GUEST_PROPS
5882 ReturnComNotImplemented();
5883#else // VBOX_WITH_GUEST_PROPS
5884
5885 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5886
5887 if (rc == E_ACCESSDENIED)
5888 /* The VM is not running or the service is not (yet) accessible */
5889 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5890 return rc;
5891#endif // VBOX_WITH_GUEST_PROPS
5892}
5893
5894HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5895 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5896{
5897 MediaData::AttachmentList atts;
5898
5899 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5900 if (FAILED(rc)) return rc;
5901
5902 size_t i = 0;
5903 aMediumAttachments.resize(atts.size());
5904 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5905 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5906
5907 return S_OK;
5908}
5909
5910HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5911 LONG aControllerPort,
5912 LONG aDevice,
5913 ComPtr<IMediumAttachment> &aAttachment)
5914{
5915 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5916 aName.c_str(), aControllerPort, aDevice));
5917
5918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5919
5920 aAttachment = NULL;
5921
5922 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5923 Bstr(aName).raw(),
5924 aControllerPort,
5925 aDevice);
5926 if (pAttach.isNull())
5927 return setError(VBOX_E_OBJECT_NOT_FOUND,
5928 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5929 aDevice, aControllerPort, aName.c_str());
5930
5931 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5932
5933 return S_OK;
5934}
5935
5936
5937HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5938 StorageBus_T aConnectionType,
5939 ComPtr<IStorageController> &aController)
5940{
5941 if ( (aConnectionType <= StorageBus_Null)
5942 || (aConnectionType > StorageBus_USB))
5943 return setError(E_INVALIDARG,
5944 tr("Invalid connection type: %d"),
5945 aConnectionType);
5946
5947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5948
5949 HRESULT rc = i_checkStateDependency(MutableStateDep);
5950 if (FAILED(rc)) return rc;
5951
5952 /* try to find one with the name first. */
5953 ComObjPtr<StorageController> ctrl;
5954
5955 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5956 if (SUCCEEDED(rc))
5957 return setError(VBOX_E_OBJECT_IN_USE,
5958 tr("Storage controller named '%s' already exists"),
5959 aName.c_str());
5960
5961 ctrl.createObject();
5962
5963 /* get a new instance number for the storage controller */
5964 ULONG ulInstance = 0;
5965 bool fBootable = true;
5966 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5967 it != mStorageControllers->end();
5968 ++it)
5969 {
5970 if ((*it)->i_getStorageBus() == aConnectionType)
5971 {
5972 ULONG ulCurInst = (*it)->i_getInstance();
5973
5974 if (ulCurInst >= ulInstance)
5975 ulInstance = ulCurInst + 1;
5976
5977 /* Only one controller of each type can be marked as bootable. */
5978 if ((*it)->i_getBootable())
5979 fBootable = false;
5980 }
5981 }
5982
5983 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5984 if (FAILED(rc)) return rc;
5985
5986 i_setModified(IsModified_Storage);
5987 mStorageControllers.backup();
5988 mStorageControllers->push_back(ctrl);
5989
5990 ctrl.queryInterfaceTo(aController.asOutParam());
5991
5992 /* inform the direct session if any */
5993 alock.release();
5994 i_onStorageControllerChange();
5995
5996 return S_OK;
5997}
5998
5999HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6000 ComPtr<IStorageController> &aStorageController)
6001{
6002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6003
6004 ComObjPtr<StorageController> ctrl;
6005
6006 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6007 if (SUCCEEDED(rc))
6008 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6009
6010 return rc;
6011}
6012
6013HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6014 ComPtr<IStorageController> &aStorageController)
6015{
6016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6017
6018 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6019 it != mStorageControllers->end();
6020 ++it)
6021 {
6022 if ((*it)->i_getInstance() == aInstance)
6023 {
6024 (*it).queryInterfaceTo(aStorageController.asOutParam());
6025 return S_OK;
6026 }
6027 }
6028
6029 return setError(VBOX_E_OBJECT_NOT_FOUND,
6030 tr("Could not find a storage controller with instance number '%lu'"),
6031 aInstance);
6032}
6033
6034HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6035{
6036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6037
6038 HRESULT rc = i_checkStateDependency(MutableStateDep);
6039 if (FAILED(rc)) return rc;
6040
6041 ComObjPtr<StorageController> ctrl;
6042
6043 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6044 if (SUCCEEDED(rc))
6045 {
6046 /* Ensure that only one controller of each type is marked as bootable. */
6047 if (aBootable == TRUE)
6048 {
6049 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6050 it != mStorageControllers->end();
6051 ++it)
6052 {
6053 ComObjPtr<StorageController> aCtrl = (*it);
6054
6055 if ( (aCtrl->i_getName() != aName)
6056 && aCtrl->i_getBootable() == TRUE
6057 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6058 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6059 {
6060 aCtrl->i_setBootable(FALSE);
6061 break;
6062 }
6063 }
6064 }
6065
6066 if (SUCCEEDED(rc))
6067 {
6068 ctrl->i_setBootable(aBootable);
6069 i_setModified(IsModified_Storage);
6070 }
6071 }
6072
6073 if (SUCCEEDED(rc))
6074 {
6075 /* inform the direct session if any */
6076 alock.release();
6077 i_onStorageControllerChange();
6078 }
6079
6080 return rc;
6081}
6082
6083HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6084{
6085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6086
6087 HRESULT rc = i_checkStateDependency(MutableStateDep);
6088 if (FAILED(rc)) return rc;
6089
6090 ComObjPtr<StorageController> ctrl;
6091 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6092 if (FAILED(rc)) return rc;
6093
6094 {
6095 /* find all attached devices to the appropriate storage controller and detach them all */
6096 // make a temporary list because detachDevice invalidates iterators into
6097 // mMediaData->mAttachments
6098 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6099
6100 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6101 it != llAttachments2.end();
6102 ++it)
6103 {
6104 MediumAttachment *pAttachTemp = *it;
6105
6106 AutoCaller localAutoCaller(pAttachTemp);
6107 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6108
6109 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6110
6111 if (pAttachTemp->i_getControllerName() == aName)
6112 {
6113 rc = i_detachDevice(pAttachTemp, alock, NULL);
6114 if (FAILED(rc)) return rc;
6115 }
6116 }
6117 }
6118
6119 /* We can remove it now. */
6120 i_setModified(IsModified_Storage);
6121 mStorageControllers.backup();
6122
6123 ctrl->i_unshare();
6124
6125 mStorageControllers->remove(ctrl);
6126
6127 /* inform the direct session if any */
6128 alock.release();
6129 i_onStorageControllerChange();
6130
6131 return S_OK;
6132}
6133
6134HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6135 ComPtr<IUSBController> &aController)
6136{
6137 if ( (aType <= USBControllerType_Null)
6138 || (aType >= USBControllerType_Last))
6139 return setError(E_INVALIDARG,
6140 tr("Invalid USB controller type: %d"),
6141 aType);
6142
6143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6144
6145 HRESULT rc = i_checkStateDependency(MutableStateDep);
6146 if (FAILED(rc)) return rc;
6147
6148 /* try to find one with the same type first. */
6149 ComObjPtr<USBController> ctrl;
6150
6151 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6152 if (SUCCEEDED(rc))
6153 return setError(VBOX_E_OBJECT_IN_USE,
6154 tr("USB controller named '%s' already exists"),
6155 aName.c_str());
6156
6157 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6158 ULONG maxInstances;
6159 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6160 if (FAILED(rc))
6161 return rc;
6162
6163 ULONG cInstances = i_getUSBControllerCountByType(aType);
6164 if (cInstances >= maxInstances)
6165 return setError(E_INVALIDARG,
6166 tr("Too many USB controllers of this type"));
6167
6168 ctrl.createObject();
6169
6170 rc = ctrl->init(this, aName, aType);
6171 if (FAILED(rc)) return rc;
6172
6173 i_setModified(IsModified_USB);
6174 mUSBControllers.backup();
6175 mUSBControllers->push_back(ctrl);
6176
6177 ctrl.queryInterfaceTo(aController.asOutParam());
6178
6179 /* inform the direct session if any */
6180 alock.release();
6181 i_onUSBControllerChange();
6182
6183 return S_OK;
6184}
6185
6186HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6187{
6188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6189
6190 ComObjPtr<USBController> ctrl;
6191
6192 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6193 if (SUCCEEDED(rc))
6194 ctrl.queryInterfaceTo(aController.asOutParam());
6195
6196 return rc;
6197}
6198
6199HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6200 ULONG *aControllers)
6201{
6202 if ( (aType <= USBControllerType_Null)
6203 || (aType >= USBControllerType_Last))
6204 return setError(E_INVALIDARG,
6205 tr("Invalid USB controller type: %d"),
6206 aType);
6207
6208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6209
6210 ComObjPtr<USBController> ctrl;
6211
6212 *aControllers = i_getUSBControllerCountByType(aType);
6213
6214 return S_OK;
6215}
6216
6217HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6218{
6219
6220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6221
6222 HRESULT rc = i_checkStateDependency(MutableStateDep);
6223 if (FAILED(rc)) return rc;
6224
6225 ComObjPtr<USBController> ctrl;
6226 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6227 if (FAILED(rc)) return rc;
6228
6229 i_setModified(IsModified_USB);
6230 mUSBControllers.backup();
6231
6232 ctrl->i_unshare();
6233
6234 mUSBControllers->remove(ctrl);
6235
6236 /* inform the direct session if any */
6237 alock.release();
6238 i_onUSBControllerChange();
6239
6240 return S_OK;
6241}
6242
6243HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6244 ULONG *aOriginX,
6245 ULONG *aOriginY,
6246 ULONG *aWidth,
6247 ULONG *aHeight,
6248 BOOL *aEnabled)
6249{
6250 uint32_t u32OriginX= 0;
6251 uint32_t u32OriginY= 0;
6252 uint32_t u32Width = 0;
6253 uint32_t u32Height = 0;
6254 uint16_t u16Flags = 0;
6255
6256 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6257 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6258 if (RT_FAILURE(vrc))
6259 {
6260#ifdef RT_OS_WINDOWS
6261 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6262 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6263 * So just assign fEnable to TRUE again.
6264 * The right fix would be to change GUI API wrappers to make sure that parameters
6265 * are changed only if API succeeds.
6266 */
6267 *aEnabled = TRUE;
6268#endif
6269 return setError(VBOX_E_IPRT_ERROR,
6270 tr("Saved guest size is not available (%Rrc)"),
6271 vrc);
6272 }
6273
6274 *aOriginX = u32OriginX;
6275 *aOriginY = u32OriginY;
6276 *aWidth = u32Width;
6277 *aHeight = u32Height;
6278 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6279
6280 return S_OK;
6281}
6282
6283HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6284{
6285 if (aScreenId != 0)
6286 return E_NOTIMPL;
6287
6288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6289
6290 uint8_t *pu8Data = NULL;
6291 uint32_t cbData = 0;
6292 uint32_t u32Width = 0;
6293 uint32_t u32Height = 0;
6294
6295 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6296
6297 if (RT_FAILURE(vrc))
6298 return setError(VBOX_E_IPRT_ERROR,
6299 tr("Saved screenshot data is not available (%Rrc)"),
6300 vrc);
6301
6302 *aSize = cbData;
6303 *aWidth = u32Width;
6304 *aHeight = u32Height;
6305
6306 freeSavedDisplayScreenshot(pu8Data);
6307
6308 return S_OK;
6309}
6310
6311
6312HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6313{
6314 if (aScreenId != 0)
6315 return E_NOTIMPL;
6316
6317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6318
6319 uint8_t *pu8Data = NULL;
6320 uint32_t cbData = 0;
6321 uint32_t u32Width = 0;
6322 uint32_t u32Height = 0;
6323
6324 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6325
6326 if (RT_FAILURE(vrc))
6327 return setError(VBOX_E_IPRT_ERROR,
6328 tr("Saved screenshot data is not available (%Rrc)"),
6329 vrc);
6330
6331 *aWidth = u32Width;
6332 *aHeight = u32Height;
6333
6334 com::SafeArray<BYTE> bitmap(cbData);
6335 /* Convert pixels to format expected by the API caller. */
6336 if (aBGR)
6337 {
6338 /* [0] B, [1] G, [2] R, [3] A. */
6339 for (unsigned i = 0; i < cbData; i += 4)
6340 {
6341 bitmap[i] = pu8Data[i];
6342 bitmap[i + 1] = pu8Data[i + 1];
6343 bitmap[i + 2] = pu8Data[i + 2];
6344 bitmap[i + 3] = 0xff;
6345 }
6346 }
6347 else
6348 {
6349 /* [0] R, [1] G, [2] B, [3] A. */
6350 for (unsigned i = 0; i < cbData; i += 4)
6351 {
6352 bitmap[i] = pu8Data[i + 2];
6353 bitmap[i + 1] = pu8Data[i + 1];
6354 bitmap[i + 2] = pu8Data[i];
6355 bitmap[i + 3] = 0xff;
6356 }
6357 }
6358 aData.resize(bitmap.size());
6359 for (size_t i = 0; i < bitmap.size(); ++i)
6360 aData[i] = bitmap[i];
6361
6362 freeSavedDisplayScreenshot(pu8Data);
6363
6364 return S_OK;
6365}
6366
6367HRESULT Machine::readSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6368{
6369 if (aScreenId != 0)
6370 return E_NOTIMPL;
6371
6372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6373
6374 uint8_t *pu8Data = NULL;
6375 uint32_t cbData = 0;
6376 uint32_t u32Width = 0;
6377 uint32_t u32Height = 0;
6378
6379 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6380
6381 if (RT_FAILURE(vrc))
6382 return setError(VBOX_E_IPRT_ERROR,
6383 tr("Saved screenshot data is not available (%Rrc)"),
6384 vrc);
6385
6386 *aWidth = u32Width;
6387 *aHeight = u32Height;
6388
6389 HRESULT rc = S_OK;
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 com::SafeArray<BYTE> screenData(cbPNG);
6400 screenData.initFrom(pu8PNG, cbPNG);
6401 if (pu8PNG)
6402 RTMemFree(pu8PNG);
6403 aData.resize(screenData.size());
6404 for (size_t i = 0; i < screenData.size(); ++i)
6405 aData[i] = screenData[i];
6406 }
6407 else
6408 {
6409 if (pu8PNG)
6410 RTMemFree(pu8PNG);
6411 return setError(VBOX_E_IPRT_ERROR,
6412 tr("Could not convert screenshot to PNG (%Rrc)"),
6413 vrc);
6414 }
6415
6416 freeSavedDisplayScreenshot(pu8Data);
6417
6418 return rc;
6419}
6420
6421HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6422{
6423 if (aScreenId != 0)
6424 return E_NOTIMPL;
6425
6426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6427
6428 uint8_t *pu8Data = NULL;
6429 uint32_t cbData = 0;
6430 uint32_t u32Width = 0;
6431 uint32_t u32Height = 0;
6432
6433 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6434
6435 if (RT_FAILURE(vrc))
6436 return setError(VBOX_E_IPRT_ERROR,
6437 tr("Saved screenshot data is not available (%Rrc)"),
6438 vrc);
6439
6440 *aSize = cbData;
6441 *aWidth = u32Width;
6442 *aHeight = u32Height;
6443
6444 freeSavedDisplayScreenshot(pu8Data);
6445
6446 return S_OK;
6447}
6448
6449HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6450{
6451 if (aScreenId != 0)
6452 return E_NOTIMPL;
6453
6454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6455
6456 uint8_t *pu8Data = NULL;
6457 uint32_t cbData = 0;
6458 uint32_t u32Width = 0;
6459 uint32_t u32Height = 0;
6460
6461 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6462
6463 if (RT_FAILURE(vrc))
6464 return setError(VBOX_E_IPRT_ERROR,
6465 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6466 vrc);
6467
6468 *aWidth = u32Width;
6469 *aHeight = u32Height;
6470
6471 com::SafeArray<BYTE> png(cbData);
6472 png.initFrom(pu8Data, cbData);
6473 aData.resize(png.size());
6474 for (size_t i = 0; i < png.size(); ++i)
6475 aData[i] = png[i];
6476
6477 freeSavedDisplayScreenshot(pu8Data);
6478
6479 return S_OK;
6480}
6481
6482HRESULT Machine::hotPlugCPU(ULONG aCpu)
6483{
6484 HRESULT rc = S_OK;
6485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6486
6487 if (!mHWData->mCPUHotPlugEnabled)
6488 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6489
6490 if (aCpu >= mHWData->mCPUCount)
6491 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6492
6493 if (mHWData->mCPUAttached[aCpu])
6494 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6495
6496 alock.release();
6497 rc = i_onCPUChange(aCpu, false);
6498 alock.acquire();
6499 if (FAILED(rc)) return rc;
6500
6501 i_setModified(IsModified_MachineData);
6502 mHWData.backup();
6503 mHWData->mCPUAttached[aCpu] = true;
6504
6505 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6506 if (Global::IsOnline(mData->mMachineState))
6507 i_saveSettings(NULL);
6508
6509 return S_OK;
6510}
6511
6512HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6513{
6514 HRESULT rc = S_OK;
6515
6516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6517
6518 if (!mHWData->mCPUHotPlugEnabled)
6519 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6520
6521 if (aCpu >= SchemaDefs::MaxCPUCount)
6522 return setError(E_INVALIDARG,
6523 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6524 SchemaDefs::MaxCPUCount);
6525
6526 if (!mHWData->mCPUAttached[aCpu])
6527 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6528
6529 /* CPU 0 can't be detached */
6530 if (aCpu == 0)
6531 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6532
6533 alock.release();
6534 rc = i_onCPUChange(aCpu, true);
6535 alock.acquire();
6536 if (FAILED(rc)) return rc;
6537
6538 i_setModified(IsModified_MachineData);
6539 mHWData.backup();
6540 mHWData->mCPUAttached[aCpu] = false;
6541
6542 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6543 if (Global::IsOnline(mData->mMachineState))
6544 i_saveSettings(NULL);
6545
6546 return S_OK;
6547}
6548
6549HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6550{
6551 *aAttached = false;
6552
6553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6554
6555 /* If hotplug is enabled the CPU is always enabled. */
6556 if (!mHWData->mCPUHotPlugEnabled)
6557 {
6558 if (aCpu < mHWData->mCPUCount)
6559 *aAttached = true;
6560 }
6561 else
6562 {
6563 if (aCpu < SchemaDefs::MaxCPUCount)
6564 *aAttached = mHWData->mCPUAttached[aCpu];
6565 }
6566
6567 return S_OK;
6568}
6569
6570HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6571{
6572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6573
6574 Utf8Str log = i_queryLogFilename(aIdx);
6575 if (!RTFileExists(log.c_str()))
6576 log.setNull();
6577 aFilename = log;
6578
6579 return S_OK;
6580}
6581
6582HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6583{
6584 if (aSize < 0)
6585 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6586
6587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6588
6589 HRESULT rc = S_OK;
6590 Utf8Str log = i_queryLogFilename(aIdx);
6591
6592 /* do not unnecessarily hold the lock while doing something which does
6593 * not need the lock and potentially takes a long time. */
6594 alock.release();
6595
6596 /* Limit the chunk size to 32K for now, as that gives better performance
6597 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6598 * One byte expands to approx. 25 bytes of breathtaking XML. */
6599 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6600 com::SafeArray<BYTE> logData(cbData);
6601
6602 RTFILE LogFile;
6603 int vrc = RTFileOpen(&LogFile, log.c_str(),
6604 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6605 if (RT_SUCCESS(vrc))
6606 {
6607 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6608 if (RT_SUCCESS(vrc))
6609 logData.resize(cbData);
6610 else
6611 rc = setError(VBOX_E_IPRT_ERROR,
6612 tr("Could not read log file '%s' (%Rrc)"),
6613 log.c_str(), vrc);
6614 RTFileClose(LogFile);
6615 }
6616 else
6617 rc = setError(VBOX_E_IPRT_ERROR,
6618 tr("Could not open log file '%s' (%Rrc)"),
6619 log.c_str(), vrc);
6620
6621 if (FAILED(rc))
6622 logData.resize(0);
6623
6624 aData.resize(logData.size());
6625 for (size_t i = 0; i < logData.size(); ++i)
6626 aData[i] = logData[i];
6627
6628 return rc;
6629}
6630
6631
6632/**
6633 * Currently this method doesn't attach device to the running VM,
6634 * just makes sure it's plugged on next VM start.
6635 */
6636HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6637{
6638 // lock scope
6639 {
6640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6641
6642 HRESULT rc = i_checkStateDependency(MutableStateDep);
6643 if (FAILED(rc)) return rc;
6644
6645 ChipsetType_T aChipset = ChipsetType_PIIX3;
6646 COMGETTER(ChipsetType)(&aChipset);
6647
6648 if (aChipset != ChipsetType_ICH9)
6649 {
6650 return setError(E_INVALIDARG,
6651 tr("Host PCI attachment only supported with ICH9 chipset"));
6652 }
6653
6654 // check if device with this host PCI address already attached
6655 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6656 it != mHWData->mPCIDeviceAssignments.end();
6657 ++it)
6658 {
6659 LONG iHostAddress = -1;
6660 ComPtr<PCIDeviceAttachment> pAttach;
6661 pAttach = *it;
6662 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6663 if (iHostAddress == aHostAddress)
6664 return setError(E_INVALIDARG,
6665 tr("Device with host PCI address already attached to this VM"));
6666 }
6667
6668 ComObjPtr<PCIDeviceAttachment> pda;
6669 char name[32];
6670
6671 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6672 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6673 Bstr bname(name);
6674 pda.createObject();
6675 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6676 i_setModified(IsModified_MachineData);
6677 mHWData.backup();
6678 mHWData->mPCIDeviceAssignments.push_back(pda);
6679 }
6680
6681 return S_OK;
6682}
6683
6684/**
6685 * Currently this method doesn't detach device from the running VM,
6686 * just makes sure it's not plugged on next VM start.
6687 */
6688HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6689{
6690 ComObjPtr<PCIDeviceAttachment> pAttach;
6691 bool fRemoved = false;
6692 HRESULT rc;
6693
6694 // lock scope
6695 {
6696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6697
6698 rc = i_checkStateDependency(MutableStateDep);
6699 if (FAILED(rc)) return rc;
6700
6701 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6702 it != mHWData->mPCIDeviceAssignments.end();
6703 ++it)
6704 {
6705 LONG iHostAddress = -1;
6706 pAttach = *it;
6707 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6708 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6709 {
6710 i_setModified(IsModified_MachineData);
6711 mHWData.backup();
6712 mHWData->mPCIDeviceAssignments.remove(pAttach);
6713 fRemoved = true;
6714 break;
6715 }
6716 }
6717 }
6718
6719
6720 /* Fire event outside of the lock */
6721 if (fRemoved)
6722 {
6723 Assert(!pAttach.isNull());
6724 ComPtr<IEventSource> es;
6725 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6726 Assert(SUCCEEDED(rc));
6727 Bstr mid;
6728 rc = this->COMGETTER(Id)(mid.asOutParam());
6729 Assert(SUCCEEDED(rc));
6730 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6731 }
6732
6733 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6734 tr("No host PCI device %08x attached"),
6735 aHostAddress
6736 );
6737}
6738
6739HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6740{
6741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6742
6743 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6744
6745 size_t i = 0;
6746 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6747 it != mHWData->mPCIDeviceAssignments.end();
6748 ++i, ++it)
6749 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6750
6751 return S_OK;
6752}
6753
6754HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6755{
6756 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6757
6758 return S_OK;
6759}
6760
6761HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6762{
6763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6764
6765 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6766
6767 return S_OK;
6768}
6769
6770HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6771{
6772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6773 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6774 if (SUCCEEDED(hrc))
6775 {
6776 hrc = mHWData.backupEx();
6777 if (SUCCEEDED(hrc))
6778 {
6779 i_setModified(IsModified_MachineData);
6780 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6781 }
6782 }
6783 return hrc;
6784}
6785
6786HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6787{
6788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6789 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6790 return S_OK;
6791}
6792
6793HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6794{
6795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6796 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6797 if (SUCCEEDED(hrc))
6798 {
6799 hrc = mHWData.backupEx();
6800 if (SUCCEEDED(hrc))
6801 {
6802 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6803 if (SUCCEEDED(hrc))
6804 i_setModified(IsModified_MachineData);
6805 }
6806 }
6807 return hrc;
6808}
6809
6810HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6811{
6812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6813
6814 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6815
6816 return S_OK;
6817}
6818
6819HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6820{
6821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6822 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6823 if (SUCCEEDED(hrc))
6824 {
6825 hrc = mHWData.backupEx();
6826 if (SUCCEEDED(hrc))
6827 {
6828 i_setModified(IsModified_MachineData);
6829 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6830 }
6831 }
6832 return hrc;
6833}
6834
6835HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6836{
6837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6838
6839 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6840
6841 return S_OK;
6842}
6843
6844HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6845{
6846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6847
6848 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6849 if ( SUCCEEDED(hrc)
6850 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6851 {
6852 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6853 int vrc;
6854
6855 if (aAutostartEnabled)
6856 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6857 else
6858 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6859
6860 if (RT_SUCCESS(vrc))
6861 {
6862 hrc = mHWData.backupEx();
6863 if (SUCCEEDED(hrc))
6864 {
6865 i_setModified(IsModified_MachineData);
6866 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6867 }
6868 }
6869 else if (vrc == VERR_NOT_SUPPORTED)
6870 hrc = setError(VBOX_E_NOT_SUPPORTED,
6871 tr("The VM autostart feature is not supported on this platform"));
6872 else if (vrc == VERR_PATH_NOT_FOUND)
6873 hrc = setError(E_FAIL,
6874 tr("The path to the autostart database is not set"));
6875 else
6876 hrc = setError(E_UNEXPECTED,
6877 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6878 aAutostartEnabled ? "Adding" : "Removing",
6879 mUserData->s.strName.c_str(), vrc);
6880 }
6881 return hrc;
6882}
6883
6884HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6885{
6886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6887
6888 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6889
6890 return S_OK;
6891}
6892
6893HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6894{
6895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6896 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6897 if (SUCCEEDED(hrc))
6898 {
6899 hrc = mHWData.backupEx();
6900 if (SUCCEEDED(hrc))
6901 {
6902 i_setModified(IsModified_MachineData);
6903 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6904 }
6905 }
6906 return hrc;
6907}
6908
6909HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6910{
6911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6912
6913 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6914
6915 return S_OK;
6916}
6917
6918HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6919{
6920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6921 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6922 if ( SUCCEEDED(hrc)
6923 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6924 {
6925 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6926 int vrc;
6927
6928 if (aAutostopType != AutostopType_Disabled)
6929 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6930 else
6931 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6932
6933 if (RT_SUCCESS(vrc))
6934 {
6935 hrc = mHWData.backupEx();
6936 if (SUCCEEDED(hrc))
6937 {
6938 i_setModified(IsModified_MachineData);
6939 mHWData->mAutostart.enmAutostopType = aAutostopType;
6940 }
6941 }
6942 else if (vrc == VERR_NOT_SUPPORTED)
6943 hrc = setError(VBOX_E_NOT_SUPPORTED,
6944 tr("The VM autostop feature is not supported on this platform"));
6945 else if (vrc == VERR_PATH_NOT_FOUND)
6946 hrc = setError(E_FAIL,
6947 tr("The path to the autostart database is not set"));
6948 else
6949 hrc = setError(E_UNEXPECTED,
6950 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6951 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6952 mUserData->s.strName.c_str(), vrc);
6953 }
6954 return hrc;
6955}
6956
6957HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6958{
6959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6960
6961 aDefaultFrontend = mHWData->mDefaultFrontend;
6962
6963 return S_OK;
6964}
6965
6966HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6967{
6968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6969 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6970 if (SUCCEEDED(hrc))
6971 {
6972 hrc = mHWData.backupEx();
6973 if (SUCCEEDED(hrc))
6974 {
6975 i_setModified(IsModified_MachineData);
6976 mHWData->mDefaultFrontend = aDefaultFrontend;
6977 }
6978 }
6979 return hrc;
6980}
6981
6982HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6983{
6984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6985 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
6986 aIcon.resize(mUserData->mIcon.size());
6987 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
6988 aIcon.resize(icon.size());
6989 for (size_t i = 0; i < icon.size(); ++i)
6990 aIcon[i] = icon[i];
6991 return S_OK;
6992}
6993
6994HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6995{
6996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6997 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6998 if (SUCCEEDED(hrc))
6999 {
7000 i_setModified(IsModified_MachineData);
7001 mUserData.backup();
7002 com::SafeArray<BYTE> icon(aIcon);
7003 mUserData->mIcon.resize(aIcon.size());
7004 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7005 }
7006 return hrc;
7007}
7008
7009HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7010{
7011
7012#ifdef VBOX_WITH_USB
7013 *aUSBProxyAvailable = true;
7014#else
7015 *aUSBProxyAvailable = false;
7016#endif
7017 return S_OK;
7018}
7019
7020HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7021 ComPtr<IProgress> &aProgress)
7022{
7023 ComObjPtr<Progress> pP;
7024 Progress *ppP = pP;
7025 IProgress *iP = static_cast<IProgress *>(ppP);
7026 IProgress **pProgress = &iP;
7027
7028 IMachine *pTarget = aTarget;
7029
7030 /* Convert the options. */
7031 RTCList<CloneOptions_T> optList;
7032 if (aOptions.size())
7033 for (size_t i = 0; i < aOptions.size(); ++i)
7034 optList.append(aOptions[i]);
7035
7036 if (optList.contains(CloneOptions_Link))
7037 {
7038 if (!i_isSnapshotMachine())
7039 return setError(E_INVALIDARG,
7040 tr("Linked clone can only be created from a snapshot"));
7041 if (aMode != CloneMode_MachineState)
7042 return setError(E_INVALIDARG,
7043 tr("Linked clone can only be created for a single machine state"));
7044 }
7045 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7046
7047 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7048
7049 HRESULT rc = pWorker->start(pProgress);
7050
7051 pP = static_cast<Progress *>(*pProgress);
7052 pP.queryInterfaceTo(aProgress.asOutParam());
7053
7054 return rc;
7055
7056}
7057
7058// public methods for internal purposes
7059/////////////////////////////////////////////////////////////////////////////
7060
7061/**
7062 * Adds the given IsModified_* flag to the dirty flags of the machine.
7063 * This must be called either during i_loadSettings or under the machine write lock.
7064 * @param fl
7065 */
7066void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7067{
7068 mData->flModifications |= fl;
7069 if (fAllowStateModification && i_isStateModificationAllowed())
7070 mData->mCurrentStateModified = true;
7071}
7072
7073/**
7074 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7075 * care of the write locking.
7076 *
7077 * @param fModifications The flag to add.
7078 */
7079void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7080{
7081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7082 i_setModified(fModification, fAllowStateModification);
7083}
7084
7085/**
7086 * Saves the registry entry of this machine to the given configuration node.
7087 *
7088 * @param aEntryNode Node to save the registry entry to.
7089 *
7090 * @note locks this object for reading.
7091 */
7092HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7093{
7094 AutoLimitedCaller autoCaller(this);
7095 AssertComRCReturnRC(autoCaller.rc());
7096
7097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7098
7099 data.uuid = mData->mUuid;
7100 data.strSettingsFile = mData->m_strConfigFile;
7101
7102 return S_OK;
7103}
7104
7105/**
7106 * Calculates the absolute path of the given path taking the directory of the
7107 * machine settings file as the current directory.
7108 *
7109 * @param aPath Path to calculate the absolute path for.
7110 * @param aResult Where to put the result (used only on success, can be the
7111 * same Utf8Str instance as passed in @a aPath).
7112 * @return IPRT result.
7113 *
7114 * @note Locks this object for reading.
7115 */
7116int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7117{
7118 AutoCaller autoCaller(this);
7119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7120
7121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7122
7123 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7124
7125 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7126
7127 strSettingsDir.stripFilename();
7128 char folder[RTPATH_MAX];
7129 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7130 if (RT_SUCCESS(vrc))
7131 aResult = folder;
7132
7133 return vrc;
7134}
7135
7136/**
7137 * Copies strSource to strTarget, making it relative to the machine folder
7138 * if it is a subdirectory thereof, or simply copying it otherwise.
7139 *
7140 * @param strSource Path to evaluate and copy.
7141 * @param strTarget Buffer to receive target path.
7142 *
7143 * @note Locks this object for reading.
7144 */
7145void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7146 Utf8Str &strTarget)
7147{
7148 AutoCaller autoCaller(this);
7149 AssertComRCReturn(autoCaller.rc(), (void)0);
7150
7151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7152
7153 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7154 // use strTarget as a temporary buffer to hold the machine settings dir
7155 strTarget = mData->m_strConfigFileFull;
7156 strTarget.stripFilename();
7157 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7158 {
7159 // is relative: then append what's left
7160 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7161 // for empty paths (only possible for subdirs) use "." to avoid
7162 // triggering default settings for not present config attributes.
7163 if (strTarget.isEmpty())
7164 strTarget = ".";
7165 }
7166 else
7167 // is not relative: then overwrite
7168 strTarget = strSource;
7169}
7170
7171/**
7172 * Returns the full path to the machine's log folder in the
7173 * \a aLogFolder argument.
7174 */
7175void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7176{
7177 AutoCaller autoCaller(this);
7178 AssertComRCReturnVoid(autoCaller.rc());
7179
7180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7181
7182 char szTmp[RTPATH_MAX];
7183 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7184 if (RT_SUCCESS(vrc))
7185 {
7186 if (szTmp[0] && !mUserData.isNull())
7187 {
7188 char szTmp2[RTPATH_MAX];
7189 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7190 if (RT_SUCCESS(vrc))
7191 aLogFolder = BstrFmt("%s%c%s",
7192 szTmp2,
7193 RTPATH_DELIMITER,
7194 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7195 }
7196 else
7197 vrc = VERR_PATH_IS_RELATIVE;
7198 }
7199
7200 if (RT_FAILURE(vrc))
7201 {
7202 // fallback if VBOX_USER_LOGHOME is not set or invalid
7203 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7204 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7205 aLogFolder.append(RTPATH_DELIMITER);
7206 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7207 }
7208}
7209
7210/**
7211 * Returns the full path to the machine's log file for an given index.
7212 */
7213Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7214 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7215{
7216 Utf8Str logFolder;
7217 getLogFolder(logFolder);
7218 Assert(logFolder.length());
7219 Utf8Str log;
7220 if (idx == 0)
7221 log = Utf8StrFmt("%s%cVBox.log",
7222 logFolder.c_str(), RTPATH_DELIMITER);
7223 else
7224 log = Utf8StrFmt("%s%cVBox.log.%d",
7225 logFolder.c_str(), RTPATH_DELIMITER, idx);
7226 return log;
7227}
7228
7229/**
7230 * Returns the full path to the machine's (hardened) startup log file.
7231 */
7232Utf8Str Machine::i_getStartupLogFilename(void)
7233{
7234 Utf8Str strFilename;
7235 getLogFolder(strFilename);
7236 Assert(strFilename.length());
7237 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7238 return strFilename;
7239}
7240
7241
7242/**
7243 * Composes a unique saved state filename based on the current system time. The filename is
7244 * granular to the second so this will work so long as no more than one snapshot is taken on
7245 * a machine per second.
7246 *
7247 * Before version 4.1, we used this formula for saved state files:
7248 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7249 * which no longer works because saved state files can now be shared between the saved state of the
7250 * "saved" machine and an online snapshot, and the following would cause problems:
7251 * 1) save machine
7252 * 2) create online snapshot from that machine state --> reusing saved state file
7253 * 3) save machine again --> filename would be reused, breaking the online snapshot
7254 *
7255 * So instead we now use a timestamp.
7256 *
7257 * @param str
7258 */
7259
7260void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7261{
7262 AutoCaller autoCaller(this);
7263 AssertComRCReturnVoid(autoCaller.rc());
7264
7265 {
7266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7267 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7268 }
7269
7270 RTTIMESPEC ts;
7271 RTTimeNow(&ts);
7272 RTTIME time;
7273 RTTimeExplode(&time, &ts);
7274
7275 strStateFilePath += RTPATH_DELIMITER;
7276 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7277 time.i32Year, time.u8Month, time.u8MonthDay,
7278 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7279}
7280
7281/**
7282 * Returns the full path to the default video capture file.
7283 */
7284void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7285{
7286 AutoCaller autoCaller(this);
7287 AssertComRCReturnVoid(autoCaller.rc());
7288
7289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7290
7291 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7292 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7293 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7294}
7295
7296/**
7297 * Returns whether at least one USB controller is present for the VM.
7298 */
7299bool Machine::i_isUSBControllerPresent()
7300{
7301 AutoCaller autoCaller(this);
7302 AssertComRCReturn(autoCaller.rc(), false);
7303
7304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7305
7306 return (mUSBControllers->size() > 0);
7307}
7308
7309/**
7310 * @note Locks this object for writing, calls the client process
7311 * (inside the lock).
7312 */
7313HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7314 const Utf8Str &strFrontend,
7315 const Utf8Str &strEnvironment,
7316 ProgressProxy *aProgress)
7317{
7318 LogFlowThisFuncEnter();
7319
7320 AssertReturn(aControl, E_FAIL);
7321 AssertReturn(aProgress, E_FAIL);
7322 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7323
7324 AutoCaller autoCaller(this);
7325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7326
7327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7328
7329 if (!mData->mRegistered)
7330 return setError(E_UNEXPECTED,
7331 tr("The machine '%s' is not registered"),
7332 mUserData->s.strName.c_str());
7333
7334 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7335
7336 if ( mData->mSession.mState == SessionState_Locked
7337 || mData->mSession.mState == SessionState_Spawning
7338 || mData->mSession.mState == SessionState_Unlocking)
7339 return setError(VBOX_E_INVALID_OBJECT_STATE,
7340 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7341 mUserData->s.strName.c_str());
7342
7343 /* may not be busy */
7344 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7345
7346 /* get the path to the executable */
7347 char szPath[RTPATH_MAX];
7348 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7349 size_t cchBufLeft = strlen(szPath);
7350 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7351 szPath[cchBufLeft] = 0;
7352 char *pszNamePart = szPath + cchBufLeft;
7353 cchBufLeft = sizeof(szPath) - cchBufLeft;
7354
7355 int vrc = VINF_SUCCESS;
7356 RTPROCESS pid = NIL_RTPROCESS;
7357
7358 RTENV env = RTENV_DEFAULT;
7359
7360 if (!strEnvironment.isEmpty())
7361 {
7362 char *newEnvStr = NULL;
7363
7364 do
7365 {
7366 /* clone the current environment */
7367 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7368 AssertRCBreakStmt(vrc2, vrc = vrc2);
7369
7370 newEnvStr = RTStrDup(strEnvironment.c_str());
7371 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7372
7373 /* put new variables to the environment
7374 * (ignore empty variable names here since RTEnv API
7375 * intentionally doesn't do that) */
7376 char *var = newEnvStr;
7377 for (char *p = newEnvStr; *p; ++p)
7378 {
7379 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7380 {
7381 *p = '\0';
7382 if (*var)
7383 {
7384 char *val = strchr(var, '=');
7385 if (val)
7386 {
7387 *val++ = '\0';
7388 vrc2 = RTEnvSetEx(env, var, val);
7389 }
7390 else
7391 vrc2 = RTEnvUnsetEx(env, var);
7392 if (RT_FAILURE(vrc2))
7393 break;
7394 }
7395 var = p + 1;
7396 }
7397 }
7398 if (RT_SUCCESS(vrc2) && *var)
7399 vrc2 = RTEnvPutEx(env, var);
7400
7401 AssertRCBreakStmt(vrc2, vrc = vrc2);
7402 }
7403 while (0);
7404
7405 if (newEnvStr != NULL)
7406 RTStrFree(newEnvStr);
7407 }
7408
7409 /* Hardened startup logging */
7410#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7411 Utf8Str strSupStartLogArg("--sup-startup-log=");
7412 {
7413 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7414 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7415 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7416 {
7417 Utf8Str strStartupLogDir = strStartupLogFile;
7418 strStartupLogDir.stripFilename();
7419 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7420 file without stripping the file. */
7421 }
7422 strSupStartLogArg.append(strStartupLogFile);
7423 }
7424 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7425#else
7426 const char *pszSupStartupLogArg = NULL;
7427#endif
7428
7429
7430#ifdef VBOX_WITH_QTGUI
7431 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7432 {
7433# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7434 /* Modify the base path so that we don't need to use ".." below. */
7435 RTPathStripTrailingSlash(szPath);
7436 RTPathStripFilename(szPath);
7437 cchBufLeft = strlen(szPath);
7438 pszNamePart = szPath + cchBufLeft;
7439 cchBufLeft = sizeof(szPath) - cchBufLeft;
7440
7441# define OSX_APP_NAME "VirtualBoxVM"
7442# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7443
7444 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7445 if ( strAppOverride.contains(".")
7446 || strAppOverride.contains("/")
7447 || strAppOverride.contains("\\")
7448 || strAppOverride.contains(":"))
7449 strAppOverride.setNull();
7450 Utf8Str strAppPath;
7451 if (!strAppOverride.isEmpty())
7452 {
7453 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7454 Utf8Str strFullPath(szPath);
7455 strFullPath.append(strAppPath);
7456 /* there is a race, but people using this deserve the failure */
7457 if (!RTFileExists(strFullPath.c_str()))
7458 strAppOverride.setNull();
7459 }
7460 if (strAppOverride.isEmpty())
7461 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7462 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7463 strcpy(pszNamePart, strAppPath.c_str());
7464# else
7465 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7466 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7467 strcpy(pszNamePart, s_szVirtualBox_exe);
7468# endif
7469
7470 Utf8Str idStr = mData->mUuid.toString();
7471 const char *apszArgs[] =
7472 {
7473 szPath,
7474 "--comment", mUserData->s.strName.c_str(),
7475 "--startvm", idStr.c_str(),
7476 "--no-startvm-errormsgbox",
7477 pszSupStartupLogArg,
7478 NULL
7479 };
7480 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7481 }
7482#else /* !VBOX_WITH_QTGUI */
7483 if (0)
7484 ;
7485#endif /* VBOX_WITH_QTGUI */
7486
7487 else
7488
7489#ifdef VBOX_WITH_VBOXSDL
7490 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7491 {
7492 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7493 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7494 strcpy(pszNamePart, s_szVBoxSDL_exe);
7495
7496 Utf8Str idStr = mData->mUuid.toString();
7497 const char *apszArgs[] =
7498 {
7499 szPath,
7500 "--comment", mUserData->s.strName.c_str(),
7501 "--startvm", idStr.c_str(),
7502 pszSupStartupLogArg,
7503 NULL
7504 };
7505 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7506 }
7507#else /* !VBOX_WITH_VBOXSDL */
7508 if (0)
7509 ;
7510#endif /* !VBOX_WITH_VBOXSDL */
7511
7512 else
7513
7514#ifdef VBOX_WITH_HEADLESS
7515 if ( strFrontend == "headless"
7516 || strFrontend == "capture"
7517 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7518 )
7519 {
7520 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7521 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7522 * and a VM works even if the server has not been installed.
7523 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7524 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7525 * differently in 4.0 and 3.x.
7526 */
7527 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7528 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7529 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7530
7531 Utf8Str idStr = mData->mUuid.toString();
7532 const char *apszArgs[] =
7533 {
7534 szPath,
7535 "--comment", mUserData->s.strName.c_str(),
7536 "--startvm", idStr.c_str(),
7537 "--vrde", "config",
7538 0, /* For "--capture". */
7539 0, /* For "--sup-startup-log". */
7540 0
7541 };
7542 unsigned iArg = 7;
7543 if (strFrontend == "capture")
7544 apszArgs[iArg++] = "--capture";
7545 apszArgs[iArg++] = pszSupStartupLogArg;
7546
7547# ifdef RT_OS_WINDOWS
7548 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7549# else
7550 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7551# endif
7552 }
7553#else /* !VBOX_WITH_HEADLESS */
7554 if (0)
7555 ;
7556#endif /* !VBOX_WITH_HEADLESS */
7557 else
7558 {
7559 RTEnvDestroy(env);
7560 return setError(E_INVALIDARG,
7561 tr("Invalid frontend name: '%s'"),
7562 strFrontend.c_str());
7563 }
7564
7565 RTEnvDestroy(env);
7566
7567 if (RT_FAILURE(vrc))
7568 return setError(VBOX_E_IPRT_ERROR,
7569 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7570 mUserData->s.strName.c_str(), vrc);
7571
7572 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7573
7574 /*
7575 * Note that we don't release the lock here before calling the client,
7576 * because it doesn't need to call us back if called with a NULL argument.
7577 * Releasing the lock here is dangerous because we didn't prepare the
7578 * launch data yet, but the client we've just started may happen to be
7579 * too fast and call LockMachine() that will fail (because of PID, etc.),
7580 * so that the Machine will never get out of the Spawning session state.
7581 */
7582
7583 /* inform the session that it will be a remote one */
7584 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7585#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7586 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7587#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7588 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7589#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7590 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7591
7592 if (FAILED(rc))
7593 {
7594 /* restore the session state */
7595 mData->mSession.mState = SessionState_Unlocked;
7596 alock.release();
7597 mParent->i_addProcessToReap(pid);
7598 /* The failure may occur w/o any error info (from RPC), so provide one */
7599 return setError(VBOX_E_VM_ERROR,
7600 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7601 }
7602
7603 /* attach launch data to the machine */
7604 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7605 mData->mSession.mRemoteControls.push_back(aControl);
7606 mData->mSession.mProgress = aProgress;
7607 mData->mSession.mPID = pid;
7608 mData->mSession.mState = SessionState_Spawning;
7609 mData->mSession.mType = strFrontend;
7610
7611 alock.release();
7612 mParent->i_addProcessToReap(pid);
7613
7614 LogFlowThisFuncLeave();
7615 return S_OK;
7616}
7617
7618/**
7619 * Returns @c true if the given session machine instance has an open direct
7620 * session (and optionally also for direct sessions which are closing) and
7621 * returns the session control machine instance if so.
7622 *
7623 * Note that when the method returns @c false, the arguments remain unchanged.
7624 *
7625 * @param aMachine Session machine object.
7626 * @param aControl Direct session control object (optional).
7627 * @param aAllowClosing If true then additionally a session which is currently
7628 * being closed will also be allowed.
7629 *
7630 * @note locks this object for reading.
7631 */
7632bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7633 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7634 bool aAllowClosing /*= false*/)
7635{
7636 AutoLimitedCaller autoCaller(this);
7637 AssertComRCReturn(autoCaller.rc(), false);
7638
7639 /* just return false for inaccessible machines */
7640 if (getObjectState().getState() != ObjectState::Ready)
7641 return false;
7642
7643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7644
7645 if ( mData->mSession.mState == SessionState_Locked
7646 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7647 )
7648 {
7649 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7650
7651 aMachine = mData->mSession.mMachine;
7652
7653 if (aControl != NULL)
7654 *aControl = mData->mSession.mDirectControl;
7655
7656 return true;
7657 }
7658
7659 return false;
7660}
7661
7662/**
7663 * Returns @c true if the given machine has an spawning direct session.
7664 *
7665 * @note locks this object for reading.
7666 */
7667bool Machine::i_isSessionSpawning()
7668{
7669 AutoLimitedCaller autoCaller(this);
7670 AssertComRCReturn(autoCaller.rc(), false);
7671
7672 /* just return false for inaccessible machines */
7673 if (getObjectState().getState() != ObjectState::Ready)
7674 return false;
7675
7676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7677
7678 if (mData->mSession.mState == SessionState_Spawning)
7679 return true;
7680
7681 return false;
7682}
7683
7684/**
7685 * Called from the client watcher thread to check for unexpected client process
7686 * death during Session_Spawning state (e.g. before it successfully opened a
7687 * direct session).
7688 *
7689 * On Win32 and on OS/2, this method is called only when we've got the
7690 * direct client's process termination notification, so it always returns @c
7691 * true.
7692 *
7693 * On other platforms, this method returns @c true if the client process is
7694 * terminated and @c false if it's still alive.
7695 *
7696 * @note Locks this object for writing.
7697 */
7698bool Machine::i_checkForSpawnFailure()
7699{
7700 AutoCaller autoCaller(this);
7701 if (!autoCaller.isOk())
7702 {
7703 /* nothing to do */
7704 LogFlowThisFunc(("Already uninitialized!\n"));
7705 return true;
7706 }
7707
7708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7709
7710 if (mData->mSession.mState != SessionState_Spawning)
7711 {
7712 /* nothing to do */
7713 LogFlowThisFunc(("Not spawning any more!\n"));
7714 return true;
7715 }
7716
7717 HRESULT rc = S_OK;
7718
7719 /* PID not yet initialized, skip check. */
7720 if (mData->mSession.mPID == NIL_RTPROCESS)
7721 return false;
7722
7723 RTPROCSTATUS status;
7724 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7725
7726 if (vrc != VERR_PROCESS_RUNNING)
7727 {
7728 Utf8Str strExtraInfo;
7729
7730#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7731 /* If the startup logfile exists and is of non-zero length, tell the
7732 user to look there for more details to encourage them to attach it
7733 when reporting startup issues. */
7734 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7735 uint64_t cbStartupLogFile = 0;
7736 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7737 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7738 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7739#endif
7740
7741 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7742 rc = setError(E_FAIL,
7743 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7744 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7745 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7746 rc = setError(E_FAIL,
7747 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7748 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7749 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7750 rc = setError(E_FAIL,
7751 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7752 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7753 else
7754 rc = setError(E_FAIL,
7755 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7756 i_getName().c_str(), vrc, strExtraInfo.c_str());
7757 }
7758
7759 if (FAILED(rc))
7760 {
7761 /* Close the remote session, remove the remote control from the list
7762 * and reset session state to Closed (@note keep the code in sync with
7763 * the relevant part in LockMachine()). */
7764
7765 Assert(mData->mSession.mRemoteControls.size() == 1);
7766 if (mData->mSession.mRemoteControls.size() == 1)
7767 {
7768 ErrorInfoKeeper eik;
7769 mData->mSession.mRemoteControls.front()->Uninitialize();
7770 }
7771
7772 mData->mSession.mRemoteControls.clear();
7773 mData->mSession.mState = SessionState_Unlocked;
7774
7775 /* finalize the progress after setting the state */
7776 if (!mData->mSession.mProgress.isNull())
7777 {
7778 mData->mSession.mProgress->notifyComplete(rc);
7779 mData->mSession.mProgress.setNull();
7780 }
7781
7782 mData->mSession.mPID = NIL_RTPROCESS;
7783
7784 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7785 return true;
7786 }
7787
7788 return false;
7789}
7790
7791/**
7792 * Checks whether the machine can be registered. If so, commits and saves
7793 * all settings.
7794 *
7795 * @note Must be called from mParent's write lock. Locks this object and
7796 * children for writing.
7797 */
7798HRESULT Machine::i_prepareRegister()
7799{
7800 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7801
7802 AutoLimitedCaller autoCaller(this);
7803 AssertComRCReturnRC(autoCaller.rc());
7804
7805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7806
7807 /* wait for state dependents to drop to zero */
7808 i_ensureNoStateDependencies();
7809
7810 if (!mData->mAccessible)
7811 return setError(VBOX_E_INVALID_OBJECT_STATE,
7812 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7813 mUserData->s.strName.c_str(),
7814 mData->mUuid.toString().c_str());
7815
7816 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7817
7818 if (mData->mRegistered)
7819 return setError(VBOX_E_INVALID_OBJECT_STATE,
7820 tr("The machine '%s' with UUID {%s} is already registered"),
7821 mUserData->s.strName.c_str(),
7822 mData->mUuid.toString().c_str());
7823
7824 HRESULT rc = S_OK;
7825
7826 // Ensure the settings are saved. If we are going to be registered and
7827 // no config file exists yet, create it by calling i_saveSettings() too.
7828 if ( (mData->flModifications)
7829 || (!mData->pMachineConfigFile->fileExists())
7830 )
7831 {
7832 rc = i_saveSettings(NULL);
7833 // no need to check whether VirtualBox.xml needs saving too since
7834 // we can't have a machine XML file rename pending
7835 if (FAILED(rc)) return rc;
7836 }
7837
7838 /* more config checking goes here */
7839
7840 if (SUCCEEDED(rc))
7841 {
7842 /* we may have had implicit modifications we want to fix on success */
7843 i_commit();
7844
7845 mData->mRegistered = true;
7846 }
7847 else
7848 {
7849 /* we may have had implicit modifications we want to cancel on failure*/
7850 i_rollback(false /* aNotify */);
7851 }
7852
7853 return rc;
7854}
7855
7856/**
7857 * Increases the number of objects dependent on the machine state or on the
7858 * registered state. Guarantees that these two states will not change at least
7859 * until #releaseStateDependency() is called.
7860 *
7861 * Depending on the @a aDepType value, additional state checks may be made.
7862 * These checks will set extended error info on failure. See
7863 * #checkStateDependency() for more info.
7864 *
7865 * If this method returns a failure, the dependency is not added and the caller
7866 * is not allowed to rely on any particular machine state or registration state
7867 * value and may return the failed result code to the upper level.
7868 *
7869 * @param aDepType Dependency type to add.
7870 * @param aState Current machine state (NULL if not interested).
7871 * @param aRegistered Current registered state (NULL if not interested).
7872 *
7873 * @note Locks this object for writing.
7874 */
7875HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7876 MachineState_T *aState /* = NULL */,
7877 BOOL *aRegistered /* = NULL */)
7878{
7879 AutoCaller autoCaller(this);
7880 AssertComRCReturnRC(autoCaller.rc());
7881
7882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7883
7884 HRESULT rc = i_checkStateDependency(aDepType);
7885 if (FAILED(rc)) return rc;
7886
7887 {
7888 if (mData->mMachineStateChangePending != 0)
7889 {
7890 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7891 * drop to zero so don't add more. It may make sense to wait a bit
7892 * and retry before reporting an error (since the pending state
7893 * transition should be really quick) but let's just assert for
7894 * now to see if it ever happens on practice. */
7895
7896 AssertFailed();
7897
7898 return setError(E_ACCESSDENIED,
7899 tr("Machine state change is in progress. Please retry the operation later."));
7900 }
7901
7902 ++mData->mMachineStateDeps;
7903 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7904 }
7905
7906 if (aState)
7907 *aState = mData->mMachineState;
7908 if (aRegistered)
7909 *aRegistered = mData->mRegistered;
7910
7911 return S_OK;
7912}
7913
7914/**
7915 * Decreases the number of objects dependent on the machine state.
7916 * Must always complete the #addStateDependency() call after the state
7917 * dependency is no more necessary.
7918 */
7919void Machine::i_releaseStateDependency()
7920{
7921 AutoCaller autoCaller(this);
7922 AssertComRCReturnVoid(autoCaller.rc());
7923
7924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7925
7926 /* releaseStateDependency() w/o addStateDependency()? */
7927 AssertReturnVoid(mData->mMachineStateDeps != 0);
7928 -- mData->mMachineStateDeps;
7929
7930 if (mData->mMachineStateDeps == 0)
7931 {
7932 /* inform i_ensureNoStateDependencies() that there are no more deps */
7933 if (mData->mMachineStateChangePending != 0)
7934 {
7935 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7936 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7937 }
7938 }
7939}
7940
7941Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7942{
7943 /* start with nothing found */
7944 Utf8Str strResult("");
7945
7946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7947
7948 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7949 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7950 // found:
7951 strResult = it->second; // source is a Utf8Str
7952
7953 return strResult;
7954}
7955
7956// protected methods
7957/////////////////////////////////////////////////////////////////////////////
7958
7959/**
7960 * Performs machine state checks based on the @a aDepType value. If a check
7961 * fails, this method will set extended error info, otherwise it will return
7962 * S_OK. It is supposed, that on failure, the caller will immediately return
7963 * the return value of this method to the upper level.
7964 *
7965 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7966 *
7967 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7968 * current state of this machine object allows to change settings of the
7969 * machine (i.e. the machine is not registered, or registered but not running
7970 * and not saved). It is useful to call this method from Machine setters
7971 * before performing any change.
7972 *
7973 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7974 * as for MutableStateDep except that if the machine is saved, S_OK is also
7975 * returned. This is useful in setters which allow changing machine
7976 * properties when it is in the saved state.
7977 *
7978 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7979 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7980 * Aborted).
7981 *
7982 * @param aDepType Dependency type to check.
7983 *
7984 * @note Non Machine based classes should use #addStateDependency() and
7985 * #releaseStateDependency() methods or the smart AutoStateDependency
7986 * template.
7987 *
7988 * @note This method must be called from under this object's read or write
7989 * lock.
7990 */
7991HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7992{
7993 switch (aDepType)
7994 {
7995 case AnyStateDep:
7996 {
7997 break;
7998 }
7999 case MutableStateDep:
8000 {
8001 if ( mData->mRegistered
8002 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8003 Paused should actually be included here... (Live Migration) */
8004 || ( mData->mMachineState != MachineState_Paused
8005 && mData->mMachineState != MachineState_Running
8006 && mData->mMachineState != MachineState_Aborted
8007 && mData->mMachineState != MachineState_Teleported
8008 && mData->mMachineState != MachineState_PoweredOff
8009 )
8010 )
8011 )
8012 return setError(VBOX_E_INVALID_VM_STATE,
8013 tr("The machine is not mutable (state is %s)"),
8014 Global::stringifyMachineState(mData->mMachineState));
8015 break;
8016 }
8017 case MutableOrSavedStateDep:
8018 {
8019 if ( mData->mRegistered
8020 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8021 Paused should actually be included here... (Live Migration) */
8022 || ( mData->mMachineState != MachineState_Paused
8023 && mData->mMachineState != MachineState_Running
8024 && mData->mMachineState != MachineState_Aborted
8025 && mData->mMachineState != MachineState_Teleported
8026 && mData->mMachineState != MachineState_Saved
8027 && mData->mMachineState != MachineState_PoweredOff
8028 )
8029 )
8030 )
8031 return setError(VBOX_E_INVALID_VM_STATE,
8032 tr("The machine is not mutable (state is %s)"),
8033 Global::stringifyMachineState(mData->mMachineState));
8034 break;
8035 }
8036 case OfflineStateDep:
8037 {
8038 if ( mData->mRegistered
8039 && ( !i_isSessionMachine()
8040 || ( mData->mMachineState != MachineState_PoweredOff
8041 && mData->mMachineState != MachineState_Saved
8042 && mData->mMachineState != MachineState_Aborted
8043 && mData->mMachineState != MachineState_Teleported
8044 )
8045 )
8046 )
8047 return setError(VBOX_E_INVALID_VM_STATE,
8048 tr("The machine is not offline (state is %s)"),
8049 Global::stringifyMachineState(mData->mMachineState));
8050 break;
8051 }
8052 }
8053
8054 return S_OK;
8055}
8056
8057/**
8058 * Helper to initialize all associated child objects and allocate data
8059 * structures.
8060 *
8061 * This method must be called as a part of the object's initialization procedure
8062 * (usually done in the #init() method).
8063 *
8064 * @note Must be called only from #init() or from #registeredInit().
8065 */
8066HRESULT Machine::initDataAndChildObjects()
8067{
8068 AutoCaller autoCaller(this);
8069 AssertComRCReturnRC(autoCaller.rc());
8070 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8071 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8072
8073 AssertReturn(!mData->mAccessible, E_FAIL);
8074
8075 /* allocate data structures */
8076 mSSData.allocate();
8077 mUserData.allocate();
8078 mHWData.allocate();
8079 mMediaData.allocate();
8080 mStorageControllers.allocate();
8081 mUSBControllers.allocate();
8082
8083 /* initialize mOSTypeId */
8084 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8085
8086 /* create associated BIOS settings object */
8087 unconst(mBIOSSettings).createObject();
8088 mBIOSSettings->init(this);
8089
8090 /* create an associated VRDE object (default is disabled) */
8091 unconst(mVRDEServer).createObject();
8092 mVRDEServer->init(this);
8093
8094 /* create associated serial port objects */
8095 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8096 {
8097 unconst(mSerialPorts[slot]).createObject();
8098 mSerialPorts[slot]->init(this, slot);
8099 }
8100
8101 /* create associated parallel port objects */
8102 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8103 {
8104 unconst(mParallelPorts[slot]).createObject();
8105 mParallelPorts[slot]->init(this, slot);
8106 }
8107
8108 /* create the audio adapter object (always present, default is disabled) */
8109 unconst(mAudioAdapter).createObject();
8110 mAudioAdapter->init(this);
8111
8112 /* create the USB device filters object (always present) */
8113 unconst(mUSBDeviceFilters).createObject();
8114 mUSBDeviceFilters->init(this);
8115
8116 /* create associated network adapter objects */
8117 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8118 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8119 {
8120 unconst(mNetworkAdapters[slot]).createObject();
8121 mNetworkAdapters[slot]->init(this, slot);
8122 }
8123
8124 /* create the bandwidth control */
8125 unconst(mBandwidthControl).createObject();
8126 mBandwidthControl->init(this);
8127
8128 return S_OK;
8129}
8130
8131/**
8132 * Helper to uninitialize all associated child objects and to free all data
8133 * structures.
8134 *
8135 * This method must be called as a part of the object's uninitialization
8136 * procedure (usually done in the #uninit() method).
8137 *
8138 * @note Must be called only from #uninit() or from #registeredInit().
8139 */
8140void Machine::uninitDataAndChildObjects()
8141{
8142 AutoCaller autoCaller(this);
8143 AssertComRCReturnVoid(autoCaller.rc());
8144 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8145 || getObjectState().getState() == ObjectState::Limited);
8146
8147 /* tell all our other child objects we've been uninitialized */
8148 if (mBandwidthControl)
8149 {
8150 mBandwidthControl->uninit();
8151 unconst(mBandwidthControl).setNull();
8152 }
8153
8154 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8155 {
8156 if (mNetworkAdapters[slot])
8157 {
8158 mNetworkAdapters[slot]->uninit();
8159 unconst(mNetworkAdapters[slot]).setNull();
8160 }
8161 }
8162
8163 if (mUSBDeviceFilters)
8164 {
8165 mUSBDeviceFilters->uninit();
8166 unconst(mUSBDeviceFilters).setNull();
8167 }
8168
8169 if (mAudioAdapter)
8170 {
8171 mAudioAdapter->uninit();
8172 unconst(mAudioAdapter).setNull();
8173 }
8174
8175 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8176 {
8177 if (mParallelPorts[slot])
8178 {
8179 mParallelPorts[slot]->uninit();
8180 unconst(mParallelPorts[slot]).setNull();
8181 }
8182 }
8183
8184 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8185 {
8186 if (mSerialPorts[slot])
8187 {
8188 mSerialPorts[slot]->uninit();
8189 unconst(mSerialPorts[slot]).setNull();
8190 }
8191 }
8192
8193 if (mVRDEServer)
8194 {
8195 mVRDEServer->uninit();
8196 unconst(mVRDEServer).setNull();
8197 }
8198
8199 if (mBIOSSettings)
8200 {
8201 mBIOSSettings->uninit();
8202 unconst(mBIOSSettings).setNull();
8203 }
8204
8205 /* Deassociate media (only when a real Machine or a SnapshotMachine
8206 * instance is uninitialized; SessionMachine instances refer to real
8207 * Machine media). This is necessary for a clean re-initialization of
8208 * the VM after successfully re-checking the accessibility state. Note
8209 * that in case of normal Machine or SnapshotMachine uninitialization (as
8210 * a result of unregistering or deleting the snapshot), outdated media
8211 * attachments will already be uninitialized and deleted, so this
8212 * code will not affect them. */
8213 if ( !!mMediaData
8214 && (!i_isSessionMachine())
8215 )
8216 {
8217 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8218 it != mMediaData->mAttachments.end();
8219 ++it)
8220 {
8221 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8222 if (pMedium.isNull())
8223 continue;
8224 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8225 AssertComRC(rc);
8226 }
8227 }
8228
8229 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8230 {
8231 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8232 if (mData->mFirstSnapshot)
8233 {
8234 // snapshots tree is protected by machine write lock; strictly
8235 // this isn't necessary here since we're deleting the entire
8236 // machine, but otherwise we assert in Snapshot::uninit()
8237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8238 mData->mFirstSnapshot->uninit();
8239 mData->mFirstSnapshot.setNull();
8240 }
8241
8242 mData->mCurrentSnapshot.setNull();
8243 }
8244
8245 /* free data structures (the essential mData structure is not freed here
8246 * since it may be still in use) */
8247 mMediaData.free();
8248 mStorageControllers.free();
8249 mUSBControllers.free();
8250 mHWData.free();
8251 mUserData.free();
8252 mSSData.free();
8253}
8254
8255/**
8256 * Returns a pointer to the Machine object for this machine that acts like a
8257 * parent for complex machine data objects such as shared folders, etc.
8258 *
8259 * For primary Machine objects and for SnapshotMachine objects, returns this
8260 * object's pointer itself. For SessionMachine objects, returns the peer
8261 * (primary) machine pointer.
8262 */
8263Machine* Machine::i_getMachine()
8264{
8265 if (i_isSessionMachine())
8266 return (Machine*)mPeer;
8267 return this;
8268}
8269
8270/**
8271 * Makes sure that there are no machine state dependents. If necessary, waits
8272 * for the number of dependents to drop to zero.
8273 *
8274 * Make sure this method is called from under this object's write lock to
8275 * guarantee that no new dependents may be added when this method returns
8276 * control to the caller.
8277 *
8278 * @note Locks this object for writing. The lock will be released while waiting
8279 * (if necessary).
8280 *
8281 * @warning To be used only in methods that change the machine state!
8282 */
8283void Machine::i_ensureNoStateDependencies()
8284{
8285 AssertReturnVoid(isWriteLockOnCurrentThread());
8286
8287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8288
8289 /* Wait for all state dependents if necessary */
8290 if (mData->mMachineStateDeps != 0)
8291 {
8292 /* lazy semaphore creation */
8293 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8294 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8295
8296 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8297 mData->mMachineStateDeps));
8298
8299 ++mData->mMachineStateChangePending;
8300
8301 /* reset the semaphore before waiting, the last dependent will signal
8302 * it */
8303 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8304
8305 alock.release();
8306
8307 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8308
8309 alock.acquire();
8310
8311 -- mData->mMachineStateChangePending;
8312 }
8313}
8314
8315/**
8316 * Changes the machine state and informs callbacks.
8317 *
8318 * This method is not intended to fail so it either returns S_OK or asserts (and
8319 * returns a failure).
8320 *
8321 * @note Locks this object for writing.
8322 */
8323HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8324{
8325 LogFlowThisFuncEnter();
8326 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8327
8328 AutoCaller autoCaller(this);
8329 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8330
8331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8332
8333 /* wait for state dependents to drop to zero */
8334 i_ensureNoStateDependencies();
8335
8336 if (mData->mMachineState != aMachineState)
8337 {
8338 mData->mMachineState = aMachineState;
8339
8340 RTTimeNow(&mData->mLastStateChange);
8341
8342 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8343 }
8344
8345 LogFlowThisFuncLeave();
8346 return S_OK;
8347}
8348
8349/**
8350 * Searches for a shared folder with the given logical name
8351 * in the collection of shared folders.
8352 *
8353 * @param aName logical name of the shared folder
8354 * @param aSharedFolder where to return the found object
8355 * @param aSetError whether to set the error info if the folder is
8356 * not found
8357 * @return
8358 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8359 *
8360 * @note
8361 * must be called from under the object's lock!
8362 */
8363HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8364 ComObjPtr<SharedFolder> &aSharedFolder,
8365 bool aSetError /* = false */)
8366{
8367 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8368 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8369 it != mHWData->mSharedFolders.end();
8370 ++it)
8371 {
8372 SharedFolder *pSF = *it;
8373 AutoCaller autoCaller(pSF);
8374 if (pSF->i_getName() == aName)
8375 {
8376 aSharedFolder = pSF;
8377 rc = S_OK;
8378 break;
8379 }
8380 }
8381
8382 if (aSetError && FAILED(rc))
8383 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8384
8385 return rc;
8386}
8387
8388/**
8389 * Initializes all machine instance data from the given settings structures
8390 * from XML. The exception is the machine UUID which needs special handling
8391 * depending on the caller's use case, so the caller needs to set that herself.
8392 *
8393 * This gets called in several contexts during machine initialization:
8394 *
8395 * -- When machine XML exists on disk already and needs to be loaded into memory,
8396 * for example, from registeredInit() to load all registered machines on
8397 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8398 * attached to the machine should be part of some media registry already.
8399 *
8400 * -- During OVF import, when a machine config has been constructed from an
8401 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8402 * ensure that the media listed as attachments in the config (which have
8403 * been imported from the OVF) receive the correct registry ID.
8404 *
8405 * -- During VM cloning.
8406 *
8407 * @param config Machine settings from XML.
8408 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8409 * for each attached medium in the config.
8410 * @return
8411 */
8412HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8413 const Guid *puuidRegistry)
8414{
8415 // copy name, description, OS type, teleporter, UTC etc.
8416 mUserData->s = config.machineUserData;
8417
8418 // Decode the Icon overide data from config userdata and set onto Machine.
8419 #define DECODE_STR_MAX _1M
8420 const char* pszStr = config.machineUserData.ovIcon.c_str();
8421 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8422 if (cbOut > DECODE_STR_MAX)
8423 return setError(E_FAIL,
8424 tr("Icon Data too long.'%d' > '%d'"),
8425 cbOut,
8426 DECODE_STR_MAX);
8427 com::SafeArray<BYTE> iconByte(cbOut);
8428 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8429 if (FAILED(rc))
8430 return setError(E_FAIL,
8431 tr("Failure to Decode Icon Data. '%s' (%d)"),
8432 pszStr,
8433 rc);
8434 mUserData->mIcon.resize(iconByte.size());
8435 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8436
8437 // look up the object by Id to check it is valid
8438 ComPtr<IGuestOSType> guestOSType;
8439 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8440 guestOSType.asOutParam());
8441 if (FAILED(rc)) return rc;
8442
8443 // stateFile (optional)
8444 if (config.strStateFile.isEmpty())
8445 mSSData->strStateFilePath.setNull();
8446 else
8447 {
8448 Utf8Str stateFilePathFull(config.strStateFile);
8449 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8450 if (RT_FAILURE(vrc))
8451 return setError(E_FAIL,
8452 tr("Invalid saved state file path '%s' (%Rrc)"),
8453 config.strStateFile.c_str(),
8454 vrc);
8455 mSSData->strStateFilePath = stateFilePathFull;
8456 }
8457
8458 // snapshot folder needs special processing so set it again
8459 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8460 if (FAILED(rc)) return rc;
8461
8462 /* Copy the extra data items (Not in any case config is already the same as
8463 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8464 * make sure the extra data map is copied). */
8465 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8466
8467 /* currentStateModified (optional, default is true) */
8468 mData->mCurrentStateModified = config.fCurrentStateModified;
8469
8470 mData->mLastStateChange = config.timeLastStateChange;
8471
8472 /*
8473 * note: all mUserData members must be assigned prior this point because
8474 * we need to commit changes in order to let mUserData be shared by all
8475 * snapshot machine instances.
8476 */
8477 mUserData.commitCopy();
8478
8479 // machine registry, if present (must be loaded before snapshots)
8480 if (config.canHaveOwnMediaRegistry())
8481 {
8482 // determine machine folder
8483 Utf8Str strMachineFolder = i_getSettingsFileFull();
8484 strMachineFolder.stripFilename();
8485 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8486 config.mediaRegistry,
8487 strMachineFolder);
8488 if (FAILED(rc)) return rc;
8489 }
8490
8491 /* Snapshot node (optional) */
8492 size_t cRootSnapshots;
8493 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8494 {
8495 // there must be only one root snapshot
8496 Assert(cRootSnapshots == 1);
8497
8498 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8499
8500 rc = i_loadSnapshot(snap,
8501 config.uuidCurrentSnapshot,
8502 NULL); // no parent == first snapshot
8503 if (FAILED(rc)) return rc;
8504 }
8505
8506 // hardware data
8507 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8508 if (FAILED(rc)) return rc;
8509
8510 // load storage controllers
8511 rc = i_loadStorageControllers(config.storageMachine,
8512 puuidRegistry,
8513 NULL /* puuidSnapshot */);
8514 if (FAILED(rc)) return rc;
8515
8516 /*
8517 * NOTE: the assignment below must be the last thing to do,
8518 * otherwise it will be not possible to change the settings
8519 * somewhere in the code above because all setters will be
8520 * blocked by i_checkStateDependency(MutableStateDep).
8521 */
8522
8523 /* set the machine state to Aborted or Saved when appropriate */
8524 if (config.fAborted)
8525 {
8526 mSSData->strStateFilePath.setNull();
8527
8528 /* no need to use i_setMachineState() during init() */
8529 mData->mMachineState = MachineState_Aborted;
8530 }
8531 else if (!mSSData->strStateFilePath.isEmpty())
8532 {
8533 /* no need to use i_setMachineState() during init() */
8534 mData->mMachineState = MachineState_Saved;
8535 }
8536
8537 // after loading settings, we are no longer different from the XML on disk
8538 mData->flModifications = 0;
8539
8540 return S_OK;
8541}
8542
8543/**
8544 * Recursively loads all snapshots starting from the given.
8545 *
8546 * @param aNode <Snapshot> node.
8547 * @param aCurSnapshotId Current snapshot ID from the settings file.
8548 * @param aParentSnapshot Parent snapshot.
8549 */
8550HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8551 const Guid &aCurSnapshotId,
8552 Snapshot *aParentSnapshot)
8553{
8554 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8555 AssertReturn(!i_isSessionMachine(), E_FAIL);
8556
8557 HRESULT rc = S_OK;
8558
8559 Utf8Str strStateFile;
8560 if (!data.strStateFile.isEmpty())
8561 {
8562 /* optional */
8563 strStateFile = data.strStateFile;
8564 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8565 if (RT_FAILURE(vrc))
8566 return setError(E_FAIL,
8567 tr("Invalid saved state file path '%s' (%Rrc)"),
8568 strStateFile.c_str(),
8569 vrc);
8570 }
8571
8572 /* create a snapshot machine object */
8573 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8574 pSnapshotMachine.createObject();
8575 rc = pSnapshotMachine->initFromSettings(this,
8576 data.hardware,
8577 &data.debugging,
8578 &data.autostart,
8579 data.storage,
8580 data.uuid.ref(),
8581 strStateFile);
8582 if (FAILED(rc)) return rc;
8583
8584 /* create a snapshot object */
8585 ComObjPtr<Snapshot> pSnapshot;
8586 pSnapshot.createObject();
8587 /* initialize the snapshot */
8588 rc = pSnapshot->init(mParent, // VirtualBox object
8589 data.uuid,
8590 data.strName,
8591 data.strDescription,
8592 data.timestamp,
8593 pSnapshotMachine,
8594 aParentSnapshot);
8595 if (FAILED(rc)) return rc;
8596
8597 /* memorize the first snapshot if necessary */
8598 if (!mData->mFirstSnapshot)
8599 mData->mFirstSnapshot = pSnapshot;
8600
8601 /* memorize the current snapshot when appropriate */
8602 if ( !mData->mCurrentSnapshot
8603 && pSnapshot->i_getId() == aCurSnapshotId
8604 )
8605 mData->mCurrentSnapshot = pSnapshot;
8606
8607 // now create the children
8608 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8609 it != data.llChildSnapshots.end();
8610 ++it)
8611 {
8612 const settings::Snapshot &childData = *it;
8613 // recurse
8614 rc = i_loadSnapshot(childData,
8615 aCurSnapshotId,
8616 pSnapshot); // parent = the one we created above
8617 if (FAILED(rc)) return rc;
8618 }
8619
8620 return rc;
8621}
8622
8623/**
8624 * Loads settings into mHWData.
8625 *
8626 * @param data Reference to the hardware settings.
8627 * @param pDbg Pointer to the debugging settings.
8628 * @param pAutostart Pointer to the autostart settings.
8629 */
8630HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8631 const settings::Autostart *pAutostart)
8632{
8633 AssertReturn(!i_isSessionMachine(), E_FAIL);
8634
8635 HRESULT rc = S_OK;
8636
8637 try
8638 {
8639 /* The hardware version attribute (optional). */
8640 mHWData->mHWVersion = data.strVersion;
8641 mHWData->mHardwareUUID = data.uuid;
8642
8643 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8644 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8645 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8646 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8647 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8648 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8649 mHWData->mPAEEnabled = data.fPAE;
8650 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8651 mHWData->mLongMode = data.enmLongMode;
8652 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8653 mHWData->mCPUCount = data.cCPUs;
8654 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8655 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8656
8657 // cpu
8658 if (mHWData->mCPUHotPlugEnabled)
8659 {
8660 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8661 it != data.llCpus.end();
8662 ++it)
8663 {
8664 const settings::Cpu &cpu = *it;
8665
8666 mHWData->mCPUAttached[cpu.ulId] = true;
8667 }
8668 }
8669
8670 // cpuid leafs
8671 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8672 it != data.llCpuIdLeafs.end();
8673 ++it)
8674 {
8675 const settings::CpuIdLeaf &leaf = *it;
8676
8677 switch (leaf.ulId)
8678 {
8679 case 0x0:
8680 case 0x1:
8681 case 0x2:
8682 case 0x3:
8683 case 0x4:
8684 case 0x5:
8685 case 0x6:
8686 case 0x7:
8687 case 0x8:
8688 case 0x9:
8689 case 0xA:
8690 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8691 break;
8692
8693 case 0x80000000:
8694 case 0x80000001:
8695 case 0x80000002:
8696 case 0x80000003:
8697 case 0x80000004:
8698 case 0x80000005:
8699 case 0x80000006:
8700 case 0x80000007:
8701 case 0x80000008:
8702 case 0x80000009:
8703 case 0x8000000A:
8704 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8705 break;
8706
8707 default:
8708 /* just ignore */
8709 break;
8710 }
8711 }
8712
8713 mHWData->mMemorySize = data.ulMemorySizeMB;
8714 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8715
8716 // boot order
8717 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8718 {
8719 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8720 if (it == data.mapBootOrder.end())
8721 mHWData->mBootOrder[i] = DeviceType_Null;
8722 else
8723 mHWData->mBootOrder[i] = it->second;
8724 }
8725
8726 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8727 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8728 mHWData->mMonitorCount = data.cMonitors;
8729 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8730 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8731 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8732 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8733 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8734 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8735 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8736 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8737 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8738 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8739 if (!data.strVideoCaptureFile.isEmpty())
8740 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8741 else
8742 mHWData->mVideoCaptureFile.setNull();
8743 mHWData->mFirmwareType = data.firmwareType;
8744 mHWData->mPointingHIDType = data.pointingHIDType;
8745 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8746 mHWData->mChipsetType = data.chipsetType;
8747 mHWData->mParavirtProvider = data.paravirtProvider;
8748 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8749 mHWData->mHPETEnabled = data.fHPETEnabled;
8750
8751 /* VRDEServer */
8752 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8753 if (FAILED(rc)) return rc;
8754
8755 /* BIOS */
8756 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8757 if (FAILED(rc)) return rc;
8758
8759 // Bandwidth control (must come before network adapters)
8760 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8761 if (FAILED(rc)) return rc;
8762
8763 /* Shared folders */
8764 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8765 it != data.usbSettings.llUSBControllers.end();
8766 ++it)
8767 {
8768 const settings::USBController &settingsCtrl = *it;
8769 ComObjPtr<USBController> newCtrl;
8770
8771 newCtrl.createObject();
8772 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8773 mUSBControllers->push_back(newCtrl);
8774 }
8775
8776 /* USB device filters */
8777 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8778 if (FAILED(rc)) return rc;
8779
8780 // network adapters
8781 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8782 size_t oldCount = mNetworkAdapters.size();
8783 if (newCount > oldCount)
8784 {
8785 mNetworkAdapters.resize(newCount);
8786 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8787 {
8788 unconst(mNetworkAdapters[slot]).createObject();
8789 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8790 }
8791 }
8792 else if (newCount < oldCount)
8793 mNetworkAdapters.resize(newCount);
8794 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8795 it != data.llNetworkAdapters.end();
8796 ++it)
8797 {
8798 const settings::NetworkAdapter &nic = *it;
8799
8800 /* slot unicity is guaranteed by XML Schema */
8801 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8802 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8803 if (FAILED(rc)) return rc;
8804 }
8805
8806 // serial ports
8807 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8808 it != data.llSerialPorts.end();
8809 ++it)
8810 {
8811 const settings::SerialPort &s = *it;
8812
8813 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8814 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8815 if (FAILED(rc)) return rc;
8816 }
8817
8818 // parallel ports (optional)
8819 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8820 it != data.llParallelPorts.end();
8821 ++it)
8822 {
8823 const settings::ParallelPort &p = *it;
8824
8825 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8826 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8827 if (FAILED(rc)) return rc;
8828 }
8829
8830 /* AudioAdapter */
8831 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8832 if (FAILED(rc)) return rc;
8833
8834 /* Shared folders */
8835 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8836 it != data.llSharedFolders.end();
8837 ++it)
8838 {
8839 const settings::SharedFolder &sf = *it;
8840
8841 ComObjPtr<SharedFolder> sharedFolder;
8842 /* Check for double entries. Not allowed! */
8843 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8844 if (SUCCEEDED(rc))
8845 return setError(VBOX_E_OBJECT_IN_USE,
8846 tr("Shared folder named '%s' already exists"),
8847 sf.strName.c_str());
8848
8849 /* Create the new shared folder. Don't break on error. This will be
8850 * reported when the machine starts. */
8851 sharedFolder.createObject();
8852 rc = sharedFolder->init(i_getMachine(),
8853 sf.strName,
8854 sf.strHostPath,
8855 RT_BOOL(sf.fWritable),
8856 RT_BOOL(sf.fAutoMount),
8857 false /* fFailOnError */);
8858 if (FAILED(rc)) return rc;
8859 mHWData->mSharedFolders.push_back(sharedFolder);
8860 }
8861
8862 // Clipboard
8863 mHWData->mClipboardMode = data.clipboardMode;
8864
8865 // drag'n'drop
8866 mHWData->mDnDMode = data.dndMode;
8867
8868 // guest settings
8869 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8870
8871 // IO settings
8872 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8873 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8874
8875 // Host PCI devices
8876 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8877 it != data.pciAttachments.end();
8878 ++it)
8879 {
8880 const settings::HostPCIDeviceAttachment &hpda = *it;
8881 ComObjPtr<PCIDeviceAttachment> pda;
8882
8883 pda.createObject();
8884 pda->i_loadSettings(this, hpda);
8885 mHWData->mPCIDeviceAssignments.push_back(pda);
8886 }
8887
8888 /*
8889 * (The following isn't really real hardware, but it lives in HWData
8890 * for reasons of convenience.)
8891 */
8892
8893#ifdef VBOX_WITH_GUEST_PROPS
8894 /* Guest properties (optional) */
8895 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8896 it != data.llGuestProperties.end();
8897 ++it)
8898 {
8899 const settings::GuestProperty &prop = *it;
8900 uint32_t fFlags = guestProp::NILFLAG;
8901 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8902 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8903 mHWData->mGuestProperties[prop.strName] = property;
8904 }
8905
8906 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8907#endif /* VBOX_WITH_GUEST_PROPS defined */
8908
8909 rc = i_loadDebugging(pDbg);
8910 if (FAILED(rc))
8911 return rc;
8912
8913 mHWData->mAutostart = *pAutostart;
8914
8915 /* default frontend */
8916 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8917 }
8918 catch(std::bad_alloc &)
8919 {
8920 return E_OUTOFMEMORY;
8921 }
8922
8923 AssertComRC(rc);
8924 return rc;
8925}
8926
8927/**
8928 * Called from Machine::loadHardware() to load the debugging settings of the
8929 * machine.
8930 *
8931 * @param pDbg Pointer to the settings.
8932 */
8933HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8934{
8935 mHWData->mDebugging = *pDbg;
8936 /* no more processing currently required, this will probably change. */
8937 return S_OK;
8938}
8939
8940/**
8941 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8942 *
8943 * @param data
8944 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8945 * @param puuidSnapshot
8946 * @return
8947 */
8948HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8949 const Guid *puuidRegistry,
8950 const Guid *puuidSnapshot)
8951{
8952 AssertReturn(!i_isSessionMachine(), E_FAIL);
8953
8954 HRESULT rc = S_OK;
8955
8956 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8957 it != data.llStorageControllers.end();
8958 ++it)
8959 {
8960 const settings::StorageController &ctlData = *it;
8961
8962 ComObjPtr<StorageController> pCtl;
8963 /* Try to find one with the name first. */
8964 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8965 if (SUCCEEDED(rc))
8966 return setError(VBOX_E_OBJECT_IN_USE,
8967 tr("Storage controller named '%s' already exists"),
8968 ctlData.strName.c_str());
8969
8970 pCtl.createObject();
8971 rc = pCtl->init(this,
8972 ctlData.strName,
8973 ctlData.storageBus,
8974 ctlData.ulInstance,
8975 ctlData.fBootable);
8976 if (FAILED(rc)) return rc;
8977
8978 mStorageControllers->push_back(pCtl);
8979
8980 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8981 if (FAILED(rc)) return rc;
8982
8983 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8984 if (FAILED(rc)) return rc;
8985
8986 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8987 if (FAILED(rc)) return rc;
8988
8989 /* Set IDE emulation settings (only for AHCI controller). */
8990 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8991 {
8992 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8993 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8994 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8995 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8996 )
8997 return rc;
8998 }
8999
9000 /* Load the attached devices now. */
9001 rc = i_loadStorageDevices(pCtl,
9002 ctlData,
9003 puuidRegistry,
9004 puuidSnapshot);
9005 if (FAILED(rc)) return rc;
9006 }
9007
9008 return S_OK;
9009}
9010
9011/**
9012 * Called from i_loadStorageControllers for a controller's devices.
9013 *
9014 * @param aStorageController
9015 * @param data
9016 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9017 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9018 * @return
9019 */
9020HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9021 const settings::StorageController &data,
9022 const Guid *puuidRegistry,
9023 const Guid *puuidSnapshot)
9024{
9025 HRESULT rc = S_OK;
9026
9027 /* paranoia: detect duplicate attachments */
9028 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9029 it != data.llAttachedDevices.end();
9030 ++it)
9031 {
9032 const settings::AttachedDevice &ad = *it;
9033
9034 for (settings::AttachedDevicesList::const_iterator it2 = it;
9035 it2 != data.llAttachedDevices.end();
9036 ++it2)
9037 {
9038 if (it == it2)
9039 continue;
9040
9041 const settings::AttachedDevice &ad2 = *it2;
9042
9043 if ( ad.lPort == ad2.lPort
9044 && ad.lDevice == ad2.lDevice)
9045 {
9046 return setError(E_FAIL,
9047 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9048 aStorageController->i_getName().c_str(),
9049 ad.lPort,
9050 ad.lDevice,
9051 mUserData->s.strName.c_str());
9052 }
9053 }
9054 }
9055
9056 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9057 it != data.llAttachedDevices.end();
9058 ++it)
9059 {
9060 const settings::AttachedDevice &dev = *it;
9061 ComObjPtr<Medium> medium;
9062
9063 switch (dev.deviceType)
9064 {
9065 case DeviceType_Floppy:
9066 case DeviceType_DVD:
9067 if (dev.strHostDriveSrc.isNotEmpty())
9068 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9069 false /* fRefresh */, medium);
9070 else
9071 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9072 dev.uuid,
9073 false /* fRefresh */,
9074 false /* aSetError */,
9075 medium);
9076 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9077 // This is not an error. The host drive or UUID might have vanished, so just go
9078 // ahead without this removeable medium attachment
9079 rc = S_OK;
9080 break;
9081
9082 case DeviceType_HardDisk:
9083 {
9084 /* find a hard disk by UUID */
9085 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9086 if (FAILED(rc))
9087 {
9088 if (i_isSnapshotMachine())
9089 {
9090 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9091 // so the user knows that the bad disk is in a snapshot somewhere
9092 com::ErrorInfo info;
9093 return setError(E_FAIL,
9094 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9095 puuidSnapshot->raw(),
9096 info.getText().raw());
9097 }
9098 else
9099 return rc;
9100 }
9101
9102 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9103
9104 if (medium->i_getType() == MediumType_Immutable)
9105 {
9106 if (i_isSnapshotMachine())
9107 return setError(E_FAIL,
9108 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9109 "of the virtual machine '%s' ('%s')"),
9110 medium->i_getLocationFull().c_str(),
9111 dev.uuid.raw(),
9112 puuidSnapshot->raw(),
9113 mUserData->s.strName.c_str(),
9114 mData->m_strConfigFileFull.c_str());
9115
9116 return setError(E_FAIL,
9117 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9118 medium->i_getLocationFull().c_str(),
9119 dev.uuid.raw(),
9120 mUserData->s.strName.c_str(),
9121 mData->m_strConfigFileFull.c_str());
9122 }
9123
9124 if (medium->i_getType() == MediumType_MultiAttach)
9125 {
9126 if (i_isSnapshotMachine())
9127 return setError(E_FAIL,
9128 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9129 "of the virtual machine '%s' ('%s')"),
9130 medium->i_getLocationFull().c_str(),
9131 dev.uuid.raw(),
9132 puuidSnapshot->raw(),
9133 mUserData->s.strName.c_str(),
9134 mData->m_strConfigFileFull.c_str());
9135
9136 return setError(E_FAIL,
9137 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9138 medium->i_getLocationFull().c_str(),
9139 dev.uuid.raw(),
9140 mUserData->s.strName.c_str(),
9141 mData->m_strConfigFileFull.c_str());
9142 }
9143
9144 if ( !i_isSnapshotMachine()
9145 && medium->i_getChildren().size() != 0
9146 )
9147 return setError(E_FAIL,
9148 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9149 "because it has %d differencing child hard disks"),
9150 medium->i_getLocationFull().c_str(),
9151 dev.uuid.raw(),
9152 mUserData->s.strName.c_str(),
9153 mData->m_strConfigFileFull.c_str(),
9154 medium->i_getChildren().size());
9155
9156 if (i_findAttachment(mMediaData->mAttachments,
9157 medium))
9158 return setError(E_FAIL,
9159 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9160 medium->i_getLocationFull().c_str(),
9161 dev.uuid.raw(),
9162 mUserData->s.strName.c_str(),
9163 mData->m_strConfigFileFull.c_str());
9164
9165 break;
9166 }
9167
9168 default:
9169 return setError(E_FAIL,
9170 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9171 medium->i_getLocationFull().c_str(),
9172 mUserData->s.strName.c_str(),
9173 mData->m_strConfigFileFull.c_str());
9174 }
9175
9176 if (FAILED(rc))
9177 break;
9178
9179 /* Bandwidth groups are loaded at this point. */
9180 ComObjPtr<BandwidthGroup> pBwGroup;
9181
9182 if (!dev.strBwGroup.isEmpty())
9183 {
9184 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9185 if (FAILED(rc))
9186 return setError(E_FAIL,
9187 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9188 medium->i_getLocationFull().c_str(),
9189 dev.strBwGroup.c_str(),
9190 mUserData->s.strName.c_str(),
9191 mData->m_strConfigFileFull.c_str());
9192 pBwGroup->i_reference();
9193 }
9194
9195 const Bstr controllerName = aStorageController->i_getName();
9196 ComObjPtr<MediumAttachment> pAttachment;
9197 pAttachment.createObject();
9198 rc = pAttachment->init(this,
9199 medium,
9200 controllerName,
9201 dev.lPort,
9202 dev.lDevice,
9203 dev.deviceType,
9204 false,
9205 dev.fPassThrough,
9206 dev.fTempEject,
9207 dev.fNonRotational,
9208 dev.fDiscard,
9209 dev.fHotPluggable,
9210 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9211 if (FAILED(rc)) break;
9212
9213 /* associate the medium with this machine and snapshot */
9214 if (!medium.isNull())
9215 {
9216 AutoCaller medCaller(medium);
9217 if (FAILED(medCaller.rc())) return medCaller.rc();
9218 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9219
9220 if (i_isSnapshotMachine())
9221 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9222 else
9223 rc = medium->i_addBackReference(mData->mUuid);
9224 /* If the medium->addBackReference fails it sets an appropriate
9225 * error message, so no need to do any guesswork here. */
9226
9227 if (puuidRegistry)
9228 // caller wants registry ID to be set on all attached media (OVF import case)
9229 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9230 }
9231
9232 if (FAILED(rc))
9233 break;
9234
9235 /* back up mMediaData to let registeredInit() properly rollback on failure
9236 * (= limited accessibility) */
9237 i_setModified(IsModified_Storage);
9238 mMediaData.backup();
9239 mMediaData->mAttachments.push_back(pAttachment);
9240 }
9241
9242 return rc;
9243}
9244
9245/**
9246 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9247 *
9248 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9249 * @param aSnapshot where to return the found snapshot
9250 * @param aSetError true to set extended error info on failure
9251 */
9252HRESULT Machine::i_findSnapshotById(const Guid &aId,
9253 ComObjPtr<Snapshot> &aSnapshot,
9254 bool aSetError /* = false */)
9255{
9256 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9257
9258 if (!mData->mFirstSnapshot)
9259 {
9260 if (aSetError)
9261 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9262 return E_FAIL;
9263 }
9264
9265 if (aId.isZero())
9266 aSnapshot = mData->mFirstSnapshot;
9267 else
9268 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9269
9270 if (!aSnapshot)
9271 {
9272 if (aSetError)
9273 return setError(E_FAIL,
9274 tr("Could not find a snapshot with UUID {%s}"),
9275 aId.toString().c_str());
9276 return E_FAIL;
9277 }
9278
9279 return S_OK;
9280}
9281
9282/**
9283 * Returns the snapshot with the given name or fails of no such snapshot.
9284 *
9285 * @param aName snapshot name to find
9286 * @param aSnapshot where to return the found snapshot
9287 * @param aSetError true to set extended error info on failure
9288 */
9289HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9290 ComObjPtr<Snapshot> &aSnapshot,
9291 bool aSetError /* = false */)
9292{
9293 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9294
9295 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9296
9297 if (!mData->mFirstSnapshot)
9298 {
9299 if (aSetError)
9300 return setError(VBOX_E_OBJECT_NOT_FOUND,
9301 tr("This machine does not have any snapshots"));
9302 return VBOX_E_OBJECT_NOT_FOUND;
9303 }
9304
9305 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9306
9307 if (!aSnapshot)
9308 {
9309 if (aSetError)
9310 return setError(VBOX_E_OBJECT_NOT_FOUND,
9311 tr("Could not find a snapshot named '%s'"), strName.c_str());
9312 return VBOX_E_OBJECT_NOT_FOUND;
9313 }
9314
9315 return S_OK;
9316}
9317
9318/**
9319 * Returns a storage controller object with the given name.
9320 *
9321 * @param aName storage controller name to find
9322 * @param aStorageController where to return the found storage controller
9323 * @param aSetError true to set extended error info on failure
9324 */
9325HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9326 ComObjPtr<StorageController> &aStorageController,
9327 bool aSetError /* = false */)
9328{
9329 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9330
9331 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9332 it != mStorageControllers->end();
9333 ++it)
9334 {
9335 if ((*it)->i_getName() == aName)
9336 {
9337 aStorageController = (*it);
9338 return S_OK;
9339 }
9340 }
9341
9342 if (aSetError)
9343 return setError(VBOX_E_OBJECT_NOT_FOUND,
9344 tr("Could not find a storage controller named '%s'"),
9345 aName.c_str());
9346 return VBOX_E_OBJECT_NOT_FOUND;
9347}
9348
9349/**
9350 * Returns a USB controller object with the given name.
9351 *
9352 * @param aName USB controller name to find
9353 * @param aUSBController where to return the found USB controller
9354 * @param aSetError true to set extended error info on failure
9355 */
9356HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9357 ComObjPtr<USBController> &aUSBController,
9358 bool aSetError /* = false */)
9359{
9360 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9361
9362 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9363 it != mUSBControllers->end();
9364 ++it)
9365 {
9366 if ((*it)->i_getName() == aName)
9367 {
9368 aUSBController = (*it);
9369 return S_OK;
9370 }
9371 }
9372
9373 if (aSetError)
9374 return setError(VBOX_E_OBJECT_NOT_FOUND,
9375 tr("Could not find a storage controller named '%s'"),
9376 aName.c_str());
9377 return VBOX_E_OBJECT_NOT_FOUND;
9378}
9379
9380/**
9381 * Returns the number of USB controller instance of the given type.
9382 *
9383 * @param enmType USB controller type.
9384 */
9385ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9386{
9387 ULONG cCtrls = 0;
9388
9389 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9390 it != mUSBControllers->end();
9391 ++it)
9392 {
9393 if ((*it)->i_getControllerType() == enmType)
9394 cCtrls++;
9395 }
9396
9397 return cCtrls;
9398}
9399
9400HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9401 MediaData::AttachmentList &atts)
9402{
9403 AutoCaller autoCaller(this);
9404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9405
9406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9407
9408 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9409 it != mMediaData->mAttachments.end();
9410 ++it)
9411 {
9412 const ComObjPtr<MediumAttachment> &pAtt = *it;
9413 // should never happen, but deal with NULL pointers in the list.
9414 AssertStmt(!pAtt.isNull(), continue);
9415
9416 // getControllerName() needs caller+read lock
9417 AutoCaller autoAttCaller(pAtt);
9418 if (FAILED(autoAttCaller.rc()))
9419 {
9420 atts.clear();
9421 return autoAttCaller.rc();
9422 }
9423 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9424
9425 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9426 atts.push_back(pAtt);
9427 }
9428
9429 return S_OK;
9430}
9431
9432
9433/**
9434 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9435 * file if the machine name was changed and about creating a new settings file
9436 * if this is a new machine.
9437 *
9438 * @note Must be never called directly but only from #saveSettings().
9439 */
9440HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9441{
9442 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9443
9444 HRESULT rc = S_OK;
9445
9446 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9447
9448 /// @todo need to handle primary group change, too
9449
9450 /* attempt to rename the settings file if machine name is changed */
9451 if ( mUserData->s.fNameSync
9452 && mUserData.isBackedUp()
9453 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9454 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9455 )
9456 {
9457 bool dirRenamed = false;
9458 bool fileRenamed = false;
9459
9460 Utf8Str configFile, newConfigFile;
9461 Utf8Str configFilePrev, newConfigFilePrev;
9462 Utf8Str configDir, newConfigDir;
9463
9464 do
9465 {
9466 int vrc = VINF_SUCCESS;
9467
9468 Utf8Str name = mUserData.backedUpData()->s.strName;
9469 Utf8Str newName = mUserData->s.strName;
9470 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9471 if (group == "/")
9472 group.setNull();
9473 Utf8Str newGroup = mUserData->s.llGroups.front();
9474 if (newGroup == "/")
9475 newGroup.setNull();
9476
9477 configFile = mData->m_strConfigFileFull;
9478
9479 /* first, rename the directory if it matches the group and machine name */
9480 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9481 group.c_str(), RTPATH_DELIMITER, name.c_str());
9482 /** @todo hack, make somehow use of ComposeMachineFilename */
9483 if (mUserData->s.fDirectoryIncludesUUID)
9484 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9485 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9486 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9487 /** @todo hack, make somehow use of ComposeMachineFilename */
9488 if (mUserData->s.fDirectoryIncludesUUID)
9489 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9490 configDir = configFile;
9491 configDir.stripFilename();
9492 newConfigDir = configDir;
9493 if ( configDir.length() >= groupPlusName.length()
9494 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9495 groupPlusName.c_str()))
9496 {
9497 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9498 Utf8Str newConfigBaseDir(newConfigDir);
9499 newConfigDir.append(newGroupPlusName);
9500 /* consistency: use \ if appropriate on the platform */
9501 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9502 /* new dir and old dir cannot be equal here because of 'if'
9503 * above and because name != newName */
9504 Assert(configDir != newConfigDir);
9505 if (!fSettingsFileIsNew)
9506 {
9507 /* perform real rename only if the machine is not new */
9508 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9509 if ( vrc == VERR_FILE_NOT_FOUND
9510 || vrc == VERR_PATH_NOT_FOUND)
9511 {
9512 /* create the parent directory, then retry renaming */
9513 Utf8Str parent(newConfigDir);
9514 parent.stripFilename();
9515 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9516 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9517 }
9518 if (RT_FAILURE(vrc))
9519 {
9520 rc = setError(E_FAIL,
9521 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9522 configDir.c_str(),
9523 newConfigDir.c_str(),
9524 vrc);
9525 break;
9526 }
9527 /* delete subdirectories which are no longer needed */
9528 Utf8Str dir(configDir);
9529 dir.stripFilename();
9530 while (dir != newConfigBaseDir && dir != ".")
9531 {
9532 vrc = RTDirRemove(dir.c_str());
9533 if (RT_FAILURE(vrc))
9534 break;
9535 dir.stripFilename();
9536 }
9537 dirRenamed = true;
9538 }
9539 }
9540
9541 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9542 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9543
9544 /* then try to rename the settings file itself */
9545 if (newConfigFile != configFile)
9546 {
9547 /* get the path to old settings file in renamed directory */
9548 configFile = Utf8StrFmt("%s%c%s",
9549 newConfigDir.c_str(),
9550 RTPATH_DELIMITER,
9551 RTPathFilename(configFile.c_str()));
9552 if (!fSettingsFileIsNew)
9553 {
9554 /* perform real rename only if the machine is not new */
9555 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9556 if (RT_FAILURE(vrc))
9557 {
9558 rc = setError(E_FAIL,
9559 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9560 configFile.c_str(),
9561 newConfigFile.c_str(),
9562 vrc);
9563 break;
9564 }
9565 fileRenamed = true;
9566 configFilePrev = configFile;
9567 configFilePrev += "-prev";
9568 newConfigFilePrev = newConfigFile;
9569 newConfigFilePrev += "-prev";
9570 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9571 }
9572 }
9573
9574 // update m_strConfigFileFull amd mConfigFile
9575 mData->m_strConfigFileFull = newConfigFile;
9576 // compute the relative path too
9577 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9578
9579 // store the old and new so that VirtualBox::i_saveSettings() can update
9580 // the media registry
9581 if ( mData->mRegistered
9582 && (configDir != newConfigDir || configFile != newConfigFile))
9583 {
9584 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9585
9586 if (pfNeedsGlobalSaveSettings)
9587 *pfNeedsGlobalSaveSettings = true;
9588 }
9589
9590 // in the saved state file path, replace the old directory with the new directory
9591 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9592 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9593
9594 // and do the same thing for the saved state file paths of all the online snapshots
9595 if (mData->mFirstSnapshot)
9596 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9597 newConfigDir.c_str());
9598 }
9599 while (0);
9600
9601 if (FAILED(rc))
9602 {
9603 /* silently try to rename everything back */
9604 if (fileRenamed)
9605 {
9606 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9607 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9608 }
9609 if (dirRenamed)
9610 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9611 }
9612
9613 if (FAILED(rc)) return rc;
9614 }
9615
9616 if (fSettingsFileIsNew)
9617 {
9618 /* create a virgin config file */
9619 int vrc = VINF_SUCCESS;
9620
9621 /* ensure the settings directory exists */
9622 Utf8Str path(mData->m_strConfigFileFull);
9623 path.stripFilename();
9624 if (!RTDirExists(path.c_str()))
9625 {
9626 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9627 if (RT_FAILURE(vrc))
9628 {
9629 return setError(E_FAIL,
9630 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9631 path.c_str(),
9632 vrc);
9633 }
9634 }
9635
9636 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9637 path = Utf8Str(mData->m_strConfigFileFull);
9638 RTFILE f = NIL_RTFILE;
9639 vrc = RTFileOpen(&f, path.c_str(),
9640 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9641 if (RT_FAILURE(vrc))
9642 return setError(E_FAIL,
9643 tr("Could not create the settings file '%s' (%Rrc)"),
9644 path.c_str(),
9645 vrc);
9646 RTFileClose(f);
9647 }
9648
9649 return rc;
9650}
9651
9652/**
9653 * Saves and commits machine data, user data and hardware data.
9654 *
9655 * Note that on failure, the data remains uncommitted.
9656 *
9657 * @a aFlags may combine the following flags:
9658 *
9659 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9660 * Used when saving settings after an operation that makes them 100%
9661 * correspond to the settings from the current snapshot.
9662 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9663 * #isReallyModified() returns false. This is necessary for cases when we
9664 * change machine data directly, not through the backup()/commit() mechanism.
9665 * - SaveS_Force: settings will be saved without doing a deep compare of the
9666 * settings structures. This is used when this is called because snapshots
9667 * have changed to avoid the overhead of the deep compare.
9668 *
9669 * @note Must be called from under this object's write lock. Locks children for
9670 * writing.
9671 *
9672 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9673 * initialized to false and that will be set to true by this function if
9674 * the caller must invoke VirtualBox::i_saveSettings() because the global
9675 * settings have changed. This will happen if a machine rename has been
9676 * saved and the global machine and media registries will therefore need
9677 * updating.
9678 */
9679HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9680 int aFlags /*= 0*/)
9681{
9682 LogFlowThisFuncEnter();
9683
9684 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9685
9686 /* make sure child objects are unable to modify the settings while we are
9687 * saving them */
9688 i_ensureNoStateDependencies();
9689
9690 AssertReturn(!i_isSnapshotMachine(),
9691 E_FAIL);
9692
9693 HRESULT rc = S_OK;
9694 bool fNeedsWrite = false;
9695
9696 /* First, prepare to save settings. It will care about renaming the
9697 * settings directory and file if the machine name was changed and about
9698 * creating a new settings file if this is a new machine. */
9699 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9700 if (FAILED(rc)) return rc;
9701
9702 // keep a pointer to the current settings structures
9703 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9704 settings::MachineConfigFile *pNewConfig = NULL;
9705
9706 try
9707 {
9708 // make a fresh one to have everyone write stuff into
9709 pNewConfig = new settings::MachineConfigFile(NULL);
9710 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9711
9712 // now go and copy all the settings data from COM to the settings structures
9713 // (this calles i_saveSettings() on all the COM objects in the machine)
9714 i_copyMachineDataToSettings(*pNewConfig);
9715
9716 if (aFlags & SaveS_ResetCurStateModified)
9717 {
9718 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9719 mData->mCurrentStateModified = FALSE;
9720 fNeedsWrite = true; // always, no need to compare
9721 }
9722 else if (aFlags & SaveS_Force)
9723 {
9724 fNeedsWrite = true; // always, no need to compare
9725 }
9726 else
9727 {
9728 if (!mData->mCurrentStateModified)
9729 {
9730 // do a deep compare of the settings that we just saved with the settings
9731 // previously stored in the config file; this invokes MachineConfigFile::operator==
9732 // which does a deep compare of all the settings, which is expensive but less expensive
9733 // than writing out XML in vain
9734 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9735
9736 // could still be modified if any settings changed
9737 mData->mCurrentStateModified = fAnySettingsChanged;
9738
9739 fNeedsWrite = fAnySettingsChanged;
9740 }
9741 else
9742 fNeedsWrite = true;
9743 }
9744
9745 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9746
9747 if (fNeedsWrite)
9748 // now spit it all out!
9749 pNewConfig->write(mData->m_strConfigFileFull);
9750
9751 mData->pMachineConfigFile = pNewConfig;
9752 delete pOldConfig;
9753 i_commit();
9754
9755 // after saving settings, we are no longer different from the XML on disk
9756 mData->flModifications = 0;
9757 }
9758 catch (HRESULT err)
9759 {
9760 // we assume that error info is set by the thrower
9761 rc = err;
9762
9763 // restore old config
9764 delete pNewConfig;
9765 mData->pMachineConfigFile = pOldConfig;
9766 }
9767 catch (...)
9768 {
9769 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9770 }
9771
9772 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9773 {
9774 /* Fire the data change event, even on failure (since we've already
9775 * committed all data). This is done only for SessionMachines because
9776 * mutable Machine instances are always not registered (i.e. private
9777 * to the client process that creates them) and thus don't need to
9778 * inform callbacks. */
9779 if (i_isSessionMachine())
9780 mParent->i_onMachineDataChange(mData->mUuid);
9781 }
9782
9783 LogFlowThisFunc(("rc=%08X\n", rc));
9784 LogFlowThisFuncLeave();
9785 return rc;
9786}
9787
9788/**
9789 * Implementation for saving the machine settings into the given
9790 * settings::MachineConfigFile instance. This copies machine extradata
9791 * from the previous machine config file in the instance data, if any.
9792 *
9793 * This gets called from two locations:
9794 *
9795 * -- Machine::i_saveSettings(), during the regular XML writing;
9796 *
9797 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9798 * exported to OVF and we write the VirtualBox proprietary XML
9799 * into a <vbox:Machine> tag.
9800 *
9801 * This routine fills all the fields in there, including snapshots, *except*
9802 * for the following:
9803 *
9804 * -- fCurrentStateModified. There is some special logic associated with that.
9805 *
9806 * The caller can then call MachineConfigFile::write() or do something else
9807 * with it.
9808 *
9809 * Caller must hold the machine lock!
9810 *
9811 * This throws XML errors and HRESULT, so the caller must have a catch block!
9812 */
9813void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9814{
9815 // deep copy extradata
9816 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9817
9818 config.uuid = mData->mUuid;
9819
9820 // copy name, description, OS type, teleport, UTC etc.
9821 config.machineUserData = mUserData->s;
9822
9823 // Encode the Icon Override data from Machine and store on config userdata.
9824 com::SafeArray<BYTE> iconByte;
9825 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9826 ssize_t cbData = iconByte.size();
9827 if (cbData > 0)
9828 {
9829 ssize_t cchOut = RTBase64EncodedLength(cbData);
9830 Utf8Str strIconData;
9831 strIconData.reserve(cchOut+1);
9832 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9833 strIconData.mutableRaw(), strIconData.capacity(),
9834 NULL);
9835 if (RT_FAILURE(vrc))
9836 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9837 strIconData.jolt();
9838 config.machineUserData.ovIcon = strIconData;
9839 }
9840 else
9841 config.machineUserData.ovIcon.setNull();
9842
9843 if ( mData->mMachineState == MachineState_Saved
9844 || mData->mMachineState == MachineState_Restoring
9845 // when deleting a snapshot we may or may not have a saved state in the current state,
9846 // so let's not assert here please
9847 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9848 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9849 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9850 && (!mSSData->strStateFilePath.isEmpty())
9851 )
9852 )
9853 {
9854 Assert(!mSSData->strStateFilePath.isEmpty());
9855 /* try to make the file name relative to the settings file dir */
9856 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9857 }
9858 else
9859 {
9860 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9861 config.strStateFile.setNull();
9862 }
9863
9864 if (mData->mCurrentSnapshot)
9865 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9866 else
9867 config.uuidCurrentSnapshot.clear();
9868
9869 config.timeLastStateChange = mData->mLastStateChange;
9870 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9871 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9872
9873 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9874 if (FAILED(rc)) throw rc;
9875
9876 rc = i_saveStorageControllers(config.storageMachine);
9877 if (FAILED(rc)) throw rc;
9878
9879 // save machine's media registry if this is VirtualBox 4.0 or later
9880 if (config.canHaveOwnMediaRegistry())
9881 {
9882 // determine machine folder
9883 Utf8Str strMachineFolder = i_getSettingsFileFull();
9884 strMachineFolder.stripFilename();
9885 mParent->i_saveMediaRegistry(config.mediaRegistry,
9886 i_getId(), // only media with registry ID == machine UUID
9887 strMachineFolder);
9888 // this throws HRESULT
9889 }
9890
9891 // save snapshots
9892 rc = i_saveAllSnapshots(config);
9893 if (FAILED(rc)) throw rc;
9894}
9895
9896/**
9897 * Saves all snapshots of the machine into the given machine config file. Called
9898 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9899 * @param config
9900 * @return
9901 */
9902HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9903{
9904 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9905
9906 HRESULT rc = S_OK;
9907
9908 try
9909 {
9910 config.llFirstSnapshot.clear();
9911
9912 if (mData->mFirstSnapshot)
9913 {
9914 settings::Snapshot snapNew;
9915 config.llFirstSnapshot.push_back(snapNew);
9916
9917 // get reference to the fresh copy of the snapshot on the list and
9918 // work on that copy directly to avoid excessive copying later
9919 settings::Snapshot &snap = config.llFirstSnapshot.front();
9920
9921 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9922 if (FAILED(rc)) throw rc;
9923 }
9924
9925// if (mType == IsSessionMachine)
9926// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9927
9928 }
9929 catch (HRESULT err)
9930 {
9931 /* we assume that error info is set by the thrower */
9932 rc = err;
9933 }
9934 catch (...)
9935 {
9936 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9937 }
9938
9939 return rc;
9940}
9941
9942/**
9943 * Saves the VM hardware configuration. It is assumed that the
9944 * given node is empty.
9945 *
9946 * @param data Reference to the settings object for the hardware config.
9947 * @param pDbg Pointer to the settings object for the debugging config
9948 * which happens to live in mHWData.
9949 * @param pAutostart Pointer to the settings object for the autostart config
9950 * which happens to live in mHWData.
9951 */
9952HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9953 settings::Autostart *pAutostart)
9954{
9955 HRESULT rc = S_OK;
9956
9957 try
9958 {
9959 /* The hardware version attribute (optional).
9960 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9961 if ( mHWData->mHWVersion == "1"
9962 && mSSData->strStateFilePath.isEmpty()
9963 )
9964 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9965 other point needs to be found where this can be done. */
9966
9967 data.strVersion = mHWData->mHWVersion;
9968 data.uuid = mHWData->mHardwareUUID;
9969
9970 // CPU
9971 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9972 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9973 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9974 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9975 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9976 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9977 data.fPAE = !!mHWData->mPAEEnabled;
9978 data.enmLongMode = mHWData->mLongMode;
9979 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9980 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9981
9982 /* Standard and Extended CPUID leafs. */
9983 data.llCpuIdLeafs.clear();
9984 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9985 {
9986 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9987 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9988 }
9989 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9990 {
9991 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9992 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9993 }
9994
9995 data.cCPUs = mHWData->mCPUCount;
9996 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9997 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9998
9999 data.llCpus.clear();
10000 if (data.fCpuHotPlug)
10001 {
10002 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10003 {
10004 if (mHWData->mCPUAttached[idx])
10005 {
10006 settings::Cpu cpu;
10007 cpu.ulId = idx;
10008 data.llCpus.push_back(cpu);
10009 }
10010 }
10011 }
10012
10013 // memory
10014 data.ulMemorySizeMB = mHWData->mMemorySize;
10015 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10016
10017 // firmware
10018 data.firmwareType = mHWData->mFirmwareType;
10019
10020 // HID
10021 data.pointingHIDType = mHWData->mPointingHIDType;
10022 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10023
10024 // chipset
10025 data.chipsetType = mHWData->mChipsetType;
10026
10027 // paravirt
10028 data.paravirtProvider = mHWData->mParavirtProvider;
10029
10030
10031 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10032
10033 // HPET
10034 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10035
10036 // boot order
10037 data.mapBootOrder.clear();
10038 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10039 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10040
10041 // display
10042 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10043 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10044 data.cMonitors = mHWData->mMonitorCount;
10045 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10046 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10047 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10048 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10049 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10050 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10051 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10052 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10053 {
10054 if (mHWData->maVideoCaptureScreens[i])
10055 ASMBitSet(&data.u64VideoCaptureScreens, i);
10056 else
10057 ASMBitClear(&data.u64VideoCaptureScreens, i);
10058 }
10059 /* store relative video capture file if possible */
10060 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10061
10062 /* VRDEServer settings (optional) */
10063 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10064 if (FAILED(rc)) throw rc;
10065
10066 /* BIOS (required) */
10067 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10068 if (FAILED(rc)) throw rc;
10069
10070 /* USB Controller (required) */
10071 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10072 {
10073 ComObjPtr<USBController> ctrl = *it;
10074 settings::USBController settingsCtrl;
10075
10076 settingsCtrl.strName = ctrl->i_getName();
10077 settingsCtrl.enmType = ctrl->i_getControllerType();
10078
10079 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10080 }
10081
10082 /* USB device filters (required) */
10083 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10084 if (FAILED(rc)) throw rc;
10085
10086 /* Network adapters (required) */
10087 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10088 data.llNetworkAdapters.clear();
10089 /* Write out only the nominal number of network adapters for this
10090 * chipset type. Since Machine::commit() hasn't been called there
10091 * may be extra NIC settings in the vector. */
10092 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10093 {
10094 settings::NetworkAdapter nic;
10095 nic.ulSlot = (uint32_t)slot;
10096 /* paranoia check... must not be NULL, but must not crash either. */
10097 if (mNetworkAdapters[slot])
10098 {
10099 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10100 if (FAILED(rc)) throw rc;
10101
10102 data.llNetworkAdapters.push_back(nic);
10103 }
10104 }
10105
10106 /* Serial ports */
10107 data.llSerialPorts.clear();
10108 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10109 {
10110 settings::SerialPort s;
10111 s.ulSlot = slot;
10112 rc = mSerialPorts[slot]->i_saveSettings(s);
10113 if (FAILED(rc)) return rc;
10114
10115 data.llSerialPorts.push_back(s);
10116 }
10117
10118 /* Parallel ports */
10119 data.llParallelPorts.clear();
10120 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10121 {
10122 settings::ParallelPort p;
10123 p.ulSlot = slot;
10124 rc = mParallelPorts[slot]->i_saveSettings(p);
10125 if (FAILED(rc)) return rc;
10126
10127 data.llParallelPorts.push_back(p);
10128 }
10129
10130 /* Audio adapter */
10131 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10132 if (FAILED(rc)) return rc;
10133
10134 /* Shared folders */
10135 data.llSharedFolders.clear();
10136 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10137 it != mHWData->mSharedFolders.end();
10138 ++it)
10139 {
10140 SharedFolder *pSF = *it;
10141 AutoCaller sfCaller(pSF);
10142 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10143 settings::SharedFolder sf;
10144 sf.strName = pSF->i_getName();
10145 sf.strHostPath = pSF->i_getHostPath();
10146 sf.fWritable = !!pSF->i_isWritable();
10147 sf.fAutoMount = !!pSF->i_isAutoMounted();
10148
10149 data.llSharedFolders.push_back(sf);
10150 }
10151
10152 // clipboard
10153 data.clipboardMode = mHWData->mClipboardMode;
10154
10155 // drag'n'drop
10156 data.dndMode = mHWData->mDnDMode;
10157
10158 /* Guest */
10159 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10160
10161 // IO settings
10162 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10163 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10164
10165 /* BandwidthControl (required) */
10166 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10167 if (FAILED(rc)) throw rc;
10168
10169 /* Host PCI devices */
10170 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10171 it != mHWData->mPCIDeviceAssignments.end();
10172 ++it)
10173 {
10174 ComObjPtr<PCIDeviceAttachment> pda = *it;
10175 settings::HostPCIDeviceAttachment hpda;
10176
10177 rc = pda->i_saveSettings(hpda);
10178 if (FAILED(rc)) throw rc;
10179
10180 data.pciAttachments.push_back(hpda);
10181 }
10182
10183
10184 // guest properties
10185 data.llGuestProperties.clear();
10186#ifdef VBOX_WITH_GUEST_PROPS
10187 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10188 it != mHWData->mGuestProperties.end();
10189 ++it)
10190 {
10191 HWData::GuestProperty property = it->second;
10192
10193 /* Remove transient guest properties at shutdown unless we
10194 * are saving state */
10195 if ( ( mData->mMachineState == MachineState_PoweredOff
10196 || mData->mMachineState == MachineState_Aborted
10197 || mData->mMachineState == MachineState_Teleported)
10198 && ( property.mFlags & guestProp::TRANSIENT
10199 || property.mFlags & guestProp::TRANSRESET))
10200 continue;
10201 settings::GuestProperty prop;
10202 prop.strName = it->first;
10203 prop.strValue = property.strValue;
10204 prop.timestamp = property.mTimestamp;
10205 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10206 guestProp::writeFlags(property.mFlags, szFlags);
10207 prop.strFlags = szFlags;
10208
10209 data.llGuestProperties.push_back(prop);
10210 }
10211
10212 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10213 /* I presume this doesn't require a backup(). */
10214 mData->mGuestPropertiesModified = FALSE;
10215#endif /* VBOX_WITH_GUEST_PROPS defined */
10216
10217 *pDbg = mHWData->mDebugging;
10218 *pAutostart = mHWData->mAutostart;
10219
10220 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10221 }
10222 catch(std::bad_alloc &)
10223 {
10224 return E_OUTOFMEMORY;
10225 }
10226
10227 AssertComRC(rc);
10228 return rc;
10229}
10230
10231/**
10232 * Saves the storage controller configuration.
10233 *
10234 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10235 */
10236HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10237{
10238 data.llStorageControllers.clear();
10239
10240 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10241 it != mStorageControllers->end();
10242 ++it)
10243 {
10244 HRESULT rc;
10245 ComObjPtr<StorageController> pCtl = *it;
10246
10247 settings::StorageController ctl;
10248 ctl.strName = pCtl->i_getName();
10249 ctl.controllerType = pCtl->i_getControllerType();
10250 ctl.storageBus = pCtl->i_getStorageBus();
10251 ctl.ulInstance = pCtl->i_getInstance();
10252 ctl.fBootable = pCtl->i_getBootable();
10253
10254 /* Save the port count. */
10255 ULONG portCount;
10256 rc = pCtl->COMGETTER(PortCount)(&portCount);
10257 ComAssertComRCRet(rc, rc);
10258 ctl.ulPortCount = portCount;
10259
10260 /* Save fUseHostIOCache */
10261 BOOL fUseHostIOCache;
10262 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10263 ComAssertComRCRet(rc, rc);
10264 ctl.fUseHostIOCache = !!fUseHostIOCache;
10265
10266 /* Save IDE emulation settings. */
10267 if (ctl.controllerType == StorageControllerType_IntelAhci)
10268 {
10269 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10270 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10271 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10272 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10273 )
10274 ComAssertComRCRet(rc, rc);
10275 }
10276
10277 /* save the devices now. */
10278 rc = i_saveStorageDevices(pCtl, ctl);
10279 ComAssertComRCRet(rc, rc);
10280
10281 data.llStorageControllers.push_back(ctl);
10282 }
10283
10284 return S_OK;
10285}
10286
10287/**
10288 * Saves the hard disk configuration.
10289 */
10290HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10291 settings::StorageController &data)
10292{
10293 MediaData::AttachmentList atts;
10294
10295 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10296 if (FAILED(rc)) return rc;
10297
10298 data.llAttachedDevices.clear();
10299 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10300 it != atts.end();
10301 ++it)
10302 {
10303 settings::AttachedDevice dev;
10304 IMediumAttachment *iA = *it;
10305 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10306 Medium *pMedium = pAttach->i_getMedium();
10307
10308 dev.deviceType = pAttach->i_getType();
10309 dev.lPort = pAttach->i_getPort();
10310 dev.lDevice = pAttach->i_getDevice();
10311 dev.fPassThrough = pAttach->i_getPassthrough();
10312 dev.fHotPluggable = pAttach->i_getHotPluggable();
10313 if (pMedium)
10314 {
10315 if (pMedium->i_isHostDrive())
10316 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10317 else
10318 dev.uuid = pMedium->i_getId();
10319 dev.fTempEject = pAttach->i_getTempEject();
10320 dev.fNonRotational = pAttach->i_getNonRotational();
10321 dev.fDiscard = pAttach->i_getDiscard();
10322 }
10323
10324 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10325
10326 data.llAttachedDevices.push_back(dev);
10327 }
10328
10329 return S_OK;
10330}
10331
10332/**
10333 * Saves machine state settings as defined by aFlags
10334 * (SaveSTS_* values).
10335 *
10336 * @param aFlags Combination of SaveSTS_* flags.
10337 *
10338 * @note Locks objects for writing.
10339 */
10340HRESULT Machine::i_saveStateSettings(int aFlags)
10341{
10342 if (aFlags == 0)
10343 return S_OK;
10344
10345 AutoCaller autoCaller(this);
10346 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10347
10348 /* This object's write lock is also necessary to serialize file access
10349 * (prevent concurrent reads and writes) */
10350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10351
10352 HRESULT rc = S_OK;
10353
10354 Assert(mData->pMachineConfigFile);
10355
10356 try
10357 {
10358 if (aFlags & SaveSTS_CurStateModified)
10359 mData->pMachineConfigFile->fCurrentStateModified = true;
10360
10361 if (aFlags & SaveSTS_StateFilePath)
10362 {
10363 if (!mSSData->strStateFilePath.isEmpty())
10364 /* try to make the file name relative to the settings file dir */
10365 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10366 else
10367 mData->pMachineConfigFile->strStateFile.setNull();
10368 }
10369
10370 if (aFlags & SaveSTS_StateTimeStamp)
10371 {
10372 Assert( mData->mMachineState != MachineState_Aborted
10373 || mSSData->strStateFilePath.isEmpty());
10374
10375 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10376
10377 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10378//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10379 }
10380
10381 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10382 }
10383 catch (...)
10384 {
10385 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10386 }
10387
10388 return rc;
10389}
10390
10391/**
10392 * Ensures that the given medium is added to a media registry. If this machine
10393 * was created with 4.0 or later, then the machine registry is used. Otherwise
10394 * the global VirtualBox media registry is used.
10395 *
10396 * Caller must NOT hold machine lock, media tree or any medium locks!
10397 *
10398 * @param pMedium
10399 */
10400void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10401{
10402 /* Paranoia checks: do not hold machine or media tree locks. */
10403 AssertReturnVoid(!isWriteLockOnCurrentThread());
10404 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10405
10406 ComObjPtr<Medium> pBase;
10407 {
10408 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10409 pBase = pMedium->i_getBase();
10410 }
10411
10412 /* Paranoia checks: do not hold medium locks. */
10413 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10414 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10415
10416 // decide which medium registry to use now that the medium is attached:
10417 Guid uuid;
10418 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10419 // machine XML is VirtualBox 4.0 or higher:
10420 uuid = i_getId(); // machine UUID
10421 else
10422 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10423
10424 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10425 mParent->i_markRegistryModified(uuid);
10426
10427 /* For more complex hard disk structures it can happen that the base
10428 * medium isn't yet associated with any medium registry. Do that now. */
10429 if (pMedium != pBase)
10430 {
10431 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10432 mParent->i_markRegistryModified(uuid);
10433 }
10434}
10435
10436/**
10437 * Creates differencing hard disks for all normal hard disks attached to this
10438 * machine and a new set of attachments to refer to created disks.
10439 *
10440 * Used when taking a snapshot or when deleting the current state. Gets called
10441 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10442 *
10443 * This method assumes that mMediaData contains the original hard disk attachments
10444 * it needs to create diffs for. On success, these attachments will be replaced
10445 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10446 * called to delete created diffs which will also rollback mMediaData and restore
10447 * whatever was backed up before calling this method.
10448 *
10449 * Attachments with non-normal hard disks are left as is.
10450 *
10451 * If @a aOnline is @c false then the original hard disks that require implicit
10452 * diffs will be locked for reading. Otherwise it is assumed that they are
10453 * already locked for writing (when the VM was started). Note that in the latter
10454 * case it is responsibility of the caller to lock the newly created diffs for
10455 * writing if this method succeeds.
10456 *
10457 * @param aProgress Progress object to run (must contain at least as
10458 * many operations left as the number of hard disks
10459 * attached).
10460 * @param aOnline Whether the VM was online prior to this operation.
10461 *
10462 * @note The progress object is not marked as completed, neither on success nor
10463 * on failure. This is a responsibility of the caller.
10464 *
10465 * @note Locks this object and the media tree for writing.
10466 */
10467HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10468 ULONG aWeight,
10469 bool aOnline)
10470{
10471 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10472
10473 AutoCaller autoCaller(this);
10474 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10475
10476 AutoMultiWriteLock2 alock(this->lockHandle(),
10477 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10478
10479 /* must be in a protective state because we release the lock below */
10480 AssertReturn( mData->mMachineState == MachineState_Saving
10481 || mData->mMachineState == MachineState_LiveSnapshotting
10482 || mData->mMachineState == MachineState_RestoringSnapshot
10483 || mData->mMachineState == MachineState_DeletingSnapshot
10484 , E_FAIL);
10485
10486 HRESULT rc = S_OK;
10487
10488 // use appropriate locked media map (online or offline)
10489 MediumLockListMap lockedMediaOffline;
10490 MediumLockListMap *lockedMediaMap;
10491 if (aOnline)
10492 lockedMediaMap = &mData->mSession.mLockedMedia;
10493 else
10494 lockedMediaMap = &lockedMediaOffline;
10495
10496 try
10497 {
10498 if (!aOnline)
10499 {
10500 /* lock all attached hard disks early to detect "in use"
10501 * situations before creating actual diffs */
10502 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10503 it != mMediaData->mAttachments.end();
10504 ++it)
10505 {
10506 MediumAttachment* pAtt = *it;
10507 if (pAtt->i_getType() == DeviceType_HardDisk)
10508 {
10509 Medium* pMedium = pAtt->i_getMedium();
10510 Assert(pMedium);
10511
10512 MediumLockList *pMediumLockList(new MediumLockList());
10513 alock.release();
10514 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10515 false /* fMediumLockWrite */,
10516 NULL,
10517 *pMediumLockList);
10518 alock.acquire();
10519 if (FAILED(rc))
10520 {
10521 delete pMediumLockList;
10522 throw rc;
10523 }
10524 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10525 if (FAILED(rc))
10526 {
10527 throw setError(rc,
10528 tr("Collecting locking information for all attached media failed"));
10529 }
10530 }
10531 }
10532
10533 /* Now lock all media. If this fails, nothing is locked. */
10534 alock.release();
10535 rc = lockedMediaMap->Lock();
10536 alock.acquire();
10537 if (FAILED(rc))
10538 {
10539 throw setError(rc,
10540 tr("Locking of attached media failed"));
10541 }
10542 }
10543
10544 /* remember the current list (note that we don't use backup() since
10545 * mMediaData may be already backed up) */
10546 MediaData::AttachmentList atts = mMediaData->mAttachments;
10547
10548 /* start from scratch */
10549 mMediaData->mAttachments.clear();
10550
10551 /* go through remembered attachments and create diffs for normal hard
10552 * disks and attach them */
10553 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10554 it != atts.end();
10555 ++it)
10556 {
10557 MediumAttachment* pAtt = *it;
10558
10559 DeviceType_T devType = pAtt->i_getType();
10560 Medium* pMedium = pAtt->i_getMedium();
10561
10562 if ( devType != DeviceType_HardDisk
10563 || pMedium == NULL
10564 || pMedium->i_getType() != MediumType_Normal)
10565 {
10566 /* copy the attachment as is */
10567
10568 /** @todo the progress object created in Console::TakeSnaphot
10569 * only expects operations for hard disks. Later other
10570 * device types need to show up in the progress as well. */
10571 if (devType == DeviceType_HardDisk)
10572 {
10573 if (pMedium == NULL)
10574 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10575 aWeight); // weight
10576 else
10577 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10578 pMedium->i_getBase()->i_getName().c_str()).raw(),
10579 aWeight); // weight
10580 }
10581
10582 mMediaData->mAttachments.push_back(pAtt);
10583 continue;
10584 }
10585
10586 /* need a diff */
10587 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10588 pMedium->i_getBase()->i_getName().c_str()).raw(),
10589 aWeight); // weight
10590
10591 Utf8Str strFullSnapshotFolder;
10592 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10593
10594 ComObjPtr<Medium> diff;
10595 diff.createObject();
10596 // store the diff in the same registry as the parent
10597 // (this cannot fail here because we can't create implicit diffs for
10598 // unregistered images)
10599 Guid uuidRegistryParent;
10600 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10601 Assert(fInRegistry); NOREF(fInRegistry);
10602 rc = diff->init(mParent,
10603 pMedium->i_getPreferredDiffFormat(),
10604 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10605 uuidRegistryParent);
10606 if (FAILED(rc)) throw rc;
10607
10608 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10609 * the push_back? Looks like we're going to release medium with the
10610 * wrong kind of lock (general issue with if we fail anywhere at all)
10611 * and an orphaned VDI in the snapshots folder. */
10612
10613 /* update the appropriate lock list */
10614 MediumLockList *pMediumLockList;
10615 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10616 AssertComRCThrowRC(rc);
10617 if (aOnline)
10618 {
10619 alock.release();
10620 /* The currently attached medium will be read-only, change
10621 * the lock type to read. */
10622 rc = pMediumLockList->Update(pMedium, false);
10623 alock.acquire();
10624 AssertComRCThrowRC(rc);
10625 }
10626
10627 /* release the locks before the potentially lengthy operation */
10628 alock.release();
10629 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10630 pMediumLockList,
10631 NULL /* aProgress */,
10632 true /* aWait */);
10633 alock.acquire();
10634 if (FAILED(rc)) throw rc;
10635
10636 /* actual lock list update is done in Medium::commitMedia */
10637
10638 rc = diff->i_addBackReference(mData->mUuid);
10639 AssertComRCThrowRC(rc);
10640
10641 /* add a new attachment */
10642 ComObjPtr<MediumAttachment> attachment;
10643 attachment.createObject();
10644 rc = attachment->init(this,
10645 diff,
10646 pAtt->i_getControllerName(),
10647 pAtt->i_getPort(),
10648 pAtt->i_getDevice(),
10649 DeviceType_HardDisk,
10650 true /* aImplicit */,
10651 false /* aPassthrough */,
10652 false /* aTempEject */,
10653 pAtt->i_getNonRotational(),
10654 pAtt->i_getDiscard(),
10655 pAtt->i_getHotPluggable(),
10656 pAtt->i_getBandwidthGroup());
10657 if (FAILED(rc)) throw rc;
10658
10659 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10660 AssertComRCThrowRC(rc);
10661 mMediaData->mAttachments.push_back(attachment);
10662 }
10663 }
10664 catch (HRESULT aRC) { rc = aRC; }
10665
10666 /* unlock all hard disks we locked when there is no VM */
10667 if (!aOnline)
10668 {
10669 ErrorInfoKeeper eik;
10670
10671 HRESULT rc1 = lockedMediaMap->Clear();
10672 AssertComRC(rc1);
10673 }
10674
10675 return rc;
10676}
10677
10678/**
10679 * Deletes implicit differencing hard disks created either by
10680 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10681 *
10682 * Note that to delete hard disks created by #AttachDevice() this method is
10683 * called from #fixupMedia() when the changes are rolled back.
10684 *
10685 * @note Locks this object and the media tree for writing.
10686 */
10687HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10688{
10689 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10690
10691 AutoCaller autoCaller(this);
10692 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10693
10694 AutoMultiWriteLock2 alock(this->lockHandle(),
10695 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10696
10697 /* We absolutely must have backed up state. */
10698 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10699
10700 /* Check if there are any implicitly created diff images. */
10701 bool fImplicitDiffs = false;
10702 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10703 it != mMediaData->mAttachments.end();
10704 ++it)
10705 {
10706 const ComObjPtr<MediumAttachment> &pAtt = *it;
10707 if (pAtt->i_isImplicit())
10708 {
10709 fImplicitDiffs = true;
10710 break;
10711 }
10712 }
10713 /* If there is nothing to do, leave early. This saves lots of image locking
10714 * effort. It also avoids a MachineStateChanged event without real reason.
10715 * This is important e.g. when loading a VM config, because there should be
10716 * no events. Otherwise API clients can become thoroughly confused for
10717 * inaccessible VMs (the code for loading VM configs uses this method for
10718 * cleanup if the config makes no sense), as they take such events as an
10719 * indication that the VM is alive, and they would force the VM config to
10720 * be reread, leading to an endless loop. */
10721 if (!fImplicitDiffs)
10722 return S_OK;
10723
10724 HRESULT rc = S_OK;
10725 MachineState_T oldState = mData->mMachineState;
10726
10727 /* will release the lock before the potentially lengthy operation,
10728 * so protect with the special state (unless already protected) */
10729 if ( oldState != MachineState_Saving
10730 && oldState != MachineState_LiveSnapshotting
10731 && oldState != MachineState_RestoringSnapshot
10732 && oldState != MachineState_DeletingSnapshot
10733 && oldState != MachineState_DeletingSnapshotOnline
10734 && oldState != MachineState_DeletingSnapshotPaused
10735 )
10736 i_setMachineState(MachineState_SettingUp);
10737
10738 // use appropriate locked media map (online or offline)
10739 MediumLockListMap lockedMediaOffline;
10740 MediumLockListMap *lockedMediaMap;
10741 if (aOnline)
10742 lockedMediaMap = &mData->mSession.mLockedMedia;
10743 else
10744 lockedMediaMap = &lockedMediaOffline;
10745
10746 try
10747 {
10748 if (!aOnline)
10749 {
10750 /* lock all attached hard disks early to detect "in use"
10751 * situations before deleting actual diffs */
10752 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10753 it != mMediaData->mAttachments.end();
10754 ++it)
10755 {
10756 MediumAttachment* pAtt = *it;
10757 if (pAtt->i_getType() == DeviceType_HardDisk)
10758 {
10759 Medium* pMedium = pAtt->i_getMedium();
10760 Assert(pMedium);
10761
10762 MediumLockList *pMediumLockList(new MediumLockList());
10763 alock.release();
10764 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10765 false /* fMediumLockWrite */,
10766 NULL,
10767 *pMediumLockList);
10768 alock.acquire();
10769
10770 if (FAILED(rc))
10771 {
10772 delete pMediumLockList;
10773 throw rc;
10774 }
10775
10776 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10777 if (FAILED(rc))
10778 throw rc;
10779 }
10780 }
10781
10782 if (FAILED(rc))
10783 throw rc;
10784 } // end of offline
10785
10786 /* Lock lists are now up to date and include implicitly created media */
10787
10788 /* Go through remembered attachments and delete all implicitly created
10789 * diffs and fix up the attachment information */
10790 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10791 MediaData::AttachmentList implicitAtts;
10792 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10793 it != mMediaData->mAttachments.end();
10794 ++it)
10795 {
10796 ComObjPtr<MediumAttachment> pAtt = *it;
10797 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10798 if (pMedium.isNull())
10799 continue;
10800
10801 // Implicit attachments go on the list for deletion and back references are removed.
10802 if (pAtt->i_isImplicit())
10803 {
10804 /* Deassociate and mark for deletion */
10805 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10806 rc = pMedium->i_removeBackReference(mData->mUuid);
10807 if (FAILED(rc))
10808 throw rc;
10809 implicitAtts.push_back(pAtt);
10810 continue;
10811 }
10812
10813 /* Was this medium attached before? */
10814 if (!i_findAttachment(oldAtts, pMedium))
10815 {
10816 /* no: de-associate */
10817 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10818 rc = pMedium->i_removeBackReference(mData->mUuid);
10819 if (FAILED(rc))
10820 throw rc;
10821 continue;
10822 }
10823 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10824 }
10825
10826 /* If there are implicit attachments to delete, throw away the lock
10827 * map contents (which will unlock all media) since the medium
10828 * attachments will be rolled back. Below we need to completely
10829 * recreate the lock map anyway since it is infinitely complex to
10830 * do this incrementally (would need reconstructing each attachment
10831 * change, which would be extremely hairy). */
10832 if (implicitAtts.size() != 0)
10833 {
10834 ErrorInfoKeeper eik;
10835
10836 HRESULT rc1 = lockedMediaMap->Clear();
10837 AssertComRC(rc1);
10838 }
10839
10840 /* rollback hard disk changes */
10841 mMediaData.rollback();
10842
10843 MultiResult mrc(S_OK);
10844
10845 // Delete unused implicit diffs.
10846 if (implicitAtts.size() != 0)
10847 {
10848 alock.release();
10849
10850 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10851 {
10852 // Remove medium associated with this attachment.
10853 ComObjPtr<MediumAttachment> pAtt = *it;
10854 Assert(pAtt);
10855 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10856 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10857 Assert(pMedium);
10858
10859 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10860 // continue on delete failure, just collect error messages
10861 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10862 pMedium->i_getLocationFull().c_str() ));
10863 mrc = rc;
10864 }
10865
10866 alock.acquire();
10867
10868 /* if there is a VM recreate media lock map as mentioned above,
10869 * otherwise it is a waste of time and we leave things unlocked */
10870 if (aOnline)
10871 {
10872 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10873 /* must never be NULL, but better safe than sorry */
10874 if (!pMachine.isNull())
10875 {
10876 alock.release();
10877 rc = mData->mSession.mMachine->i_lockMedia();
10878 alock.acquire();
10879 if (FAILED(rc))
10880 throw rc;
10881 }
10882 }
10883 }
10884 }
10885 catch (HRESULT aRC) {rc = aRC;}
10886
10887 if (mData->mMachineState == MachineState_SettingUp)
10888 i_setMachineState(oldState);
10889
10890 /* unlock all hard disks we locked when there is no VM */
10891 if (!aOnline)
10892 {
10893 ErrorInfoKeeper eik;
10894
10895 HRESULT rc1 = lockedMediaMap->Clear();
10896 AssertComRC(rc1);
10897 }
10898
10899 return rc;
10900}
10901
10902
10903/**
10904 * Looks through the given list of media attachments for one with the given parameters
10905 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10906 * can be searched as well if needed.
10907 *
10908 * @param list
10909 * @param aControllerName
10910 * @param aControllerPort
10911 * @param aDevice
10912 * @return
10913 */
10914MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10915 IN_BSTR aControllerName,
10916 LONG aControllerPort,
10917 LONG aDevice)
10918{
10919 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10920 {
10921 MediumAttachment *pAttach = *it;
10922 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10923 return pAttach;
10924 }
10925
10926 return NULL;
10927}
10928
10929/**
10930 * Looks through the given list of media attachments for one with the given parameters
10931 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10932 * can be searched as well if needed.
10933 *
10934 * @param list
10935 * @param aControllerName
10936 * @param aControllerPort
10937 * @param aDevice
10938 * @return
10939 */
10940MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10941 ComObjPtr<Medium> pMedium)
10942{
10943 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10944 {
10945 MediumAttachment *pAttach = *it;
10946 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10947 if (pMediumThis == pMedium)
10948 return pAttach;
10949 }
10950
10951 return NULL;
10952}
10953
10954/**
10955 * Looks through the given list of media attachments for one with the given parameters
10956 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10957 * can be searched as well if needed.
10958 *
10959 * @param list
10960 * @param aControllerName
10961 * @param aControllerPort
10962 * @param aDevice
10963 * @return
10964 */
10965MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10966 Guid &id)
10967{
10968 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10969 {
10970 MediumAttachment *pAttach = *it;
10971 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10972 if (pMediumThis->i_getId() == id)
10973 return pAttach;
10974 }
10975
10976 return NULL;
10977}
10978
10979/**
10980 * Main implementation for Machine::DetachDevice. This also gets called
10981 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10982 *
10983 * @param pAttach Medium attachment to detach.
10984 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10985 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10986 * SnapshotMachine, and this must be its snapshot.
10987 * @return
10988 */
10989HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10990 AutoWriteLock &writeLock,
10991 Snapshot *pSnapshot)
10992{
10993 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10994 DeviceType_T mediumType = pAttach->i_getType();
10995
10996 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10997
10998 if (pAttach->i_isImplicit())
10999 {
11000 /* attempt to implicitly delete the implicitly created diff */
11001
11002 /// @todo move the implicit flag from MediumAttachment to Medium
11003 /// and forbid any hard disk operation when it is implicit. Or maybe
11004 /// a special media state for it to make it even more simple.
11005
11006 Assert(mMediaData.isBackedUp());
11007
11008 /* will release the lock before the potentially lengthy operation, so
11009 * protect with the special state */
11010 MachineState_T oldState = mData->mMachineState;
11011 i_setMachineState(MachineState_SettingUp);
11012
11013 writeLock.release();
11014
11015 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11016 true /*aWait*/);
11017
11018 writeLock.acquire();
11019
11020 i_setMachineState(oldState);
11021
11022 if (FAILED(rc)) return rc;
11023 }
11024
11025 i_setModified(IsModified_Storage);
11026 mMediaData.backup();
11027 mMediaData->mAttachments.remove(pAttach);
11028
11029 if (!oldmedium.isNull())
11030 {
11031 // if this is from a snapshot, do not defer detachment to commitMedia()
11032 if (pSnapshot)
11033 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11034 // else if non-hard disk media, do not defer detachment to commitMedia() either
11035 else if (mediumType != DeviceType_HardDisk)
11036 oldmedium->i_removeBackReference(mData->mUuid);
11037 }
11038
11039 return S_OK;
11040}
11041
11042/**
11043 * Goes thru all media of the given list and
11044 *
11045 * 1) calls i_detachDevice() on each of them for this machine and
11046 * 2) adds all Medium objects found in the process to the given list,
11047 * depending on cleanupMode.
11048 *
11049 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11050 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11051 * media to the list.
11052 *
11053 * This gets called from Machine::Unregister, both for the actual Machine and
11054 * the SnapshotMachine objects that might be found in the snapshots.
11055 *
11056 * Requires caller and locking. The machine lock must be passed in because it
11057 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11058 *
11059 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11060 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11061 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11062 * Full, then all media get added;
11063 * otherwise no media get added.
11064 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11065 * @return
11066 */
11067HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11068 Snapshot *pSnapshot,
11069 CleanupMode_T cleanupMode,
11070 MediaList &llMedia)
11071{
11072 Assert(isWriteLockOnCurrentThread());
11073
11074 HRESULT rc;
11075
11076 // make a temporary list because i_detachDevice invalidates iterators into
11077 // mMediaData->mAttachments
11078 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11079
11080 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11081 {
11082 ComObjPtr<MediumAttachment> &pAttach = *it;
11083 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11084
11085 if (!pMedium.isNull())
11086 {
11087 AutoCaller mac(pMedium);
11088 if (FAILED(mac.rc())) return mac.rc();
11089 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11090 DeviceType_T devType = pMedium->i_getDeviceType();
11091 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11092 && devType == DeviceType_HardDisk)
11093 || (cleanupMode == CleanupMode_Full)
11094 )
11095 {
11096 llMedia.push_back(pMedium);
11097 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11098 /*
11099 * Search for medias which are not attached to any machine, but
11100 * in the chain to an attached disk. Mediums are only consided
11101 * if they are:
11102 * - have only one child
11103 * - no references to any machines
11104 * - are of normal medium type
11105 */
11106 while (!pParent.isNull())
11107 {
11108 AutoCaller mac1(pParent);
11109 if (FAILED(mac1.rc())) return mac1.rc();
11110 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11111 if (pParent->i_getChildren().size() == 1)
11112 {
11113 if ( pParent->i_getMachineBackRefCount() == 0
11114 && pParent->i_getType() == MediumType_Normal
11115 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11116 llMedia.push_back(pParent);
11117 }
11118 else
11119 break;
11120 pParent = pParent->i_getParent();
11121 }
11122 }
11123 }
11124
11125 // real machine: then we need to use the proper method
11126 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11127
11128 if (FAILED(rc))
11129 return rc;
11130 }
11131
11132 return S_OK;
11133}
11134
11135/**
11136 * Perform deferred hard disk detachments.
11137 *
11138 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11139 * backed up).
11140 *
11141 * If @a aOnline is @c true then this method will also unlock the old hard disks
11142 * for which the new implicit diffs were created and will lock these new diffs for
11143 * writing.
11144 *
11145 * @param aOnline Whether the VM was online prior to this operation.
11146 *
11147 * @note Locks this object for writing!
11148 */
11149void Machine::i_commitMedia(bool aOnline /*= false*/)
11150{
11151 AutoCaller autoCaller(this);
11152 AssertComRCReturnVoid(autoCaller.rc());
11153
11154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11155
11156 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11157
11158 HRESULT rc = S_OK;
11159
11160 /* no attach/detach operations -- nothing to do */
11161 if (!mMediaData.isBackedUp())
11162 return;
11163
11164 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11165 bool fMediaNeedsLocking = false;
11166
11167 /* enumerate new attachments */
11168 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11169 it != mMediaData->mAttachments.end();
11170 ++it)
11171 {
11172 MediumAttachment *pAttach = *it;
11173
11174 pAttach->i_commit();
11175
11176 Medium* pMedium = pAttach->i_getMedium();
11177 bool fImplicit = pAttach->i_isImplicit();
11178
11179 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11180 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11181 fImplicit));
11182
11183 /** @todo convert all this Machine-based voodoo to MediumAttachment
11184 * based commit logic. */
11185 if (fImplicit)
11186 {
11187 /* convert implicit attachment to normal */
11188 pAttach->i_setImplicit(false);
11189
11190 if ( aOnline
11191 && pMedium
11192 && pAttach->i_getType() == DeviceType_HardDisk
11193 )
11194 {
11195 ComObjPtr<Medium> parent = pMedium->i_getParent();
11196 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11197
11198 /* update the appropriate lock list */
11199 MediumLockList *pMediumLockList;
11200 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11201 AssertComRC(rc);
11202 if (pMediumLockList)
11203 {
11204 /* unlock if there's a need to change the locking */
11205 if (!fMediaNeedsLocking)
11206 {
11207 rc = mData->mSession.mLockedMedia.Unlock();
11208 AssertComRC(rc);
11209 fMediaNeedsLocking = true;
11210 }
11211 rc = pMediumLockList->Update(parent, false);
11212 AssertComRC(rc);
11213 rc = pMediumLockList->Append(pMedium, true);
11214 AssertComRC(rc);
11215 }
11216 }
11217
11218 continue;
11219 }
11220
11221 if (pMedium)
11222 {
11223 /* was this medium attached before? */
11224 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11225 {
11226 MediumAttachment *pOldAttach = *oldIt;
11227 if (pOldAttach->i_getMedium() == pMedium)
11228 {
11229 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11230
11231 /* yes: remove from old to avoid de-association */
11232 oldAtts.erase(oldIt);
11233 break;
11234 }
11235 }
11236 }
11237 }
11238
11239 /* enumerate remaining old attachments and de-associate from the
11240 * current machine state */
11241 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11242 {
11243 MediumAttachment *pAttach = *it;
11244 Medium* pMedium = pAttach->i_getMedium();
11245
11246 /* Detach only hard disks, since DVD/floppy media is detached
11247 * instantly in MountMedium. */
11248 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11249 {
11250 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11251
11252 /* now de-associate from the current machine state */
11253 rc = pMedium->i_removeBackReference(mData->mUuid);
11254 AssertComRC(rc);
11255
11256 if (aOnline)
11257 {
11258 /* unlock since medium is not used anymore */
11259 MediumLockList *pMediumLockList;
11260 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11261 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11262 {
11263 /* this happens for online snapshots, there the attachment
11264 * is changing, but only to a diff image created under
11265 * the old one, so there is no separate lock list */
11266 Assert(!pMediumLockList);
11267 }
11268 else
11269 {
11270 AssertComRC(rc);
11271 if (pMediumLockList)
11272 {
11273 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11274 AssertComRC(rc);
11275 }
11276 }
11277 }
11278 }
11279 }
11280
11281 /* take media locks again so that the locking state is consistent */
11282 if (fMediaNeedsLocking)
11283 {
11284 Assert(aOnline);
11285 rc = mData->mSession.mLockedMedia.Lock();
11286 AssertComRC(rc);
11287 }
11288
11289 /* commit the hard disk changes */
11290 mMediaData.commit();
11291
11292 if (i_isSessionMachine())
11293 {
11294 /*
11295 * Update the parent machine to point to the new owner.
11296 * This is necessary because the stored parent will point to the
11297 * session machine otherwise and cause crashes or errors later
11298 * when the session machine gets invalid.
11299 */
11300 /** @todo Change the MediumAttachment class to behave like any other
11301 * class in this regard by creating peer MediumAttachment
11302 * objects for session machines and share the data with the peer
11303 * machine.
11304 */
11305 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11306 it != mMediaData->mAttachments.end();
11307 ++it)
11308 (*it)->i_updateParentMachine(mPeer);
11309
11310 /* attach new data to the primary machine and reshare it */
11311 mPeer->mMediaData.attach(mMediaData);
11312 }
11313
11314 return;
11315}
11316
11317/**
11318 * Perform deferred deletion of implicitly created diffs.
11319 *
11320 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11321 * backed up).
11322 *
11323 * @note Locks this object for writing!
11324 */
11325void Machine::i_rollbackMedia()
11326{
11327 AutoCaller autoCaller(this);
11328 AssertComRCReturnVoid(autoCaller.rc());
11329
11330 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11331 LogFlowThisFunc(("Entering rollbackMedia\n"));
11332
11333 HRESULT rc = S_OK;
11334
11335 /* no attach/detach operations -- nothing to do */
11336 if (!mMediaData.isBackedUp())
11337 return;
11338
11339 /* enumerate new attachments */
11340 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11341 it != mMediaData->mAttachments.end();
11342 ++it)
11343 {
11344 MediumAttachment *pAttach = *it;
11345 /* Fix up the backrefs for DVD/floppy media. */
11346 if (pAttach->i_getType() != DeviceType_HardDisk)
11347 {
11348 Medium* pMedium = pAttach->i_getMedium();
11349 if (pMedium)
11350 {
11351 rc = pMedium->i_removeBackReference(mData->mUuid);
11352 AssertComRC(rc);
11353 }
11354 }
11355
11356 (*it)->i_rollback();
11357
11358 pAttach = *it;
11359 /* Fix up the backrefs for DVD/floppy media. */
11360 if (pAttach->i_getType() != DeviceType_HardDisk)
11361 {
11362 Medium* pMedium = pAttach->i_getMedium();
11363 if (pMedium)
11364 {
11365 rc = pMedium->i_addBackReference(mData->mUuid);
11366 AssertComRC(rc);
11367 }
11368 }
11369 }
11370
11371 /** @todo convert all this Machine-based voodoo to MediumAttachment
11372 * based rollback logic. */
11373 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11374
11375 return;
11376}
11377
11378/**
11379 * Returns true if the settings file is located in the directory named exactly
11380 * as the machine; this means, among other things, that the machine directory
11381 * should be auto-renamed.
11382 *
11383 * @param aSettingsDir if not NULL, the full machine settings file directory
11384 * name will be assigned there.
11385 *
11386 * @note Doesn't lock anything.
11387 * @note Not thread safe (must be called from this object's lock).
11388 */
11389bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11390{
11391 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11392 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11393 if (aSettingsDir)
11394 *aSettingsDir = strMachineDirName;
11395 strMachineDirName.stripPath(); // vmname
11396 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11397 strConfigFileOnly.stripPath() // vmname.vbox
11398 .stripSuffix(); // vmname
11399 /** @todo hack, make somehow use of ComposeMachineFilename */
11400 if (mUserData->s.fDirectoryIncludesUUID)
11401 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11402
11403 AssertReturn(!strMachineDirName.isEmpty(), false);
11404 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11405
11406 return strMachineDirName == strConfigFileOnly;
11407}
11408
11409/**
11410 * Discards all changes to machine settings.
11411 *
11412 * @param aNotify Whether to notify the direct session about changes or not.
11413 *
11414 * @note Locks objects for writing!
11415 */
11416void Machine::i_rollback(bool aNotify)
11417{
11418 AutoCaller autoCaller(this);
11419 AssertComRCReturn(autoCaller.rc(), (void)0);
11420
11421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11422
11423 if (!mStorageControllers.isNull())
11424 {
11425 if (mStorageControllers.isBackedUp())
11426 {
11427 /* unitialize all new devices (absent in the backed up list). */
11428 StorageControllerList::const_iterator it = mStorageControllers->begin();
11429 StorageControllerList *backedList = mStorageControllers.backedUpData();
11430 while (it != mStorageControllers->end())
11431 {
11432 if ( std::find(backedList->begin(), backedList->end(), *it)
11433 == backedList->end()
11434 )
11435 {
11436 (*it)->uninit();
11437 }
11438 ++it;
11439 }
11440
11441 /* restore the list */
11442 mStorageControllers.rollback();
11443 }
11444
11445 /* rollback any changes to devices after restoring the list */
11446 if (mData->flModifications & IsModified_Storage)
11447 {
11448 StorageControllerList::const_iterator it = mStorageControllers->begin();
11449 while (it != mStorageControllers->end())
11450 {
11451 (*it)->i_rollback();
11452 ++it;
11453 }
11454 }
11455 }
11456
11457 if (!mUSBControllers.isNull())
11458 {
11459 if (mUSBControllers.isBackedUp())
11460 {
11461 /* unitialize all new devices (absent in the backed up list). */
11462 USBControllerList::const_iterator it = mUSBControllers->begin();
11463 USBControllerList *backedList = mUSBControllers.backedUpData();
11464 while (it != mUSBControllers->end())
11465 {
11466 if ( std::find(backedList->begin(), backedList->end(), *it)
11467 == backedList->end()
11468 )
11469 {
11470 (*it)->uninit();
11471 }
11472 ++it;
11473 }
11474
11475 /* restore the list */
11476 mUSBControllers.rollback();
11477 }
11478
11479 /* rollback any changes to devices after restoring the list */
11480 if (mData->flModifications & IsModified_USB)
11481 {
11482 USBControllerList::const_iterator it = mUSBControllers->begin();
11483 while (it != mUSBControllers->end())
11484 {
11485 (*it)->i_rollback();
11486 ++it;
11487 }
11488 }
11489 }
11490
11491 mUserData.rollback();
11492
11493 mHWData.rollback();
11494
11495 if (mData->flModifications & IsModified_Storage)
11496 i_rollbackMedia();
11497
11498 if (mBIOSSettings)
11499 mBIOSSettings->i_rollback();
11500
11501 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11502 mVRDEServer->i_rollback();
11503
11504 if (mAudioAdapter)
11505 mAudioAdapter->i_rollback();
11506
11507 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11508 mUSBDeviceFilters->i_rollback();
11509
11510 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11511 mBandwidthControl->i_rollback();
11512
11513 if (!mHWData.isNull())
11514 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11515 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11516 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11517 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11518
11519 if (mData->flModifications & IsModified_NetworkAdapters)
11520 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11521 if ( mNetworkAdapters[slot]
11522 && mNetworkAdapters[slot]->i_isModified())
11523 {
11524 mNetworkAdapters[slot]->i_rollback();
11525 networkAdapters[slot] = mNetworkAdapters[slot];
11526 }
11527
11528 if (mData->flModifications & IsModified_SerialPorts)
11529 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11530 if ( mSerialPorts[slot]
11531 && mSerialPorts[slot]->i_isModified())
11532 {
11533 mSerialPorts[slot]->i_rollback();
11534 serialPorts[slot] = mSerialPorts[slot];
11535 }
11536
11537 if (mData->flModifications & IsModified_ParallelPorts)
11538 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11539 if ( mParallelPorts[slot]
11540 && mParallelPorts[slot]->i_isModified())
11541 {
11542 mParallelPorts[slot]->i_rollback();
11543 parallelPorts[slot] = mParallelPorts[slot];
11544 }
11545
11546 if (aNotify)
11547 {
11548 /* inform the direct session about changes */
11549
11550 ComObjPtr<Machine> that = this;
11551 uint32_t flModifications = mData->flModifications;
11552 alock.release();
11553
11554 if (flModifications & IsModified_SharedFolders)
11555 that->i_onSharedFolderChange();
11556
11557 if (flModifications & IsModified_VRDEServer)
11558 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11559 if (flModifications & IsModified_USB)
11560 that->i_onUSBControllerChange();
11561
11562 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11563 if (networkAdapters[slot])
11564 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11565 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11566 if (serialPorts[slot])
11567 that->i_onSerialPortChange(serialPorts[slot]);
11568 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11569 if (parallelPorts[slot])
11570 that->i_onParallelPortChange(parallelPorts[slot]);
11571
11572 if (flModifications & IsModified_Storage)
11573 that->i_onStorageControllerChange();
11574
11575#if 0
11576 if (flModifications & IsModified_BandwidthControl)
11577 that->onBandwidthControlChange();
11578#endif
11579 }
11580}
11581
11582/**
11583 * Commits all the changes to machine settings.
11584 *
11585 * Note that this operation is supposed to never fail.
11586 *
11587 * @note Locks this object and children for writing.
11588 */
11589void Machine::i_commit()
11590{
11591 AutoCaller autoCaller(this);
11592 AssertComRCReturnVoid(autoCaller.rc());
11593
11594 AutoCaller peerCaller(mPeer);
11595 AssertComRCReturnVoid(peerCaller.rc());
11596
11597 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11598
11599 /*
11600 * use safe commit to ensure Snapshot machines (that share mUserData)
11601 * will still refer to a valid memory location
11602 */
11603 mUserData.commitCopy();
11604
11605 mHWData.commit();
11606
11607 if (mMediaData.isBackedUp())
11608 i_commitMedia(Global::IsOnline(mData->mMachineState));
11609
11610 mBIOSSettings->i_commit();
11611 mVRDEServer->i_commit();
11612 mAudioAdapter->i_commit();
11613 mUSBDeviceFilters->i_commit();
11614 mBandwidthControl->i_commit();
11615
11616 /* Since mNetworkAdapters is a list which might have been changed (resized)
11617 * without using the Backupable<> template we need to handle the copying
11618 * of the list entries manually, including the creation of peers for the
11619 * new objects. */
11620 bool commitNetworkAdapters = false;
11621 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11622 if (mPeer)
11623 {
11624 /* commit everything, even the ones which will go away */
11625 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11626 mNetworkAdapters[slot]->i_commit();
11627 /* copy over the new entries, creating a peer and uninit the original */
11628 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11629 for (size_t slot = 0; slot < newSize; slot++)
11630 {
11631 /* look if this adapter has a peer device */
11632 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11633 if (!peer)
11634 {
11635 /* no peer means the adapter is a newly created one;
11636 * create a peer owning data this data share it with */
11637 peer.createObject();
11638 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11639 }
11640 mPeer->mNetworkAdapters[slot] = peer;
11641 }
11642 /* uninit any no longer needed network adapters */
11643 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11644 mNetworkAdapters[slot]->uninit();
11645 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11646 {
11647 if (mPeer->mNetworkAdapters[slot])
11648 mPeer->mNetworkAdapters[slot]->uninit();
11649 }
11650 /* Keep the original network adapter count until this point, so that
11651 * discarding a chipset type change will not lose settings. */
11652 mNetworkAdapters.resize(newSize);
11653 mPeer->mNetworkAdapters.resize(newSize);
11654 }
11655 else
11656 {
11657 /* we have no peer (our parent is the newly created machine);
11658 * just commit changes to the network adapters */
11659 commitNetworkAdapters = true;
11660 }
11661 if (commitNetworkAdapters)
11662 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11663 mNetworkAdapters[slot]->i_commit();
11664
11665 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11666 mSerialPorts[slot]->i_commit();
11667 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11668 mParallelPorts[slot]->i_commit();
11669
11670 bool commitStorageControllers = false;
11671
11672 if (mStorageControllers.isBackedUp())
11673 {
11674 mStorageControllers.commit();
11675
11676 if (mPeer)
11677 {
11678 /* Commit all changes to new controllers (this will reshare data with
11679 * peers for those who have peers) */
11680 StorageControllerList *newList = new StorageControllerList();
11681 StorageControllerList::const_iterator it = mStorageControllers->begin();
11682 while (it != mStorageControllers->end())
11683 {
11684 (*it)->i_commit();
11685
11686 /* look if this controller has a peer device */
11687 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11688 if (!peer)
11689 {
11690 /* no peer means the device is a newly created one;
11691 * create a peer owning data this device share it with */
11692 peer.createObject();
11693 peer->init(mPeer, *it, true /* aReshare */);
11694 }
11695 else
11696 {
11697 /* remove peer from the old list */
11698 mPeer->mStorageControllers->remove(peer);
11699 }
11700 /* and add it to the new list */
11701 newList->push_back(peer);
11702
11703 ++it;
11704 }
11705
11706 /* uninit old peer's controllers that are left */
11707 it = mPeer->mStorageControllers->begin();
11708 while (it != mPeer->mStorageControllers->end())
11709 {
11710 (*it)->uninit();
11711 ++it;
11712 }
11713
11714 /* attach new list of controllers to our peer */
11715 mPeer->mStorageControllers.attach(newList);
11716 }
11717 else
11718 {
11719 /* we have no peer (our parent is the newly created machine);
11720 * just commit changes to devices */
11721 commitStorageControllers = true;
11722 }
11723 }
11724 else
11725 {
11726 /* the list of controllers itself is not changed,
11727 * just commit changes to controllers themselves */
11728 commitStorageControllers = true;
11729 }
11730
11731 if (commitStorageControllers)
11732 {
11733 StorageControllerList::const_iterator it = mStorageControllers->begin();
11734 while (it != mStorageControllers->end())
11735 {
11736 (*it)->i_commit();
11737 ++it;
11738 }
11739 }
11740
11741 bool commitUSBControllers = false;
11742
11743 if (mUSBControllers.isBackedUp())
11744 {
11745 mUSBControllers.commit();
11746
11747 if (mPeer)
11748 {
11749 /* Commit all changes to new controllers (this will reshare data with
11750 * peers for those who have peers) */
11751 USBControllerList *newList = new USBControllerList();
11752 USBControllerList::const_iterator it = mUSBControllers->begin();
11753 while (it != mUSBControllers->end())
11754 {
11755 (*it)->i_commit();
11756
11757 /* look if this controller has a peer device */
11758 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11759 if (!peer)
11760 {
11761 /* no peer means the device is a newly created one;
11762 * create a peer owning data this device share it with */
11763 peer.createObject();
11764 peer->init(mPeer, *it, true /* aReshare */);
11765 }
11766 else
11767 {
11768 /* remove peer from the old list */
11769 mPeer->mUSBControllers->remove(peer);
11770 }
11771 /* and add it to the new list */
11772 newList->push_back(peer);
11773
11774 ++it;
11775 }
11776
11777 /* uninit old peer's controllers that are left */
11778 it = mPeer->mUSBControllers->begin();
11779 while (it != mPeer->mUSBControllers->end())
11780 {
11781 (*it)->uninit();
11782 ++it;
11783 }
11784
11785 /* attach new list of controllers to our peer */
11786 mPeer->mUSBControllers.attach(newList);
11787 }
11788 else
11789 {
11790 /* we have no peer (our parent is the newly created machine);
11791 * just commit changes to devices */
11792 commitUSBControllers = true;
11793 }
11794 }
11795 else
11796 {
11797 /* the list of controllers itself is not changed,
11798 * just commit changes to controllers themselves */
11799 commitUSBControllers = true;
11800 }
11801
11802 if (commitUSBControllers)
11803 {
11804 USBControllerList::const_iterator it = mUSBControllers->begin();
11805 while (it != mUSBControllers->end())
11806 {
11807 (*it)->i_commit();
11808 ++it;
11809 }
11810 }
11811
11812 if (i_isSessionMachine())
11813 {
11814 /* attach new data to the primary machine and reshare it */
11815 mPeer->mUserData.attach(mUserData);
11816 mPeer->mHWData.attach(mHWData);
11817 /* mMediaData is reshared by fixupMedia */
11818 // mPeer->mMediaData.attach(mMediaData);
11819 Assert(mPeer->mMediaData.data() == mMediaData.data());
11820 }
11821}
11822
11823/**
11824 * Copies all the hardware data from the given machine.
11825 *
11826 * Currently, only called when the VM is being restored from a snapshot. In
11827 * particular, this implies that the VM is not running during this method's
11828 * call.
11829 *
11830 * @note This method must be called from under this object's lock.
11831 *
11832 * @note This method doesn't call #commit(), so all data remains backed up and
11833 * unsaved.
11834 */
11835void Machine::i_copyFrom(Machine *aThat)
11836{
11837 AssertReturnVoid(!i_isSnapshotMachine());
11838 AssertReturnVoid(aThat->i_isSnapshotMachine());
11839
11840 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11841
11842 mHWData.assignCopy(aThat->mHWData);
11843
11844 // create copies of all shared folders (mHWData after attaching a copy
11845 // contains just references to original objects)
11846 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11847 it != mHWData->mSharedFolders.end();
11848 ++it)
11849 {
11850 ComObjPtr<SharedFolder> folder;
11851 folder.createObject();
11852 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11853 AssertComRC(rc);
11854 *it = folder;
11855 }
11856
11857 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11858 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11859 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11860 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11861 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11862
11863 /* create private copies of all controllers */
11864 mStorageControllers.backup();
11865 mStorageControllers->clear();
11866 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11867 it != aThat->mStorageControllers->end();
11868 ++it)
11869 {
11870 ComObjPtr<StorageController> ctrl;
11871 ctrl.createObject();
11872 ctrl->initCopy(this, *it);
11873 mStorageControllers->push_back(ctrl);
11874 }
11875
11876 /* create private copies of all USB controllers */
11877 mUSBControllers.backup();
11878 mUSBControllers->clear();
11879 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11880 it != aThat->mUSBControllers->end();
11881 ++it)
11882 {
11883 ComObjPtr<USBController> ctrl;
11884 ctrl.createObject();
11885 ctrl->initCopy(this, *it);
11886 mUSBControllers->push_back(ctrl);
11887 }
11888
11889 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11890 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11891 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11892 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11893 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11894 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11895 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11896}
11897
11898/**
11899 * Returns whether the given storage controller is hotplug capable.
11900 *
11901 * @returns true if the controller supports hotplugging
11902 * false otherwise.
11903 * @param enmCtrlType The controller type to check for.
11904 */
11905bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11906{
11907 ComPtr<ISystemProperties> systemProperties;
11908 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11909 if (FAILED(rc))
11910 return false;
11911
11912 BOOL aHotplugCapable = FALSE;
11913 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11914
11915 return RT_BOOL(aHotplugCapable);
11916}
11917
11918#ifdef VBOX_WITH_RESOURCE_USAGE_API
11919
11920void Machine::i_getDiskList(MediaList &list)
11921{
11922 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11923 it != mMediaData->mAttachments.end();
11924 ++it)
11925 {
11926 MediumAttachment* pAttach = *it;
11927 /* just in case */
11928 AssertStmt(pAttach, continue);
11929
11930 AutoCaller localAutoCallerA(pAttach);
11931 if (FAILED(localAutoCallerA.rc())) continue;
11932
11933 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11934
11935 if (pAttach->i_getType() == DeviceType_HardDisk)
11936 list.push_back(pAttach->i_getMedium());
11937 }
11938}
11939
11940void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11941{
11942 AssertReturnVoid(isWriteLockOnCurrentThread());
11943 AssertPtrReturnVoid(aCollector);
11944
11945 pm::CollectorHAL *hal = aCollector->getHAL();
11946 /* Create sub metrics */
11947 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11948 "Percentage of processor time spent in user mode by the VM process.");
11949 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11950 "Percentage of processor time spent in kernel mode by the VM process.");
11951 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11952 "Size of resident portion of VM process in memory.");
11953 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11954 "Actual size of all VM disks combined.");
11955 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11956 "Network receive rate.");
11957 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11958 "Network transmit rate.");
11959 /* Create and register base metrics */
11960 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11961 cpuLoadUser, cpuLoadKernel);
11962 aCollector->registerBaseMetric(cpuLoad);
11963 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11964 ramUsageUsed);
11965 aCollector->registerBaseMetric(ramUsage);
11966 MediaList disks;
11967 i_getDiskList(disks);
11968 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11969 diskUsageUsed);
11970 aCollector->registerBaseMetric(diskUsage);
11971
11972 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11973 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11974 new pm::AggregateAvg()));
11975 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11976 new pm::AggregateMin()));
11977 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11978 new pm::AggregateMax()));
11979 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11980 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11981 new pm::AggregateAvg()));
11982 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11983 new pm::AggregateMin()));
11984 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11985 new pm::AggregateMax()));
11986
11987 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11988 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11989 new pm::AggregateAvg()));
11990 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11991 new pm::AggregateMin()));
11992 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11993 new pm::AggregateMax()));
11994
11995 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11996 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11997 new pm::AggregateAvg()));
11998 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11999 new pm::AggregateMin()));
12000 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12001 new pm::AggregateMax()));
12002
12003
12004 /* Guest metrics collector */
12005 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12006 aCollector->registerGuest(mCollectorGuest);
12007 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12008 this, __PRETTY_FUNCTION__, mCollectorGuest));
12009
12010 /* Create sub metrics */
12011 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12012 "Percentage of processor time spent in user mode as seen by the guest.");
12013 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12014 "Percentage of processor time spent in kernel mode as seen by the guest.");
12015 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12016 "Percentage of processor time spent idling as seen by the guest.");
12017
12018 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12019 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12020 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12021 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12022 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12023 pm::SubMetric *guestMemCache = new pm::SubMetric(
12024 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12025
12026 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12027 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12028
12029 /* Create and register base metrics */
12030 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12031 machineNetRx, machineNetTx);
12032 aCollector->registerBaseMetric(machineNetRate);
12033
12034 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12035 guestLoadUser, guestLoadKernel, guestLoadIdle);
12036 aCollector->registerBaseMetric(guestCpuLoad);
12037
12038 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12039 guestMemTotal, guestMemFree,
12040 guestMemBalloon, guestMemShared,
12041 guestMemCache, guestPagedTotal);
12042 aCollector->registerBaseMetric(guestCpuMem);
12043
12044 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12045 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12046 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12047 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12048
12049 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12050 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12051 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12052 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12053
12054 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12055 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12056 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12057 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12058
12059 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12060 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12061 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12062 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12063
12064 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12065 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12066 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12067 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12068
12069 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12071 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12073
12074 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12075 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12076 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12077 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12078
12079 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12080 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12081 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12082 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12083
12084 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12085 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12086 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12087 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12088
12089 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12090 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12091 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12092 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12093
12094 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12095 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12096 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12097 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12098}
12099
12100void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12101{
12102 AssertReturnVoid(isWriteLockOnCurrentThread());
12103
12104 if (aCollector)
12105 {
12106 aCollector->unregisterMetricsFor(aMachine);
12107 aCollector->unregisterBaseMetricsFor(aMachine);
12108 }
12109}
12110
12111#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12112
12113
12114////////////////////////////////////////////////////////////////////////////////
12115
12116DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12117
12118HRESULT SessionMachine::FinalConstruct()
12119{
12120 LogFlowThisFunc(("\n"));
12121
12122 mClientToken = NULL;
12123
12124 return BaseFinalConstruct();
12125}
12126
12127void SessionMachine::FinalRelease()
12128{
12129 LogFlowThisFunc(("\n"));
12130
12131 Assert(!mClientToken);
12132 /* paranoia, should not hang around any more */
12133 if (mClientToken)
12134 {
12135 delete mClientToken;
12136 mClientToken = NULL;
12137 }
12138
12139 uninit(Uninit::Unexpected);
12140
12141 BaseFinalRelease();
12142}
12143
12144/**
12145 * @note Must be called only by Machine::LockMachine() from its own write lock.
12146 */
12147HRESULT SessionMachine::init(Machine *aMachine)
12148{
12149 LogFlowThisFuncEnter();
12150 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12151
12152 AssertReturn(aMachine, E_INVALIDARG);
12153
12154 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12155
12156 /* Enclose the state transition NotReady->InInit->Ready */
12157 AutoInitSpan autoInitSpan(this);
12158 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12159
12160 HRESULT rc = S_OK;
12161
12162 /* create the machine client token */
12163 try
12164 {
12165 mClientToken = new ClientToken(aMachine, this);
12166 if (!mClientToken->isReady())
12167 {
12168 delete mClientToken;
12169 mClientToken = NULL;
12170 rc = E_FAIL;
12171 }
12172 }
12173 catch (std::bad_alloc &)
12174 {
12175 rc = E_OUTOFMEMORY;
12176 }
12177 if (FAILED(rc))
12178 return rc;
12179
12180 /* memorize the peer Machine */
12181 unconst(mPeer) = aMachine;
12182 /* share the parent pointer */
12183 unconst(mParent) = aMachine->mParent;
12184
12185 /* take the pointers to data to share */
12186 mData.share(aMachine->mData);
12187 mSSData.share(aMachine->mSSData);
12188
12189 mUserData.share(aMachine->mUserData);
12190 mHWData.share(aMachine->mHWData);
12191 mMediaData.share(aMachine->mMediaData);
12192
12193 mStorageControllers.allocate();
12194 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12195 it != aMachine->mStorageControllers->end();
12196 ++it)
12197 {
12198 ComObjPtr<StorageController> ctl;
12199 ctl.createObject();
12200 ctl->init(this, *it);
12201 mStorageControllers->push_back(ctl);
12202 }
12203
12204 mUSBControllers.allocate();
12205 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12206 it != aMachine->mUSBControllers->end();
12207 ++it)
12208 {
12209 ComObjPtr<USBController> ctl;
12210 ctl.createObject();
12211 ctl->init(this, *it);
12212 mUSBControllers->push_back(ctl);
12213 }
12214
12215 unconst(mBIOSSettings).createObject();
12216 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12217 /* create another VRDEServer object that will be mutable */
12218 unconst(mVRDEServer).createObject();
12219 mVRDEServer->init(this, aMachine->mVRDEServer);
12220 /* create another audio adapter object that will be mutable */
12221 unconst(mAudioAdapter).createObject();
12222 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12223 /* create a list of serial ports that will be mutable */
12224 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12225 {
12226 unconst(mSerialPorts[slot]).createObject();
12227 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12228 }
12229 /* create a list of parallel ports that will be mutable */
12230 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12231 {
12232 unconst(mParallelPorts[slot]).createObject();
12233 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12234 }
12235
12236 /* create another USB device filters object that will be mutable */
12237 unconst(mUSBDeviceFilters).createObject();
12238 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12239
12240 /* create a list of network adapters that will be mutable */
12241 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12242 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12243 {
12244 unconst(mNetworkAdapters[slot]).createObject();
12245 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12246 }
12247
12248 /* create another bandwidth control object that will be mutable */
12249 unconst(mBandwidthControl).createObject();
12250 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12251
12252 /* default is to delete saved state on Saved -> PoweredOff transition */
12253 mRemoveSavedState = true;
12254
12255 /* Confirm a successful initialization when it's the case */
12256 autoInitSpan.setSucceeded();
12257
12258 miNATNetworksStarted = 0;
12259
12260 LogFlowThisFuncLeave();
12261 return rc;
12262}
12263
12264/**
12265 * Uninitializes this session object. If the reason is other than
12266 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12267 * or the client watcher code.
12268 *
12269 * @param aReason uninitialization reason
12270 *
12271 * @note Locks mParent + this object for writing.
12272 */
12273void SessionMachine::uninit(Uninit::Reason aReason)
12274{
12275 LogFlowThisFuncEnter();
12276 LogFlowThisFunc(("reason=%d\n", aReason));
12277
12278 /*
12279 * Strongly reference ourselves to prevent this object deletion after
12280 * mData->mSession.mMachine.setNull() below (which can release the last
12281 * reference and call the destructor). Important: this must be done before
12282 * accessing any members (and before AutoUninitSpan that does it as well).
12283 * This self reference will be released as the very last step on return.
12284 */
12285 ComObjPtr<SessionMachine> selfRef = this;
12286
12287 /* Enclose the state transition Ready->InUninit->NotReady */
12288 AutoUninitSpan autoUninitSpan(this);
12289 if (autoUninitSpan.uninitDone())
12290 {
12291 LogFlowThisFunc(("Already uninitialized\n"));
12292 LogFlowThisFuncLeave();
12293 return;
12294 }
12295
12296 if (autoUninitSpan.initFailed())
12297 {
12298 /* We've been called by init() because it's failed. It's not really
12299 * necessary (nor it's safe) to perform the regular uninit sequence
12300 * below, the following is enough.
12301 */
12302 LogFlowThisFunc(("Initialization failed.\n"));
12303 /* destroy the machine client token */
12304 if (mClientToken)
12305 {
12306 delete mClientToken;
12307 mClientToken = NULL;
12308 }
12309 uninitDataAndChildObjects();
12310 mData.free();
12311 unconst(mParent) = NULL;
12312 unconst(mPeer) = NULL;
12313 LogFlowThisFuncLeave();
12314 return;
12315 }
12316
12317 MachineState_T lastState;
12318 {
12319 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12320 lastState = mData->mMachineState;
12321 }
12322 NOREF(lastState);
12323
12324#ifdef VBOX_WITH_USB
12325 // release all captured USB devices, but do this before requesting the locks below
12326 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12327 {
12328 /* Console::captureUSBDevices() is called in the VM process only after
12329 * setting the machine state to Starting or Restoring.
12330 * Console::detachAllUSBDevices() will be called upon successful
12331 * termination. So, we need to release USB devices only if there was
12332 * an abnormal termination of a running VM.
12333 *
12334 * This is identical to SessionMachine::DetachAllUSBDevices except
12335 * for the aAbnormal argument. */
12336 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12337 AssertComRC(rc);
12338 NOREF(rc);
12339
12340 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12341 if (service)
12342 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12343 }
12344#endif /* VBOX_WITH_USB */
12345
12346 // we need to lock this object in uninit() because the lock is shared
12347 // with mPeer (as well as data we modify below). mParent lock is needed
12348 // by several calls to it, and USB needs host lock.
12349 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12350
12351#ifdef VBOX_WITH_RESOURCE_USAGE_API
12352 /*
12353 * It is safe to call Machine::i_unregisterMetrics() here because
12354 * PerformanceCollector::samplerCallback no longer accesses guest methods
12355 * holding the lock.
12356 */
12357 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12358 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12359 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12360 this, __PRETTY_FUNCTION__, mCollectorGuest));
12361 if (mCollectorGuest)
12362 {
12363 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12364 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12365 mCollectorGuest = NULL;
12366 }
12367#endif
12368
12369 if (aReason == Uninit::Abnormal)
12370 {
12371 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12372 Global::IsOnlineOrTransient(lastState)));
12373
12374 /* reset the state to Aborted */
12375 if (mData->mMachineState != MachineState_Aborted)
12376 i_setMachineState(MachineState_Aborted);
12377 }
12378
12379 // any machine settings modified?
12380 if (mData->flModifications)
12381 {
12382 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12383 i_rollback(false /* aNotify */);
12384 }
12385
12386 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12387 || !mConsoleTaskData.mSnapshot);
12388 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12389 {
12390 LogWarningThisFunc(("canceling failed save state request!\n"));
12391 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12392 }
12393 else if (!mConsoleTaskData.mSnapshot.isNull())
12394 {
12395 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12396
12397 /* delete all differencing hard disks created (this will also attach
12398 * their parents back by rolling back mMediaData) */
12399 i_rollbackMedia();
12400
12401 // delete the saved state file (it might have been already created)
12402 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12403 // think it's still in use
12404 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12405 mConsoleTaskData.mSnapshot->uninit();
12406 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12407 }
12408
12409 mData->mSession.mPID = NIL_RTPROCESS;
12410
12411 if (aReason == Uninit::Unexpected)
12412 {
12413 /* Uninitialization didn't come from #checkForDeath(), so tell the
12414 * client watcher thread to update the set of machines that have open
12415 * sessions. */
12416 mParent->i_updateClientWatcher();
12417 }
12418
12419 /* uninitialize all remote controls */
12420 if (mData->mSession.mRemoteControls.size())
12421 {
12422 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12423 mData->mSession.mRemoteControls.size()));
12424
12425 Data::Session::RemoteControlList::iterator it =
12426 mData->mSession.mRemoteControls.begin();
12427 while (it != mData->mSession.mRemoteControls.end())
12428 {
12429 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12430 HRESULT rc = (*it)->Uninitialize();
12431 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12432 if (FAILED(rc))
12433 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12434 ++it;
12435 }
12436 mData->mSession.mRemoteControls.clear();
12437 }
12438
12439 /* Remove all references to the NAT network service. The service will stop
12440 * if all references (also from other VMs) are removed. */
12441 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12442 {
12443 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12444 {
12445 NetworkAttachmentType_T type;
12446 HRESULT hrc;
12447
12448 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12449 if ( SUCCEEDED(hrc)
12450 && type == NetworkAttachmentType_NATNetwork)
12451 {
12452 Bstr name;
12453 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12454 if (SUCCEEDED(hrc))
12455 {
12456 multilock.release();
12457 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12458 mUserData->s.strName.c_str(), name.raw()));
12459 mParent->i_natNetworkRefDec(name.raw());
12460 multilock.acquire();
12461 }
12462 }
12463 }
12464 }
12465
12466 /*
12467 * An expected uninitialization can come only from #checkForDeath().
12468 * Otherwise it means that something's gone really wrong (for example,
12469 * the Session implementation has released the VirtualBox reference
12470 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12471 * etc). However, it's also possible, that the client releases the IPC
12472 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12473 * but the VirtualBox release event comes first to the server process.
12474 * This case is practically possible, so we should not assert on an
12475 * unexpected uninit, just log a warning.
12476 */
12477
12478 if ((aReason == Uninit::Unexpected))
12479 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12480
12481 if (aReason != Uninit::Normal)
12482 {
12483 mData->mSession.mDirectControl.setNull();
12484 }
12485 else
12486 {
12487 /* this must be null here (see #OnSessionEnd()) */
12488 Assert(mData->mSession.mDirectControl.isNull());
12489 Assert(mData->mSession.mState == SessionState_Unlocking);
12490 Assert(!mData->mSession.mProgress.isNull());
12491 }
12492 if (mData->mSession.mProgress)
12493 {
12494 if (aReason == Uninit::Normal)
12495 mData->mSession.mProgress->i_notifyComplete(S_OK);
12496 else
12497 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12498 COM_IIDOF(ISession),
12499 getComponentName(),
12500 tr("The VM session was aborted"));
12501 mData->mSession.mProgress.setNull();
12502 }
12503
12504 /* remove the association between the peer machine and this session machine */
12505 Assert( (SessionMachine*)mData->mSession.mMachine == this
12506 || aReason == Uninit::Unexpected);
12507
12508 /* reset the rest of session data */
12509 mData->mSession.mMachine.setNull();
12510 mData->mSession.mState = SessionState_Unlocked;
12511 mData->mSession.mType.setNull();
12512
12513 /* destroy the machine client token before leaving the exclusive lock */
12514 if (mClientToken)
12515 {
12516 delete mClientToken;
12517 mClientToken = NULL;
12518 }
12519
12520 /* fire an event */
12521 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12522
12523 uninitDataAndChildObjects();
12524
12525 /* free the essential data structure last */
12526 mData.free();
12527
12528 /* release the exclusive lock before setting the below two to NULL */
12529 multilock.release();
12530
12531 unconst(mParent) = NULL;
12532 unconst(mPeer) = NULL;
12533
12534 LogFlowThisFuncLeave();
12535}
12536
12537// util::Lockable interface
12538////////////////////////////////////////////////////////////////////////////////
12539
12540/**
12541 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12542 * with the primary Machine instance (mPeer).
12543 */
12544RWLockHandle *SessionMachine::lockHandle() const
12545{
12546 AssertReturn(mPeer != NULL, NULL);
12547 return mPeer->lockHandle();
12548}
12549
12550// IInternalMachineControl methods
12551////////////////////////////////////////////////////////////////////////////////
12552
12553/**
12554 * Passes collected guest statistics to performance collector object
12555 */
12556HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12557 ULONG aCpuKernel, ULONG aCpuIdle,
12558 ULONG aMemTotal, ULONG aMemFree,
12559 ULONG aMemBalloon, ULONG aMemShared,
12560 ULONG aMemCache, ULONG aPageTotal,
12561 ULONG aAllocVMM, ULONG aFreeVMM,
12562 ULONG aBalloonedVMM, ULONG aSharedVMM,
12563 ULONG aVmNetRx, ULONG aVmNetTx)
12564{
12565#ifdef VBOX_WITH_RESOURCE_USAGE_API
12566 if (mCollectorGuest)
12567 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12568 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12569 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12570 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12571
12572 return S_OK;
12573#else
12574 NOREF(aValidStats);
12575 NOREF(aCpuUser);
12576 NOREF(aCpuKernel);
12577 NOREF(aCpuIdle);
12578 NOREF(aMemTotal);
12579 NOREF(aMemFree);
12580 NOREF(aMemBalloon);
12581 NOREF(aMemShared);
12582 NOREF(aMemCache);
12583 NOREF(aPageTotal);
12584 NOREF(aAllocVMM);
12585 NOREF(aFreeVMM);
12586 NOREF(aBalloonedVMM);
12587 NOREF(aSharedVMM);
12588 NOREF(aVmNetRx);
12589 NOREF(aVmNetTx);
12590 return E_NOTIMPL;
12591#endif
12592}
12593
12594/**
12595 * @note Locks this object for writing.
12596 */
12597HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12598{
12599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12600
12601 mRemoveSavedState = aRemove;
12602
12603 return S_OK;
12604}
12605
12606/**
12607 * @note Locks the same as #i_setMachineState() does.
12608 */
12609HRESULT SessionMachine::updateState(MachineState_T aState)
12610{
12611 return i_setMachineState(aState);
12612}
12613
12614/**
12615 * @note Locks this object for writing.
12616 */
12617HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12618{
12619 IProgress* pProgress(aProgress);
12620
12621 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12622
12623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12624
12625 if (mData->mSession.mState != SessionState_Locked)
12626 return VBOX_E_INVALID_OBJECT_STATE;
12627
12628 if (!mData->mSession.mProgress.isNull())
12629 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12630
12631 /* If we didn't reference the NAT network service yet, add a reference to
12632 * force a start */
12633 if (miNATNetworksStarted < 1)
12634 {
12635 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12636 {
12637 NetworkAttachmentType_T type;
12638 HRESULT hrc;
12639 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12640 if ( SUCCEEDED(hrc)
12641 && type == NetworkAttachmentType_NATNetwork)
12642 {
12643 Bstr name;
12644 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12645 if (SUCCEEDED(hrc))
12646 {
12647 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12648 mUserData->s.strName.c_str(), name.raw()));
12649 mPeer->lockHandle()->unlockWrite();
12650 mParent->i_natNetworkRefInc(name.raw());
12651#ifdef RT_LOCK_STRICT
12652 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12653#else
12654 mPeer->lockHandle()->lockWrite();
12655#endif
12656 }
12657 }
12658 }
12659 miNATNetworksStarted++;
12660 }
12661
12662 LogFlowThisFunc(("returns S_OK.\n"));
12663 return S_OK;
12664}
12665
12666/**
12667 * @note Locks this object for writing.
12668 */
12669HRESULT SessionMachine::endPowerUp(LONG aResult)
12670{
12671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12672
12673 if (mData->mSession.mState != SessionState_Locked)
12674 return VBOX_E_INVALID_OBJECT_STATE;
12675
12676 /* Finalize the LaunchVMProcess progress object. */
12677 if (mData->mSession.mProgress)
12678 {
12679 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12680 mData->mSession.mProgress.setNull();
12681 }
12682
12683 if (SUCCEEDED((HRESULT)aResult))
12684 {
12685#ifdef VBOX_WITH_RESOURCE_USAGE_API
12686 /* The VM has been powered up successfully, so it makes sense
12687 * now to offer the performance metrics for a running machine
12688 * object. Doing it earlier wouldn't be safe. */
12689 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12690 mData->mSession.mPID);
12691#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12692 }
12693
12694 return S_OK;
12695}
12696
12697/**
12698 * @note Locks this object for writing.
12699 */
12700HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12701{
12702 LogFlowThisFuncEnter();
12703
12704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12705
12706 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12707 E_FAIL);
12708
12709 /* create a progress object to track operation completion */
12710 ComObjPtr<Progress> pProgress;
12711 pProgress.createObject();
12712 pProgress->init(i_getVirtualBox(),
12713 static_cast<IMachine *>(this) /* aInitiator */,
12714 Bstr(tr("Stopping the virtual machine")).raw(),
12715 FALSE /* aCancelable */);
12716
12717 /* fill in the console task data */
12718 mConsoleTaskData.mLastState = mData->mMachineState;
12719 mConsoleTaskData.mProgress = pProgress;
12720
12721 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12722 i_setMachineState(MachineState_Stopping);
12723
12724 pProgress.queryInterfaceTo(aProgress.asOutParam());
12725
12726 return S_OK;
12727}
12728
12729/**
12730 * @note Locks this object for writing.
12731 */
12732HRESULT SessionMachine::endPoweringDown(LONG aResult,
12733 const com::Utf8Str &aErrMsg)
12734{
12735 LogFlowThisFuncEnter();
12736
12737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12738
12739 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12740 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12741 && mConsoleTaskData.mLastState != MachineState_Null,
12742 E_FAIL);
12743
12744 /*
12745 * On failure, set the state to the state we had when BeginPoweringDown()
12746 * was called (this is expected by Console::PowerDown() and the associated
12747 * task). On success the VM process already changed the state to
12748 * MachineState_PoweredOff, so no need to do anything.
12749 */
12750 if (FAILED(aResult))
12751 i_setMachineState(mConsoleTaskData.mLastState);
12752
12753 /* notify the progress object about operation completion */
12754 Assert(mConsoleTaskData.mProgress);
12755 if (SUCCEEDED(aResult))
12756 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12757 else
12758 {
12759 if (aErrMsg.length())
12760 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12761 COM_IIDOF(ISession),
12762 getComponentName(),
12763 aErrMsg.c_str());
12764 else
12765 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12766 }
12767
12768 /* clear out the temporary saved state data */
12769 mConsoleTaskData.mLastState = MachineState_Null;
12770 mConsoleTaskData.mProgress.setNull();
12771
12772 LogFlowThisFuncLeave();
12773 return S_OK;
12774}
12775
12776
12777/**
12778 * Goes through the USB filters of the given machine to see if the given
12779 * device matches any filter or not.
12780 *
12781 * @note Locks the same as USBController::hasMatchingFilter() does.
12782 */
12783HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12784 BOOL *aMatched,
12785 ULONG *aMaskedInterfaces)
12786{
12787 LogFlowThisFunc(("\n"));
12788
12789#ifdef VBOX_WITH_USB
12790 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12791#else
12792 NOREF(aDevice);
12793 NOREF(aMaskedInterfaces);
12794 *aMatched = FALSE;
12795#endif
12796
12797 return S_OK;
12798}
12799
12800/**
12801 * @note Locks the same as Host::captureUSBDevice() does.
12802 */
12803HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId)
12804{
12805 LogFlowThisFunc(("\n"));
12806
12807#ifdef VBOX_WITH_USB
12808 /* if captureDeviceForVM() fails, it must have set extended error info */
12809 clearError();
12810 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12811 if (FAILED(rc)) return rc;
12812
12813 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12814 AssertReturn(service, E_FAIL);
12815 return service->captureDeviceForVM(this, aId.ref());
12816#else
12817 NOREF(aId);
12818 return E_NOTIMPL;
12819#endif
12820}
12821
12822/**
12823 * @note Locks the same as Host::detachUSBDevice() does.
12824 */
12825HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12826 BOOL aDone)
12827{
12828 LogFlowThisFunc(("\n"));
12829
12830#ifdef VBOX_WITH_USB
12831 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12832 AssertReturn(service, E_FAIL);
12833 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12834#else
12835 NOREF(aId);
12836 NOREF(aDone);
12837 return E_NOTIMPL;
12838#endif
12839}
12840
12841/**
12842 * Inserts all machine filters to the USB proxy service and then calls
12843 * Host::autoCaptureUSBDevices().
12844 *
12845 * Called by Console from the VM process upon VM startup.
12846 *
12847 * @note Locks what called methods lock.
12848 */
12849HRESULT SessionMachine::autoCaptureUSBDevices()
12850{
12851 LogFlowThisFunc(("\n"));
12852
12853#ifdef VBOX_WITH_USB
12854 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12855 AssertComRC(rc);
12856 NOREF(rc);
12857
12858 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12859 AssertReturn(service, E_FAIL);
12860 return service->autoCaptureDevicesForVM(this);
12861#else
12862 return S_OK;
12863#endif
12864}
12865
12866/**
12867 * Removes all machine filters from the USB proxy service and then calls
12868 * Host::detachAllUSBDevices().
12869 *
12870 * Called by Console from the VM process upon normal VM termination or by
12871 * SessionMachine::uninit() upon abnormal VM termination (from under the
12872 * Machine/SessionMachine lock).
12873 *
12874 * @note Locks what called methods lock.
12875 */
12876HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12877{
12878 LogFlowThisFunc(("\n"));
12879
12880#ifdef VBOX_WITH_USB
12881 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12882 AssertComRC(rc);
12883 NOREF(rc);
12884
12885 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12886 AssertReturn(service, E_FAIL);
12887 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12888#else
12889 NOREF(aDone);
12890 return S_OK;
12891#endif
12892}
12893
12894/**
12895 * @note Locks this object for writing.
12896 */
12897HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12898 ComPtr<IProgress> &aProgress)
12899{
12900 LogFlowThisFuncEnter();
12901
12902 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12903 /*
12904 * We don't assert below because it might happen that a non-direct session
12905 * informs us it is closed right after we've been uninitialized -- it's ok.
12906 */
12907
12908 /* get IInternalSessionControl interface */
12909 ComPtr<IInternalSessionControl> control(aSession);
12910
12911 ComAssertRet(!control.isNull(), E_INVALIDARG);
12912
12913 /* Creating a Progress object requires the VirtualBox lock, and
12914 * thus locking it here is required by the lock order rules. */
12915 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12916
12917 if (control == mData->mSession.mDirectControl)
12918 {
12919 /* The direct session is being normally closed by the client process
12920 * ----------------------------------------------------------------- */
12921
12922 /* go to the closing state (essential for all open*Session() calls and
12923 * for #checkForDeath()) */
12924 Assert(mData->mSession.mState == SessionState_Locked);
12925 mData->mSession.mState = SessionState_Unlocking;
12926
12927 /* set direct control to NULL to release the remote instance */
12928 mData->mSession.mDirectControl.setNull();
12929 LogFlowThisFunc(("Direct control is set to NULL\n"));
12930
12931 if (mData->mSession.mProgress)
12932 {
12933 /* finalize the progress, someone might wait if a frontend
12934 * closes the session before powering on the VM. */
12935 mData->mSession.mProgress->notifyComplete(E_FAIL,
12936 COM_IIDOF(ISession),
12937 getComponentName(),
12938 tr("The VM session was closed before any attempt to power it on"));
12939 mData->mSession.mProgress.setNull();
12940 }
12941
12942 /* Create the progress object the client will use to wait until
12943 * #checkForDeath() is called to uninitialize this session object after
12944 * it releases the IPC semaphore.
12945 * Note! Because we're "reusing" mProgress here, this must be a proxy
12946 * object just like for LaunchVMProcess. */
12947 Assert(mData->mSession.mProgress.isNull());
12948 ComObjPtr<ProgressProxy> progress;
12949 progress.createObject();
12950 ComPtr<IUnknown> pPeer(mPeer);
12951 progress->init(mParent, pPeer,
12952 Bstr(tr("Closing session")).raw(),
12953 FALSE /* aCancelable */);
12954 progress.queryInterfaceTo(aProgress.asOutParam());
12955 mData->mSession.mProgress = progress;
12956 }
12957 else
12958 {
12959 /* the remote session is being normally closed */
12960 Data::Session::RemoteControlList::iterator it =
12961 mData->mSession.mRemoteControls.begin();
12962 while (it != mData->mSession.mRemoteControls.end())
12963 {
12964 if (control == *it)
12965 break;
12966 ++it;
12967 }
12968 BOOL found = it != mData->mSession.mRemoteControls.end();
12969 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12970 E_INVALIDARG);
12971 // This MUST be erase(it), not remove(*it) as the latter triggers a
12972 // very nasty use after free due to the place where the value "lives".
12973 mData->mSession.mRemoteControls.erase(it);
12974 }
12975
12976 /* signal the client watcher thread, because the client is going away */
12977 mParent->i_updateClientWatcher();
12978
12979 LogFlowThisFuncLeave();
12980 return S_OK;
12981}
12982
12983/**
12984 * @note Locks this object for writing.
12985 */
12986HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
12987 com::Utf8Str &aStateFilePath)
12988{
12989 LogFlowThisFuncEnter();
12990
12991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12992
12993 AssertReturn( mData->mMachineState == MachineState_Paused
12994 && mConsoleTaskData.mLastState == MachineState_Null
12995 && mConsoleTaskData.strStateFilePath.isEmpty(),
12996 E_FAIL);
12997
12998 /* create a progress object to track operation completion */
12999 ComObjPtr<Progress> pProgress;
13000 pProgress.createObject();
13001 pProgress->init(i_getVirtualBox(),
13002 static_cast<IMachine *>(this) /* aInitiator */,
13003 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13004 FALSE /* aCancelable */);
13005
13006 /* stateFilePath is null when the machine is not running */
13007 if (mData->mMachineState == MachineState_Paused)
13008 i_composeSavedStateFilename(aStateFilePath);
13009
13010 /* fill in the console task data */
13011 mConsoleTaskData.mLastState = mData->mMachineState;
13012 mConsoleTaskData.strStateFilePath = aStateFilePath;
13013 mConsoleTaskData.mProgress = pProgress;
13014
13015 /* set the state to Saving (this is expected by Console::SaveState()) */
13016 i_setMachineState(MachineState_Saving);
13017
13018 pProgress.queryInterfaceTo(aProgress.asOutParam());
13019
13020 return S_OK;
13021}
13022
13023/**
13024 * @note Locks mParent + this object for writing.
13025 */
13026HRESULT SessionMachine::endSavingState(LONG aResult,
13027 const com::Utf8Str &aErrMsg)
13028{
13029 LogFlowThisFunc(("\n"));
13030
13031 /* endSavingState() need mParent lock */
13032 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13033
13034 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13035 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13036 && mConsoleTaskData.mLastState != MachineState_Null
13037 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13038 E_FAIL);
13039
13040 /*
13041 * On failure, set the state to the state we had when BeginSavingState()
13042 * was called (this is expected by Console::SaveState() and the associated
13043 * task). On success the VM process already changed the state to
13044 * MachineState_Saved, so no need to do anything.
13045 */
13046 if (FAILED(aResult))
13047 i_setMachineState(mConsoleTaskData.mLastState);
13048
13049 return i_endSavingState(aResult, aErrMsg);
13050}
13051
13052/**
13053 * @note Locks this object for writing.
13054 */
13055HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13056{
13057 LogFlowThisFunc(("\n"));
13058
13059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13060
13061 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13062 || mData->mMachineState == MachineState_Teleported
13063 || mData->mMachineState == MachineState_Aborted
13064 , E_FAIL); /** @todo setError. */
13065
13066 com::Utf8Str stateFilePathFull;
13067 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13068 if (RT_FAILURE(vrc))
13069 return setError(VBOX_E_FILE_ERROR,
13070 tr("Invalid saved state file path '%s' (%Rrc)"),
13071 aSavedStateFile.c_str(),
13072 vrc);
13073
13074 mSSData->strStateFilePath = stateFilePathFull;
13075
13076 /* The below i_setMachineState() will detect the state transition and will
13077 * update the settings file */
13078
13079 return i_setMachineState(MachineState_Saved);
13080}
13081
13082HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13083 std::vector<com::Utf8Str> &aValues,
13084 std::vector<LONG64> &aTimestamps,
13085 std::vector<com::Utf8Str> &aFlags)
13086{
13087 LogFlowThisFunc(("\n"));
13088
13089#ifdef VBOX_WITH_GUEST_PROPS
13090 using namespace guestProp;
13091
13092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13093
13094 size_t cEntries = mHWData->mGuestProperties.size();
13095 aNames.resize(cEntries);
13096 aValues.resize(cEntries);
13097 aTimestamps.resize(cEntries);
13098 aFlags.resize(cEntries);
13099
13100 size_t i = 0;
13101 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13102 it != mHWData->mGuestProperties.end();
13103 ++it, ++i)
13104 {
13105 char szFlags[MAX_FLAGS_LEN + 1];
13106 aNames[i] = it->first;
13107 aValues[i] = it->second.strValue;
13108 aTimestamps[i] = it->second.mTimestamp;
13109
13110 /* If it is NULL, keep it NULL. */
13111 if (it->second.mFlags)
13112 {
13113 writeFlags(it->second.mFlags, szFlags);
13114 aFlags[i] = szFlags;
13115 }
13116 else
13117 aFlags[i] = "";
13118 }
13119 return S_OK;
13120#else
13121 ReturnComNotImplemented();
13122#endif
13123}
13124
13125HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13126 const com::Utf8Str &aValue,
13127 LONG64 aTimestamp,
13128 const com::Utf8Str &aFlags)
13129{
13130 LogFlowThisFunc(("\n"));
13131
13132#ifdef VBOX_WITH_GUEST_PROPS
13133 using namespace guestProp;
13134
13135 try
13136 {
13137 /*
13138 * Convert input up front.
13139 */
13140 uint32_t fFlags = NILFLAG;
13141 if (aFlags.length())
13142 {
13143 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13144 AssertRCReturn(vrc, E_INVALIDARG);
13145 }
13146
13147 /*
13148 * Now grab the object lock, validate the state and do the update.
13149 */
13150
13151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13152
13153 switch (mData->mMachineState)
13154 {
13155 case MachineState_Paused:
13156 case MachineState_Running:
13157 case MachineState_Teleporting:
13158 case MachineState_TeleportingPausedVM:
13159 case MachineState_LiveSnapshotting:
13160 case MachineState_DeletingSnapshotOnline:
13161 case MachineState_DeletingSnapshotPaused:
13162 case MachineState_Saving:
13163 case MachineState_Stopping:
13164 break;
13165
13166 default:
13167 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13168 VBOX_E_INVALID_VM_STATE);
13169 }
13170
13171 i_setModified(IsModified_MachineData);
13172 mHWData.backup();
13173
13174 bool fDelete = !aValue.length();
13175 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13176 if (it != mHWData->mGuestProperties.end())
13177 {
13178 if (!fDelete)
13179 {
13180 it->second.strValue = aValue;
13181 it->second.mTimestamp = aTimestamp;
13182 it->second.mFlags = fFlags;
13183 }
13184 else
13185 mHWData->mGuestProperties.erase(it);
13186
13187 mData->mGuestPropertiesModified = TRUE;
13188 }
13189 else if (!fDelete)
13190 {
13191 HWData::GuestProperty prop;
13192 prop.strValue = aValue;
13193 prop.mTimestamp = aTimestamp;
13194 prop.mFlags = fFlags;
13195
13196 mHWData->mGuestProperties[aName] = prop;
13197 mData->mGuestPropertiesModified = TRUE;
13198 }
13199
13200 /*
13201 * Send a callback notification if appropriate
13202 */
13203 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13204 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13205 RTSTR_MAX,
13206 aName.c_str(),
13207 RTSTR_MAX, NULL)
13208 )
13209 {
13210 alock.release();
13211
13212 mParent->i_onGuestPropertyChange(mData->mUuid,
13213 Bstr(aName).raw(),
13214 Bstr(aValue).raw(),
13215 Bstr(aFlags).raw());
13216 }
13217 }
13218 catch (...)
13219 {
13220 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13221 }
13222 return S_OK;
13223#else
13224 ReturnComNotImplemented();
13225#endif
13226}
13227
13228
13229HRESULT SessionMachine::lockMedia()
13230{
13231 AutoMultiWriteLock2 alock(this->lockHandle(),
13232 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13233
13234 AssertReturn( mData->mMachineState == MachineState_Starting
13235 || mData->mMachineState == MachineState_Restoring
13236 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13237
13238 clearError();
13239 alock.release();
13240 return i_lockMedia();
13241}
13242
13243HRESULT SessionMachine::unlockMedia()
13244{
13245 HRESULT hrc = i_unlockMedia();
13246 return hrc;
13247}
13248
13249HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13250 ComPtr<IMediumAttachment> &aNewAttachment)
13251{
13252 // request the host lock first, since might be calling Host methods for getting host drives;
13253 // next, protect the media tree all the while we're in here, as well as our member variables
13254 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13255 this->lockHandle(),
13256 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13257
13258 IMediumAttachment *iAttach = aAttachment;
13259 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13260
13261 Bstr ctrlName;
13262 LONG lPort;
13263 LONG lDevice;
13264 bool fTempEject;
13265 {
13266 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13267
13268 /* Need to query the details first, as the IMediumAttachment reference
13269 * might be to the original settings, which we are going to change. */
13270 ctrlName = pAttach->i_getControllerName();
13271 lPort = pAttach->i_getPort();
13272 lDevice = pAttach->i_getDevice();
13273 fTempEject = pAttach->i_getTempEject();
13274 }
13275
13276 if (!fTempEject)
13277 {
13278 /* Remember previously mounted medium. The medium before taking the
13279 * backup is not necessarily the same thing. */
13280 ComObjPtr<Medium> oldmedium;
13281 oldmedium = pAttach->i_getMedium();
13282
13283 i_setModified(IsModified_Storage);
13284 mMediaData.backup();
13285
13286 // The backup operation makes the pAttach reference point to the
13287 // old settings. Re-get the correct reference.
13288 pAttach = i_findAttachment(mMediaData->mAttachments,
13289 ctrlName.raw(),
13290 lPort,
13291 lDevice);
13292
13293 {
13294 AutoCaller autoAttachCaller(this);
13295 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13296
13297 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13298 if (!oldmedium.isNull())
13299 oldmedium->i_removeBackReference(mData->mUuid);
13300
13301 pAttach->i_updateMedium(NULL);
13302 pAttach->i_updateEjected();
13303 }
13304
13305 i_setModified(IsModified_Storage);
13306 }
13307 else
13308 {
13309 {
13310 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13311 pAttach->i_updateEjected();
13312 }
13313 }
13314
13315 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13316
13317 return S_OK;
13318}
13319
13320// public methods only for internal purposes
13321/////////////////////////////////////////////////////////////////////////////
13322
13323#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13324/**
13325 * Called from the client watcher thread to check for expected or unexpected
13326 * death of the client process that has a direct session to this machine.
13327 *
13328 * On Win32 and on OS/2, this method is called only when we've got the
13329 * mutex (i.e. the client has either died or terminated normally) so it always
13330 * returns @c true (the client is terminated, the session machine is
13331 * uninitialized).
13332 *
13333 * On other platforms, the method returns @c true if the client process has
13334 * terminated normally or abnormally and the session machine was uninitialized,
13335 * and @c false if the client process is still alive.
13336 *
13337 * @note Locks this object for writing.
13338 */
13339bool SessionMachine::i_checkForDeath()
13340{
13341 Uninit::Reason reason;
13342 bool terminated = false;
13343
13344 /* Enclose autoCaller with a block because calling uninit() from under it
13345 * will deadlock. */
13346 {
13347 AutoCaller autoCaller(this);
13348 if (!autoCaller.isOk())
13349 {
13350 /* return true if not ready, to cause the client watcher to exclude
13351 * the corresponding session from watching */
13352 LogFlowThisFunc(("Already uninitialized!\n"));
13353 return true;
13354 }
13355
13356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13357
13358 /* Determine the reason of death: if the session state is Closing here,
13359 * everything is fine. Otherwise it means that the client did not call
13360 * OnSessionEnd() before it released the IPC semaphore. This may happen
13361 * either because the client process has abnormally terminated, or
13362 * because it simply forgot to call ISession::Close() before exiting. We
13363 * threat the latter also as an abnormal termination (see
13364 * Session::uninit() for details). */
13365 reason = mData->mSession.mState == SessionState_Unlocking ?
13366 Uninit::Normal :
13367 Uninit::Abnormal;
13368
13369 if (mClientToken)
13370 terminated = mClientToken->release();
13371 } /* AutoCaller block */
13372
13373 if (terminated)
13374 uninit(reason);
13375
13376 return terminated;
13377}
13378
13379void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13380{
13381 LogFlowThisFunc(("\n"));
13382
13383 strTokenId.setNull();
13384
13385 AutoCaller autoCaller(this);
13386 AssertComRCReturnVoid(autoCaller.rc());
13387
13388 Assert(mClientToken);
13389 if (mClientToken)
13390 mClientToken->getId(strTokenId);
13391}
13392#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13393IToken *SessionMachine::i_getToken()
13394{
13395 LogFlowThisFunc(("\n"));
13396
13397 AutoCaller autoCaller(this);
13398 AssertComRCReturn(autoCaller.rc(), NULL);
13399
13400 Assert(mClientToken);
13401 if (mClientToken)
13402 return mClientToken->getToken();
13403 else
13404 return NULL;
13405}
13406#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13407
13408Machine::ClientToken *SessionMachine::i_getClientToken()
13409{
13410 LogFlowThisFunc(("\n"));
13411
13412 AutoCaller autoCaller(this);
13413 AssertComRCReturn(autoCaller.rc(), NULL);
13414
13415 return mClientToken;
13416}
13417
13418
13419/**
13420 * @note Locks this object for reading.
13421 */
13422HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13423{
13424 LogFlowThisFunc(("\n"));
13425
13426 AutoCaller autoCaller(this);
13427 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13428
13429 ComPtr<IInternalSessionControl> directControl;
13430 {
13431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13432 directControl = mData->mSession.mDirectControl;
13433 }
13434
13435 /* ignore notifications sent after #OnSessionEnd() is called */
13436 if (!directControl)
13437 return S_OK;
13438
13439 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13440}
13441
13442/**
13443 * @note Locks this object for reading.
13444 */
13445HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13446 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13447 IN_BSTR aGuestIp, LONG aGuestPort)
13448{
13449 LogFlowThisFunc(("\n"));
13450
13451 AutoCaller autoCaller(this);
13452 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13453
13454 ComPtr<IInternalSessionControl> directControl;
13455 {
13456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13457 directControl = mData->mSession.mDirectControl;
13458 }
13459
13460 /* ignore notifications sent after #OnSessionEnd() is called */
13461 if (!directControl)
13462 return S_OK;
13463 /*
13464 * instead acting like callback we ask IVirtualBox deliver corresponding event
13465 */
13466
13467 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13468 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13469 return S_OK;
13470}
13471
13472/**
13473 * @note Locks this object for reading.
13474 */
13475HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13476{
13477 LogFlowThisFunc(("\n"));
13478
13479 AutoCaller autoCaller(this);
13480 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13481
13482 ComPtr<IInternalSessionControl> directControl;
13483 {
13484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13485 directControl = mData->mSession.mDirectControl;
13486 }
13487
13488 /* ignore notifications sent after #OnSessionEnd() is called */
13489 if (!directControl)
13490 return S_OK;
13491
13492 return directControl->OnSerialPortChange(serialPort);
13493}
13494
13495/**
13496 * @note Locks this object for reading.
13497 */
13498HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13499{
13500 LogFlowThisFunc(("\n"));
13501
13502 AutoCaller autoCaller(this);
13503 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13504
13505 ComPtr<IInternalSessionControl> directControl;
13506 {
13507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13508 directControl = mData->mSession.mDirectControl;
13509 }
13510
13511 /* ignore notifications sent after #OnSessionEnd() is called */
13512 if (!directControl)
13513 return S_OK;
13514
13515 return directControl->OnParallelPortChange(parallelPort);
13516}
13517
13518/**
13519 * @note Locks this object for reading.
13520 */
13521HRESULT SessionMachine::i_onStorageControllerChange()
13522{
13523 LogFlowThisFunc(("\n"));
13524
13525 AutoCaller autoCaller(this);
13526 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13527
13528 ComPtr<IInternalSessionControl> directControl;
13529 {
13530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13531 directControl = mData->mSession.mDirectControl;
13532 }
13533
13534 /* ignore notifications sent after #OnSessionEnd() is called */
13535 if (!directControl)
13536 return S_OK;
13537
13538 return directControl->OnStorageControllerChange();
13539}
13540
13541/**
13542 * @note Locks this object for reading.
13543 */
13544HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13545{
13546 LogFlowThisFunc(("\n"));
13547
13548 AutoCaller autoCaller(this);
13549 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13550
13551 ComPtr<IInternalSessionControl> directControl;
13552 {
13553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13554 directControl = mData->mSession.mDirectControl;
13555 }
13556
13557 /* ignore notifications sent after #OnSessionEnd() is called */
13558 if (!directControl)
13559 return S_OK;
13560
13561 return directControl->OnMediumChange(aAttachment, aForce);
13562}
13563
13564/**
13565 * @note Locks this object for reading.
13566 */
13567HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13568{
13569 LogFlowThisFunc(("\n"));
13570
13571 AutoCaller autoCaller(this);
13572 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13573
13574 ComPtr<IInternalSessionControl> directControl;
13575 {
13576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13577 directControl = mData->mSession.mDirectControl;
13578 }
13579
13580 /* ignore notifications sent after #OnSessionEnd() is called */
13581 if (!directControl)
13582 return S_OK;
13583
13584 return directControl->OnCPUChange(aCPU, aRemove);
13585}
13586
13587HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13588{
13589 LogFlowThisFunc(("\n"));
13590
13591 AutoCaller autoCaller(this);
13592 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13593
13594 ComPtr<IInternalSessionControl> directControl;
13595 {
13596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13597 directControl = mData->mSession.mDirectControl;
13598 }
13599
13600 /* ignore notifications sent after #OnSessionEnd() is called */
13601 if (!directControl)
13602 return S_OK;
13603
13604 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13605}
13606
13607/**
13608 * @note Locks this object for reading.
13609 */
13610HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13611{
13612 LogFlowThisFunc(("\n"));
13613
13614 AutoCaller autoCaller(this);
13615 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13616
13617 ComPtr<IInternalSessionControl> directControl;
13618 {
13619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13620 directControl = mData->mSession.mDirectControl;
13621 }
13622
13623 /* ignore notifications sent after #OnSessionEnd() is called */
13624 if (!directControl)
13625 return S_OK;
13626
13627 return directControl->OnVRDEServerChange(aRestart);
13628}
13629
13630/**
13631 * @note Locks this object for reading.
13632 */
13633HRESULT SessionMachine::i_onVideoCaptureChange()
13634{
13635 LogFlowThisFunc(("\n"));
13636
13637 AutoCaller autoCaller(this);
13638 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13639
13640 ComPtr<IInternalSessionControl> directControl;
13641 {
13642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13643 directControl = mData->mSession.mDirectControl;
13644 }
13645
13646 /* ignore notifications sent after #OnSessionEnd() is called */
13647 if (!directControl)
13648 return S_OK;
13649
13650 return directControl->OnVideoCaptureChange();
13651}
13652
13653/**
13654 * @note Locks this object for reading.
13655 */
13656HRESULT SessionMachine::i_onUSBControllerChange()
13657{
13658 LogFlowThisFunc(("\n"));
13659
13660 AutoCaller autoCaller(this);
13661 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13662
13663 ComPtr<IInternalSessionControl> directControl;
13664 {
13665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13666 directControl = mData->mSession.mDirectControl;
13667 }
13668
13669 /* ignore notifications sent after #OnSessionEnd() is called */
13670 if (!directControl)
13671 return S_OK;
13672
13673 return directControl->OnUSBControllerChange();
13674}
13675
13676/**
13677 * @note Locks this object for reading.
13678 */
13679HRESULT SessionMachine::i_onSharedFolderChange()
13680{
13681 LogFlowThisFunc(("\n"));
13682
13683 AutoCaller autoCaller(this);
13684 AssertComRCReturnRC(autoCaller.rc());
13685
13686 ComPtr<IInternalSessionControl> directControl;
13687 {
13688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13689 directControl = mData->mSession.mDirectControl;
13690 }
13691
13692 /* ignore notifications sent after #OnSessionEnd() is called */
13693 if (!directControl)
13694 return S_OK;
13695
13696 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13697}
13698
13699/**
13700 * @note Locks this object for reading.
13701 */
13702HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13703{
13704 LogFlowThisFunc(("\n"));
13705
13706 AutoCaller autoCaller(this);
13707 AssertComRCReturnRC(autoCaller.rc());
13708
13709 ComPtr<IInternalSessionControl> directControl;
13710 {
13711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13712 directControl = mData->mSession.mDirectControl;
13713 }
13714
13715 /* ignore notifications sent after #OnSessionEnd() is called */
13716 if (!directControl)
13717 return S_OK;
13718
13719 return directControl->OnClipboardModeChange(aClipboardMode);
13720}
13721
13722/**
13723 * @note Locks this object for reading.
13724 */
13725HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13726{
13727 LogFlowThisFunc(("\n"));
13728
13729 AutoCaller autoCaller(this);
13730 AssertComRCReturnRC(autoCaller.rc());
13731
13732 ComPtr<IInternalSessionControl> directControl;
13733 {
13734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13735 directControl = mData->mSession.mDirectControl;
13736 }
13737
13738 /* ignore notifications sent after #OnSessionEnd() is called */
13739 if (!directControl)
13740 return S_OK;
13741
13742 return directControl->OnDnDModeChange(aDnDMode);
13743}
13744
13745/**
13746 * @note Locks this object for reading.
13747 */
13748HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13749{
13750 LogFlowThisFunc(("\n"));
13751
13752 AutoCaller autoCaller(this);
13753 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13754
13755 ComPtr<IInternalSessionControl> directControl;
13756 {
13757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13758 directControl = mData->mSession.mDirectControl;
13759 }
13760
13761 /* ignore notifications sent after #OnSessionEnd() is called */
13762 if (!directControl)
13763 return S_OK;
13764
13765 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13766}
13767
13768/**
13769 * @note Locks this object for reading.
13770 */
13771HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13772{
13773 LogFlowThisFunc(("\n"));
13774
13775 AutoCaller autoCaller(this);
13776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13777
13778 ComPtr<IInternalSessionControl> directControl;
13779 {
13780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13781 directControl = mData->mSession.mDirectControl;
13782 }
13783
13784 /* ignore notifications sent after #OnSessionEnd() is called */
13785 if (!directControl)
13786 return S_OK;
13787
13788 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13789}
13790
13791/**
13792 * Returns @c true if this machine's USB controller reports it has a matching
13793 * filter for the given USB device and @c false otherwise.
13794 *
13795 * @note locks this object for reading.
13796 */
13797bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13798{
13799 AutoCaller autoCaller(this);
13800 /* silently return if not ready -- this method may be called after the
13801 * direct machine session has been called */
13802 if (!autoCaller.isOk())
13803 return false;
13804
13805#ifdef VBOX_WITH_USB
13806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13807
13808 switch (mData->mMachineState)
13809 {
13810 case MachineState_Starting:
13811 case MachineState_Restoring:
13812 case MachineState_TeleportingIn:
13813 case MachineState_Paused:
13814 case MachineState_Running:
13815 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13816 * elsewhere... */
13817 alock.release();
13818 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13819 default: break;
13820 }
13821#else
13822 NOREF(aDevice);
13823 NOREF(aMaskedIfs);
13824#endif
13825 return false;
13826}
13827
13828/**
13829 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13830 */
13831HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13832 IVirtualBoxErrorInfo *aError,
13833 ULONG aMaskedIfs)
13834{
13835 LogFlowThisFunc(("\n"));
13836
13837 AutoCaller autoCaller(this);
13838
13839 /* This notification may happen after the machine object has been
13840 * uninitialized (the session was closed), so don't assert. */
13841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13842
13843 ComPtr<IInternalSessionControl> directControl;
13844 {
13845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13846 directControl = mData->mSession.mDirectControl;
13847 }
13848
13849 /* fail on notifications sent after #OnSessionEnd() is called, it is
13850 * expected by the caller */
13851 if (!directControl)
13852 return E_FAIL;
13853
13854 /* No locks should be held at this point. */
13855 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13856 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13857
13858 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13859}
13860
13861/**
13862 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13863 */
13864HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13865 IVirtualBoxErrorInfo *aError)
13866{
13867 LogFlowThisFunc(("\n"));
13868
13869 AutoCaller autoCaller(this);
13870
13871 /* This notification may happen after the machine object has been
13872 * uninitialized (the session was closed), so don't assert. */
13873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13874
13875 ComPtr<IInternalSessionControl> directControl;
13876 {
13877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13878 directControl = mData->mSession.mDirectControl;
13879 }
13880
13881 /* fail on notifications sent after #OnSessionEnd() is called, it is
13882 * expected by the caller */
13883 if (!directControl)
13884 return E_FAIL;
13885
13886 /* No locks should be held at this point. */
13887 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13888 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13889
13890 return directControl->OnUSBDeviceDetach(aId, aError);
13891}
13892
13893// protected methods
13894/////////////////////////////////////////////////////////////////////////////
13895
13896/**
13897 * Helper method to finalize saving the state.
13898 *
13899 * @note Must be called from under this object's lock.
13900 *
13901 * @param aRc S_OK if the snapshot has been taken successfully
13902 * @param aErrMsg human readable error message for failure
13903 *
13904 * @note Locks mParent + this objects for writing.
13905 */
13906HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13907{
13908 LogFlowThisFuncEnter();
13909
13910 AutoCaller autoCaller(this);
13911 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13912
13913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13914
13915 HRESULT rc = S_OK;
13916
13917 if (SUCCEEDED(aRc))
13918 {
13919 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13920
13921 /* save all VM settings */
13922 rc = i_saveSettings(NULL);
13923 // no need to check whether VirtualBox.xml needs saving also since
13924 // we can't have a name change pending at this point
13925 }
13926 else
13927 {
13928 // delete the saved state file (it might have been already created);
13929 // we need not check whether this is shared with a snapshot here because
13930 // we certainly created this saved state file here anew
13931 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13932 }
13933
13934 /* notify the progress object about operation completion */
13935 Assert(mConsoleTaskData.mProgress);
13936 if (SUCCEEDED(aRc))
13937 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13938 else
13939 {
13940 if (aErrMsg.length())
13941 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13942 COM_IIDOF(ISession),
13943 getComponentName(),
13944 aErrMsg.c_str());
13945 else
13946 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13947 }
13948
13949 /* clear out the temporary saved state data */
13950 mConsoleTaskData.mLastState = MachineState_Null;
13951 mConsoleTaskData.strStateFilePath.setNull();
13952 mConsoleTaskData.mProgress.setNull();
13953
13954 LogFlowThisFuncLeave();
13955 return rc;
13956}
13957
13958/**
13959 * Deletes the given file if it is no longer in use by either the current machine state
13960 * (if the machine is "saved") or any of the machine's snapshots.
13961 *
13962 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13963 * but is different for each SnapshotMachine. When calling this, the order of calling this
13964 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13965 * is therefore critical. I know, it's all rather messy.
13966 *
13967 * @param strStateFile
13968 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13969 * the test for whether the saved state file is in use.
13970 */
13971void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13972 Snapshot *pSnapshotToIgnore)
13973{
13974 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13975 if ( (strStateFile.isNotEmpty())
13976 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13977 )
13978 // ... and it must also not be shared with other snapshots
13979 if ( !mData->mFirstSnapshot
13980 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13981 // this checks the SnapshotMachine's state file paths
13982 )
13983 RTFileDelete(strStateFile.c_str());
13984}
13985
13986/**
13987 * Locks the attached media.
13988 *
13989 * All attached hard disks are locked for writing and DVD/floppy are locked for
13990 * reading. Parents of attached hard disks (if any) are locked for reading.
13991 *
13992 * This method also performs accessibility check of all media it locks: if some
13993 * media is inaccessible, the method will return a failure and a bunch of
13994 * extended error info objects per each inaccessible medium.
13995 *
13996 * Note that this method is atomic: if it returns a success, all media are
13997 * locked as described above; on failure no media is locked at all (all
13998 * succeeded individual locks will be undone).
13999 *
14000 * The caller is responsible for doing the necessary state sanity checks.
14001 *
14002 * The locks made by this method must be undone by calling #unlockMedia() when
14003 * no more needed.
14004 */
14005HRESULT SessionMachine::i_lockMedia()
14006{
14007 AutoCaller autoCaller(this);
14008 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14009
14010 AutoMultiWriteLock2 alock(this->lockHandle(),
14011 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14012
14013 /* bail out if trying to lock things with already set up locking */
14014 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14015
14016 MultiResult mrc(S_OK);
14017
14018 /* Collect locking information for all medium objects attached to the VM. */
14019 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14020 it != mMediaData->mAttachments.end();
14021 ++it)
14022 {
14023 MediumAttachment* pAtt = *it;
14024 DeviceType_T devType = pAtt->i_getType();
14025 Medium *pMedium = pAtt->i_getMedium();
14026
14027 MediumLockList *pMediumLockList(new MediumLockList());
14028 // There can be attachments without a medium (floppy/dvd), and thus
14029 // it's impossible to create a medium lock list. It still makes sense
14030 // to have the empty medium lock list in the map in case a medium is
14031 // attached later.
14032 if (pMedium != NULL)
14033 {
14034 MediumType_T mediumType = pMedium->i_getType();
14035 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14036 || mediumType == MediumType_Shareable;
14037 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14038
14039 alock.release();
14040 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14041 !fIsReadOnlyLock /* fMediumLockWrite */,
14042 NULL,
14043 *pMediumLockList);
14044 alock.acquire();
14045 if (FAILED(mrc))
14046 {
14047 delete pMediumLockList;
14048 mData->mSession.mLockedMedia.Clear();
14049 break;
14050 }
14051 }
14052
14053 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14054 if (FAILED(rc))
14055 {
14056 mData->mSession.mLockedMedia.Clear();
14057 mrc = setError(rc,
14058 tr("Collecting locking information for all attached media failed"));
14059 break;
14060 }
14061 }
14062
14063 if (SUCCEEDED(mrc))
14064 {
14065 /* Now lock all media. If this fails, nothing is locked. */
14066 alock.release();
14067 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14068 alock.acquire();
14069 if (FAILED(rc))
14070 {
14071 mrc = setError(rc,
14072 tr("Locking of attached media failed"));
14073 }
14074 }
14075
14076 return mrc;
14077}
14078
14079/**
14080 * Undoes the locks made by by #lockMedia().
14081 */
14082HRESULT SessionMachine::i_unlockMedia()
14083{
14084 AutoCaller autoCaller(this);
14085 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14086
14087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14088
14089 /* we may be holding important error info on the current thread;
14090 * preserve it */
14091 ErrorInfoKeeper eik;
14092
14093 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14094 AssertComRC(rc);
14095 return rc;
14096}
14097
14098/**
14099 * Helper to change the machine state (reimplementation).
14100 *
14101 * @note Locks this object for writing.
14102 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14103 * it can cause crashes in random places due to unexpectedly committing
14104 * the current settings. The caller is responsible for that. The call
14105 * to saveStateSettings is fine, because this method does not commit.
14106 */
14107HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14108{
14109 LogFlowThisFuncEnter();
14110 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14111
14112 AutoCaller autoCaller(this);
14113 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14114
14115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14116
14117 MachineState_T oldMachineState = mData->mMachineState;
14118
14119 AssertMsgReturn(oldMachineState != aMachineState,
14120 ("oldMachineState=%s, aMachineState=%s\n",
14121 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14122 E_FAIL);
14123
14124 HRESULT rc = S_OK;
14125
14126 int stsFlags = 0;
14127 bool deleteSavedState = false;
14128
14129 /* detect some state transitions */
14130
14131 if ( ( oldMachineState == MachineState_Saved
14132 && aMachineState == MachineState_Restoring)
14133 || ( ( oldMachineState == MachineState_PoweredOff
14134 || oldMachineState == MachineState_Teleported
14135 || oldMachineState == MachineState_Aborted
14136 )
14137 && ( aMachineState == MachineState_TeleportingIn
14138 || aMachineState == MachineState_Starting
14139 )
14140 )
14141 )
14142 {
14143 /* The EMT thread is about to start */
14144
14145 /* Nothing to do here for now... */
14146
14147 /// @todo NEWMEDIA don't let mDVDDrive and other children
14148 /// change anything when in the Starting/Restoring state
14149 }
14150 else if ( ( oldMachineState == MachineState_Running
14151 || oldMachineState == MachineState_Paused
14152 || oldMachineState == MachineState_Teleporting
14153 || oldMachineState == MachineState_LiveSnapshotting
14154 || oldMachineState == MachineState_Stuck
14155 || oldMachineState == MachineState_Starting
14156 || oldMachineState == MachineState_Stopping
14157 || oldMachineState == MachineState_Saving
14158 || oldMachineState == MachineState_Restoring
14159 || oldMachineState == MachineState_TeleportingPausedVM
14160 || oldMachineState == MachineState_TeleportingIn
14161 )
14162 && ( aMachineState == MachineState_PoweredOff
14163 || aMachineState == MachineState_Saved
14164 || aMachineState == MachineState_Teleported
14165 || aMachineState == MachineState_Aborted
14166 )
14167 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14168 * snapshot */
14169 && ( mConsoleTaskData.mSnapshot.isNull()
14170 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14171 )
14172 )
14173 {
14174 /* The EMT thread has just stopped, unlock attached media. Note that as
14175 * opposed to locking that is done from Console, we do unlocking here
14176 * because the VM process may have aborted before having a chance to
14177 * properly unlock all media it locked. */
14178
14179 unlockMedia();
14180 }
14181
14182 if (oldMachineState == MachineState_Restoring)
14183 {
14184 if (aMachineState != MachineState_Saved)
14185 {
14186 /*
14187 * delete the saved state file once the machine has finished
14188 * restoring from it (note that Console sets the state from
14189 * Restoring to Saved if the VM couldn't restore successfully,
14190 * to give the user an ability to fix an error and retry --
14191 * we keep the saved state file in this case)
14192 */
14193 deleteSavedState = true;
14194 }
14195 }
14196 else if ( oldMachineState == MachineState_Saved
14197 && ( aMachineState == MachineState_PoweredOff
14198 || aMachineState == MachineState_Aborted
14199 || aMachineState == MachineState_Teleported
14200 )
14201 )
14202 {
14203 /*
14204 * delete the saved state after Console::ForgetSavedState() is called
14205 * or if the VM process (owning a direct VM session) crashed while the
14206 * VM was Saved
14207 */
14208
14209 /// @todo (dmik)
14210 // Not sure that deleting the saved state file just because of the
14211 // client death before it attempted to restore the VM is a good
14212 // thing. But when it crashes we need to go to the Aborted state
14213 // which cannot have the saved state file associated... The only
14214 // way to fix this is to make the Aborted condition not a VM state
14215 // but a bool flag: i.e., when a crash occurs, set it to true and
14216 // change the state to PoweredOff or Saved depending on the
14217 // saved state presence.
14218
14219 deleteSavedState = true;
14220 mData->mCurrentStateModified = TRUE;
14221 stsFlags |= SaveSTS_CurStateModified;
14222 }
14223
14224 if ( aMachineState == MachineState_Starting
14225 || aMachineState == MachineState_Restoring
14226 || aMachineState == MachineState_TeleportingIn
14227 )
14228 {
14229 /* set the current state modified flag to indicate that the current
14230 * state is no more identical to the state in the
14231 * current snapshot */
14232 if (!mData->mCurrentSnapshot.isNull())
14233 {
14234 mData->mCurrentStateModified = TRUE;
14235 stsFlags |= SaveSTS_CurStateModified;
14236 }
14237 }
14238
14239 if (deleteSavedState)
14240 {
14241 if (mRemoveSavedState)
14242 {
14243 Assert(!mSSData->strStateFilePath.isEmpty());
14244
14245 // it is safe to delete the saved state file if ...
14246 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14247 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14248 // ... none of the snapshots share the saved state file
14249 )
14250 RTFileDelete(mSSData->strStateFilePath.c_str());
14251 }
14252
14253 mSSData->strStateFilePath.setNull();
14254 stsFlags |= SaveSTS_StateFilePath;
14255 }
14256
14257 /* redirect to the underlying peer machine */
14258 mPeer->i_setMachineState(aMachineState);
14259
14260 if ( aMachineState == MachineState_PoweredOff
14261 || aMachineState == MachineState_Teleported
14262 || aMachineState == MachineState_Aborted
14263 || aMachineState == MachineState_Saved)
14264 {
14265 /* the machine has stopped execution
14266 * (or the saved state file was adopted) */
14267 stsFlags |= SaveSTS_StateTimeStamp;
14268 }
14269
14270 if ( ( oldMachineState == MachineState_PoweredOff
14271 || oldMachineState == MachineState_Aborted
14272 || oldMachineState == MachineState_Teleported
14273 )
14274 && aMachineState == MachineState_Saved)
14275 {
14276 /* the saved state file was adopted */
14277 Assert(!mSSData->strStateFilePath.isEmpty());
14278 stsFlags |= SaveSTS_StateFilePath;
14279 }
14280
14281#ifdef VBOX_WITH_GUEST_PROPS
14282 if ( aMachineState == MachineState_PoweredOff
14283 || aMachineState == MachineState_Aborted
14284 || aMachineState == MachineState_Teleported)
14285 {
14286 /* Make sure any transient guest properties get removed from the
14287 * property store on shutdown. */
14288
14289 HWData::GuestPropertyMap::const_iterator it;
14290 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14291 if (!fNeedsSaving)
14292 for (it = mHWData->mGuestProperties.begin();
14293 it != mHWData->mGuestProperties.end(); ++it)
14294 if ( (it->second.mFlags & guestProp::TRANSIENT)
14295 || (it->second.mFlags & guestProp::TRANSRESET))
14296 {
14297 fNeedsSaving = true;
14298 break;
14299 }
14300 if (fNeedsSaving)
14301 {
14302 mData->mCurrentStateModified = TRUE;
14303 stsFlags |= SaveSTS_CurStateModified;
14304 }
14305 }
14306#endif
14307
14308 rc = i_saveStateSettings(stsFlags);
14309
14310 if ( ( oldMachineState != MachineState_PoweredOff
14311 && oldMachineState != MachineState_Aborted
14312 && oldMachineState != MachineState_Teleported
14313 )
14314 && ( aMachineState == MachineState_PoweredOff
14315 || aMachineState == MachineState_Aborted
14316 || aMachineState == MachineState_Teleported
14317 )
14318 )
14319 {
14320 /* we've been shut down for any reason */
14321 /* no special action so far */
14322 }
14323
14324 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14325 LogFlowThisFuncLeave();
14326 return rc;
14327}
14328
14329/**
14330 * Sends the current machine state value to the VM process.
14331 *
14332 * @note Locks this object for reading, then calls a client process.
14333 */
14334HRESULT SessionMachine::i_updateMachineStateOnClient()
14335{
14336 AutoCaller autoCaller(this);
14337 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14338
14339 ComPtr<IInternalSessionControl> directControl;
14340 {
14341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14342 AssertReturn(!!mData, E_FAIL);
14343 directControl = mData->mSession.mDirectControl;
14344
14345 /* directControl may be already set to NULL here in #OnSessionEnd()
14346 * called too early by the direct session process while there is still
14347 * some operation (like deleting the snapshot) in progress. The client
14348 * process in this case is waiting inside Session::close() for the
14349 * "end session" process object to complete, while #uninit() called by
14350 * #checkForDeath() on the Watcher thread is waiting for the pending
14351 * operation to complete. For now, we accept this inconsistent behavior
14352 * and simply do nothing here. */
14353
14354 if (mData->mSession.mState == SessionState_Unlocking)
14355 return S_OK;
14356
14357 AssertReturn(!directControl.isNull(), E_FAIL);
14358 }
14359
14360 return directControl->UpdateMachineState(mData->mMachineState);
14361}
14362
14363HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14364{
14365 NOREF(aRemove);
14366 ReturnComNotImplemented();
14367}
14368
14369HRESULT Machine::updateState(MachineState_T aState)
14370{
14371 NOREF(aState);
14372 ReturnComNotImplemented();
14373}
14374
14375HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14376{
14377 NOREF(aProgress);
14378 ReturnComNotImplemented();
14379}
14380
14381HRESULT Machine::endPowerUp(LONG aResult)
14382{
14383 NOREF(aResult);
14384 ReturnComNotImplemented();
14385}
14386
14387HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14388{
14389 NOREF(aProgress);
14390 ReturnComNotImplemented();
14391}
14392
14393HRESULT Machine::endPoweringDown(LONG aResult,
14394 const com::Utf8Str &aErrMsg)
14395{
14396 NOREF(aResult);
14397 NOREF(aErrMsg);
14398 ReturnComNotImplemented();
14399}
14400
14401HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14402 BOOL *aMatched,
14403 ULONG *aMaskedInterfaces)
14404{
14405 NOREF(aDevice);
14406 NOREF(aMatched);
14407 NOREF(aMaskedInterfaces);
14408 ReturnComNotImplemented();
14409
14410}
14411
14412HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14413{
14414 NOREF(aId);
14415 ReturnComNotImplemented();
14416}
14417
14418HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14419 BOOL aDone)
14420{
14421 NOREF(aId);
14422 NOREF(aDone);
14423 ReturnComNotImplemented();
14424}
14425
14426HRESULT Machine::autoCaptureUSBDevices()
14427{
14428 ReturnComNotImplemented();
14429}
14430
14431HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14432{
14433 NOREF(aDone);
14434 ReturnComNotImplemented();
14435}
14436
14437HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14438 ComPtr<IProgress> &aProgress)
14439{
14440 NOREF(aSession);
14441 NOREF(aProgress);
14442 ReturnComNotImplemented();
14443}
14444
14445HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14446 com::Utf8Str &aStateFilePath)
14447{
14448 NOREF(aProgress);
14449 NOREF(aStateFilePath);
14450 ReturnComNotImplemented();
14451}
14452
14453HRESULT Machine::endSavingState(LONG aResult,
14454 const com::Utf8Str &aErrMsg)
14455{
14456 NOREF(aResult);
14457 NOREF(aErrMsg);
14458 ReturnComNotImplemented();
14459}
14460
14461HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14462{
14463 NOREF(aSavedStateFile);
14464 ReturnComNotImplemented();
14465}
14466
14467HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14468 const com::Utf8Str &aName,
14469 const com::Utf8Str &aDescription,
14470 const ComPtr<IProgress> &aConsoleProgress,
14471 BOOL aFTakingSnapshotOnline,
14472 com::Utf8Str &aStateFilePath)
14473{
14474 NOREF(aInitiator);
14475 NOREF(aName);
14476 NOREF(aDescription);
14477 NOREF(aConsoleProgress);
14478 NOREF(aFTakingSnapshotOnline);
14479 NOREF(aStateFilePath);
14480 ReturnComNotImplemented();
14481}
14482
14483HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14484{
14485 NOREF(aSuccess);
14486 ReturnComNotImplemented();
14487}
14488
14489HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14490 const com::Guid &aStartId,
14491 const com::Guid &aEndId,
14492 BOOL aDeleteAllChildren,
14493 MachineState_T *aMachineState,
14494 ComPtr<IProgress> &aProgress)
14495{
14496 NOREF(aInitiator);
14497 NOREF(aStartId);
14498 NOREF(aEndId);
14499 NOREF(aDeleteAllChildren);
14500 NOREF(aMachineState);
14501 NOREF(aProgress);
14502 ReturnComNotImplemented();
14503}
14504
14505HRESULT Machine::finishOnlineMergeMedium()
14506{
14507 ReturnComNotImplemented();
14508}
14509
14510HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14511 const ComPtr<ISnapshot> &aSnapshot,
14512 MachineState_T *aMachineState,
14513 ComPtr<IProgress> &aProgress)
14514{
14515 NOREF(aInitiator);
14516 NOREF(aSnapshot);
14517 NOREF(aMachineState);
14518 NOREF(aProgress);
14519 ReturnComNotImplemented();
14520}
14521
14522HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14523 std::vector<com::Utf8Str> &aValues,
14524 std::vector<LONG64> &aTimestamps,
14525 std::vector<com::Utf8Str> &aFlags)
14526{
14527 NOREF(aNames);
14528 NOREF(aValues);
14529 NOREF(aTimestamps);
14530 NOREF(aFlags);
14531 ReturnComNotImplemented();
14532}
14533
14534HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14535 const com::Utf8Str &aValue,
14536 LONG64 aTimestamp,
14537 const com::Utf8Str &aFlags)
14538{
14539 NOREF(aName);
14540 NOREF(aValue);
14541 NOREF(aTimestamp);
14542 NOREF(aFlags);
14543 ReturnComNotImplemented();
14544}
14545
14546HRESULT Machine::lockMedia()
14547{
14548 ReturnComNotImplemented();
14549}
14550
14551HRESULT Machine::unlockMedia()
14552{
14553 ReturnComNotImplemented();
14554}
14555
14556HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14557 ComPtr<IMediumAttachment> &aNewAttachment)
14558{
14559 NOREF(aAttachment);
14560 NOREF(aNewAttachment);
14561 ReturnComNotImplemented();
14562}
14563
14564HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14565 ULONG aCpuUser,
14566 ULONG aCpuKernel,
14567 ULONG aCpuIdle,
14568 ULONG aMemTotal,
14569 ULONG aMemFree,
14570 ULONG aMemBalloon,
14571 ULONG aMemShared,
14572 ULONG aMemCache,
14573 ULONG aPagedTotal,
14574 ULONG aMemAllocTotal,
14575 ULONG aMemFreeTotal,
14576 ULONG aMemBalloonTotal,
14577 ULONG aMemSharedTotal,
14578 ULONG aVmNetRx,
14579 ULONG aVmNetTx)
14580{
14581 NOREF(aValidStats);
14582 NOREF(aCpuUser);
14583 NOREF(aCpuKernel);
14584 NOREF(aCpuIdle);
14585 NOREF(aMemTotal);
14586 NOREF(aMemFree);
14587 NOREF(aMemBalloon);
14588 NOREF(aMemShared);
14589 NOREF(aMemCache);
14590 NOREF(aPagedTotal);
14591 NOREF(aMemAllocTotal);
14592 NOREF(aMemFreeTotal);
14593 NOREF(aMemBalloonTotal);
14594 NOREF(aMemSharedTotal);
14595 NOREF(aVmNetRx);
14596 NOREF(aVmNetTx);
14597 ReturnComNotImplemented();
14598}
Note: See TracBrowser for help on using the repository browser.

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