VirtualBox

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

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

MachineImpl: return code mismatch

  • 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 52933 2014-10-02 13:44:24Z 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 /* "emergencystop" doesn't need the session, so skip the checks/interface
3521 * retrieval. This code doesn't quite fit in here, but introducing a
3522 * special API method would be even more effort, and would require explicit
3523 * support by every API client. It's better to hide the feature a bit. */
3524 if (strFrontend != "emergencystop")
3525 CheckComArgNotNull(aSession);
3526
3527 HRESULT rc = S_OK;
3528 if (strFrontend.isEmpty())
3529 {
3530 Bstr bstrFrontend;
3531 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3532 if (FAILED(rc))
3533 return rc;
3534 strFrontend = bstrFrontend;
3535 if (strFrontend.isEmpty())
3536 {
3537 ComPtr<ISystemProperties> systemProperties;
3538 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3539 if (FAILED(rc))
3540 return rc;
3541 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3542 if (FAILED(rc))
3543 return rc;
3544 strFrontend = bstrFrontend;
3545 }
3546 /* paranoia - emergencystop is not a valid default */
3547 if (strFrontend == "emergencystop")
3548 strFrontend = Utf8Str::Empty;
3549 }
3550 /* default frontend: Qt GUI */
3551 if (strFrontend.isEmpty())
3552 strFrontend = "GUI/Qt";
3553
3554 if (strFrontend != "emergencystop")
3555 {
3556 /* check the session state */
3557 SessionState_T state;
3558 rc = aSession->COMGETTER(State)(&state);
3559 if (FAILED(rc))
3560 return rc;
3561
3562 if (state != SessionState_Unlocked)
3563 return setError(VBOX_E_INVALID_OBJECT_STATE,
3564 tr("The given session is busy"));
3565
3566 /* get the IInternalSessionControl interface */
3567 ComPtr<IInternalSessionControl> control(aSession);
3568 ComAssertMsgRet(!control.isNull(),
3569 ("No IInternalSessionControl interface"),
3570 E_INVALIDARG);
3571
3572 /* get the teleporter enable state for the progress object init. */
3573 BOOL fTeleporterEnabled;
3574 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3575 if (FAILED(rc))
3576 return rc;
3577
3578 /* create a progress object */
3579 ComObjPtr<ProgressProxy> progress;
3580 progress.createObject();
3581 rc = progress->init(mParent,
3582 static_cast<IMachine*>(this),
3583 Bstr(tr("Starting VM")).raw(),
3584 TRUE /* aCancelable */,
3585 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3586 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3587 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3588 2 /* uFirstOperationWeight */,
3589 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3590
3591 if (SUCCEEDED(rc))
3592 {
3593 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3594 if (SUCCEEDED(rc))
3595 {
3596 aProgress = progress;
3597
3598 /* signal the client watcher thread */
3599 mParent->i_updateClientWatcher();
3600
3601 /* fire an event */
3602 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3603 }
3604 }
3605 }
3606 else
3607 {
3608 /* no progress object - either instant success or failure */
3609 aProgress = NULL;
3610
3611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3612
3613 if (mData->mSession.mState != SessionState_Locked)
3614 return setError(VBOX_E_INVALID_OBJECT_STATE,
3615 tr("The machine '%s' is not locked by a session"),
3616 mUserData->s.strName.c_str());
3617
3618 /* must have a VM process associated - do not kill normal API clients
3619 * with an open session */
3620 if (!Global::IsOnline(mData->mMachineState))
3621 return setError(VBOX_E_INVALID_OBJECT_STATE,
3622 tr("The machine '%s' does not have a VM process"),
3623 mUserData->s.strName.c_str());
3624
3625 /* forcibly terminate the VM process */
3626 if (mData->mSession.mPID != NIL_RTPROCESS)
3627 RTProcTerminate(mData->mSession.mPID);
3628
3629 /* signal the client watcher thread, as most likely the client has
3630 * been terminated */
3631 mParent->i_updateClientWatcher();
3632 }
3633
3634 return rc;
3635}
3636
3637HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3638{
3639 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3640 return setError(E_INVALIDARG,
3641 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3642 aPosition, SchemaDefs::MaxBootPosition);
3643
3644 if (aDevice == DeviceType_USB)
3645 return setError(E_NOTIMPL,
3646 tr("Booting from USB device is currently not supported"));
3647
3648 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3649
3650 HRESULT rc = i_checkStateDependency(MutableStateDep);
3651 if (FAILED(rc)) return rc;
3652
3653 i_setModified(IsModified_MachineData);
3654 mHWData.backup();
3655 mHWData->mBootOrder[aPosition - 1] = aDevice;
3656
3657 return S_OK;
3658}
3659
3660HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3661{
3662 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3663 return setError(E_INVALIDARG,
3664 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3665 aPosition, SchemaDefs::MaxBootPosition);
3666
3667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3668
3669 *aDevice = mHWData->mBootOrder[aPosition - 1];
3670
3671 return S_OK;
3672}
3673
3674HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3675 LONG aControllerPort,
3676 LONG aDevice,
3677 DeviceType_T aType,
3678 const ComPtr<IMedium> &aMedium)
3679{
3680 IMedium *aM = aMedium;
3681 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3682 aName.c_str(), aControllerPort, aDevice, aType, aM));
3683
3684 // request the host lock first, since might be calling Host methods for getting host drives;
3685 // next, protect the media tree all the while we're in here, as well as our member variables
3686 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3687 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3688
3689 HRESULT rc = i_checkStateDependency(MutableStateDep);
3690 if (FAILED(rc)) return rc;
3691
3692 /// @todo NEWMEDIA implicit machine registration
3693 if (!mData->mRegistered)
3694 return setError(VBOX_E_INVALID_OBJECT_STATE,
3695 tr("Cannot attach storage devices to an unregistered machine"));
3696
3697 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3698
3699 /* Check for an existing controller. */
3700 ComObjPtr<StorageController> ctl;
3701 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3702 if (FAILED(rc)) return rc;
3703
3704 StorageControllerType_T ctrlType;
3705 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3706 if (FAILED(rc))
3707 return setError(E_FAIL,
3708 tr("Could not get type of controller '%s'"),
3709 aName.c_str());
3710
3711 bool fSilent = false;
3712 Utf8Str strReconfig;
3713
3714 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3715 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3716 if ( mData->mMachineState == MachineState_Paused
3717 && strReconfig == "1")
3718 fSilent = true;
3719
3720 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3721 bool fHotplug = false;
3722 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3723 fHotplug = true;
3724
3725 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3726 return setError(VBOX_E_INVALID_VM_STATE,
3727 tr("Controller '%s' does not support hotplugging"),
3728 aName.c_str());
3729
3730 // check that the port and device are not out of range
3731 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3732 if (FAILED(rc)) return rc;
3733
3734 /* check if the device slot is already busy */
3735 MediumAttachment *pAttachTemp;
3736 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3737 Bstr(aName).raw(),
3738 aControllerPort,
3739 aDevice)))
3740 {
3741 Medium *pMedium = pAttachTemp->i_getMedium();
3742 if (pMedium)
3743 {
3744 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3745 return setError(VBOX_E_OBJECT_IN_USE,
3746 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3747 pMedium->i_getLocationFull().c_str(),
3748 aControllerPort,
3749 aDevice,
3750 aName.c_str());
3751 }
3752 else
3753 return setError(VBOX_E_OBJECT_IN_USE,
3754 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3755 aControllerPort, aDevice, aName.c_str());
3756 }
3757
3758 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3759 if (aMedium && medium.isNull())
3760 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3761
3762 AutoCaller mediumCaller(medium);
3763 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3764
3765 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3766
3767 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3768 && !medium.isNull()
3769 )
3770 return setError(VBOX_E_OBJECT_IN_USE,
3771 tr("Medium '%s' is already attached to this virtual machine"),
3772 medium->i_getLocationFull().c_str());
3773
3774 if (!medium.isNull())
3775 {
3776 MediumType_T mtype = medium->i_getType();
3777 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3778 // For DVDs it's not written to the config file, so needs no global config
3779 // version bump. For floppies it's a new attribute "type", which is ignored
3780 // by older VirtualBox version, so needs no global config version bump either.
3781 // For hard disks this type is not accepted.
3782 if (mtype == MediumType_MultiAttach)
3783 {
3784 // This type is new with VirtualBox 4.0 and therefore requires settings
3785 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3786 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3787 // two reasons: The medium type is a property of the media registry tree, which
3788 // can reside in the global config file (for pre-4.0 media); we would therefore
3789 // possibly need to bump the global config version. We don't want to do that though
3790 // because that might make downgrading to pre-4.0 impossible.
3791 // As a result, we can only use these two new types if the medium is NOT in the
3792 // global registry:
3793 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3794 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3795 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3796 )
3797 return setError(VBOX_E_INVALID_OBJECT_STATE,
3798 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3799 "to machines that were created with VirtualBox 4.0 or later"),
3800 medium->i_getLocationFull().c_str());
3801 }
3802 }
3803
3804 bool fIndirect = false;
3805 if (!medium.isNull())
3806 fIndirect = medium->i_isReadOnly();
3807 bool associate = true;
3808
3809 do
3810 {
3811 if ( aType == DeviceType_HardDisk
3812 && mMediaData.isBackedUp())
3813 {
3814 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3815
3816 /* check if the medium was attached to the VM before we started
3817 * changing attachments in which case the attachment just needs to
3818 * be restored */
3819 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3820 {
3821 AssertReturn(!fIndirect, E_FAIL);
3822
3823 /* see if it's the same bus/channel/device */
3824 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3825 {
3826 /* the simplest case: restore the whole attachment
3827 * and return, nothing else to do */
3828 mMediaData->mAttachments.push_back(pAttachTemp);
3829
3830 /* Reattach the medium to the VM. */
3831 if (fHotplug || fSilent)
3832 {
3833 mediumLock.release();
3834 treeLock.release();
3835 alock.release();
3836
3837 MediumLockList *pMediumLockList(new MediumLockList());
3838
3839 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3840 true /* fMediumLockWrite */,
3841 NULL,
3842 *pMediumLockList);
3843 alock.acquire();
3844 if (FAILED(rc))
3845 delete pMediumLockList;
3846 else
3847 {
3848 mData->mSession.mLockedMedia.Unlock();
3849 alock.release();
3850 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3851 mData->mSession.mLockedMedia.Lock();
3852 alock.acquire();
3853 }
3854 alock.release();
3855
3856 if (SUCCEEDED(rc))
3857 {
3858 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3859 /* Remove lock list in case of error. */
3860 if (FAILED(rc))
3861 {
3862 mData->mSession.mLockedMedia.Unlock();
3863 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3864 mData->mSession.mLockedMedia.Lock();
3865 }
3866 }
3867 }
3868
3869 return S_OK;
3870 }
3871
3872 /* bus/channel/device differ; we need a new attachment object,
3873 * but don't try to associate it again */
3874 associate = false;
3875 break;
3876 }
3877 }
3878
3879 /* go further only if the attachment is to be indirect */
3880 if (!fIndirect)
3881 break;
3882
3883 /* perform the so called smart attachment logic for indirect
3884 * attachments. Note that smart attachment is only applicable to base
3885 * hard disks. */
3886
3887 if (medium->i_getParent().isNull())
3888 {
3889 /* first, investigate the backup copy of the current hard disk
3890 * attachments to make it possible to re-attach existing diffs to
3891 * another device slot w/o losing their contents */
3892 if (mMediaData.isBackedUp())
3893 {
3894 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3895
3896 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3897 uint32_t foundLevel = 0;
3898
3899 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3900 {
3901 uint32_t level = 0;
3902 MediumAttachment *pAttach = *it;
3903 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3904 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3905 if (pMedium.isNull())
3906 continue;
3907
3908 if (pMedium->i_getBase(&level) == medium)
3909 {
3910 /* skip the hard disk if its currently attached (we
3911 * cannot attach the same hard disk twice) */
3912 if (i_findAttachment(mMediaData->mAttachments,
3913 pMedium))
3914 continue;
3915
3916 /* matched device, channel and bus (i.e. attached to the
3917 * same place) will win and immediately stop the search;
3918 * otherwise the attachment that has the youngest
3919 * descendant of medium will be used
3920 */
3921 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3922 {
3923 /* the simplest case: restore the whole attachment
3924 * and return, nothing else to do */
3925 mMediaData->mAttachments.push_back(*it);
3926
3927 /* Reattach the medium to the VM. */
3928 if (fHotplug || fSilent)
3929 {
3930 mediumLock.release();
3931 treeLock.release();
3932 alock.release();
3933
3934 MediumLockList *pMediumLockList(new MediumLockList());
3935
3936 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3937 true /* fMediumLockWrite */,
3938 NULL,
3939 *pMediumLockList);
3940 alock.acquire();
3941 if (FAILED(rc))
3942 delete pMediumLockList;
3943 else
3944 {
3945 mData->mSession.mLockedMedia.Unlock();
3946 alock.release();
3947 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3948 mData->mSession.mLockedMedia.Lock();
3949 alock.acquire();
3950 }
3951 alock.release();
3952
3953 if (SUCCEEDED(rc))
3954 {
3955 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3956 /* Remove lock list in case of error. */
3957 if (FAILED(rc))
3958 {
3959 mData->mSession.mLockedMedia.Unlock();
3960 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3961 mData->mSession.mLockedMedia.Lock();
3962 }
3963 }
3964 }
3965
3966 return S_OK;
3967 }
3968 else if ( foundIt == oldAtts.end()
3969 || level > foundLevel /* prefer younger */
3970 )
3971 {
3972 foundIt = it;
3973 foundLevel = level;
3974 }
3975 }
3976 }
3977
3978 if (foundIt != oldAtts.end())
3979 {
3980 /* use the previously attached hard disk */
3981 medium = (*foundIt)->i_getMedium();
3982 mediumCaller.attach(medium);
3983 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3984 mediumLock.attach(medium);
3985 /* not implicit, doesn't require association with this VM */
3986 fIndirect = false;
3987 associate = false;
3988 /* go right to the MediumAttachment creation */
3989 break;
3990 }
3991 }
3992
3993 /* must give up the medium lock and medium tree lock as below we
3994 * go over snapshots, which needs a lock with higher lock order. */
3995 mediumLock.release();
3996 treeLock.release();
3997
3998 /* then, search through snapshots for the best diff in the given
3999 * hard disk's chain to base the new diff on */
4000
4001 ComObjPtr<Medium> base;
4002 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4003 while (snap)
4004 {
4005 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4006
4007 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4008
4009 MediumAttachment *pAttachFound = NULL;
4010 uint32_t foundLevel = 0;
4011
4012 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4013 {
4014 MediumAttachment *pAttach = *it;
4015 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4016 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4017 if (pMedium.isNull())
4018 continue;
4019
4020 uint32_t level = 0;
4021 if (pMedium->i_getBase(&level) == medium)
4022 {
4023 /* matched device, channel and bus (i.e. attached to the
4024 * same place) will win and immediately stop the search;
4025 * otherwise the attachment that has the youngest
4026 * descendant of medium will be used
4027 */
4028 if ( pAttach->i_getDevice() == aDevice
4029 && pAttach->i_getPort() == aControllerPort
4030 && pAttach->i_getControllerName() == aName
4031 )
4032 {
4033 pAttachFound = pAttach;
4034 break;
4035 }
4036 else if ( !pAttachFound
4037 || level > foundLevel /* prefer younger */
4038 )
4039 {
4040 pAttachFound = pAttach;
4041 foundLevel = level;
4042 }
4043 }
4044 }
4045
4046 if (pAttachFound)
4047 {
4048 base = pAttachFound->i_getMedium();
4049 break;
4050 }
4051
4052 snap = snap->i_getParent();
4053 }
4054
4055 /* re-lock medium tree and the medium, as we need it below */
4056 treeLock.acquire();
4057 mediumLock.acquire();
4058
4059 /* found a suitable diff, use it as a base */
4060 if (!base.isNull())
4061 {
4062 medium = base;
4063 mediumCaller.attach(medium);
4064 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4065 mediumLock.attach(medium);
4066 }
4067 }
4068
4069 Utf8Str strFullSnapshotFolder;
4070 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4071
4072 ComObjPtr<Medium> diff;
4073 diff.createObject();
4074 // store this diff in the same registry as the parent
4075 Guid uuidRegistryParent;
4076 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4077 {
4078 // parent image has no registry: this can happen if we're attaching a new immutable
4079 // image that has not yet been attached (medium then points to the base and we're
4080 // creating the diff image for the immutable, and the parent is not yet registered);
4081 // put the parent in the machine registry then
4082 mediumLock.release();
4083 treeLock.release();
4084 alock.release();
4085 i_addMediumToRegistry(medium);
4086 alock.acquire();
4087 treeLock.acquire();
4088 mediumLock.acquire();
4089 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4090 }
4091 rc = diff->init(mParent,
4092 medium->i_getPreferredDiffFormat(),
4093 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4094 uuidRegistryParent);
4095 if (FAILED(rc)) return rc;
4096
4097 /* Apply the normal locking logic to the entire chain. */
4098 MediumLockList *pMediumLockList(new MediumLockList());
4099 mediumLock.release();
4100 treeLock.release();
4101 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4102 true /* fMediumLockWrite */,
4103 medium,
4104 *pMediumLockList);
4105 treeLock.acquire();
4106 mediumLock.acquire();
4107 if (SUCCEEDED(rc))
4108 {
4109 mediumLock.release();
4110 treeLock.release();
4111 rc = pMediumLockList->Lock();
4112 treeLock.acquire();
4113 mediumLock.acquire();
4114 if (FAILED(rc))
4115 setError(rc,
4116 tr("Could not lock medium when creating diff '%s'"),
4117 diff->i_getLocationFull().c_str());
4118 else
4119 {
4120 /* will release the lock before the potentially lengthy
4121 * operation, so protect with the special state */
4122 MachineState_T oldState = mData->mMachineState;
4123 i_setMachineState(MachineState_SettingUp);
4124
4125 mediumLock.release();
4126 treeLock.release();
4127 alock.release();
4128
4129 rc = medium->i_createDiffStorage(diff,
4130 MediumVariant_Standard,
4131 pMediumLockList,
4132 NULL /* aProgress */,
4133 true /* aWait */);
4134
4135 alock.acquire();
4136 treeLock.acquire();
4137 mediumLock.acquire();
4138
4139 i_setMachineState(oldState);
4140 }
4141 }
4142
4143 /* Unlock the media and free the associated memory. */
4144 delete pMediumLockList;
4145
4146 if (FAILED(rc)) return rc;
4147
4148 /* use the created diff for the actual attachment */
4149 medium = diff;
4150 mediumCaller.attach(medium);
4151 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4152 mediumLock.attach(medium);
4153 }
4154 while (0);
4155
4156 ComObjPtr<MediumAttachment> attachment;
4157 attachment.createObject();
4158 rc = attachment->init(this,
4159 medium,
4160 aName,
4161 aControllerPort,
4162 aDevice,
4163 aType,
4164 fIndirect,
4165 false /* fPassthrough */,
4166 false /* fTempEject */,
4167 false /* fNonRotational */,
4168 false /* fDiscard */,
4169 fHotplug /* fHotPluggable */,
4170 Utf8Str::Empty);
4171 if (FAILED(rc)) return rc;
4172
4173 if (associate && !medium.isNull())
4174 {
4175 // as the last step, associate the medium to the VM
4176 rc = medium->i_addBackReference(mData->mUuid);
4177 // here we can fail because of Deleting, or being in process of creating a Diff
4178 if (FAILED(rc)) return rc;
4179
4180 mediumLock.release();
4181 treeLock.release();
4182 alock.release();
4183 i_addMediumToRegistry(medium);
4184 alock.acquire();
4185 treeLock.acquire();
4186 mediumLock.acquire();
4187 }
4188
4189 /* success: finally remember the attachment */
4190 i_setModified(IsModified_Storage);
4191 mMediaData.backup();
4192 mMediaData->mAttachments.push_back(attachment);
4193
4194 mediumLock.release();
4195 treeLock.release();
4196 alock.release();
4197
4198 if (fHotplug || fSilent)
4199 {
4200 if (!medium.isNull())
4201 {
4202 MediumLockList *pMediumLockList(new MediumLockList());
4203
4204 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4205 true /* fMediumLockWrite */,
4206 NULL,
4207 *pMediumLockList);
4208 alock.acquire();
4209 if (FAILED(rc))
4210 delete pMediumLockList;
4211 else
4212 {
4213 mData->mSession.mLockedMedia.Unlock();
4214 alock.release();
4215 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4216 mData->mSession.mLockedMedia.Lock();
4217 alock.acquire();
4218 }
4219 alock.release();
4220 }
4221
4222 if (SUCCEEDED(rc))
4223 {
4224 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4225 /* Remove lock list in case of error. */
4226 if (FAILED(rc))
4227 {
4228 mData->mSession.mLockedMedia.Unlock();
4229 mData->mSession.mLockedMedia.Remove(attachment);
4230 mData->mSession.mLockedMedia.Lock();
4231 }
4232 }
4233 }
4234
4235 mParent->i_saveModifiedRegistries();
4236
4237 return rc;
4238}
4239
4240HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4241 LONG aDevice)
4242{
4243 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4244 aName.c_str(), aControllerPort, aDevice));
4245
4246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4247
4248 HRESULT rc = i_checkStateDependency(MutableStateDep);
4249 if (FAILED(rc)) return rc;
4250
4251 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4252
4253 /* Check for an existing controller. */
4254 ComObjPtr<StorageController> ctl;
4255 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4256 if (FAILED(rc)) return rc;
4257
4258 StorageControllerType_T ctrlType;
4259 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4260 if (FAILED(rc))
4261 return setError(E_FAIL,
4262 tr("Could not get type of controller '%s'"),
4263 aName.c_str());
4264
4265 bool fSilent = false;
4266 Utf8Str strReconfig;
4267
4268 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4269 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4270 if ( mData->mMachineState == MachineState_Paused
4271 && strReconfig == "1")
4272 fSilent = true;
4273
4274 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4275 bool fHotplug = false;
4276 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4277 fHotplug = true;
4278
4279 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4280 return setError(VBOX_E_INVALID_VM_STATE,
4281 tr("Controller '%s' does not support hotplugging"),
4282 aName.c_str());
4283
4284 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4285 Bstr(aName).raw(),
4286 aControllerPort,
4287 aDevice);
4288 if (!pAttach)
4289 return setError(VBOX_E_OBJECT_NOT_FOUND,
4290 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4291 aDevice, aControllerPort, aName.c_str());
4292
4293 if (fHotplug && !pAttach->i_getHotPluggable())
4294 return setError(VBOX_E_NOT_SUPPORTED,
4295 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4296 aDevice, aControllerPort, aName.c_str());
4297
4298 /*
4299 * The VM has to detach the device before we delete any implicit diffs.
4300 * If this fails we can roll back without loosing data.
4301 */
4302 if (fHotplug || fSilent)
4303 {
4304 alock.release();
4305 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4306 alock.acquire();
4307 }
4308 if (FAILED(rc)) return rc;
4309
4310 /* If we are here everything went well and we can delete the implicit now. */
4311 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4312
4313 alock.release();
4314
4315 mParent->i_saveModifiedRegistries();
4316
4317 return rc;
4318}
4319
4320HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4321 LONG aDevice, BOOL aPassthrough)
4322{
4323 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4324 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4325
4326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4327
4328 HRESULT rc = i_checkStateDependency(MutableStateDep);
4329 if (FAILED(rc)) return rc;
4330
4331 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4332
4333 if (Global::IsOnlineOrTransient(mData->mMachineState))
4334 return setError(VBOX_E_INVALID_VM_STATE,
4335 tr("Invalid machine state: %s"),
4336 Global::stringifyMachineState(mData->mMachineState));
4337
4338 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4339 Bstr(aName).raw(),
4340 aControllerPort,
4341 aDevice);
4342 if (!pAttach)
4343 return setError(VBOX_E_OBJECT_NOT_FOUND,
4344 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4345 aDevice, aControllerPort, aName.c_str());
4346
4347
4348 i_setModified(IsModified_Storage);
4349 mMediaData.backup();
4350
4351 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4352
4353 if (pAttach->i_getType() != DeviceType_DVD)
4354 return setError(E_INVALIDARG,
4355 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4356 aDevice, aControllerPort, aName.c_str());
4357 pAttach->i_updatePassthrough(!!aPassthrough);
4358
4359 return S_OK;
4360}
4361
4362HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4363 LONG aDevice, BOOL aTemporaryEject)
4364{
4365
4366 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4367 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4368
4369 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4370
4371 HRESULT rc = i_checkStateDependency(MutableStateDep);
4372 if (FAILED(rc)) return rc;
4373
4374 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4375 Bstr(aName).raw(),
4376 aControllerPort,
4377 aDevice);
4378 if (!pAttach)
4379 return setError(VBOX_E_OBJECT_NOT_FOUND,
4380 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4381 aDevice, aControllerPort, aName.c_str());
4382
4383
4384 i_setModified(IsModified_Storage);
4385 mMediaData.backup();
4386
4387 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4388
4389 if (pAttach->i_getType() != DeviceType_DVD)
4390 return setError(E_INVALIDARG,
4391 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4392 aDevice, aControllerPort, aName.c_str());
4393 pAttach->i_updateTempEject(!!aTemporaryEject);
4394
4395 return S_OK;
4396}
4397
4398HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4399 LONG aDevice, BOOL aNonRotational)
4400{
4401
4402 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4403 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4404
4405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4406
4407 HRESULT rc = i_checkStateDependency(MutableStateDep);
4408 if (FAILED(rc)) return rc;
4409
4410 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4411
4412 if (Global::IsOnlineOrTransient(mData->mMachineState))
4413 return setError(VBOX_E_INVALID_VM_STATE,
4414 tr("Invalid machine state: %s"),
4415 Global::stringifyMachineState(mData->mMachineState));
4416
4417 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4418 Bstr(aName).raw(),
4419 aControllerPort,
4420 aDevice);
4421 if (!pAttach)
4422 return setError(VBOX_E_OBJECT_NOT_FOUND,
4423 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4424 aDevice, aControllerPort, aName.c_str());
4425
4426
4427 i_setModified(IsModified_Storage);
4428 mMediaData.backup();
4429
4430 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4431
4432 if (pAttach->i_getType() != DeviceType_HardDisk)
4433 return setError(E_INVALIDARG,
4434 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"),
4435 aDevice, aControllerPort, aName.c_str());
4436 pAttach->i_updateNonRotational(!!aNonRotational);
4437
4438 return S_OK;
4439}
4440
4441HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4442 LONG aDevice, BOOL aDiscard)
4443{
4444
4445 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4446 aName.c_str(), aControllerPort, aDevice, aDiscard));
4447
4448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4449
4450 HRESULT rc = i_checkStateDependency(MutableStateDep);
4451 if (FAILED(rc)) return rc;
4452
4453 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4454
4455 if (Global::IsOnlineOrTransient(mData->mMachineState))
4456 return setError(VBOX_E_INVALID_VM_STATE,
4457 tr("Invalid machine state: %s"),
4458 Global::stringifyMachineState(mData->mMachineState));
4459
4460 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4461 Bstr(aName).raw(),
4462 aControllerPort,
4463 aDevice);
4464 if (!pAttach)
4465 return setError(VBOX_E_OBJECT_NOT_FOUND,
4466 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4467 aDevice, aControllerPort, aName.c_str());
4468
4469
4470 i_setModified(IsModified_Storage);
4471 mMediaData.backup();
4472
4473 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4474
4475 if (pAttach->i_getType() != DeviceType_HardDisk)
4476 return setError(E_INVALIDARG,
4477 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"),
4478 aDevice, aControllerPort, aName.c_str());
4479 pAttach->i_updateDiscard(!!aDiscard);
4480
4481 return S_OK;
4482}
4483
4484HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4485 LONG aDevice, BOOL aHotPluggable)
4486{
4487 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4488 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4489
4490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4491
4492 HRESULT rc = i_checkStateDependency(MutableStateDep);
4493 if (FAILED(rc)) return rc;
4494
4495 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4496
4497 if (Global::IsOnlineOrTransient(mData->mMachineState))
4498 return setError(VBOX_E_INVALID_VM_STATE,
4499 tr("Invalid machine state: %s"),
4500 Global::stringifyMachineState(mData->mMachineState));
4501
4502 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4503 Bstr(aName).raw(),
4504 aControllerPort,
4505 aDevice);
4506 if (!pAttach)
4507 return setError(VBOX_E_OBJECT_NOT_FOUND,
4508 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4509 aDevice, aControllerPort, aName.c_str());
4510
4511 /* Check for an existing controller. */
4512 ComObjPtr<StorageController> ctl;
4513 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4514 if (FAILED(rc)) return rc;
4515
4516 StorageControllerType_T ctrlType;
4517 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4518 if (FAILED(rc))
4519 return setError(E_FAIL,
4520 tr("Could not get type of controller '%s'"),
4521 aName.c_str());
4522
4523 if (!i_isControllerHotplugCapable(ctrlType))
4524 return setError(VBOX_E_NOT_SUPPORTED,
4525 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4526 aName.c_str());
4527
4528 i_setModified(IsModified_Storage);
4529 mMediaData.backup();
4530
4531 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4532
4533 if (pAttach->i_getType() == DeviceType_Floppy)
4534 return setError(E_INVALIDARG,
4535 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"),
4536 aDevice, aControllerPort, aName.c_str());
4537 pAttach->i_updateHotPluggable(!!aHotPluggable);
4538
4539 return S_OK;
4540}
4541
4542HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4543 LONG aDevice)
4544{
4545 int rc = S_OK;
4546 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4547 aName.c_str(), aControllerPort, aDevice));
4548
4549 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4550
4551 return rc;
4552}
4553
4554HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4555 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4556{
4557 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4558 aName.c_str(), aControllerPort, aDevice));
4559
4560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4561
4562 HRESULT rc = i_checkStateDependency(MutableStateDep);
4563 if (FAILED(rc)) return rc;
4564
4565 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4566
4567 if (Global::IsOnlineOrTransient(mData->mMachineState))
4568 return setError(VBOX_E_INVALID_VM_STATE,
4569 tr("Invalid machine state: %s"),
4570 Global::stringifyMachineState(mData->mMachineState));
4571
4572 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4573 Bstr(aName).raw(),
4574 aControllerPort,
4575 aDevice);
4576 if (!pAttach)
4577 return setError(VBOX_E_OBJECT_NOT_FOUND,
4578 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4579 aDevice, aControllerPort, aName.c_str());
4580
4581
4582 i_setModified(IsModified_Storage);
4583 mMediaData.backup();
4584
4585 IBandwidthGroup *iB = aBandwidthGroup;
4586 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4587 if (aBandwidthGroup && group.isNull())
4588 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4589
4590 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4591
4592 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4593 if (strBandwidthGroupOld.isNotEmpty())
4594 {
4595 /* Get the bandwidth group object and release it - this must not fail. */
4596 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4597 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4598 Assert(SUCCEEDED(rc));
4599
4600 pBandwidthGroupOld->i_release();
4601 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4602 }
4603
4604 if (!group.isNull())
4605 {
4606 group->i_reference();
4607 pAttach->i_updateBandwidthGroup(group->i_getName());
4608 }
4609
4610 return S_OK;
4611}
4612
4613HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4614 LONG aControllerPort,
4615 LONG aDevice,
4616 DeviceType_T aType)
4617{
4618 HRESULT rc = S_OK;
4619
4620 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4621 aName.c_str(), aControllerPort, aDevice, aType));
4622
4623 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4624
4625 return rc;
4626}
4627
4628
4629HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4630 LONG aControllerPort,
4631 LONG aDevice,
4632 BOOL aForce)
4633{
4634 int rc = S_OK;
4635 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4636 aName.c_str(), aControllerPort, aForce));
4637
4638 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4639
4640 return rc;
4641}
4642
4643HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4644 LONG aControllerPort,
4645 LONG aDevice,
4646 const ComPtr<IMedium> &aMedium,
4647 BOOL aForce)
4648{
4649 int rc = S_OK;
4650 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4651 aName.c_str(), aControllerPort, aDevice, aForce));
4652
4653 // request the host lock first, since might be calling Host methods for getting host drives;
4654 // next, protect the media tree all the while we're in here, as well as our member variables
4655 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4656 this->lockHandle(),
4657 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4658
4659 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4660 Bstr(aName).raw(),
4661 aControllerPort,
4662 aDevice);
4663 if (pAttach.isNull())
4664 return setError(VBOX_E_OBJECT_NOT_FOUND,
4665 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4666 aDevice, aControllerPort, aName.c_str());
4667
4668 /* Remember previously mounted medium. The medium before taking the
4669 * backup is not necessarily the same thing. */
4670 ComObjPtr<Medium> oldmedium;
4671 oldmedium = pAttach->i_getMedium();
4672
4673 IMedium *iM = aMedium;
4674 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4675 if (aMedium && pMedium.isNull())
4676 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4677
4678 AutoCaller mediumCaller(pMedium);
4679 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4680
4681 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4682 if (pMedium)
4683 {
4684 DeviceType_T mediumType = pAttach->i_getType();
4685 switch (mediumType)
4686 {
4687 case DeviceType_DVD:
4688 case DeviceType_Floppy:
4689 break;
4690
4691 default:
4692 return setError(VBOX_E_INVALID_OBJECT_STATE,
4693 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4694 aControllerPort,
4695 aDevice,
4696 aName.c_str());
4697 }
4698 }
4699
4700 i_setModified(IsModified_Storage);
4701 mMediaData.backup();
4702
4703 {
4704 // The backup operation makes the pAttach reference point to the
4705 // old settings. Re-get the correct reference.
4706 pAttach = i_findAttachment(mMediaData->mAttachments,
4707 Bstr(aName).raw(),
4708 aControllerPort,
4709 aDevice);
4710 if (!oldmedium.isNull())
4711 oldmedium->i_removeBackReference(mData->mUuid);
4712 if (!pMedium.isNull())
4713 {
4714 pMedium->i_addBackReference(mData->mUuid);
4715
4716 mediumLock.release();
4717 multiLock.release();
4718 i_addMediumToRegistry(pMedium);
4719 multiLock.acquire();
4720 mediumLock.acquire();
4721 }
4722
4723 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4724 pAttach->i_updateMedium(pMedium);
4725 }
4726
4727 i_setModified(IsModified_Storage);
4728
4729 mediumLock.release();
4730 multiLock.release();
4731 rc = i_onMediumChange(pAttach, aForce);
4732 multiLock.acquire();
4733 mediumLock.acquire();
4734
4735 /* On error roll back this change only. */
4736 if (FAILED(rc))
4737 {
4738 if (!pMedium.isNull())
4739 pMedium->i_removeBackReference(mData->mUuid);
4740 pAttach = i_findAttachment(mMediaData->mAttachments,
4741 Bstr(aName).raw(),
4742 aControllerPort,
4743 aDevice);
4744 /* If the attachment is gone in the meantime, bail out. */
4745 if (pAttach.isNull())
4746 return rc;
4747 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4748 if (!oldmedium.isNull())
4749 oldmedium->i_addBackReference(mData->mUuid);
4750 pAttach->i_updateMedium(oldmedium);
4751 }
4752
4753 mediumLock.release();
4754 multiLock.release();
4755
4756 mParent->i_saveModifiedRegistries();
4757
4758 return rc;
4759}
4760HRESULT Machine::getMedium(const com::Utf8Str &aName,
4761 LONG aControllerPort,
4762 LONG aDevice,
4763 ComPtr<IMedium> &aMedium)
4764{
4765 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4766 aName.c_str(), aControllerPort, aDevice));
4767
4768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4769
4770 aMedium = NULL;
4771
4772 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4773 Bstr(aName).raw(),
4774 aControllerPort,
4775 aDevice);
4776 if (pAttach.isNull())
4777 return setError(VBOX_E_OBJECT_NOT_FOUND,
4778 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4779 aDevice, aControllerPort, aName.c_str());
4780
4781 aMedium = pAttach->i_getMedium();
4782
4783 return S_OK;
4784}
4785
4786HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4787{
4788
4789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4790
4791 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4792
4793 return S_OK;
4794}
4795
4796HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4797{
4798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4799
4800 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4801
4802 return S_OK;
4803}
4804
4805HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4806{
4807 /* Do not assert if slot is out of range, just return the advertised
4808 status. testdriver/vbox.py triggers this in logVmInfo. */
4809 if (aSlot >= mNetworkAdapters.size())
4810 return setError(E_INVALIDARG,
4811 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4812 aSlot, mNetworkAdapters.size());
4813
4814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4815
4816 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4817
4818 return S_OK;
4819}
4820
4821HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4822{
4823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4824
4825 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4826 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4827 size_t i = 0;
4828 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4829 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4830 ++it, ++i)
4831 aKeys[i] = it->first;
4832
4833 return S_OK;
4834}
4835
4836 /**
4837 * @note Locks this object for reading.
4838 */
4839HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4840 com::Utf8Str &aValue)
4841{
4842 /* start with nothing found */
4843 aValue = "";
4844
4845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4846
4847 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4848 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4849 // found:
4850 aValue = it->second; // source is a Utf8Str
4851
4852 /* return the result to caller (may be empty) */
4853 return S_OK;
4854}
4855
4856 /**
4857 * @note Locks mParent for writing + this object for writing.
4858 */
4859HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4860{
4861 Utf8Str strOldValue; // empty
4862
4863 // locking note: we only hold the read lock briefly to look up the old value,
4864 // then release it and call the onExtraCanChange callbacks. There is a small
4865 // chance of a race insofar as the callback might be called twice if two callers
4866 // change the same key at the same time, but that's a much better solution
4867 // than the deadlock we had here before. The actual changing of the extradata
4868 // is then performed under the write lock and race-free.
4869
4870 // look up the old value first; if nothing has changed then we need not do anything
4871 {
4872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4873 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4874 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4875 strOldValue = it->second;
4876 }
4877
4878 bool fChanged;
4879 if ((fChanged = (strOldValue != aValue)))
4880 {
4881 // ask for permission from all listeners outside the locks;
4882 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4883 // lock to copy the list of callbacks to invoke
4884 Bstr error;
4885 Bstr bstrValue(aValue);
4886
4887 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4888 {
4889 const char *sep = error.isEmpty() ? "" : ": ";
4890 CBSTR err = error.raw();
4891 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4892 sep, err));
4893 return setError(E_ACCESSDENIED,
4894 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4895 aKey.c_str(),
4896 aValue.c_str(),
4897 sep,
4898 err);
4899 }
4900
4901 // data is changing and change not vetoed: then write it out under the lock
4902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4903
4904 if (i_isSnapshotMachine())
4905 {
4906 HRESULT rc = i_checkStateDependency(MutableStateDep);
4907 if (FAILED(rc)) return rc;
4908 }
4909
4910 if (aValue.isEmpty())
4911 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4912 else
4913 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4914 // creates a new key if needed
4915
4916 bool fNeedsGlobalSaveSettings = false;
4917 // This saving of settings is tricky: there is no "old state" for the
4918 // extradata items at all (unlike all other settings), so the old/new
4919 // settings comparison would give a wrong result!
4920 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4921
4922 if (fNeedsGlobalSaveSettings)
4923 {
4924 // save the global settings; for that we should hold only the VirtualBox lock
4925 alock.release();
4926 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4927 mParent->i_saveSettings();
4928 }
4929 }
4930
4931 // fire notification outside the lock
4932 if (fChanged)
4933 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4934
4935 return S_OK;
4936}
4937
4938HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4939{
4940 aProgress = NULL;
4941 NOREF(aSettingsFilePath);
4942 ReturnComNotImplemented();
4943}
4944
4945HRESULT Machine::saveSettings()
4946{
4947 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4948
4949 /* when there was auto-conversion, we want to save the file even if
4950 * the VM is saved */
4951 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4952 if (FAILED(rc)) return rc;
4953
4954 /* the settings file path may never be null */
4955 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4956
4957 /* save all VM data excluding snapshots */
4958 bool fNeedsGlobalSaveSettings = false;
4959 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4960 mlock.release();
4961
4962 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4963 {
4964 // save the global settings; for that we should hold only the VirtualBox lock
4965 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4966 rc = mParent->i_saveSettings();
4967 }
4968
4969 return rc;
4970}
4971
4972
4973HRESULT Machine::discardSettings()
4974{
4975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4976
4977 HRESULT rc = i_checkStateDependency(MutableStateDep);
4978 if (FAILED(rc)) return rc;
4979
4980 /*
4981 * during this rollback, the session will be notified if data has
4982 * been actually changed
4983 */
4984 i_rollback(true /* aNotify */);
4985
4986 return S_OK;
4987}
4988
4989/** @note Locks objects! */
4990HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4991 std::vector<ComPtr<IMedium> > &aMedia)
4992{
4993 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4994 AutoLimitedCaller autoCaller(this);
4995 AssertComRCReturnRC(autoCaller.rc());
4996
4997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4998
4999 Guid id(i_getId());
5000
5001 if (mData->mSession.mState != SessionState_Unlocked)
5002 return setError(VBOX_E_INVALID_OBJECT_STATE,
5003 tr("Cannot unregister the machine '%s' while it is locked"),
5004 mUserData->s.strName.c_str());
5005
5006 // wait for state dependents to drop to zero
5007 i_ensureNoStateDependencies();
5008
5009 if (!mData->mAccessible)
5010 {
5011 // inaccessible maschines can only be unregistered; uninitialize ourselves
5012 // here because currently there may be no unregistered that are inaccessible
5013 // (this state combination is not supported). Note releasing the caller and
5014 // leaving the lock before calling uninit()
5015 alock.release();
5016 autoCaller.release();
5017
5018 uninit();
5019
5020 mParent->i_unregisterMachine(this, id);
5021 // calls VirtualBox::i_saveSettings()
5022
5023 return S_OK;
5024 }
5025
5026 HRESULT rc = S_OK;
5027
5028 // discard saved state
5029 if (mData->mMachineState == MachineState_Saved)
5030 {
5031 // add the saved state file to the list of files the caller should delete
5032 Assert(!mSSData->strStateFilePath.isEmpty());
5033 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5034
5035 mSSData->strStateFilePath.setNull();
5036
5037 // unconditionally set the machine state to powered off, we now
5038 // know no session has locked the machine
5039 mData->mMachineState = MachineState_PoweredOff;
5040 }
5041
5042 size_t cSnapshots = 0;
5043 if (mData->mFirstSnapshot)
5044 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5045 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5046 // fail now before we start detaching media
5047 return setError(VBOX_E_INVALID_OBJECT_STATE,
5048 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5049 mUserData->s.strName.c_str(), cSnapshots);
5050
5051 // This list collects the medium objects from all medium attachments
5052 // which we will detach from the machine and its snapshots, in a specific
5053 // order which allows for closing all media without getting "media in use"
5054 // errors, simply by going through the list from the front to the back:
5055 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5056 // and must be closed before the parent media from the snapshots, or closing the parents
5057 // will fail because they still have children);
5058 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5059 // the root ("first") snapshot of the machine.
5060 MediaList llMedia;
5061
5062 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5063 && mMediaData->mAttachments.size()
5064 )
5065 {
5066 // we have media attachments: detach them all and add the Medium objects to our list
5067 if (aCleanupMode != CleanupMode_UnregisterOnly)
5068 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5069 else
5070 return setError(VBOX_E_INVALID_OBJECT_STATE,
5071 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5072 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5073 }
5074
5075 if (cSnapshots)
5076 {
5077 // autoCleanup must be true here, or we would have failed above
5078
5079 // add the media from the medium attachments of the snapshots to llMedia
5080 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5081 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5082 // into the children first
5083
5084 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5085 MachineState_T oldState = mData->mMachineState;
5086 mData->mMachineState = MachineState_DeletingSnapshot;
5087
5088 // make a copy of the first snapshot so the refcount does not drop to 0
5089 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5090 // because of the AutoCaller voodoo)
5091 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5092
5093 // GO!
5094 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5095
5096 mData->mMachineState = oldState;
5097 }
5098
5099 if (FAILED(rc))
5100 {
5101 i_rollbackMedia();
5102 return rc;
5103 }
5104
5105 // commit all the media changes made above
5106 i_commitMedia();
5107
5108 mData->mRegistered = false;
5109
5110 // machine lock no longer needed
5111 alock.release();
5112
5113 // return media to caller
5114 size_t i = 0;
5115 aMedia.resize(llMedia.size());
5116 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5117 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5118
5119 mParent->i_unregisterMachine(this, id);
5120 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5121
5122 return S_OK;
5123}
5124
5125struct Machine::DeleteTask
5126{
5127 ComObjPtr<Machine> pMachine;
5128 RTCList<ComPtr<IMedium> > llMediums;
5129 StringsList llFilesToDelete;
5130 ComObjPtr<Progress> pProgress;
5131};
5132
5133HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5134{
5135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5136
5137 HRESULT rc = i_checkStateDependency(MutableStateDep);
5138 if (FAILED(rc)) return rc;
5139
5140 if (mData->mRegistered)
5141 return setError(VBOX_E_INVALID_VM_STATE,
5142 tr("Cannot delete settings of a registered machine"));
5143
5144 DeleteTask *pTask = new DeleteTask;
5145 pTask->pMachine = this;
5146
5147 // collect files to delete
5148 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5149
5150 for (size_t i = 0; i < aMedia.size(); ++i)
5151 {
5152 IMedium *pIMedium(aMedia[i]);
5153 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5154 if (pMedium.isNull())
5155 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5156 SafeArray<BSTR> ids;
5157 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5158 if (FAILED(rc)) return rc;
5159 /* At this point the medium should not have any back references
5160 * anymore. If it has it is attached to another VM and *must* not
5161 * deleted. */
5162 if (ids.size() < 1)
5163 pTask->llMediums.append(pMedium);
5164 }
5165 if (mData->pMachineConfigFile->fileExists())
5166 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5167
5168 pTask->pProgress.createObject();
5169 pTask->pProgress->init(i_getVirtualBox(),
5170 static_cast<IMachine*>(this) /* aInitiator */,
5171 Bstr(tr("Deleting files")).raw(),
5172 true /* fCancellable */,
5173 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5174 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5175
5176 int vrc = RTThreadCreate(NULL,
5177 Machine::deleteThread,
5178 (void*)pTask,
5179 0,
5180 RTTHREADTYPE_MAIN_WORKER,
5181 0,
5182 "MachineDelete");
5183
5184 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5185
5186 if (RT_FAILURE(vrc))
5187 {
5188 delete pTask;
5189 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5190 }
5191
5192 LogFlowFuncLeave();
5193
5194 return S_OK;
5195}
5196
5197/**
5198 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5199 * calls Machine::deleteTaskWorker() on the actual machine object.
5200 * @param Thread
5201 * @param pvUser
5202 * @return
5203 */
5204/*static*/
5205DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5206{
5207 LogFlowFuncEnter();
5208
5209 DeleteTask *pTask = (DeleteTask*)pvUser;
5210 Assert(pTask);
5211 Assert(pTask->pMachine);
5212 Assert(pTask->pProgress);
5213
5214 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5215 pTask->pProgress->i_notifyComplete(rc);
5216
5217 delete pTask;
5218
5219 LogFlowFuncLeave();
5220
5221 NOREF(Thread);
5222
5223 return VINF_SUCCESS;
5224}
5225
5226/**
5227 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5228 * @param task
5229 * @return
5230 */
5231HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5232{
5233 AutoCaller autoCaller(this);
5234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5235
5236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5237
5238 HRESULT rc = S_OK;
5239
5240 try
5241 {
5242 ULONG uLogHistoryCount = 3;
5243 ComPtr<ISystemProperties> systemProperties;
5244 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5245 if (FAILED(rc)) throw rc;
5246
5247 if (!systemProperties.isNull())
5248 {
5249 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5250 if (FAILED(rc)) throw rc;
5251 }
5252
5253 MachineState_T oldState = mData->mMachineState;
5254 i_setMachineState(MachineState_SettingUp);
5255 alock.release();
5256 for (size_t i = 0; i < task.llMediums.size(); ++i)
5257 {
5258 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5259 {
5260 AutoCaller mac(pMedium);
5261 if (FAILED(mac.rc())) throw mac.rc();
5262 Utf8Str strLocation = pMedium->i_getLocationFull();
5263 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5264 if (FAILED(rc)) throw rc;
5265 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5266 }
5267 ComPtr<IProgress> pProgress2;
5268 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5269 if (FAILED(rc)) throw rc;
5270 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5271 if (FAILED(rc)) throw rc;
5272 /* Check the result of the asynchronous process. */
5273 LONG iRc;
5274 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5275 if (FAILED(rc)) throw rc;
5276 /* If the thread of the progress object has an error, then
5277 * retrieve the error info from there, or it'll be lost. */
5278 if (FAILED(iRc))
5279 throw setError(ProgressErrorInfo(pProgress2));
5280
5281 /* Close the medium, deliberately without checking the return
5282- * code, and without leaving any trace in the error info, as
5283- * a failure here is a very minor issue, which shouldn't happen
5284- * as above we even managed to delete the medium. */
5285 {
5286 ErrorInfoKeeper eik;
5287 pMedium->Close();
5288 }
5289 }
5290 i_setMachineState(oldState);
5291 alock.acquire();
5292
5293 // delete the files pushed on the task list by Machine::Delete()
5294 // (this includes saved states of the machine and snapshots and
5295 // medium storage files from the IMedium list passed in, and the
5296 // machine XML file)
5297 StringsList::const_iterator it = task.llFilesToDelete.begin();
5298 while (it != task.llFilesToDelete.end())
5299 {
5300 const Utf8Str &strFile = *it;
5301 LogFunc(("Deleting file %s\n", strFile.c_str()));
5302 int vrc = RTFileDelete(strFile.c_str());
5303 if (RT_FAILURE(vrc))
5304 throw setError(VBOX_E_IPRT_ERROR,
5305 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5306
5307 ++it;
5308 if (it == task.llFilesToDelete.end())
5309 {
5310 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5311 if (FAILED(rc)) throw rc;
5312 break;
5313 }
5314
5315 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5316 if (FAILED(rc)) throw rc;
5317 }
5318
5319 /* delete the settings only when the file actually exists */
5320 if (mData->pMachineConfigFile->fileExists())
5321 {
5322 /* Delete any backup or uncommitted XML files. Ignore failures.
5323 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5324 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5325 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5326 RTFileDelete(otherXml.c_str());
5327 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5328 RTFileDelete(otherXml.c_str());
5329
5330 /* delete the Logs folder, nothing important should be left
5331 * there (we don't check for errors because the user might have
5332 * some private files there that we don't want to delete) */
5333 Utf8Str logFolder;
5334 getLogFolder(logFolder);
5335 Assert(logFolder.length());
5336 if (RTDirExists(logFolder.c_str()))
5337 {
5338 /* Delete all VBox.log[.N] files from the Logs folder
5339 * (this must be in sync with the rotation logic in
5340 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5341 * files that may have been created by the GUI. */
5342 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5343 logFolder.c_str(), RTPATH_DELIMITER);
5344 RTFileDelete(log.c_str());
5345 log = Utf8StrFmt("%s%cVBox.png",
5346 logFolder.c_str(), RTPATH_DELIMITER);
5347 RTFileDelete(log.c_str());
5348 for (int i = uLogHistoryCount; i > 0; i--)
5349 {
5350 log = Utf8StrFmt("%s%cVBox.log.%d",
5351 logFolder.c_str(), RTPATH_DELIMITER, i);
5352 RTFileDelete(log.c_str());
5353 log = Utf8StrFmt("%s%cVBox.png.%d",
5354 logFolder.c_str(), RTPATH_DELIMITER, i);
5355 RTFileDelete(log.c_str());
5356 }
5357
5358 RTDirRemove(logFolder.c_str());
5359 }
5360
5361 /* delete the Snapshots folder, nothing important should be left
5362 * there (we don't check for errors because the user might have
5363 * some private files there that we don't want to delete) */
5364 Utf8Str strFullSnapshotFolder;
5365 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5366 Assert(!strFullSnapshotFolder.isEmpty());
5367 if (RTDirExists(strFullSnapshotFolder.c_str()))
5368 RTDirRemove(strFullSnapshotFolder.c_str());
5369
5370 // delete the directory that contains the settings file, but only
5371 // if it matches the VM name
5372 Utf8Str settingsDir;
5373 if (i_isInOwnDir(&settingsDir))
5374 RTDirRemove(settingsDir.c_str());
5375 }
5376
5377 alock.release();
5378
5379 mParent->i_saveModifiedRegistries();
5380 }
5381 catch (HRESULT aRC) { rc = aRC; }
5382
5383 return rc;
5384}
5385
5386HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5387{
5388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5389
5390 ComObjPtr<Snapshot> pSnapshot;
5391 HRESULT rc;
5392
5393 if (aNameOrId.isEmpty())
5394 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5395 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5396 else
5397 {
5398 Guid uuid(aNameOrId);
5399 if (uuid.isValid())
5400 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5401 else
5402 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5403 }
5404 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5405
5406 return rc;
5407}
5408
5409HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5410{
5411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5412
5413 HRESULT rc = i_checkStateDependency(MutableStateDep);
5414 if (FAILED(rc)) return rc;
5415
5416 ComObjPtr<SharedFolder> sharedFolder;
5417 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5418 if (SUCCEEDED(rc))
5419 return setError(VBOX_E_OBJECT_IN_USE,
5420 tr("Shared folder named '%s' already exists"),
5421 aName.c_str());
5422
5423 sharedFolder.createObject();
5424 rc = sharedFolder->init(i_getMachine(),
5425 aName,
5426 aHostPath,
5427 !!aWritable,
5428 !!aAutomount,
5429 true /* fFailOnError */);
5430 if (FAILED(rc)) return rc;
5431
5432 i_setModified(IsModified_SharedFolders);
5433 mHWData.backup();
5434 mHWData->mSharedFolders.push_back(sharedFolder);
5435
5436 /* inform the direct session if any */
5437 alock.release();
5438 i_onSharedFolderChange();
5439
5440 return S_OK;
5441}
5442
5443HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5444{
5445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5446
5447 HRESULT rc = i_checkStateDependency(MutableStateDep);
5448 if (FAILED(rc)) return rc;
5449
5450 ComObjPtr<SharedFolder> sharedFolder;
5451 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5452 if (FAILED(rc)) return rc;
5453
5454 i_setModified(IsModified_SharedFolders);
5455 mHWData.backup();
5456 mHWData->mSharedFolders.remove(sharedFolder);
5457
5458 /* inform the direct session if any */
5459 alock.release();
5460 i_onSharedFolderChange();
5461
5462 return S_OK;
5463}
5464
5465HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5466{
5467 /* start with No */
5468 *aCanShow = FALSE;
5469
5470 ComPtr<IInternalSessionControl> directControl;
5471 {
5472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5473
5474 if (mData->mSession.mState != SessionState_Locked)
5475 return setError(VBOX_E_INVALID_VM_STATE,
5476 tr("Machine is not locked for session (session state: %s)"),
5477 Global::stringifySessionState(mData->mSession.mState));
5478
5479 directControl = mData->mSession.mDirectControl;
5480 }
5481
5482 /* ignore calls made after #OnSessionEnd() is called */
5483 if (!directControl)
5484 return S_OK;
5485
5486 LONG64 dummy;
5487 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5488}
5489
5490HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5491{
5492 ComPtr<IInternalSessionControl> directControl;
5493 {
5494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5495
5496 if (mData->mSession.mState != SessionState_Locked)
5497 return setError(E_FAIL,
5498 tr("Machine is not locked for session (session state: %s)"),
5499 Global::stringifySessionState(mData->mSession.mState));
5500
5501 directControl = mData->mSession.mDirectControl;
5502 }
5503
5504 /* ignore calls made after #OnSessionEnd() is called */
5505 if (!directControl)
5506 return S_OK;
5507
5508 BOOL dummy;
5509 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5510}
5511
5512#ifdef VBOX_WITH_GUEST_PROPS
5513/**
5514 * Look up a guest property in VBoxSVC's internal structures.
5515 */
5516HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5517 com::Utf8Str &aValue,
5518 LONG64 *aTimestamp,
5519 com::Utf8Str &aFlags) const
5520{
5521 using namespace guestProp;
5522
5523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5524 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5525
5526 if (it != mHWData->mGuestProperties.end())
5527 {
5528 char szFlags[MAX_FLAGS_LEN + 1];
5529 aValue = it->second.strValue;
5530 *aTimestamp = it->second.mTimestamp;
5531 writeFlags(it->second.mFlags, szFlags);
5532 aFlags = Utf8Str(szFlags);
5533 }
5534
5535 return S_OK;
5536}
5537
5538/**
5539 * Query the VM that a guest property belongs to for the property.
5540 * @returns E_ACCESSDENIED if the VM process is not available or not
5541 * currently handling queries and the lookup should then be done in
5542 * VBoxSVC.
5543 */
5544HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5545 com::Utf8Str &aValue,
5546 LONG64 *aTimestamp,
5547 com::Utf8Str &aFlags) const
5548{
5549 HRESULT rc = S_OK;
5550 BSTR bValue = NULL;
5551 BSTR bFlags = NULL;
5552
5553 ComPtr<IInternalSessionControl> directControl;
5554 directControl = mData->mSession.mDirectControl;
5555
5556 /* fail if we were called after #OnSessionEnd() is called. This is a
5557 * silly race condition. */
5558
5559 /** @todo This code is bothering API clients (like python script clients) with
5560 * the AccessGuestProperty call, creating unncessary IPC. Need to
5561 * have a way of figuring out which kind of direct session it is... */
5562 if (!directControl)
5563 rc = E_ACCESSDENIED;
5564 else
5565 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5566 0 /* accessMode */,
5567 &bValue, aTimestamp, &bFlags);
5568
5569 aValue = bValue;
5570 aFlags = bFlags;
5571
5572 return rc;
5573}
5574#endif // VBOX_WITH_GUEST_PROPS
5575
5576HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5577 com::Utf8Str &aValue,
5578 LONG64 *aTimestamp,
5579 com::Utf8Str &aFlags)
5580{
5581#ifndef VBOX_WITH_GUEST_PROPS
5582 ReturnComNotImplemented();
5583#else // VBOX_WITH_GUEST_PROPS
5584
5585 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5586
5587 if (rc == E_ACCESSDENIED)
5588 /* The VM is not running or the service is not (yet) accessible */
5589 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5590 return rc;
5591#endif // VBOX_WITH_GUEST_PROPS
5592}
5593
5594HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5595{
5596 LONG64 dummyTimestamp;
5597 com::Utf8Str dummyFlags;
5598 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5599 return rc;
5600
5601}
5602HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5603{
5604 com::Utf8Str dummyFlags;
5605 com::Utf8Str dummyValue;
5606 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5607 return rc;
5608}
5609
5610#ifdef VBOX_WITH_GUEST_PROPS
5611/**
5612 * Set a guest property in VBoxSVC's internal structures.
5613 */
5614HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5615 const com::Utf8Str &aFlags, bool fDelete)
5616{
5617 using namespace guestProp;
5618
5619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5620 HRESULT rc = S_OK;
5621
5622 rc = i_checkStateDependency(MutableStateDep);
5623 if (FAILED(rc)) return rc;
5624
5625 try
5626 {
5627 uint32_t fFlags = NILFLAG;
5628 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5629 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5630
5631 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5632 if (it == mHWData->mGuestProperties.end())
5633 {
5634 if (!fDelete)
5635 {
5636 i_setModified(IsModified_MachineData);
5637 mHWData.backupEx();
5638
5639 RTTIMESPEC time;
5640 HWData::GuestProperty prop;
5641 prop.strValue = Bstr(aValue).raw();
5642 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5643 prop.mFlags = fFlags;
5644 mHWData->mGuestProperties[aName] = prop;
5645 }
5646 }
5647 else
5648 {
5649 if (it->second.mFlags & (RDONLYHOST))
5650 {
5651 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5652 }
5653 else
5654 {
5655 i_setModified(IsModified_MachineData);
5656 mHWData.backupEx();
5657
5658 /* The backupEx() operation invalidates our iterator,
5659 * so get a new one. */
5660 it = mHWData->mGuestProperties.find(aName);
5661 Assert(it != mHWData->mGuestProperties.end());
5662
5663 if (!fDelete)
5664 {
5665 RTTIMESPEC time;
5666 it->second.strValue = aValue;
5667 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5668 it->second.mFlags = fFlags;
5669 }
5670 else
5671 mHWData->mGuestProperties.erase(it);
5672 }
5673 }
5674
5675 if ( SUCCEEDED(rc)
5676 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5677 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5678 RTSTR_MAX,
5679 aName.c_str(),
5680 RTSTR_MAX,
5681 NULL)
5682 )
5683 )
5684 {
5685 alock.release();
5686
5687 mParent->i_onGuestPropertyChange(mData->mUuid,
5688 Bstr(aName).raw(),
5689 Bstr(aValue).raw(),
5690 Bstr(aFlags).raw());
5691 }
5692 }
5693 catch (std::bad_alloc &)
5694 {
5695 rc = E_OUTOFMEMORY;
5696 }
5697
5698 return rc;
5699}
5700
5701/**
5702 * Set a property on the VM that that property belongs to.
5703 * @returns E_ACCESSDENIED if the VM process is not available or not
5704 * currently handling queries and the setting should then be done in
5705 * VBoxSVC.
5706 */
5707HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5708 const com::Utf8Str &aFlags, bool fDelete)
5709{
5710 HRESULT rc;
5711
5712 try
5713 {
5714 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5715
5716 BSTR dummy = NULL; /* will not be changed (setter) */
5717 LONG64 dummy64;
5718 if (!directControl)
5719 rc = E_ACCESSDENIED;
5720 else
5721 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5722 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5723 fDelete? 2: 1 /* accessMode */,
5724 &dummy, &dummy64, &dummy);
5725 }
5726 catch (std::bad_alloc &)
5727 {
5728 rc = E_OUTOFMEMORY;
5729 }
5730
5731 return rc;
5732}
5733#endif // VBOX_WITH_GUEST_PROPS
5734
5735HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5736 const com::Utf8Str &aFlags)
5737{
5738#ifndef VBOX_WITH_GUEST_PROPS
5739 ReturnComNotImplemented();
5740#else // VBOX_WITH_GUEST_PROPS
5741 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5742 if (rc == E_ACCESSDENIED)
5743 /* The VM is not running or the service is not (yet) accessible */
5744 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5745 return rc;
5746#endif // VBOX_WITH_GUEST_PROPS
5747}
5748
5749HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5750{
5751 return setGuestProperty(aProperty, aValue, "");
5752}
5753
5754HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5755{
5756#ifndef VBOX_WITH_GUEST_PROPS
5757 ReturnComNotImplemented();
5758#else // VBOX_WITH_GUEST_PROPS
5759 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5760 if (rc == E_ACCESSDENIED)
5761 /* The VM is not running or the service is not (yet) accessible */
5762 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5763 return rc;
5764#endif // VBOX_WITH_GUEST_PROPS
5765}
5766
5767#ifdef VBOX_WITH_GUEST_PROPS
5768/**
5769 * Enumerate the guest properties in VBoxSVC's internal structures.
5770 */
5771HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5772 std::vector<com::Utf8Str> &aNames,
5773 std::vector<com::Utf8Str> &aValues,
5774 std::vector<LONG64> &aTimestamps,
5775 std::vector<com::Utf8Str> &aFlags)
5776{
5777 using namespace guestProp;
5778
5779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5780 Utf8Str strPatterns(aPatterns);
5781
5782 HWData::GuestPropertyMap propMap;
5783
5784 /*
5785 * Look for matching patterns and build up a list.
5786 */
5787 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5788 while (it != mHWData->mGuestProperties.end())
5789 {
5790 if ( strPatterns.isEmpty()
5791 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5792 RTSTR_MAX,
5793 it->first.c_str(),
5794 RTSTR_MAX,
5795 NULL)
5796 )
5797 propMap.insert(*it);
5798 it++;
5799 }
5800
5801 alock.release();
5802
5803 /*
5804 * And build up the arrays for returning the property information.
5805 */
5806 size_t cEntries = propMap.size();
5807
5808 aNames.resize(cEntries);
5809 aValues.resize(cEntries);
5810 aTimestamps.resize(cEntries);
5811 aFlags.resize(cEntries);
5812
5813 char szFlags[MAX_FLAGS_LEN + 1];
5814 size_t i= 0;
5815 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5816 {
5817 aNames[i] = it->first;
5818 aValues[i] = it->second.strValue;
5819 aTimestamps[i] = it->second.mTimestamp;
5820 writeFlags(it->second.mFlags, szFlags);
5821 aFlags[i] = Utf8Str(szFlags);
5822 }
5823
5824 return S_OK;
5825}
5826
5827/**
5828 * Enumerate the properties managed by a VM.
5829 * @returns E_ACCESSDENIED if the VM process is not available or not
5830 * currently handling queries and the setting should then be done in
5831 * VBoxSVC.
5832 */
5833HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5834 std::vector<com::Utf8Str> &aNames,
5835 std::vector<com::Utf8Str> &aValues,
5836 std::vector<LONG64> &aTimestamps,
5837 std::vector<com::Utf8Str> &aFlags)
5838{
5839 HRESULT rc;
5840 ComPtr<IInternalSessionControl> directControl;
5841 directControl = mData->mSession.mDirectControl;
5842
5843
5844 com::SafeArray<BSTR> bNames;
5845 com::SafeArray<BSTR> bValues;
5846 com::SafeArray<LONG64> bTimestamps;
5847 com::SafeArray<BSTR> bFlags;
5848
5849 if (!directControl)
5850 rc = E_ACCESSDENIED;
5851 else
5852 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5853 ComSafeArrayAsOutParam(bNames),
5854 ComSafeArrayAsOutParam(bValues),
5855 ComSafeArrayAsOutParam(bTimestamps),
5856 ComSafeArrayAsOutParam(bFlags));
5857 size_t i;
5858 aNames.resize(bNames.size());
5859 for (i = 0; i < bNames.size(); ++i)
5860 aNames[i] = Utf8Str(bNames[i]);
5861 aValues.resize(bValues.size());
5862 for (i = 0; i < bValues.size(); ++i)
5863 aValues[i] = Utf8Str(bValues[i]);
5864 aTimestamps.resize(bTimestamps.size());
5865 for (i = 0; i < bTimestamps.size(); ++i)
5866 aTimestamps[i] = bTimestamps[i];
5867 aFlags.resize(bFlags.size());
5868 for (i = 0; i < bFlags.size(); ++i)
5869 aFlags[i] = Utf8Str(bFlags[i]);
5870
5871 return rc;
5872}
5873#endif // VBOX_WITH_GUEST_PROPS
5874HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5875 std::vector<com::Utf8Str> &aNames,
5876 std::vector<com::Utf8Str> &aValues,
5877 std::vector<LONG64> &aTimestamps,
5878 std::vector<com::Utf8Str> &aFlags)
5879{
5880#ifndef VBOX_WITH_GUEST_PROPS
5881 ReturnComNotImplemented();
5882#else // VBOX_WITH_GUEST_PROPS
5883
5884 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5885
5886 if (rc == E_ACCESSDENIED)
5887 /* The VM is not running or the service is not (yet) accessible */
5888 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5889 return rc;
5890#endif // VBOX_WITH_GUEST_PROPS
5891}
5892
5893HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5894 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5895{
5896 MediaData::AttachmentList atts;
5897
5898 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5899 if (FAILED(rc)) return rc;
5900
5901 size_t i = 0;
5902 aMediumAttachments.resize(atts.size());
5903 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5904 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5905
5906 return S_OK;
5907}
5908
5909HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5910 LONG aControllerPort,
5911 LONG aDevice,
5912 ComPtr<IMediumAttachment> &aAttachment)
5913{
5914 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5915 aName.c_str(), aControllerPort, aDevice));
5916
5917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5918
5919 aAttachment = NULL;
5920
5921 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5922 Bstr(aName).raw(),
5923 aControllerPort,
5924 aDevice);
5925 if (pAttach.isNull())
5926 return setError(VBOX_E_OBJECT_NOT_FOUND,
5927 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5928 aDevice, aControllerPort, aName.c_str());
5929
5930 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5931
5932 return S_OK;
5933}
5934
5935
5936HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5937 StorageBus_T aConnectionType,
5938 ComPtr<IStorageController> &aController)
5939{
5940 if ( (aConnectionType <= StorageBus_Null)
5941 || (aConnectionType > StorageBus_USB))
5942 return setError(E_INVALIDARG,
5943 tr("Invalid connection type: %d"),
5944 aConnectionType);
5945
5946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5947
5948 HRESULT rc = i_checkStateDependency(MutableStateDep);
5949 if (FAILED(rc)) return rc;
5950
5951 /* try to find one with the name first. */
5952 ComObjPtr<StorageController> ctrl;
5953
5954 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5955 if (SUCCEEDED(rc))
5956 return setError(VBOX_E_OBJECT_IN_USE,
5957 tr("Storage controller named '%s' already exists"),
5958 aName.c_str());
5959
5960 ctrl.createObject();
5961
5962 /* get a new instance number for the storage controller */
5963 ULONG ulInstance = 0;
5964 bool fBootable = true;
5965 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5966 it != mStorageControllers->end();
5967 ++it)
5968 {
5969 if ((*it)->i_getStorageBus() == aConnectionType)
5970 {
5971 ULONG ulCurInst = (*it)->i_getInstance();
5972
5973 if (ulCurInst >= ulInstance)
5974 ulInstance = ulCurInst + 1;
5975
5976 /* Only one controller of each type can be marked as bootable. */
5977 if ((*it)->i_getBootable())
5978 fBootable = false;
5979 }
5980 }
5981
5982 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5983 if (FAILED(rc)) return rc;
5984
5985 i_setModified(IsModified_Storage);
5986 mStorageControllers.backup();
5987 mStorageControllers->push_back(ctrl);
5988
5989 ctrl.queryInterfaceTo(aController.asOutParam());
5990
5991 /* inform the direct session if any */
5992 alock.release();
5993 i_onStorageControllerChange();
5994
5995 return S_OK;
5996}
5997
5998HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5999 ComPtr<IStorageController> &aStorageController)
6000{
6001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6002
6003 ComObjPtr<StorageController> ctrl;
6004
6005 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6006 if (SUCCEEDED(rc))
6007 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6008
6009 return rc;
6010}
6011
6012HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6013 ComPtr<IStorageController> &aStorageController)
6014{
6015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6016
6017 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6018 it != mStorageControllers->end();
6019 ++it)
6020 {
6021 if ((*it)->i_getInstance() == aInstance)
6022 {
6023 (*it).queryInterfaceTo(aStorageController.asOutParam());
6024 return S_OK;
6025 }
6026 }
6027
6028 return setError(VBOX_E_OBJECT_NOT_FOUND,
6029 tr("Could not find a storage controller with instance number '%lu'"),
6030 aInstance);
6031}
6032
6033HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6034{
6035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6036
6037 HRESULT rc = i_checkStateDependency(MutableStateDep);
6038 if (FAILED(rc)) return rc;
6039
6040 ComObjPtr<StorageController> ctrl;
6041
6042 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6043 if (SUCCEEDED(rc))
6044 {
6045 /* Ensure that only one controller of each type is marked as bootable. */
6046 if (aBootable == TRUE)
6047 {
6048 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6049 it != mStorageControllers->end();
6050 ++it)
6051 {
6052 ComObjPtr<StorageController> aCtrl = (*it);
6053
6054 if ( (aCtrl->i_getName() != aName)
6055 && aCtrl->i_getBootable() == TRUE
6056 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6057 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6058 {
6059 aCtrl->i_setBootable(FALSE);
6060 break;
6061 }
6062 }
6063 }
6064
6065 if (SUCCEEDED(rc))
6066 {
6067 ctrl->i_setBootable(aBootable);
6068 i_setModified(IsModified_Storage);
6069 }
6070 }
6071
6072 if (SUCCEEDED(rc))
6073 {
6074 /* inform the direct session if any */
6075 alock.release();
6076 i_onStorageControllerChange();
6077 }
6078
6079 return rc;
6080}
6081
6082HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6083{
6084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6085
6086 HRESULT rc = i_checkStateDependency(MutableStateDep);
6087 if (FAILED(rc)) return rc;
6088
6089 ComObjPtr<StorageController> ctrl;
6090 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6091 if (FAILED(rc)) return rc;
6092
6093 {
6094 /* find all attached devices to the appropriate storage controller and detach them all */
6095 // make a temporary list because detachDevice invalidates iterators into
6096 // mMediaData->mAttachments
6097 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6098
6099 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6100 it != llAttachments2.end();
6101 ++it)
6102 {
6103 MediumAttachment *pAttachTemp = *it;
6104
6105 AutoCaller localAutoCaller(pAttachTemp);
6106 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6107
6108 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6109
6110 if (pAttachTemp->i_getControllerName() == aName)
6111 {
6112 rc = i_detachDevice(pAttachTemp, alock, NULL);
6113 if (FAILED(rc)) return rc;
6114 }
6115 }
6116 }
6117
6118 /* We can remove it now. */
6119 i_setModified(IsModified_Storage);
6120 mStorageControllers.backup();
6121
6122 ctrl->i_unshare();
6123
6124 mStorageControllers->remove(ctrl);
6125
6126 /* inform the direct session if any */
6127 alock.release();
6128 i_onStorageControllerChange();
6129
6130 return S_OK;
6131}
6132
6133HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6134 ComPtr<IUSBController> &aController)
6135{
6136 if ( (aType <= USBControllerType_Null)
6137 || (aType >= USBControllerType_Last))
6138 return setError(E_INVALIDARG,
6139 tr("Invalid USB controller type: %d"),
6140 aType);
6141
6142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6143
6144 HRESULT rc = i_checkStateDependency(MutableStateDep);
6145 if (FAILED(rc)) return rc;
6146
6147 /* try to find one with the same type first. */
6148 ComObjPtr<USBController> ctrl;
6149
6150 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6151 if (SUCCEEDED(rc))
6152 return setError(VBOX_E_OBJECT_IN_USE,
6153 tr("USB controller named '%s' already exists"),
6154 aName.c_str());
6155
6156 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6157 ULONG maxInstances;
6158 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6159 if (FAILED(rc))
6160 return rc;
6161
6162 ULONG cInstances = i_getUSBControllerCountByType(aType);
6163 if (cInstances >= maxInstances)
6164 return setError(E_INVALIDARG,
6165 tr("Too many USB controllers of this type"));
6166
6167 ctrl.createObject();
6168
6169 rc = ctrl->init(this, aName, aType);
6170 if (FAILED(rc)) return rc;
6171
6172 i_setModified(IsModified_USB);
6173 mUSBControllers.backup();
6174 mUSBControllers->push_back(ctrl);
6175
6176 ctrl.queryInterfaceTo(aController.asOutParam());
6177
6178 /* inform the direct session if any */
6179 alock.release();
6180 i_onUSBControllerChange();
6181
6182 return S_OK;
6183}
6184
6185HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6186{
6187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6188
6189 ComObjPtr<USBController> ctrl;
6190
6191 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6192 if (SUCCEEDED(rc))
6193 ctrl.queryInterfaceTo(aController.asOutParam());
6194
6195 return rc;
6196}
6197
6198HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6199 ULONG *aControllers)
6200{
6201 if ( (aType <= USBControllerType_Null)
6202 || (aType >= USBControllerType_Last))
6203 return setError(E_INVALIDARG,
6204 tr("Invalid USB controller type: %d"),
6205 aType);
6206
6207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6208
6209 ComObjPtr<USBController> ctrl;
6210
6211 *aControllers = i_getUSBControllerCountByType(aType);
6212
6213 return S_OK;
6214}
6215
6216HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6217{
6218
6219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6220
6221 HRESULT rc = i_checkStateDependency(MutableStateDep);
6222 if (FAILED(rc)) return rc;
6223
6224 ComObjPtr<USBController> ctrl;
6225 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6226 if (FAILED(rc)) return rc;
6227
6228 i_setModified(IsModified_USB);
6229 mUSBControllers.backup();
6230
6231 ctrl->i_unshare();
6232
6233 mUSBControllers->remove(ctrl);
6234
6235 /* inform the direct session if any */
6236 alock.release();
6237 i_onUSBControllerChange();
6238
6239 return S_OK;
6240}
6241
6242HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6243 ULONG *aOriginX,
6244 ULONG *aOriginY,
6245 ULONG *aWidth,
6246 ULONG *aHeight,
6247 BOOL *aEnabled)
6248{
6249 uint32_t u32OriginX= 0;
6250 uint32_t u32OriginY= 0;
6251 uint32_t u32Width = 0;
6252 uint32_t u32Height = 0;
6253 uint16_t u16Flags = 0;
6254
6255 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6256 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6257 if (RT_FAILURE(vrc))
6258 {
6259#ifdef RT_OS_WINDOWS
6260 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6261 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6262 * So just assign fEnable to TRUE again.
6263 * The right fix would be to change GUI API wrappers to make sure that parameters
6264 * are changed only if API succeeds.
6265 */
6266 *aEnabled = TRUE;
6267#endif
6268 return setError(VBOX_E_IPRT_ERROR,
6269 tr("Saved guest size is not available (%Rrc)"),
6270 vrc);
6271 }
6272
6273 *aOriginX = u32OriginX;
6274 *aOriginY = u32OriginY;
6275 *aWidth = u32Width;
6276 *aHeight = u32Height;
6277 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6278
6279 return S_OK;
6280}
6281
6282HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6283{
6284 if (aScreenId != 0)
6285 return E_NOTIMPL;
6286
6287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6288
6289 uint8_t *pu8Data = NULL;
6290 uint32_t cbData = 0;
6291 uint32_t u32Width = 0;
6292 uint32_t u32Height = 0;
6293
6294 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6295
6296 if (RT_FAILURE(vrc))
6297 return setError(VBOX_E_IPRT_ERROR,
6298 tr("Saved screenshot data is not available (%Rrc)"),
6299 vrc);
6300
6301 *aSize = cbData;
6302 *aWidth = u32Width;
6303 *aHeight = u32Height;
6304
6305 freeSavedDisplayScreenshot(pu8Data);
6306
6307 return S_OK;
6308}
6309
6310
6311HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6312{
6313 if (aScreenId != 0)
6314 return E_NOTIMPL;
6315
6316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6317
6318 uint8_t *pu8Data = NULL;
6319 uint32_t cbData = 0;
6320 uint32_t u32Width = 0;
6321 uint32_t u32Height = 0;
6322
6323 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6324
6325 if (RT_FAILURE(vrc))
6326 return setError(VBOX_E_IPRT_ERROR,
6327 tr("Saved screenshot data is not available (%Rrc)"),
6328 vrc);
6329
6330 *aWidth = u32Width;
6331 *aHeight = u32Height;
6332
6333 com::SafeArray<BYTE> bitmap(cbData);
6334 /* Convert pixels to format expected by the API caller. */
6335 if (aBGR)
6336 {
6337 /* [0] B, [1] G, [2] R, [3] A. */
6338 for (unsigned i = 0; i < cbData; i += 4)
6339 {
6340 bitmap[i] = pu8Data[i];
6341 bitmap[i + 1] = pu8Data[i + 1];
6342 bitmap[i + 2] = pu8Data[i + 2];
6343 bitmap[i + 3] = 0xff;
6344 }
6345 }
6346 else
6347 {
6348 /* [0] R, [1] G, [2] B, [3] A. */
6349 for (unsigned i = 0; i < cbData; i += 4)
6350 {
6351 bitmap[i] = pu8Data[i + 2];
6352 bitmap[i + 1] = pu8Data[i + 1];
6353 bitmap[i + 2] = pu8Data[i];
6354 bitmap[i + 3] = 0xff;
6355 }
6356 }
6357 aData.resize(bitmap.size());
6358 for (size_t i = 0; i < bitmap.size(); ++i)
6359 aData[i] = bitmap[i];
6360
6361 freeSavedDisplayScreenshot(pu8Data);
6362
6363 return S_OK;
6364}
6365
6366HRESULT Machine::readSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6367{
6368 if (aScreenId != 0)
6369 return E_NOTIMPL;
6370
6371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6372
6373 uint8_t *pu8Data = NULL;
6374 uint32_t cbData = 0;
6375 uint32_t u32Width = 0;
6376 uint32_t u32Height = 0;
6377
6378 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6379
6380 if (RT_FAILURE(vrc))
6381 return setError(VBOX_E_IPRT_ERROR,
6382 tr("Saved screenshot data is not available (%Rrc)"),
6383 vrc);
6384
6385 *aWidth = u32Width;
6386 *aHeight = u32Height;
6387
6388 HRESULT rc = S_OK;
6389 uint8_t *pu8PNG = NULL;
6390 uint32_t cbPNG = 0;
6391 uint32_t cxPNG = 0;
6392 uint32_t cyPNG = 0;
6393
6394 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6395
6396 if (RT_SUCCESS(vrc))
6397 {
6398 com::SafeArray<BYTE> screenData(cbPNG);
6399 screenData.initFrom(pu8PNG, cbPNG);
6400 if (pu8PNG)
6401 RTMemFree(pu8PNG);
6402 aData.resize(screenData.size());
6403 for (size_t i = 0; i < screenData.size(); ++i)
6404 aData[i] = screenData[i];
6405 }
6406 else
6407 {
6408 if (pu8PNG)
6409 RTMemFree(pu8PNG);
6410 return setError(VBOX_E_IPRT_ERROR,
6411 tr("Could not convert screenshot to PNG (%Rrc)"),
6412 vrc);
6413 }
6414
6415 freeSavedDisplayScreenshot(pu8Data);
6416
6417 return rc;
6418}
6419
6420HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6421{
6422 if (aScreenId != 0)
6423 return E_NOTIMPL;
6424
6425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6426
6427 uint8_t *pu8Data = NULL;
6428 uint32_t cbData = 0;
6429 uint32_t u32Width = 0;
6430 uint32_t u32Height = 0;
6431
6432 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6433
6434 if (RT_FAILURE(vrc))
6435 return setError(VBOX_E_IPRT_ERROR,
6436 tr("Saved screenshot data is not available (%Rrc)"),
6437 vrc);
6438
6439 *aSize = cbData;
6440 *aWidth = u32Width;
6441 *aHeight = u32Height;
6442
6443 freeSavedDisplayScreenshot(pu8Data);
6444
6445 return S_OK;
6446}
6447
6448HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6449{
6450 if (aScreenId != 0)
6451 return E_NOTIMPL;
6452
6453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6454
6455 uint8_t *pu8Data = NULL;
6456 uint32_t cbData = 0;
6457 uint32_t u32Width = 0;
6458 uint32_t u32Height = 0;
6459
6460 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6461
6462 if (RT_FAILURE(vrc))
6463 return setError(VBOX_E_IPRT_ERROR,
6464 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6465 vrc);
6466
6467 *aWidth = u32Width;
6468 *aHeight = u32Height;
6469
6470 com::SafeArray<BYTE> png(cbData);
6471 png.initFrom(pu8Data, cbData);
6472 aData.resize(png.size());
6473 for (size_t i = 0; i < png.size(); ++i)
6474 aData[i] = png[i];
6475
6476 freeSavedDisplayScreenshot(pu8Data);
6477
6478 return S_OK;
6479}
6480
6481HRESULT Machine::hotPlugCPU(ULONG aCpu)
6482{
6483 HRESULT rc = S_OK;
6484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6485
6486 if (!mHWData->mCPUHotPlugEnabled)
6487 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6488
6489 if (aCpu >= mHWData->mCPUCount)
6490 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6491
6492 if (mHWData->mCPUAttached[aCpu])
6493 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6494
6495 alock.release();
6496 rc = i_onCPUChange(aCpu, false);
6497 alock.acquire();
6498 if (FAILED(rc)) return rc;
6499
6500 i_setModified(IsModified_MachineData);
6501 mHWData.backup();
6502 mHWData->mCPUAttached[aCpu] = true;
6503
6504 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6505 if (Global::IsOnline(mData->mMachineState))
6506 i_saveSettings(NULL);
6507
6508 return S_OK;
6509}
6510
6511HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6512{
6513 HRESULT rc = S_OK;
6514
6515 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6516
6517 if (!mHWData->mCPUHotPlugEnabled)
6518 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6519
6520 if (aCpu >= SchemaDefs::MaxCPUCount)
6521 return setError(E_INVALIDARG,
6522 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6523 SchemaDefs::MaxCPUCount);
6524
6525 if (!mHWData->mCPUAttached[aCpu])
6526 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6527
6528 /* CPU 0 can't be detached */
6529 if (aCpu == 0)
6530 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6531
6532 alock.release();
6533 rc = i_onCPUChange(aCpu, true);
6534 alock.acquire();
6535 if (FAILED(rc)) return rc;
6536
6537 i_setModified(IsModified_MachineData);
6538 mHWData.backup();
6539 mHWData->mCPUAttached[aCpu] = false;
6540
6541 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6542 if (Global::IsOnline(mData->mMachineState))
6543 i_saveSettings(NULL);
6544
6545 return S_OK;
6546}
6547
6548HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6549{
6550 *aAttached = false;
6551
6552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6553
6554 /* If hotplug is enabled the CPU is always enabled. */
6555 if (!mHWData->mCPUHotPlugEnabled)
6556 {
6557 if (aCpu < mHWData->mCPUCount)
6558 *aAttached = true;
6559 }
6560 else
6561 {
6562 if (aCpu < SchemaDefs::MaxCPUCount)
6563 *aAttached = mHWData->mCPUAttached[aCpu];
6564 }
6565
6566 return S_OK;
6567}
6568
6569HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6570{
6571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6572
6573 Utf8Str log = i_queryLogFilename(aIdx);
6574 if (!RTFileExists(log.c_str()))
6575 log.setNull();
6576 aFilename = log;
6577
6578 return S_OK;
6579}
6580
6581HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6582{
6583 if (aSize < 0)
6584 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6585
6586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6587
6588 HRESULT rc = S_OK;
6589 Utf8Str log = i_queryLogFilename(aIdx);
6590
6591 /* do not unnecessarily hold the lock while doing something which does
6592 * not need the lock and potentially takes a long time. */
6593 alock.release();
6594
6595 /* Limit the chunk size to 32K for now, as that gives better performance
6596 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6597 * One byte expands to approx. 25 bytes of breathtaking XML. */
6598 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6599 com::SafeArray<BYTE> logData(cbData);
6600
6601 RTFILE LogFile;
6602 int vrc = RTFileOpen(&LogFile, log.c_str(),
6603 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6604 if (RT_SUCCESS(vrc))
6605 {
6606 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6607 if (RT_SUCCESS(vrc))
6608 logData.resize(cbData);
6609 else
6610 rc = setError(VBOX_E_IPRT_ERROR,
6611 tr("Could not read log file '%s' (%Rrc)"),
6612 log.c_str(), vrc);
6613 RTFileClose(LogFile);
6614 }
6615 else
6616 rc = setError(VBOX_E_IPRT_ERROR,
6617 tr("Could not open log file '%s' (%Rrc)"),
6618 log.c_str(), vrc);
6619
6620 if (FAILED(rc))
6621 logData.resize(0);
6622
6623 aData.resize(logData.size());
6624 for (size_t i = 0; i < logData.size(); ++i)
6625 aData[i] = logData[i];
6626
6627 return rc;
6628}
6629
6630
6631/**
6632 * Currently this method doesn't attach device to the running VM,
6633 * just makes sure it's plugged on next VM start.
6634 */
6635HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6636{
6637 // lock scope
6638 {
6639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6640
6641 HRESULT rc = i_checkStateDependency(MutableStateDep);
6642 if (FAILED(rc)) return rc;
6643
6644 ChipsetType_T aChipset = ChipsetType_PIIX3;
6645 COMGETTER(ChipsetType)(&aChipset);
6646
6647 if (aChipset != ChipsetType_ICH9)
6648 {
6649 return setError(E_INVALIDARG,
6650 tr("Host PCI attachment only supported with ICH9 chipset"));
6651 }
6652
6653 // check if device with this host PCI address already attached
6654 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6655 it != mHWData->mPCIDeviceAssignments.end();
6656 ++it)
6657 {
6658 LONG iHostAddress = -1;
6659 ComPtr<PCIDeviceAttachment> pAttach;
6660 pAttach = *it;
6661 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6662 if (iHostAddress == aHostAddress)
6663 return setError(E_INVALIDARG,
6664 tr("Device with host PCI address already attached to this VM"));
6665 }
6666
6667 ComObjPtr<PCIDeviceAttachment> pda;
6668 char name[32];
6669
6670 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6671 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6672 Bstr bname(name);
6673 pda.createObject();
6674 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6675 i_setModified(IsModified_MachineData);
6676 mHWData.backup();
6677 mHWData->mPCIDeviceAssignments.push_back(pda);
6678 }
6679
6680 return S_OK;
6681}
6682
6683/**
6684 * Currently this method doesn't detach device from the running VM,
6685 * just makes sure it's not plugged on next VM start.
6686 */
6687HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6688{
6689 ComObjPtr<PCIDeviceAttachment> pAttach;
6690 bool fRemoved = false;
6691 HRESULT rc;
6692
6693 // lock scope
6694 {
6695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6696
6697 rc = i_checkStateDependency(MutableStateDep);
6698 if (FAILED(rc)) return rc;
6699
6700 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6701 it != mHWData->mPCIDeviceAssignments.end();
6702 ++it)
6703 {
6704 LONG iHostAddress = -1;
6705 pAttach = *it;
6706 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6707 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6708 {
6709 i_setModified(IsModified_MachineData);
6710 mHWData.backup();
6711 mHWData->mPCIDeviceAssignments.remove(pAttach);
6712 fRemoved = true;
6713 break;
6714 }
6715 }
6716 }
6717
6718
6719 /* Fire event outside of the lock */
6720 if (fRemoved)
6721 {
6722 Assert(!pAttach.isNull());
6723 ComPtr<IEventSource> es;
6724 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6725 Assert(SUCCEEDED(rc));
6726 Bstr mid;
6727 rc = this->COMGETTER(Id)(mid.asOutParam());
6728 Assert(SUCCEEDED(rc));
6729 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6730 }
6731
6732 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6733 tr("No host PCI device %08x attached"),
6734 aHostAddress
6735 );
6736}
6737
6738HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6739{
6740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6741
6742 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6743
6744 size_t i = 0;
6745 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6746 it != mHWData->mPCIDeviceAssignments.end();
6747 ++i, ++it)
6748 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6749
6750 return S_OK;
6751}
6752
6753HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6754{
6755 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6756
6757 return S_OK;
6758}
6759
6760HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6761{
6762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6763
6764 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6765
6766 return S_OK;
6767}
6768
6769HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6770{
6771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6772 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6773 if (SUCCEEDED(hrc))
6774 {
6775 hrc = mHWData.backupEx();
6776 if (SUCCEEDED(hrc))
6777 {
6778 i_setModified(IsModified_MachineData);
6779 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6780 }
6781 }
6782 return hrc;
6783}
6784
6785HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6786{
6787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6788 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6789 return S_OK;
6790}
6791
6792HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6793{
6794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6795 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6796 if (SUCCEEDED(hrc))
6797 {
6798 hrc = mHWData.backupEx();
6799 if (SUCCEEDED(hrc))
6800 {
6801 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6802 if (SUCCEEDED(hrc))
6803 i_setModified(IsModified_MachineData);
6804 }
6805 }
6806 return hrc;
6807}
6808
6809HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6810{
6811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6812
6813 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6814
6815 return S_OK;
6816}
6817
6818HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6819{
6820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6821 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6822 if (SUCCEEDED(hrc))
6823 {
6824 hrc = mHWData.backupEx();
6825 if (SUCCEEDED(hrc))
6826 {
6827 i_setModified(IsModified_MachineData);
6828 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6829 }
6830 }
6831 return hrc;
6832}
6833
6834HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6835{
6836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6839
6840 return S_OK;
6841}
6842
6843HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6844{
6845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6846
6847 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6848 if ( SUCCEEDED(hrc)
6849 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6850 {
6851 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6852 int vrc;
6853
6854 if (aAutostartEnabled)
6855 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6856 else
6857 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6858
6859 if (RT_SUCCESS(vrc))
6860 {
6861 hrc = mHWData.backupEx();
6862 if (SUCCEEDED(hrc))
6863 {
6864 i_setModified(IsModified_MachineData);
6865 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6866 }
6867 }
6868 else if (vrc == VERR_NOT_SUPPORTED)
6869 hrc = setError(VBOX_E_NOT_SUPPORTED,
6870 tr("The VM autostart feature is not supported on this platform"));
6871 else if (vrc == VERR_PATH_NOT_FOUND)
6872 hrc = setError(E_FAIL,
6873 tr("The path to the autostart database is not set"));
6874 else
6875 hrc = setError(E_UNEXPECTED,
6876 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6877 aAutostartEnabled ? "Adding" : "Removing",
6878 mUserData->s.strName.c_str(), vrc);
6879 }
6880 return hrc;
6881}
6882
6883HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6884{
6885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6886
6887 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6888
6889 return S_OK;
6890}
6891
6892HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6893{
6894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6895 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6896 if (SUCCEEDED(hrc))
6897 {
6898 hrc = mHWData.backupEx();
6899 if (SUCCEEDED(hrc))
6900 {
6901 i_setModified(IsModified_MachineData);
6902 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6903 }
6904 }
6905 return hrc;
6906}
6907
6908HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6909{
6910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6911
6912 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6913
6914 return S_OK;
6915}
6916
6917HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6918{
6919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6920 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6921 if ( SUCCEEDED(hrc)
6922 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6923 {
6924 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6925 int vrc;
6926
6927 if (aAutostopType != AutostopType_Disabled)
6928 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6929 else
6930 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6931
6932 if (RT_SUCCESS(vrc))
6933 {
6934 hrc = mHWData.backupEx();
6935 if (SUCCEEDED(hrc))
6936 {
6937 i_setModified(IsModified_MachineData);
6938 mHWData->mAutostart.enmAutostopType = aAutostopType;
6939 }
6940 }
6941 else if (vrc == VERR_NOT_SUPPORTED)
6942 hrc = setError(VBOX_E_NOT_SUPPORTED,
6943 tr("The VM autostop feature is not supported on this platform"));
6944 else if (vrc == VERR_PATH_NOT_FOUND)
6945 hrc = setError(E_FAIL,
6946 tr("The path to the autostart database is not set"));
6947 else
6948 hrc = setError(E_UNEXPECTED,
6949 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6950 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6951 mUserData->s.strName.c_str(), vrc);
6952 }
6953 return hrc;
6954}
6955
6956HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6957{
6958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6959
6960 aDefaultFrontend = mHWData->mDefaultFrontend;
6961
6962 return S_OK;
6963}
6964
6965HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6966{
6967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6968 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6969 if (SUCCEEDED(hrc))
6970 {
6971 hrc = mHWData.backupEx();
6972 if (SUCCEEDED(hrc))
6973 {
6974 i_setModified(IsModified_MachineData);
6975 mHWData->mDefaultFrontend = aDefaultFrontend;
6976 }
6977 }
6978 return hrc;
6979}
6980
6981HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6982{
6983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6984 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
6985 aIcon.resize(mUserData->mIcon.size());
6986 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
6987 aIcon.resize(icon.size());
6988 for (size_t i = 0; i < icon.size(); ++i)
6989 aIcon[i] = icon[i];
6990 return S_OK;
6991}
6992
6993HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6994{
6995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6996 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6997 if (SUCCEEDED(hrc))
6998 {
6999 i_setModified(IsModified_MachineData);
7000 mUserData.backup();
7001 com::SafeArray<BYTE> icon(aIcon);
7002 mUserData->mIcon.resize(aIcon.size());
7003 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7004 }
7005 return hrc;
7006}
7007
7008HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7009{
7010
7011#ifdef VBOX_WITH_USB
7012 *aUSBProxyAvailable = true;
7013#else
7014 *aUSBProxyAvailable = false;
7015#endif
7016 return S_OK;
7017}
7018
7019HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7020 ComPtr<IProgress> &aProgress)
7021{
7022 ComObjPtr<Progress> pP;
7023 Progress *ppP = pP;
7024 IProgress *iP = static_cast<IProgress *>(ppP);
7025 IProgress **pProgress = &iP;
7026
7027 IMachine *pTarget = aTarget;
7028
7029 /* Convert the options. */
7030 RTCList<CloneOptions_T> optList;
7031 if (aOptions.size())
7032 for (size_t i = 0; i < aOptions.size(); ++i)
7033 optList.append(aOptions[i]);
7034
7035 if (optList.contains(CloneOptions_Link))
7036 {
7037 if (!i_isSnapshotMachine())
7038 return setError(E_INVALIDARG,
7039 tr("Linked clone can only be created from a snapshot"));
7040 if (aMode != CloneMode_MachineState)
7041 return setError(E_INVALIDARG,
7042 tr("Linked clone can only be created for a single machine state"));
7043 }
7044 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7045
7046 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7047
7048 HRESULT rc = pWorker->start(pProgress);
7049
7050 pP = static_cast<Progress *>(*pProgress);
7051 pP.queryInterfaceTo(aProgress.asOutParam());
7052
7053 return rc;
7054
7055}
7056
7057// public methods for internal purposes
7058/////////////////////////////////////////////////////////////////////////////
7059
7060/**
7061 * Adds the given IsModified_* flag to the dirty flags of the machine.
7062 * This must be called either during i_loadSettings or under the machine write lock.
7063 * @param fl
7064 */
7065void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7066{
7067 mData->flModifications |= fl;
7068 if (fAllowStateModification && i_isStateModificationAllowed())
7069 mData->mCurrentStateModified = true;
7070}
7071
7072/**
7073 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7074 * care of the write locking.
7075 *
7076 * @param fModifications The flag to add.
7077 */
7078void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7079{
7080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7081 i_setModified(fModification, fAllowStateModification);
7082}
7083
7084/**
7085 * Saves the registry entry of this machine to the given configuration node.
7086 *
7087 * @param aEntryNode Node to save the registry entry to.
7088 *
7089 * @note locks this object for reading.
7090 */
7091HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7092{
7093 AutoLimitedCaller autoCaller(this);
7094 AssertComRCReturnRC(autoCaller.rc());
7095
7096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 data.uuid = mData->mUuid;
7099 data.strSettingsFile = mData->m_strConfigFile;
7100
7101 return S_OK;
7102}
7103
7104/**
7105 * Calculates the absolute path of the given path taking the directory of the
7106 * machine settings file as the current directory.
7107 *
7108 * @param aPath Path to calculate the absolute path for.
7109 * @param aResult Where to put the result (used only on success, can be the
7110 * same Utf8Str instance as passed in @a aPath).
7111 * @return IPRT result.
7112 *
7113 * @note Locks this object for reading.
7114 */
7115int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7116{
7117 AutoCaller autoCaller(this);
7118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7119
7120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7121
7122 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7123
7124 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7125
7126 strSettingsDir.stripFilename();
7127 char folder[RTPATH_MAX];
7128 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7129 if (RT_SUCCESS(vrc))
7130 aResult = folder;
7131
7132 return vrc;
7133}
7134
7135/**
7136 * Copies strSource to strTarget, making it relative to the machine folder
7137 * if it is a subdirectory thereof, or simply copying it otherwise.
7138 *
7139 * @param strSource Path to evaluate and copy.
7140 * @param strTarget Buffer to receive target path.
7141 *
7142 * @note Locks this object for reading.
7143 */
7144void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7145 Utf8Str &strTarget)
7146{
7147 AutoCaller autoCaller(this);
7148 AssertComRCReturn(autoCaller.rc(), (void)0);
7149
7150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7151
7152 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7153 // use strTarget as a temporary buffer to hold the machine settings dir
7154 strTarget = mData->m_strConfigFileFull;
7155 strTarget.stripFilename();
7156 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7157 {
7158 // is relative: then append what's left
7159 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7160 // for empty paths (only possible for subdirs) use "." to avoid
7161 // triggering default settings for not present config attributes.
7162 if (strTarget.isEmpty())
7163 strTarget = ".";
7164 }
7165 else
7166 // is not relative: then overwrite
7167 strTarget = strSource;
7168}
7169
7170/**
7171 * Returns the full path to the machine's log folder in the
7172 * \a aLogFolder argument.
7173 */
7174void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7175{
7176 AutoCaller autoCaller(this);
7177 AssertComRCReturnVoid(autoCaller.rc());
7178
7179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7180
7181 char szTmp[RTPATH_MAX];
7182 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7183 if (RT_SUCCESS(vrc))
7184 {
7185 if (szTmp[0] && !mUserData.isNull())
7186 {
7187 char szTmp2[RTPATH_MAX];
7188 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7189 if (RT_SUCCESS(vrc))
7190 aLogFolder = BstrFmt("%s%c%s",
7191 szTmp2,
7192 RTPATH_DELIMITER,
7193 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7194 }
7195 else
7196 vrc = VERR_PATH_IS_RELATIVE;
7197 }
7198
7199 if (RT_FAILURE(vrc))
7200 {
7201 // fallback if VBOX_USER_LOGHOME is not set or invalid
7202 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7203 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7204 aLogFolder.append(RTPATH_DELIMITER);
7205 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7206 }
7207}
7208
7209/**
7210 * Returns the full path to the machine's log file for an given index.
7211 */
7212Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7213 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7214{
7215 Utf8Str logFolder;
7216 getLogFolder(logFolder);
7217 Assert(logFolder.length());
7218 Utf8Str log;
7219 if (idx == 0)
7220 log = Utf8StrFmt("%s%cVBox.log",
7221 logFolder.c_str(), RTPATH_DELIMITER);
7222 else
7223 log = Utf8StrFmt("%s%cVBox.log.%d",
7224 logFolder.c_str(), RTPATH_DELIMITER, idx);
7225 return log;
7226}
7227
7228/**
7229 * Returns the full path to the machine's (hardened) startup log file.
7230 */
7231Utf8Str Machine::i_getStartupLogFilename(void)
7232{
7233 Utf8Str strFilename;
7234 getLogFolder(strFilename);
7235 Assert(strFilename.length());
7236 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7237 return strFilename;
7238}
7239
7240
7241/**
7242 * Composes a unique saved state filename based on the current system time. The filename is
7243 * granular to the second so this will work so long as no more than one snapshot is taken on
7244 * a machine per second.
7245 *
7246 * Before version 4.1, we used this formula for saved state files:
7247 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7248 * which no longer works because saved state files can now be shared between the saved state of the
7249 * "saved" machine and an online snapshot, and the following would cause problems:
7250 * 1) save machine
7251 * 2) create online snapshot from that machine state --> reusing saved state file
7252 * 3) save machine again --> filename would be reused, breaking the online snapshot
7253 *
7254 * So instead we now use a timestamp.
7255 *
7256 * @param str
7257 */
7258
7259void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7260{
7261 AutoCaller autoCaller(this);
7262 AssertComRCReturnVoid(autoCaller.rc());
7263
7264 {
7265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7266 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7267 }
7268
7269 RTTIMESPEC ts;
7270 RTTimeNow(&ts);
7271 RTTIME time;
7272 RTTimeExplode(&time, &ts);
7273
7274 strStateFilePath += RTPATH_DELIMITER;
7275 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7276 time.i32Year, time.u8Month, time.u8MonthDay,
7277 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7278}
7279
7280/**
7281 * Returns the full path to the default video capture file.
7282 */
7283void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7284{
7285 AutoCaller autoCaller(this);
7286 AssertComRCReturnVoid(autoCaller.rc());
7287
7288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7289
7290 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7291 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7292 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7293}
7294
7295/**
7296 * Returns whether at least one USB controller is present for the VM.
7297 */
7298bool Machine::i_isUSBControllerPresent()
7299{
7300 AutoCaller autoCaller(this);
7301 AssertComRCReturn(autoCaller.rc(), false);
7302
7303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7304
7305 return (mUSBControllers->size() > 0);
7306}
7307
7308/**
7309 * @note Locks this object for writing, calls the client process
7310 * (inside the lock).
7311 */
7312HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7313 const Utf8Str &strFrontend,
7314 const Utf8Str &strEnvironment,
7315 ProgressProxy *aProgress)
7316{
7317 LogFlowThisFuncEnter();
7318
7319 AssertReturn(aControl, E_FAIL);
7320 AssertReturn(aProgress, E_FAIL);
7321 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7322
7323 AutoCaller autoCaller(this);
7324 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7325
7326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7327
7328 if (!mData->mRegistered)
7329 return setError(E_UNEXPECTED,
7330 tr("The machine '%s' is not registered"),
7331 mUserData->s.strName.c_str());
7332
7333 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7334
7335 if ( mData->mSession.mState == SessionState_Locked
7336 || mData->mSession.mState == SessionState_Spawning
7337 || mData->mSession.mState == SessionState_Unlocking)
7338 return setError(VBOX_E_INVALID_OBJECT_STATE,
7339 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7340 mUserData->s.strName.c_str());
7341
7342 /* may not be busy */
7343 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7344
7345 /* get the path to the executable */
7346 char szPath[RTPATH_MAX];
7347 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7348 size_t cchBufLeft = strlen(szPath);
7349 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7350 szPath[cchBufLeft] = 0;
7351 char *pszNamePart = szPath + cchBufLeft;
7352 cchBufLeft = sizeof(szPath) - cchBufLeft;
7353
7354 int vrc = VINF_SUCCESS;
7355 RTPROCESS pid = NIL_RTPROCESS;
7356
7357 RTENV env = RTENV_DEFAULT;
7358
7359 if (!strEnvironment.isEmpty())
7360 {
7361 char *newEnvStr = NULL;
7362
7363 do
7364 {
7365 /* clone the current environment */
7366 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7367 AssertRCBreakStmt(vrc2, vrc = vrc2);
7368
7369 newEnvStr = RTStrDup(strEnvironment.c_str());
7370 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7371
7372 /* put new variables to the environment
7373 * (ignore empty variable names here since RTEnv API
7374 * intentionally doesn't do that) */
7375 char *var = newEnvStr;
7376 for (char *p = newEnvStr; *p; ++p)
7377 {
7378 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7379 {
7380 *p = '\0';
7381 if (*var)
7382 {
7383 char *val = strchr(var, '=');
7384 if (val)
7385 {
7386 *val++ = '\0';
7387 vrc2 = RTEnvSetEx(env, var, val);
7388 }
7389 else
7390 vrc2 = RTEnvUnsetEx(env, var);
7391 if (RT_FAILURE(vrc2))
7392 break;
7393 }
7394 var = p + 1;
7395 }
7396 }
7397 if (RT_SUCCESS(vrc2) && *var)
7398 vrc2 = RTEnvPutEx(env, var);
7399
7400 AssertRCBreakStmt(vrc2, vrc = vrc2);
7401 }
7402 while (0);
7403
7404 if (newEnvStr != NULL)
7405 RTStrFree(newEnvStr);
7406 }
7407
7408 /* Hardened startup logging */
7409#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7410 Utf8Str strSupStartLogArg("--sup-startup-log=");
7411 {
7412 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7413 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7414 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7415 {
7416 Utf8Str strStartupLogDir = strStartupLogFile;
7417 strStartupLogDir.stripFilename();
7418 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7419 file without stripping the file. */
7420 }
7421 strSupStartLogArg.append(strStartupLogFile);
7422 }
7423 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7424#else
7425 const char *pszSupStartupLogArg = NULL;
7426#endif
7427
7428
7429#ifdef VBOX_WITH_QTGUI
7430 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7431 {
7432# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7433 /* Modify the base path so that we don't need to use ".." below. */
7434 RTPathStripTrailingSlash(szPath);
7435 RTPathStripFilename(szPath);
7436 cchBufLeft = strlen(szPath);
7437 pszNamePart = szPath + cchBufLeft;
7438 cchBufLeft = sizeof(szPath) - cchBufLeft;
7439
7440# define OSX_APP_NAME "VirtualBoxVM"
7441# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7442
7443 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7444 if ( strAppOverride.contains(".")
7445 || strAppOverride.contains("/")
7446 || strAppOverride.contains("\\")
7447 || strAppOverride.contains(":"))
7448 strAppOverride.setNull();
7449 Utf8Str strAppPath;
7450 if (!strAppOverride.isEmpty())
7451 {
7452 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7453 Utf8Str strFullPath(szPath);
7454 strFullPath.append(strAppPath);
7455 /* there is a race, but people using this deserve the failure */
7456 if (!RTFileExists(strFullPath.c_str()))
7457 strAppOverride.setNull();
7458 }
7459 if (strAppOverride.isEmpty())
7460 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7461 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7462 strcpy(pszNamePart, strAppPath.c_str());
7463# else
7464 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7465 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7466 strcpy(pszNamePart, s_szVirtualBox_exe);
7467# endif
7468
7469 Utf8Str idStr = mData->mUuid.toString();
7470 const char *apszArgs[] =
7471 {
7472 szPath,
7473 "--comment", mUserData->s.strName.c_str(),
7474 "--startvm", idStr.c_str(),
7475 "--no-startvm-errormsgbox",
7476 pszSupStartupLogArg,
7477 NULL
7478 };
7479 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7480 }
7481#else /* !VBOX_WITH_QTGUI */
7482 if (0)
7483 ;
7484#endif /* VBOX_WITH_QTGUI */
7485
7486 else
7487
7488#ifdef VBOX_WITH_VBOXSDL
7489 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7490 {
7491 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7492 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7493 strcpy(pszNamePart, s_szVBoxSDL_exe);
7494
7495 Utf8Str idStr = mData->mUuid.toString();
7496 const char *apszArgs[] =
7497 {
7498 szPath,
7499 "--comment", mUserData->s.strName.c_str(),
7500 "--startvm", idStr.c_str(),
7501 pszSupStartupLogArg,
7502 NULL
7503 };
7504 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7505 }
7506#else /* !VBOX_WITH_VBOXSDL */
7507 if (0)
7508 ;
7509#endif /* !VBOX_WITH_VBOXSDL */
7510
7511 else
7512
7513#ifdef VBOX_WITH_HEADLESS
7514 if ( strFrontend == "headless"
7515 || strFrontend == "capture"
7516 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7517 )
7518 {
7519 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7520 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7521 * and a VM works even if the server has not been installed.
7522 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7523 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7524 * differently in 4.0 and 3.x.
7525 */
7526 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7527 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7528 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7529
7530 Utf8Str idStr = mData->mUuid.toString();
7531 const char *apszArgs[] =
7532 {
7533 szPath,
7534 "--comment", mUserData->s.strName.c_str(),
7535 "--startvm", idStr.c_str(),
7536 "--vrde", "config",
7537 0, /* For "--capture". */
7538 0, /* For "--sup-startup-log". */
7539 0
7540 };
7541 unsigned iArg = 7;
7542 if (strFrontend == "capture")
7543 apszArgs[iArg++] = "--capture";
7544 apszArgs[iArg++] = pszSupStartupLogArg;
7545
7546# ifdef RT_OS_WINDOWS
7547 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7548# else
7549 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7550# endif
7551 }
7552#else /* !VBOX_WITH_HEADLESS */
7553 if (0)
7554 ;
7555#endif /* !VBOX_WITH_HEADLESS */
7556 else
7557 {
7558 RTEnvDestroy(env);
7559 return setError(E_INVALIDARG,
7560 tr("Invalid frontend name: '%s'"),
7561 strFrontend.c_str());
7562 }
7563
7564 RTEnvDestroy(env);
7565
7566 if (RT_FAILURE(vrc))
7567 return setError(VBOX_E_IPRT_ERROR,
7568 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7569 mUserData->s.strName.c_str(), vrc);
7570
7571 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7572
7573 /*
7574 * Note that we don't release the lock here before calling the client,
7575 * because it doesn't need to call us back if called with a NULL argument.
7576 * Releasing the lock here is dangerous because we didn't prepare the
7577 * launch data yet, but the client we've just started may happen to be
7578 * too fast and call LockMachine() that will fail (because of PID, etc.),
7579 * so that the Machine will never get out of the Spawning session state.
7580 */
7581
7582 /* inform the session that it will be a remote one */
7583 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7584#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7585 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7586#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7587 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7588#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7589 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7590
7591 if (FAILED(rc))
7592 {
7593 /* restore the session state */
7594 mData->mSession.mState = SessionState_Unlocked;
7595 alock.release();
7596 mParent->i_addProcessToReap(pid);
7597 /* The failure may occur w/o any error info (from RPC), so provide one */
7598 return setError(VBOX_E_VM_ERROR,
7599 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7600 }
7601
7602 /* attach launch data to the machine */
7603 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7604 mData->mSession.mRemoteControls.push_back(aControl);
7605 mData->mSession.mProgress = aProgress;
7606 mData->mSession.mPID = pid;
7607 mData->mSession.mState = SessionState_Spawning;
7608 mData->mSession.mType = strFrontend;
7609
7610 alock.release();
7611 mParent->i_addProcessToReap(pid);
7612
7613 LogFlowThisFuncLeave();
7614 return S_OK;
7615}
7616
7617/**
7618 * Returns @c true if the given session machine instance has an open direct
7619 * session (and optionally also for direct sessions which are closing) and
7620 * returns the session control machine instance if so.
7621 *
7622 * Note that when the method returns @c false, the arguments remain unchanged.
7623 *
7624 * @param aMachine Session machine object.
7625 * @param aControl Direct session control object (optional).
7626 * @param aAllowClosing If true then additionally a session which is currently
7627 * being closed will also be allowed.
7628 *
7629 * @note locks this object for reading.
7630 */
7631bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7632 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7633 bool aAllowClosing /*= false*/)
7634{
7635 AutoLimitedCaller autoCaller(this);
7636 AssertComRCReturn(autoCaller.rc(), false);
7637
7638 /* just return false for inaccessible machines */
7639 if (getObjectState().getState() != ObjectState::Ready)
7640 return false;
7641
7642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7643
7644 if ( mData->mSession.mState == SessionState_Locked
7645 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7646 )
7647 {
7648 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7649
7650 aMachine = mData->mSession.mMachine;
7651
7652 if (aControl != NULL)
7653 *aControl = mData->mSession.mDirectControl;
7654
7655 return true;
7656 }
7657
7658 return false;
7659}
7660
7661/**
7662 * Returns @c true if the given machine has an spawning direct session.
7663 *
7664 * @note locks this object for reading.
7665 */
7666bool Machine::i_isSessionSpawning()
7667{
7668 AutoLimitedCaller autoCaller(this);
7669 AssertComRCReturn(autoCaller.rc(), false);
7670
7671 /* just return false for inaccessible machines */
7672 if (getObjectState().getState() != ObjectState::Ready)
7673 return false;
7674
7675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7676
7677 if (mData->mSession.mState == SessionState_Spawning)
7678 return true;
7679
7680 return false;
7681}
7682
7683/**
7684 * Called from the client watcher thread to check for unexpected client process
7685 * death during Session_Spawning state (e.g. before it successfully opened a
7686 * direct session).
7687 *
7688 * On Win32 and on OS/2, this method is called only when we've got the
7689 * direct client's process termination notification, so it always returns @c
7690 * true.
7691 *
7692 * On other platforms, this method returns @c true if the client process is
7693 * terminated and @c false if it's still alive.
7694 *
7695 * @note Locks this object for writing.
7696 */
7697bool Machine::i_checkForSpawnFailure()
7698{
7699 AutoCaller autoCaller(this);
7700 if (!autoCaller.isOk())
7701 {
7702 /* nothing to do */
7703 LogFlowThisFunc(("Already uninitialized!\n"));
7704 return true;
7705 }
7706
7707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7708
7709 if (mData->mSession.mState != SessionState_Spawning)
7710 {
7711 /* nothing to do */
7712 LogFlowThisFunc(("Not spawning any more!\n"));
7713 return true;
7714 }
7715
7716 HRESULT rc = S_OK;
7717
7718 /* PID not yet initialized, skip check. */
7719 if (mData->mSession.mPID == NIL_RTPROCESS)
7720 return false;
7721
7722 RTPROCSTATUS status;
7723 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7724
7725 if (vrc != VERR_PROCESS_RUNNING)
7726 {
7727 Utf8Str strExtraInfo;
7728
7729#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7730 /* If the startup logfile exists and is of non-zero length, tell the
7731 user to look there for more details to encourage them to attach it
7732 when reporting startup issues. */
7733 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7734 uint64_t cbStartupLogFile = 0;
7735 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7736 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7737 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7738#endif
7739
7740 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7741 rc = setError(E_FAIL,
7742 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7743 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7744 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7745 rc = setError(E_FAIL,
7746 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7747 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7748 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7749 rc = setError(E_FAIL,
7750 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7751 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7752 else
7753 rc = setError(E_FAIL,
7754 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7755 i_getName().c_str(), vrc, strExtraInfo.c_str());
7756 }
7757
7758 if (FAILED(rc))
7759 {
7760 /* Close the remote session, remove the remote control from the list
7761 * and reset session state to Closed (@note keep the code in sync with
7762 * the relevant part in LockMachine()). */
7763
7764 Assert(mData->mSession.mRemoteControls.size() == 1);
7765 if (mData->mSession.mRemoteControls.size() == 1)
7766 {
7767 ErrorInfoKeeper eik;
7768 mData->mSession.mRemoteControls.front()->Uninitialize();
7769 }
7770
7771 mData->mSession.mRemoteControls.clear();
7772 mData->mSession.mState = SessionState_Unlocked;
7773
7774 /* finalize the progress after setting the state */
7775 if (!mData->mSession.mProgress.isNull())
7776 {
7777 mData->mSession.mProgress->notifyComplete(rc);
7778 mData->mSession.mProgress.setNull();
7779 }
7780
7781 mData->mSession.mPID = NIL_RTPROCESS;
7782
7783 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7784 return true;
7785 }
7786
7787 return false;
7788}
7789
7790/**
7791 * Checks whether the machine can be registered. If so, commits and saves
7792 * all settings.
7793 *
7794 * @note Must be called from mParent's write lock. Locks this object and
7795 * children for writing.
7796 */
7797HRESULT Machine::i_prepareRegister()
7798{
7799 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7800
7801 AutoLimitedCaller autoCaller(this);
7802 AssertComRCReturnRC(autoCaller.rc());
7803
7804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7805
7806 /* wait for state dependents to drop to zero */
7807 i_ensureNoStateDependencies();
7808
7809 if (!mData->mAccessible)
7810 return setError(VBOX_E_INVALID_OBJECT_STATE,
7811 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7812 mUserData->s.strName.c_str(),
7813 mData->mUuid.toString().c_str());
7814
7815 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7816
7817 if (mData->mRegistered)
7818 return setError(VBOX_E_INVALID_OBJECT_STATE,
7819 tr("The machine '%s' with UUID {%s} is already registered"),
7820 mUserData->s.strName.c_str(),
7821 mData->mUuid.toString().c_str());
7822
7823 HRESULT rc = S_OK;
7824
7825 // Ensure the settings are saved. If we are going to be registered and
7826 // no config file exists yet, create it by calling i_saveSettings() too.
7827 if ( (mData->flModifications)
7828 || (!mData->pMachineConfigFile->fileExists())
7829 )
7830 {
7831 rc = i_saveSettings(NULL);
7832 // no need to check whether VirtualBox.xml needs saving too since
7833 // we can't have a machine XML file rename pending
7834 if (FAILED(rc)) return rc;
7835 }
7836
7837 /* more config checking goes here */
7838
7839 if (SUCCEEDED(rc))
7840 {
7841 /* we may have had implicit modifications we want to fix on success */
7842 i_commit();
7843
7844 mData->mRegistered = true;
7845 }
7846 else
7847 {
7848 /* we may have had implicit modifications we want to cancel on failure*/
7849 i_rollback(false /* aNotify */);
7850 }
7851
7852 return rc;
7853}
7854
7855/**
7856 * Increases the number of objects dependent on the machine state or on the
7857 * registered state. Guarantees that these two states will not change at least
7858 * until #releaseStateDependency() is called.
7859 *
7860 * Depending on the @a aDepType value, additional state checks may be made.
7861 * These checks will set extended error info on failure. See
7862 * #checkStateDependency() for more info.
7863 *
7864 * If this method returns a failure, the dependency is not added and the caller
7865 * is not allowed to rely on any particular machine state or registration state
7866 * value and may return the failed result code to the upper level.
7867 *
7868 * @param aDepType Dependency type to add.
7869 * @param aState Current machine state (NULL if not interested).
7870 * @param aRegistered Current registered state (NULL if not interested).
7871 *
7872 * @note Locks this object for writing.
7873 */
7874HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7875 MachineState_T *aState /* = NULL */,
7876 BOOL *aRegistered /* = NULL */)
7877{
7878 AutoCaller autoCaller(this);
7879 AssertComRCReturnRC(autoCaller.rc());
7880
7881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7882
7883 HRESULT rc = i_checkStateDependency(aDepType);
7884 if (FAILED(rc)) return rc;
7885
7886 {
7887 if (mData->mMachineStateChangePending != 0)
7888 {
7889 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7890 * drop to zero so don't add more. It may make sense to wait a bit
7891 * and retry before reporting an error (since the pending state
7892 * transition should be really quick) but let's just assert for
7893 * now to see if it ever happens on practice. */
7894
7895 AssertFailed();
7896
7897 return setError(E_ACCESSDENIED,
7898 tr("Machine state change is in progress. Please retry the operation later."));
7899 }
7900
7901 ++mData->mMachineStateDeps;
7902 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7903 }
7904
7905 if (aState)
7906 *aState = mData->mMachineState;
7907 if (aRegistered)
7908 *aRegistered = mData->mRegistered;
7909
7910 return S_OK;
7911}
7912
7913/**
7914 * Decreases the number of objects dependent on the machine state.
7915 * Must always complete the #addStateDependency() call after the state
7916 * dependency is no more necessary.
7917 */
7918void Machine::i_releaseStateDependency()
7919{
7920 AutoCaller autoCaller(this);
7921 AssertComRCReturnVoid(autoCaller.rc());
7922
7923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7924
7925 /* releaseStateDependency() w/o addStateDependency()? */
7926 AssertReturnVoid(mData->mMachineStateDeps != 0);
7927 -- mData->mMachineStateDeps;
7928
7929 if (mData->mMachineStateDeps == 0)
7930 {
7931 /* inform i_ensureNoStateDependencies() that there are no more deps */
7932 if (mData->mMachineStateChangePending != 0)
7933 {
7934 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7935 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7936 }
7937 }
7938}
7939
7940Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7941{
7942 /* start with nothing found */
7943 Utf8Str strResult("");
7944
7945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7946
7947 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7948 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7949 // found:
7950 strResult = it->second; // source is a Utf8Str
7951
7952 return strResult;
7953}
7954
7955// protected methods
7956/////////////////////////////////////////////////////////////////////////////
7957
7958/**
7959 * Performs machine state checks based on the @a aDepType value. If a check
7960 * fails, this method will set extended error info, otherwise it will return
7961 * S_OK. It is supposed, that on failure, the caller will immediately return
7962 * the return value of this method to the upper level.
7963 *
7964 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7965 *
7966 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7967 * current state of this machine object allows to change settings of the
7968 * machine (i.e. the machine is not registered, or registered but not running
7969 * and not saved). It is useful to call this method from Machine setters
7970 * before performing any change.
7971 *
7972 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7973 * as for MutableStateDep except that if the machine is saved, S_OK is also
7974 * returned. This is useful in setters which allow changing machine
7975 * properties when it is in the saved state.
7976 *
7977 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7978 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7979 * Aborted).
7980 *
7981 * @param aDepType Dependency type to check.
7982 *
7983 * @note Non Machine based classes should use #addStateDependency() and
7984 * #releaseStateDependency() methods or the smart AutoStateDependency
7985 * template.
7986 *
7987 * @note This method must be called from under this object's read or write
7988 * lock.
7989 */
7990HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7991{
7992 switch (aDepType)
7993 {
7994 case AnyStateDep:
7995 {
7996 break;
7997 }
7998 case MutableStateDep:
7999 {
8000 if ( mData->mRegistered
8001 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8002 Paused should actually be included here... (Live Migration) */
8003 || ( mData->mMachineState != MachineState_Paused
8004 && mData->mMachineState != MachineState_Running
8005 && mData->mMachineState != MachineState_Aborted
8006 && mData->mMachineState != MachineState_Teleported
8007 && mData->mMachineState != MachineState_PoweredOff
8008 )
8009 )
8010 )
8011 return setError(VBOX_E_INVALID_VM_STATE,
8012 tr("The machine is not mutable (state is %s)"),
8013 Global::stringifyMachineState(mData->mMachineState));
8014 break;
8015 }
8016 case MutableOrSavedStateDep:
8017 {
8018 if ( mData->mRegistered
8019 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8020 Paused should actually be included here... (Live Migration) */
8021 || ( mData->mMachineState != MachineState_Paused
8022 && mData->mMachineState != MachineState_Running
8023 && mData->mMachineState != MachineState_Aborted
8024 && mData->mMachineState != MachineState_Teleported
8025 && mData->mMachineState != MachineState_Saved
8026 && mData->mMachineState != MachineState_PoweredOff
8027 )
8028 )
8029 )
8030 return setError(VBOX_E_INVALID_VM_STATE,
8031 tr("The machine is not mutable (state is %s)"),
8032 Global::stringifyMachineState(mData->mMachineState));
8033 break;
8034 }
8035 case OfflineStateDep:
8036 {
8037 if ( mData->mRegistered
8038 && ( !i_isSessionMachine()
8039 || ( mData->mMachineState != MachineState_PoweredOff
8040 && mData->mMachineState != MachineState_Saved
8041 && mData->mMachineState != MachineState_Aborted
8042 && mData->mMachineState != MachineState_Teleported
8043 )
8044 )
8045 )
8046 return setError(VBOX_E_INVALID_VM_STATE,
8047 tr("The machine is not offline (state is %s)"),
8048 Global::stringifyMachineState(mData->mMachineState));
8049 break;
8050 }
8051 }
8052
8053 return S_OK;
8054}
8055
8056/**
8057 * Helper to initialize all associated child objects and allocate data
8058 * structures.
8059 *
8060 * This method must be called as a part of the object's initialization procedure
8061 * (usually done in the #init() method).
8062 *
8063 * @note Must be called only from #init() or from #registeredInit().
8064 */
8065HRESULT Machine::initDataAndChildObjects()
8066{
8067 AutoCaller autoCaller(this);
8068 AssertComRCReturnRC(autoCaller.rc());
8069 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8070 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8071
8072 AssertReturn(!mData->mAccessible, E_FAIL);
8073
8074 /* allocate data structures */
8075 mSSData.allocate();
8076 mUserData.allocate();
8077 mHWData.allocate();
8078 mMediaData.allocate();
8079 mStorageControllers.allocate();
8080 mUSBControllers.allocate();
8081
8082 /* initialize mOSTypeId */
8083 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8084
8085 /* create associated BIOS settings object */
8086 unconst(mBIOSSettings).createObject();
8087 mBIOSSettings->init(this);
8088
8089 /* create an associated VRDE object (default is disabled) */
8090 unconst(mVRDEServer).createObject();
8091 mVRDEServer->init(this);
8092
8093 /* create associated serial port objects */
8094 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8095 {
8096 unconst(mSerialPorts[slot]).createObject();
8097 mSerialPorts[slot]->init(this, slot);
8098 }
8099
8100 /* create associated parallel port objects */
8101 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8102 {
8103 unconst(mParallelPorts[slot]).createObject();
8104 mParallelPorts[slot]->init(this, slot);
8105 }
8106
8107 /* create the audio adapter object (always present, default is disabled) */
8108 unconst(mAudioAdapter).createObject();
8109 mAudioAdapter->init(this);
8110
8111 /* create the USB device filters object (always present) */
8112 unconst(mUSBDeviceFilters).createObject();
8113 mUSBDeviceFilters->init(this);
8114
8115 /* create associated network adapter objects */
8116 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8117 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8118 {
8119 unconst(mNetworkAdapters[slot]).createObject();
8120 mNetworkAdapters[slot]->init(this, slot);
8121 }
8122
8123 /* create the bandwidth control */
8124 unconst(mBandwidthControl).createObject();
8125 mBandwidthControl->init(this);
8126
8127 return S_OK;
8128}
8129
8130/**
8131 * Helper to uninitialize all associated child objects and to free all data
8132 * structures.
8133 *
8134 * This method must be called as a part of the object's uninitialization
8135 * procedure (usually done in the #uninit() method).
8136 *
8137 * @note Must be called only from #uninit() or from #registeredInit().
8138 */
8139void Machine::uninitDataAndChildObjects()
8140{
8141 AutoCaller autoCaller(this);
8142 AssertComRCReturnVoid(autoCaller.rc());
8143 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8144 || getObjectState().getState() == ObjectState::Limited);
8145
8146 /* tell all our other child objects we've been uninitialized */
8147 if (mBandwidthControl)
8148 {
8149 mBandwidthControl->uninit();
8150 unconst(mBandwidthControl).setNull();
8151 }
8152
8153 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8154 {
8155 if (mNetworkAdapters[slot])
8156 {
8157 mNetworkAdapters[slot]->uninit();
8158 unconst(mNetworkAdapters[slot]).setNull();
8159 }
8160 }
8161
8162 if (mUSBDeviceFilters)
8163 {
8164 mUSBDeviceFilters->uninit();
8165 unconst(mUSBDeviceFilters).setNull();
8166 }
8167
8168 if (mAudioAdapter)
8169 {
8170 mAudioAdapter->uninit();
8171 unconst(mAudioAdapter).setNull();
8172 }
8173
8174 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8175 {
8176 if (mParallelPorts[slot])
8177 {
8178 mParallelPorts[slot]->uninit();
8179 unconst(mParallelPorts[slot]).setNull();
8180 }
8181 }
8182
8183 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8184 {
8185 if (mSerialPorts[slot])
8186 {
8187 mSerialPorts[slot]->uninit();
8188 unconst(mSerialPorts[slot]).setNull();
8189 }
8190 }
8191
8192 if (mVRDEServer)
8193 {
8194 mVRDEServer->uninit();
8195 unconst(mVRDEServer).setNull();
8196 }
8197
8198 if (mBIOSSettings)
8199 {
8200 mBIOSSettings->uninit();
8201 unconst(mBIOSSettings).setNull();
8202 }
8203
8204 /* Deassociate media (only when a real Machine or a SnapshotMachine
8205 * instance is uninitialized; SessionMachine instances refer to real
8206 * Machine media). This is necessary for a clean re-initialization of
8207 * the VM after successfully re-checking the accessibility state. Note
8208 * that in case of normal Machine or SnapshotMachine uninitialization (as
8209 * a result of unregistering or deleting the snapshot), outdated media
8210 * attachments will already be uninitialized and deleted, so this
8211 * code will not affect them. */
8212 if ( !!mMediaData
8213 && (!i_isSessionMachine())
8214 )
8215 {
8216 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8217 it != mMediaData->mAttachments.end();
8218 ++it)
8219 {
8220 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8221 if (pMedium.isNull())
8222 continue;
8223 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8224 AssertComRC(rc);
8225 }
8226 }
8227
8228 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8229 {
8230 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8231 if (mData->mFirstSnapshot)
8232 {
8233 // snapshots tree is protected by machine write lock; strictly
8234 // this isn't necessary here since we're deleting the entire
8235 // machine, but otherwise we assert in Snapshot::uninit()
8236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8237 mData->mFirstSnapshot->uninit();
8238 mData->mFirstSnapshot.setNull();
8239 }
8240
8241 mData->mCurrentSnapshot.setNull();
8242 }
8243
8244 /* free data structures (the essential mData structure is not freed here
8245 * since it may be still in use) */
8246 mMediaData.free();
8247 mStorageControllers.free();
8248 mUSBControllers.free();
8249 mHWData.free();
8250 mUserData.free();
8251 mSSData.free();
8252}
8253
8254/**
8255 * Returns a pointer to the Machine object for this machine that acts like a
8256 * parent for complex machine data objects such as shared folders, etc.
8257 *
8258 * For primary Machine objects and for SnapshotMachine objects, returns this
8259 * object's pointer itself. For SessionMachine objects, returns the peer
8260 * (primary) machine pointer.
8261 */
8262Machine* Machine::i_getMachine()
8263{
8264 if (i_isSessionMachine())
8265 return (Machine*)mPeer;
8266 return this;
8267}
8268
8269/**
8270 * Makes sure that there are no machine state dependents. If necessary, waits
8271 * for the number of dependents to drop to zero.
8272 *
8273 * Make sure this method is called from under this object's write lock to
8274 * guarantee that no new dependents may be added when this method returns
8275 * control to the caller.
8276 *
8277 * @note Locks this object for writing. The lock will be released while waiting
8278 * (if necessary).
8279 *
8280 * @warning To be used only in methods that change the machine state!
8281 */
8282void Machine::i_ensureNoStateDependencies()
8283{
8284 AssertReturnVoid(isWriteLockOnCurrentThread());
8285
8286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8287
8288 /* Wait for all state dependents if necessary */
8289 if (mData->mMachineStateDeps != 0)
8290 {
8291 /* lazy semaphore creation */
8292 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8293 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8294
8295 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8296 mData->mMachineStateDeps));
8297
8298 ++mData->mMachineStateChangePending;
8299
8300 /* reset the semaphore before waiting, the last dependent will signal
8301 * it */
8302 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8303
8304 alock.release();
8305
8306 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8307
8308 alock.acquire();
8309
8310 -- mData->mMachineStateChangePending;
8311 }
8312}
8313
8314/**
8315 * Changes the machine state and informs callbacks.
8316 *
8317 * This method is not intended to fail so it either returns S_OK or asserts (and
8318 * returns a failure).
8319 *
8320 * @note Locks this object for writing.
8321 */
8322HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8323{
8324 LogFlowThisFuncEnter();
8325 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8326
8327 AutoCaller autoCaller(this);
8328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8329
8330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8331
8332 /* wait for state dependents to drop to zero */
8333 i_ensureNoStateDependencies();
8334
8335 if (mData->mMachineState != aMachineState)
8336 {
8337 mData->mMachineState = aMachineState;
8338
8339 RTTimeNow(&mData->mLastStateChange);
8340
8341 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8342 }
8343
8344 LogFlowThisFuncLeave();
8345 return S_OK;
8346}
8347
8348/**
8349 * Searches for a shared folder with the given logical name
8350 * in the collection of shared folders.
8351 *
8352 * @param aName logical name of the shared folder
8353 * @param aSharedFolder where to return the found object
8354 * @param aSetError whether to set the error info if the folder is
8355 * not found
8356 * @return
8357 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8358 *
8359 * @note
8360 * must be called from under the object's lock!
8361 */
8362HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8363 ComObjPtr<SharedFolder> &aSharedFolder,
8364 bool aSetError /* = false */)
8365{
8366 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8367 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8368 it != mHWData->mSharedFolders.end();
8369 ++it)
8370 {
8371 SharedFolder *pSF = *it;
8372 AutoCaller autoCaller(pSF);
8373 if (pSF->i_getName() == aName)
8374 {
8375 aSharedFolder = pSF;
8376 rc = S_OK;
8377 break;
8378 }
8379 }
8380
8381 if (aSetError && FAILED(rc))
8382 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8383
8384 return rc;
8385}
8386
8387/**
8388 * Initializes all machine instance data from the given settings structures
8389 * from XML. The exception is the machine UUID which needs special handling
8390 * depending on the caller's use case, so the caller needs to set that herself.
8391 *
8392 * This gets called in several contexts during machine initialization:
8393 *
8394 * -- When machine XML exists on disk already and needs to be loaded into memory,
8395 * for example, from registeredInit() to load all registered machines on
8396 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8397 * attached to the machine should be part of some media registry already.
8398 *
8399 * -- During OVF import, when a machine config has been constructed from an
8400 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8401 * ensure that the media listed as attachments in the config (which have
8402 * been imported from the OVF) receive the correct registry ID.
8403 *
8404 * -- During VM cloning.
8405 *
8406 * @param config Machine settings from XML.
8407 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8408 * for each attached medium in the config.
8409 * @return
8410 */
8411HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8412 const Guid *puuidRegistry)
8413{
8414 // copy name, description, OS type, teleporter, UTC etc.
8415 mUserData->s = config.machineUserData;
8416
8417 // Decode the Icon overide data from config userdata and set onto Machine.
8418 #define DECODE_STR_MAX _1M
8419 const char* pszStr = config.machineUserData.ovIcon.c_str();
8420 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8421 if (cbOut > DECODE_STR_MAX)
8422 return setError(E_FAIL,
8423 tr("Icon Data too long.'%d' > '%d'"),
8424 cbOut,
8425 DECODE_STR_MAX);
8426 com::SafeArray<BYTE> iconByte(cbOut);
8427 int vrc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8428 if (RT_FAILURE(vrc))
8429 return setError(E_FAIL,
8430 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8431 pszStr,
8432 vrc);
8433 mUserData->mIcon.resize(iconByte.size());
8434 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8435
8436 // look up the object by Id to check it is valid
8437 ComPtr<IGuestOSType> guestOSType;
8438 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8439 guestOSType.asOutParam());
8440 if (FAILED(rc)) return rc;
8441
8442 // stateFile (optional)
8443 if (config.strStateFile.isEmpty())
8444 mSSData->strStateFilePath.setNull();
8445 else
8446 {
8447 Utf8Str stateFilePathFull(config.strStateFile);
8448 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8449 if (RT_FAILURE(vrc))
8450 return setError(E_FAIL,
8451 tr("Invalid saved state file path '%s' (%Rrc)"),
8452 config.strStateFile.c_str(),
8453 vrc);
8454 mSSData->strStateFilePath = stateFilePathFull;
8455 }
8456
8457 // snapshot folder needs special processing so set it again
8458 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8459 if (FAILED(rc)) return rc;
8460
8461 /* Copy the extra data items (Not in any case config is already the same as
8462 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8463 * make sure the extra data map is copied). */
8464 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8465
8466 /* currentStateModified (optional, default is true) */
8467 mData->mCurrentStateModified = config.fCurrentStateModified;
8468
8469 mData->mLastStateChange = config.timeLastStateChange;
8470
8471 /*
8472 * note: all mUserData members must be assigned prior this point because
8473 * we need to commit changes in order to let mUserData be shared by all
8474 * snapshot machine instances.
8475 */
8476 mUserData.commitCopy();
8477
8478 // machine registry, if present (must be loaded before snapshots)
8479 if (config.canHaveOwnMediaRegistry())
8480 {
8481 // determine machine folder
8482 Utf8Str strMachineFolder = i_getSettingsFileFull();
8483 strMachineFolder.stripFilename();
8484 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8485 config.mediaRegistry,
8486 strMachineFolder);
8487 if (FAILED(rc)) return rc;
8488 }
8489
8490 /* Snapshot node (optional) */
8491 size_t cRootSnapshots;
8492 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8493 {
8494 // there must be only one root snapshot
8495 Assert(cRootSnapshots == 1);
8496
8497 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8498
8499 rc = i_loadSnapshot(snap,
8500 config.uuidCurrentSnapshot,
8501 NULL); // no parent == first snapshot
8502 if (FAILED(rc)) return rc;
8503 }
8504
8505 // hardware data
8506 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8507 if (FAILED(rc)) return rc;
8508
8509 // load storage controllers
8510 rc = i_loadStorageControllers(config.storageMachine,
8511 puuidRegistry,
8512 NULL /* puuidSnapshot */);
8513 if (FAILED(rc)) return rc;
8514
8515 /*
8516 * NOTE: the assignment below must be the last thing to do,
8517 * otherwise it will be not possible to change the settings
8518 * somewhere in the code above because all setters will be
8519 * blocked by i_checkStateDependency(MutableStateDep).
8520 */
8521
8522 /* set the machine state to Aborted or Saved when appropriate */
8523 if (config.fAborted)
8524 {
8525 mSSData->strStateFilePath.setNull();
8526
8527 /* no need to use i_setMachineState() during init() */
8528 mData->mMachineState = MachineState_Aborted;
8529 }
8530 else if (!mSSData->strStateFilePath.isEmpty())
8531 {
8532 /* no need to use i_setMachineState() during init() */
8533 mData->mMachineState = MachineState_Saved;
8534 }
8535
8536 // after loading settings, we are no longer different from the XML on disk
8537 mData->flModifications = 0;
8538
8539 return S_OK;
8540}
8541
8542/**
8543 * Recursively loads all snapshots starting from the given.
8544 *
8545 * @param aNode <Snapshot> node.
8546 * @param aCurSnapshotId Current snapshot ID from the settings file.
8547 * @param aParentSnapshot Parent snapshot.
8548 */
8549HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8550 const Guid &aCurSnapshotId,
8551 Snapshot *aParentSnapshot)
8552{
8553 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8554 AssertReturn(!i_isSessionMachine(), E_FAIL);
8555
8556 HRESULT rc = S_OK;
8557
8558 Utf8Str strStateFile;
8559 if (!data.strStateFile.isEmpty())
8560 {
8561 /* optional */
8562 strStateFile = data.strStateFile;
8563 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8564 if (RT_FAILURE(vrc))
8565 return setError(E_FAIL,
8566 tr("Invalid saved state file path '%s' (%Rrc)"),
8567 strStateFile.c_str(),
8568 vrc);
8569 }
8570
8571 /* create a snapshot machine object */
8572 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8573 pSnapshotMachine.createObject();
8574 rc = pSnapshotMachine->initFromSettings(this,
8575 data.hardware,
8576 &data.debugging,
8577 &data.autostart,
8578 data.storage,
8579 data.uuid.ref(),
8580 strStateFile);
8581 if (FAILED(rc)) return rc;
8582
8583 /* create a snapshot object */
8584 ComObjPtr<Snapshot> pSnapshot;
8585 pSnapshot.createObject();
8586 /* initialize the snapshot */
8587 rc = pSnapshot->init(mParent, // VirtualBox object
8588 data.uuid,
8589 data.strName,
8590 data.strDescription,
8591 data.timestamp,
8592 pSnapshotMachine,
8593 aParentSnapshot);
8594 if (FAILED(rc)) return rc;
8595
8596 /* memorize the first snapshot if necessary */
8597 if (!mData->mFirstSnapshot)
8598 mData->mFirstSnapshot = pSnapshot;
8599
8600 /* memorize the current snapshot when appropriate */
8601 if ( !mData->mCurrentSnapshot
8602 && pSnapshot->i_getId() == aCurSnapshotId
8603 )
8604 mData->mCurrentSnapshot = pSnapshot;
8605
8606 // now create the children
8607 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8608 it != data.llChildSnapshots.end();
8609 ++it)
8610 {
8611 const settings::Snapshot &childData = *it;
8612 // recurse
8613 rc = i_loadSnapshot(childData,
8614 aCurSnapshotId,
8615 pSnapshot); // parent = the one we created above
8616 if (FAILED(rc)) return rc;
8617 }
8618
8619 return rc;
8620}
8621
8622/**
8623 * Loads settings into mHWData.
8624 *
8625 * @param data Reference to the hardware settings.
8626 * @param pDbg Pointer to the debugging settings.
8627 * @param pAutostart Pointer to the autostart settings.
8628 */
8629HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8630 const settings::Autostart *pAutostart)
8631{
8632 AssertReturn(!i_isSessionMachine(), E_FAIL);
8633
8634 HRESULT rc = S_OK;
8635
8636 try
8637 {
8638 /* The hardware version attribute (optional). */
8639 mHWData->mHWVersion = data.strVersion;
8640 mHWData->mHardwareUUID = data.uuid;
8641
8642 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8643 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8644 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8645 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8646 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8647 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8648 mHWData->mPAEEnabled = data.fPAE;
8649 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8650 mHWData->mLongMode = data.enmLongMode;
8651 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8652 mHWData->mCPUCount = data.cCPUs;
8653 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8654 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8655
8656 // cpu
8657 if (mHWData->mCPUHotPlugEnabled)
8658 {
8659 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8660 it != data.llCpus.end();
8661 ++it)
8662 {
8663 const settings::Cpu &cpu = *it;
8664
8665 mHWData->mCPUAttached[cpu.ulId] = true;
8666 }
8667 }
8668
8669 // cpuid leafs
8670 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8671 it != data.llCpuIdLeafs.end();
8672 ++it)
8673 {
8674 const settings::CpuIdLeaf &leaf = *it;
8675
8676 switch (leaf.ulId)
8677 {
8678 case 0x0:
8679 case 0x1:
8680 case 0x2:
8681 case 0x3:
8682 case 0x4:
8683 case 0x5:
8684 case 0x6:
8685 case 0x7:
8686 case 0x8:
8687 case 0x9:
8688 case 0xA:
8689 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8690 break;
8691
8692 case 0x80000000:
8693 case 0x80000001:
8694 case 0x80000002:
8695 case 0x80000003:
8696 case 0x80000004:
8697 case 0x80000005:
8698 case 0x80000006:
8699 case 0x80000007:
8700 case 0x80000008:
8701 case 0x80000009:
8702 case 0x8000000A:
8703 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8704 break;
8705
8706 default:
8707 /* just ignore */
8708 break;
8709 }
8710 }
8711
8712 mHWData->mMemorySize = data.ulMemorySizeMB;
8713 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8714
8715 // boot order
8716 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8717 {
8718 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8719 if (it == data.mapBootOrder.end())
8720 mHWData->mBootOrder[i] = DeviceType_Null;
8721 else
8722 mHWData->mBootOrder[i] = it->second;
8723 }
8724
8725 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8726 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8727 mHWData->mMonitorCount = data.cMonitors;
8728 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8729 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8730 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8731 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8732 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8733 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8734 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8735 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8736 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8737 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8738 if (!data.strVideoCaptureFile.isEmpty())
8739 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8740 else
8741 mHWData->mVideoCaptureFile.setNull();
8742 mHWData->mFirmwareType = data.firmwareType;
8743 mHWData->mPointingHIDType = data.pointingHIDType;
8744 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8745 mHWData->mChipsetType = data.chipsetType;
8746 mHWData->mParavirtProvider = data.paravirtProvider;
8747 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8748 mHWData->mHPETEnabled = data.fHPETEnabled;
8749
8750 /* VRDEServer */
8751 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8752 if (FAILED(rc)) return rc;
8753
8754 /* BIOS */
8755 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8756 if (FAILED(rc)) return rc;
8757
8758 // Bandwidth control (must come before network adapters)
8759 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8760 if (FAILED(rc)) return rc;
8761
8762 /* Shared folders */
8763 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8764 it != data.usbSettings.llUSBControllers.end();
8765 ++it)
8766 {
8767 const settings::USBController &settingsCtrl = *it;
8768 ComObjPtr<USBController> newCtrl;
8769
8770 newCtrl.createObject();
8771 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8772 mUSBControllers->push_back(newCtrl);
8773 }
8774
8775 /* USB device filters */
8776 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8777 if (FAILED(rc)) return rc;
8778
8779 // network adapters
8780 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8781 size_t oldCount = mNetworkAdapters.size();
8782 if (newCount > oldCount)
8783 {
8784 mNetworkAdapters.resize(newCount);
8785 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8786 {
8787 unconst(mNetworkAdapters[slot]).createObject();
8788 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8789 }
8790 }
8791 else if (newCount < oldCount)
8792 mNetworkAdapters.resize(newCount);
8793 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8794 it != data.llNetworkAdapters.end();
8795 ++it)
8796 {
8797 const settings::NetworkAdapter &nic = *it;
8798
8799 /* slot unicity is guaranteed by XML Schema */
8800 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8801 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8802 if (FAILED(rc)) return rc;
8803 }
8804
8805 // serial ports
8806 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8807 it != data.llSerialPorts.end();
8808 ++it)
8809 {
8810 const settings::SerialPort &s = *it;
8811
8812 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8813 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8814 if (FAILED(rc)) return rc;
8815 }
8816
8817 // parallel ports (optional)
8818 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8819 it != data.llParallelPorts.end();
8820 ++it)
8821 {
8822 const settings::ParallelPort &p = *it;
8823
8824 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8825 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8826 if (FAILED(rc)) return rc;
8827 }
8828
8829 /* AudioAdapter */
8830 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8831 if (FAILED(rc)) return rc;
8832
8833 /* Shared folders */
8834 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8835 it != data.llSharedFolders.end();
8836 ++it)
8837 {
8838 const settings::SharedFolder &sf = *it;
8839
8840 ComObjPtr<SharedFolder> sharedFolder;
8841 /* Check for double entries. Not allowed! */
8842 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8843 if (SUCCEEDED(rc))
8844 return setError(VBOX_E_OBJECT_IN_USE,
8845 tr("Shared folder named '%s' already exists"),
8846 sf.strName.c_str());
8847
8848 /* Create the new shared folder. Don't break on error. This will be
8849 * reported when the machine starts. */
8850 sharedFolder.createObject();
8851 rc = sharedFolder->init(i_getMachine(),
8852 sf.strName,
8853 sf.strHostPath,
8854 RT_BOOL(sf.fWritable),
8855 RT_BOOL(sf.fAutoMount),
8856 false /* fFailOnError */);
8857 if (FAILED(rc)) return rc;
8858 mHWData->mSharedFolders.push_back(sharedFolder);
8859 }
8860
8861 // Clipboard
8862 mHWData->mClipboardMode = data.clipboardMode;
8863
8864 // drag'n'drop
8865 mHWData->mDnDMode = data.dndMode;
8866
8867 // guest settings
8868 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8869
8870 // IO settings
8871 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8872 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8873
8874 // Host PCI devices
8875 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8876 it != data.pciAttachments.end();
8877 ++it)
8878 {
8879 const settings::HostPCIDeviceAttachment &hpda = *it;
8880 ComObjPtr<PCIDeviceAttachment> pda;
8881
8882 pda.createObject();
8883 pda->i_loadSettings(this, hpda);
8884 mHWData->mPCIDeviceAssignments.push_back(pda);
8885 }
8886
8887 /*
8888 * (The following isn't really real hardware, but it lives in HWData
8889 * for reasons of convenience.)
8890 */
8891
8892#ifdef VBOX_WITH_GUEST_PROPS
8893 /* Guest properties (optional) */
8894 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8895 it != data.llGuestProperties.end();
8896 ++it)
8897 {
8898 const settings::GuestProperty &prop = *it;
8899 uint32_t fFlags = guestProp::NILFLAG;
8900 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8901 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8902 mHWData->mGuestProperties[prop.strName] = property;
8903 }
8904
8905 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8906#endif /* VBOX_WITH_GUEST_PROPS defined */
8907
8908 rc = i_loadDebugging(pDbg);
8909 if (FAILED(rc))
8910 return rc;
8911
8912 mHWData->mAutostart = *pAutostart;
8913
8914 /* default frontend */
8915 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8916 }
8917 catch(std::bad_alloc &)
8918 {
8919 return E_OUTOFMEMORY;
8920 }
8921
8922 AssertComRC(rc);
8923 return rc;
8924}
8925
8926/**
8927 * Called from Machine::loadHardware() to load the debugging settings of the
8928 * machine.
8929 *
8930 * @param pDbg Pointer to the settings.
8931 */
8932HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8933{
8934 mHWData->mDebugging = *pDbg;
8935 /* no more processing currently required, this will probably change. */
8936 return S_OK;
8937}
8938
8939/**
8940 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8941 *
8942 * @param data
8943 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8944 * @param puuidSnapshot
8945 * @return
8946 */
8947HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8948 const Guid *puuidRegistry,
8949 const Guid *puuidSnapshot)
8950{
8951 AssertReturn(!i_isSessionMachine(), E_FAIL);
8952
8953 HRESULT rc = S_OK;
8954
8955 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8956 it != data.llStorageControllers.end();
8957 ++it)
8958 {
8959 const settings::StorageController &ctlData = *it;
8960
8961 ComObjPtr<StorageController> pCtl;
8962 /* Try to find one with the name first. */
8963 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8964 if (SUCCEEDED(rc))
8965 return setError(VBOX_E_OBJECT_IN_USE,
8966 tr("Storage controller named '%s' already exists"),
8967 ctlData.strName.c_str());
8968
8969 pCtl.createObject();
8970 rc = pCtl->init(this,
8971 ctlData.strName,
8972 ctlData.storageBus,
8973 ctlData.ulInstance,
8974 ctlData.fBootable);
8975 if (FAILED(rc)) return rc;
8976
8977 mStorageControllers->push_back(pCtl);
8978
8979 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8980 if (FAILED(rc)) return rc;
8981
8982 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8983 if (FAILED(rc)) return rc;
8984
8985 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8986 if (FAILED(rc)) return rc;
8987
8988 /* Set IDE emulation settings (only for AHCI controller). */
8989 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8990 {
8991 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8992 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8993 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8994 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8995 )
8996 return rc;
8997 }
8998
8999 /* Load the attached devices now. */
9000 rc = i_loadStorageDevices(pCtl,
9001 ctlData,
9002 puuidRegistry,
9003 puuidSnapshot);
9004 if (FAILED(rc)) return rc;
9005 }
9006
9007 return S_OK;
9008}
9009
9010/**
9011 * Called from i_loadStorageControllers for a controller's devices.
9012 *
9013 * @param aStorageController
9014 * @param data
9015 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9016 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9017 * @return
9018 */
9019HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9020 const settings::StorageController &data,
9021 const Guid *puuidRegistry,
9022 const Guid *puuidSnapshot)
9023{
9024 HRESULT rc = S_OK;
9025
9026 /* paranoia: detect duplicate attachments */
9027 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9028 it != data.llAttachedDevices.end();
9029 ++it)
9030 {
9031 const settings::AttachedDevice &ad = *it;
9032
9033 for (settings::AttachedDevicesList::const_iterator it2 = it;
9034 it2 != data.llAttachedDevices.end();
9035 ++it2)
9036 {
9037 if (it == it2)
9038 continue;
9039
9040 const settings::AttachedDevice &ad2 = *it2;
9041
9042 if ( ad.lPort == ad2.lPort
9043 && ad.lDevice == ad2.lDevice)
9044 {
9045 return setError(E_FAIL,
9046 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9047 aStorageController->i_getName().c_str(),
9048 ad.lPort,
9049 ad.lDevice,
9050 mUserData->s.strName.c_str());
9051 }
9052 }
9053 }
9054
9055 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9056 it != data.llAttachedDevices.end();
9057 ++it)
9058 {
9059 const settings::AttachedDevice &dev = *it;
9060 ComObjPtr<Medium> medium;
9061
9062 switch (dev.deviceType)
9063 {
9064 case DeviceType_Floppy:
9065 case DeviceType_DVD:
9066 if (dev.strHostDriveSrc.isNotEmpty())
9067 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9068 false /* fRefresh */, medium);
9069 else
9070 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9071 dev.uuid,
9072 false /* fRefresh */,
9073 false /* aSetError */,
9074 medium);
9075 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9076 // This is not an error. The host drive or UUID might have vanished, so just go
9077 // ahead without this removeable medium attachment
9078 rc = S_OK;
9079 break;
9080
9081 case DeviceType_HardDisk:
9082 {
9083 /* find a hard disk by UUID */
9084 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9085 if (FAILED(rc))
9086 {
9087 if (i_isSnapshotMachine())
9088 {
9089 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9090 // so the user knows that the bad disk is in a snapshot somewhere
9091 com::ErrorInfo info;
9092 return setError(E_FAIL,
9093 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9094 puuidSnapshot->raw(),
9095 info.getText().raw());
9096 }
9097 else
9098 return rc;
9099 }
9100
9101 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9102
9103 if (medium->i_getType() == MediumType_Immutable)
9104 {
9105 if (i_isSnapshotMachine())
9106 return setError(E_FAIL,
9107 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9108 "of the virtual machine '%s' ('%s')"),
9109 medium->i_getLocationFull().c_str(),
9110 dev.uuid.raw(),
9111 puuidSnapshot->raw(),
9112 mUserData->s.strName.c_str(),
9113 mData->m_strConfigFileFull.c_str());
9114
9115 return setError(E_FAIL,
9116 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9117 medium->i_getLocationFull().c_str(),
9118 dev.uuid.raw(),
9119 mUserData->s.strName.c_str(),
9120 mData->m_strConfigFileFull.c_str());
9121 }
9122
9123 if (medium->i_getType() == MediumType_MultiAttach)
9124 {
9125 if (i_isSnapshotMachine())
9126 return setError(E_FAIL,
9127 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9128 "of the virtual machine '%s' ('%s')"),
9129 medium->i_getLocationFull().c_str(),
9130 dev.uuid.raw(),
9131 puuidSnapshot->raw(),
9132 mUserData->s.strName.c_str(),
9133 mData->m_strConfigFileFull.c_str());
9134
9135 return setError(E_FAIL,
9136 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9137 medium->i_getLocationFull().c_str(),
9138 dev.uuid.raw(),
9139 mUserData->s.strName.c_str(),
9140 mData->m_strConfigFileFull.c_str());
9141 }
9142
9143 if ( !i_isSnapshotMachine()
9144 && medium->i_getChildren().size() != 0
9145 )
9146 return setError(E_FAIL,
9147 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9148 "because it has %d differencing child hard disks"),
9149 medium->i_getLocationFull().c_str(),
9150 dev.uuid.raw(),
9151 mUserData->s.strName.c_str(),
9152 mData->m_strConfigFileFull.c_str(),
9153 medium->i_getChildren().size());
9154
9155 if (i_findAttachment(mMediaData->mAttachments,
9156 medium))
9157 return setError(E_FAIL,
9158 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9159 medium->i_getLocationFull().c_str(),
9160 dev.uuid.raw(),
9161 mUserData->s.strName.c_str(),
9162 mData->m_strConfigFileFull.c_str());
9163
9164 break;
9165 }
9166
9167 default:
9168 return setError(E_FAIL,
9169 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9170 medium->i_getLocationFull().c_str(),
9171 mUserData->s.strName.c_str(),
9172 mData->m_strConfigFileFull.c_str());
9173 }
9174
9175 if (FAILED(rc))
9176 break;
9177
9178 /* Bandwidth groups are loaded at this point. */
9179 ComObjPtr<BandwidthGroup> pBwGroup;
9180
9181 if (!dev.strBwGroup.isEmpty())
9182 {
9183 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9184 if (FAILED(rc))
9185 return setError(E_FAIL,
9186 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9187 medium->i_getLocationFull().c_str(),
9188 dev.strBwGroup.c_str(),
9189 mUserData->s.strName.c_str(),
9190 mData->m_strConfigFileFull.c_str());
9191 pBwGroup->i_reference();
9192 }
9193
9194 const Bstr controllerName = aStorageController->i_getName();
9195 ComObjPtr<MediumAttachment> pAttachment;
9196 pAttachment.createObject();
9197 rc = pAttachment->init(this,
9198 medium,
9199 controllerName,
9200 dev.lPort,
9201 dev.lDevice,
9202 dev.deviceType,
9203 false,
9204 dev.fPassThrough,
9205 dev.fTempEject,
9206 dev.fNonRotational,
9207 dev.fDiscard,
9208 dev.fHotPluggable,
9209 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9210 if (FAILED(rc)) break;
9211
9212 /* associate the medium with this machine and snapshot */
9213 if (!medium.isNull())
9214 {
9215 AutoCaller medCaller(medium);
9216 if (FAILED(medCaller.rc())) return medCaller.rc();
9217 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9218
9219 if (i_isSnapshotMachine())
9220 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9221 else
9222 rc = medium->i_addBackReference(mData->mUuid);
9223 /* If the medium->addBackReference fails it sets an appropriate
9224 * error message, so no need to do any guesswork here. */
9225
9226 if (puuidRegistry)
9227 // caller wants registry ID to be set on all attached media (OVF import case)
9228 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9229 }
9230
9231 if (FAILED(rc))
9232 break;
9233
9234 /* back up mMediaData to let registeredInit() properly rollback on failure
9235 * (= limited accessibility) */
9236 i_setModified(IsModified_Storage);
9237 mMediaData.backup();
9238 mMediaData->mAttachments.push_back(pAttachment);
9239 }
9240
9241 return rc;
9242}
9243
9244/**
9245 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9246 *
9247 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9248 * @param aSnapshot where to return the found snapshot
9249 * @param aSetError true to set extended error info on failure
9250 */
9251HRESULT Machine::i_findSnapshotById(const Guid &aId,
9252 ComObjPtr<Snapshot> &aSnapshot,
9253 bool aSetError /* = false */)
9254{
9255 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9256
9257 if (!mData->mFirstSnapshot)
9258 {
9259 if (aSetError)
9260 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9261 return E_FAIL;
9262 }
9263
9264 if (aId.isZero())
9265 aSnapshot = mData->mFirstSnapshot;
9266 else
9267 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9268
9269 if (!aSnapshot)
9270 {
9271 if (aSetError)
9272 return setError(E_FAIL,
9273 tr("Could not find a snapshot with UUID {%s}"),
9274 aId.toString().c_str());
9275 return E_FAIL;
9276 }
9277
9278 return S_OK;
9279}
9280
9281/**
9282 * Returns the snapshot with the given name or fails of no such snapshot.
9283 *
9284 * @param aName snapshot name to find
9285 * @param aSnapshot where to return the found snapshot
9286 * @param aSetError true to set extended error info on failure
9287 */
9288HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9289 ComObjPtr<Snapshot> &aSnapshot,
9290 bool aSetError /* = false */)
9291{
9292 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9293
9294 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9295
9296 if (!mData->mFirstSnapshot)
9297 {
9298 if (aSetError)
9299 return setError(VBOX_E_OBJECT_NOT_FOUND,
9300 tr("This machine does not have any snapshots"));
9301 return VBOX_E_OBJECT_NOT_FOUND;
9302 }
9303
9304 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9305
9306 if (!aSnapshot)
9307 {
9308 if (aSetError)
9309 return setError(VBOX_E_OBJECT_NOT_FOUND,
9310 tr("Could not find a snapshot named '%s'"), strName.c_str());
9311 return VBOX_E_OBJECT_NOT_FOUND;
9312 }
9313
9314 return S_OK;
9315}
9316
9317/**
9318 * Returns a storage controller object with the given name.
9319 *
9320 * @param aName storage controller name to find
9321 * @param aStorageController where to return the found storage controller
9322 * @param aSetError true to set extended error info on failure
9323 */
9324HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9325 ComObjPtr<StorageController> &aStorageController,
9326 bool aSetError /* = false */)
9327{
9328 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9329
9330 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9331 it != mStorageControllers->end();
9332 ++it)
9333 {
9334 if ((*it)->i_getName() == aName)
9335 {
9336 aStorageController = (*it);
9337 return S_OK;
9338 }
9339 }
9340
9341 if (aSetError)
9342 return setError(VBOX_E_OBJECT_NOT_FOUND,
9343 tr("Could not find a storage controller named '%s'"),
9344 aName.c_str());
9345 return VBOX_E_OBJECT_NOT_FOUND;
9346}
9347
9348/**
9349 * Returns a USB controller object with the given name.
9350 *
9351 * @param aName USB controller name to find
9352 * @param aUSBController where to return the found USB controller
9353 * @param aSetError true to set extended error info on failure
9354 */
9355HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9356 ComObjPtr<USBController> &aUSBController,
9357 bool aSetError /* = false */)
9358{
9359 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9360
9361 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9362 it != mUSBControllers->end();
9363 ++it)
9364 {
9365 if ((*it)->i_getName() == aName)
9366 {
9367 aUSBController = (*it);
9368 return S_OK;
9369 }
9370 }
9371
9372 if (aSetError)
9373 return setError(VBOX_E_OBJECT_NOT_FOUND,
9374 tr("Could not find a storage controller named '%s'"),
9375 aName.c_str());
9376 return VBOX_E_OBJECT_NOT_FOUND;
9377}
9378
9379/**
9380 * Returns the number of USB controller instance of the given type.
9381 *
9382 * @param enmType USB controller type.
9383 */
9384ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9385{
9386 ULONG cCtrls = 0;
9387
9388 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9389 it != mUSBControllers->end();
9390 ++it)
9391 {
9392 if ((*it)->i_getControllerType() == enmType)
9393 cCtrls++;
9394 }
9395
9396 return cCtrls;
9397}
9398
9399HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9400 MediaData::AttachmentList &atts)
9401{
9402 AutoCaller autoCaller(this);
9403 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9404
9405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9406
9407 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9408 it != mMediaData->mAttachments.end();
9409 ++it)
9410 {
9411 const ComObjPtr<MediumAttachment> &pAtt = *it;
9412 // should never happen, but deal with NULL pointers in the list.
9413 AssertStmt(!pAtt.isNull(), continue);
9414
9415 // getControllerName() needs caller+read lock
9416 AutoCaller autoAttCaller(pAtt);
9417 if (FAILED(autoAttCaller.rc()))
9418 {
9419 atts.clear();
9420 return autoAttCaller.rc();
9421 }
9422 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9423
9424 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9425 atts.push_back(pAtt);
9426 }
9427
9428 return S_OK;
9429}
9430
9431
9432/**
9433 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9434 * file if the machine name was changed and about creating a new settings file
9435 * if this is a new machine.
9436 *
9437 * @note Must be never called directly but only from #saveSettings().
9438 */
9439HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9440{
9441 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9442
9443 HRESULT rc = S_OK;
9444
9445 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9446
9447 /// @todo need to handle primary group change, too
9448
9449 /* attempt to rename the settings file if machine name is changed */
9450 if ( mUserData->s.fNameSync
9451 && mUserData.isBackedUp()
9452 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9453 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9454 )
9455 {
9456 bool dirRenamed = false;
9457 bool fileRenamed = false;
9458
9459 Utf8Str configFile, newConfigFile;
9460 Utf8Str configFilePrev, newConfigFilePrev;
9461 Utf8Str configDir, newConfigDir;
9462
9463 do
9464 {
9465 int vrc = VINF_SUCCESS;
9466
9467 Utf8Str name = mUserData.backedUpData()->s.strName;
9468 Utf8Str newName = mUserData->s.strName;
9469 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9470 if (group == "/")
9471 group.setNull();
9472 Utf8Str newGroup = mUserData->s.llGroups.front();
9473 if (newGroup == "/")
9474 newGroup.setNull();
9475
9476 configFile = mData->m_strConfigFileFull;
9477
9478 /* first, rename the directory if it matches the group and machine name */
9479 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9480 group.c_str(), RTPATH_DELIMITER, name.c_str());
9481 /** @todo hack, make somehow use of ComposeMachineFilename */
9482 if (mUserData->s.fDirectoryIncludesUUID)
9483 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9484 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9485 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9486 /** @todo hack, make somehow use of ComposeMachineFilename */
9487 if (mUserData->s.fDirectoryIncludesUUID)
9488 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9489 configDir = configFile;
9490 configDir.stripFilename();
9491 newConfigDir = configDir;
9492 if ( configDir.length() >= groupPlusName.length()
9493 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9494 groupPlusName.c_str()))
9495 {
9496 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9497 Utf8Str newConfigBaseDir(newConfigDir);
9498 newConfigDir.append(newGroupPlusName);
9499 /* consistency: use \ if appropriate on the platform */
9500 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9501 /* new dir and old dir cannot be equal here because of 'if'
9502 * above and because name != newName */
9503 Assert(configDir != newConfigDir);
9504 if (!fSettingsFileIsNew)
9505 {
9506 /* perform real rename only if the machine is not new */
9507 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9508 if ( vrc == VERR_FILE_NOT_FOUND
9509 || vrc == VERR_PATH_NOT_FOUND)
9510 {
9511 /* create the parent directory, then retry renaming */
9512 Utf8Str parent(newConfigDir);
9513 parent.stripFilename();
9514 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9515 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9516 }
9517 if (RT_FAILURE(vrc))
9518 {
9519 rc = setError(E_FAIL,
9520 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9521 configDir.c_str(),
9522 newConfigDir.c_str(),
9523 vrc);
9524 break;
9525 }
9526 /* delete subdirectories which are no longer needed */
9527 Utf8Str dir(configDir);
9528 dir.stripFilename();
9529 while (dir != newConfigBaseDir && dir != ".")
9530 {
9531 vrc = RTDirRemove(dir.c_str());
9532 if (RT_FAILURE(vrc))
9533 break;
9534 dir.stripFilename();
9535 }
9536 dirRenamed = true;
9537 }
9538 }
9539
9540 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9541 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9542
9543 /* then try to rename the settings file itself */
9544 if (newConfigFile != configFile)
9545 {
9546 /* get the path to old settings file in renamed directory */
9547 configFile = Utf8StrFmt("%s%c%s",
9548 newConfigDir.c_str(),
9549 RTPATH_DELIMITER,
9550 RTPathFilename(configFile.c_str()));
9551 if (!fSettingsFileIsNew)
9552 {
9553 /* perform real rename only if the machine is not new */
9554 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9555 if (RT_FAILURE(vrc))
9556 {
9557 rc = setError(E_FAIL,
9558 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9559 configFile.c_str(),
9560 newConfigFile.c_str(),
9561 vrc);
9562 break;
9563 }
9564 fileRenamed = true;
9565 configFilePrev = configFile;
9566 configFilePrev += "-prev";
9567 newConfigFilePrev = newConfigFile;
9568 newConfigFilePrev += "-prev";
9569 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9570 }
9571 }
9572
9573 // update m_strConfigFileFull amd mConfigFile
9574 mData->m_strConfigFileFull = newConfigFile;
9575 // compute the relative path too
9576 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9577
9578 // store the old and new so that VirtualBox::i_saveSettings() can update
9579 // the media registry
9580 if ( mData->mRegistered
9581 && (configDir != newConfigDir || configFile != newConfigFile))
9582 {
9583 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9584
9585 if (pfNeedsGlobalSaveSettings)
9586 *pfNeedsGlobalSaveSettings = true;
9587 }
9588
9589 // in the saved state file path, replace the old directory with the new directory
9590 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9591 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9592
9593 // and do the same thing for the saved state file paths of all the online snapshots
9594 if (mData->mFirstSnapshot)
9595 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9596 newConfigDir.c_str());
9597 }
9598 while (0);
9599
9600 if (FAILED(rc))
9601 {
9602 /* silently try to rename everything back */
9603 if (fileRenamed)
9604 {
9605 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9606 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9607 }
9608 if (dirRenamed)
9609 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9610 }
9611
9612 if (FAILED(rc)) return rc;
9613 }
9614
9615 if (fSettingsFileIsNew)
9616 {
9617 /* create a virgin config file */
9618 int vrc = VINF_SUCCESS;
9619
9620 /* ensure the settings directory exists */
9621 Utf8Str path(mData->m_strConfigFileFull);
9622 path.stripFilename();
9623 if (!RTDirExists(path.c_str()))
9624 {
9625 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9626 if (RT_FAILURE(vrc))
9627 {
9628 return setError(E_FAIL,
9629 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9630 path.c_str(),
9631 vrc);
9632 }
9633 }
9634
9635 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9636 path = Utf8Str(mData->m_strConfigFileFull);
9637 RTFILE f = NIL_RTFILE;
9638 vrc = RTFileOpen(&f, path.c_str(),
9639 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9640 if (RT_FAILURE(vrc))
9641 return setError(E_FAIL,
9642 tr("Could not create the settings file '%s' (%Rrc)"),
9643 path.c_str(),
9644 vrc);
9645 RTFileClose(f);
9646 }
9647
9648 return rc;
9649}
9650
9651/**
9652 * Saves and commits machine data, user data and hardware data.
9653 *
9654 * Note that on failure, the data remains uncommitted.
9655 *
9656 * @a aFlags may combine the following flags:
9657 *
9658 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9659 * Used when saving settings after an operation that makes them 100%
9660 * correspond to the settings from the current snapshot.
9661 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9662 * #isReallyModified() returns false. This is necessary for cases when we
9663 * change machine data directly, not through the backup()/commit() mechanism.
9664 * - SaveS_Force: settings will be saved without doing a deep compare of the
9665 * settings structures. This is used when this is called because snapshots
9666 * have changed to avoid the overhead of the deep compare.
9667 *
9668 * @note Must be called from under this object's write lock. Locks children for
9669 * writing.
9670 *
9671 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9672 * initialized to false and that will be set to true by this function if
9673 * the caller must invoke VirtualBox::i_saveSettings() because the global
9674 * settings have changed. This will happen if a machine rename has been
9675 * saved and the global machine and media registries will therefore need
9676 * updating.
9677 */
9678HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9679 int aFlags /*= 0*/)
9680{
9681 LogFlowThisFuncEnter();
9682
9683 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9684
9685 /* make sure child objects are unable to modify the settings while we are
9686 * saving them */
9687 i_ensureNoStateDependencies();
9688
9689 AssertReturn(!i_isSnapshotMachine(),
9690 E_FAIL);
9691
9692 HRESULT rc = S_OK;
9693 bool fNeedsWrite = false;
9694
9695 /* First, prepare to save settings. It will care about renaming the
9696 * settings directory and file if the machine name was changed and about
9697 * creating a new settings file if this is a new machine. */
9698 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9699 if (FAILED(rc)) return rc;
9700
9701 // keep a pointer to the current settings structures
9702 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9703 settings::MachineConfigFile *pNewConfig = NULL;
9704
9705 try
9706 {
9707 // make a fresh one to have everyone write stuff into
9708 pNewConfig = new settings::MachineConfigFile(NULL);
9709 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9710
9711 // now go and copy all the settings data from COM to the settings structures
9712 // (this calles i_saveSettings() on all the COM objects in the machine)
9713 i_copyMachineDataToSettings(*pNewConfig);
9714
9715 if (aFlags & SaveS_ResetCurStateModified)
9716 {
9717 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9718 mData->mCurrentStateModified = FALSE;
9719 fNeedsWrite = true; // always, no need to compare
9720 }
9721 else if (aFlags & SaveS_Force)
9722 {
9723 fNeedsWrite = true; // always, no need to compare
9724 }
9725 else
9726 {
9727 if (!mData->mCurrentStateModified)
9728 {
9729 // do a deep compare of the settings that we just saved with the settings
9730 // previously stored in the config file; this invokes MachineConfigFile::operator==
9731 // which does a deep compare of all the settings, which is expensive but less expensive
9732 // than writing out XML in vain
9733 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9734
9735 // could still be modified if any settings changed
9736 mData->mCurrentStateModified = fAnySettingsChanged;
9737
9738 fNeedsWrite = fAnySettingsChanged;
9739 }
9740 else
9741 fNeedsWrite = true;
9742 }
9743
9744 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9745
9746 if (fNeedsWrite)
9747 // now spit it all out!
9748 pNewConfig->write(mData->m_strConfigFileFull);
9749
9750 mData->pMachineConfigFile = pNewConfig;
9751 delete pOldConfig;
9752 i_commit();
9753
9754 // after saving settings, we are no longer different from the XML on disk
9755 mData->flModifications = 0;
9756 }
9757 catch (HRESULT err)
9758 {
9759 // we assume that error info is set by the thrower
9760 rc = err;
9761
9762 // restore old config
9763 delete pNewConfig;
9764 mData->pMachineConfigFile = pOldConfig;
9765 }
9766 catch (...)
9767 {
9768 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9769 }
9770
9771 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9772 {
9773 /* Fire the data change event, even on failure (since we've already
9774 * committed all data). This is done only for SessionMachines because
9775 * mutable Machine instances are always not registered (i.e. private
9776 * to the client process that creates them) and thus don't need to
9777 * inform callbacks. */
9778 if (i_isSessionMachine())
9779 mParent->i_onMachineDataChange(mData->mUuid);
9780 }
9781
9782 LogFlowThisFunc(("rc=%08X\n", rc));
9783 LogFlowThisFuncLeave();
9784 return rc;
9785}
9786
9787/**
9788 * Implementation for saving the machine settings into the given
9789 * settings::MachineConfigFile instance. This copies machine extradata
9790 * from the previous machine config file in the instance data, if any.
9791 *
9792 * This gets called from two locations:
9793 *
9794 * -- Machine::i_saveSettings(), during the regular XML writing;
9795 *
9796 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9797 * exported to OVF and we write the VirtualBox proprietary XML
9798 * into a <vbox:Machine> tag.
9799 *
9800 * This routine fills all the fields in there, including snapshots, *except*
9801 * for the following:
9802 *
9803 * -- fCurrentStateModified. There is some special logic associated with that.
9804 *
9805 * The caller can then call MachineConfigFile::write() or do something else
9806 * with it.
9807 *
9808 * Caller must hold the machine lock!
9809 *
9810 * This throws XML errors and HRESULT, so the caller must have a catch block!
9811 */
9812void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9813{
9814 // deep copy extradata
9815 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9816
9817 config.uuid = mData->mUuid;
9818
9819 // copy name, description, OS type, teleport, UTC etc.
9820 config.machineUserData = mUserData->s;
9821
9822 // Encode the Icon Override data from Machine and store on config userdata.
9823 com::SafeArray<BYTE> iconByte;
9824 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9825 ssize_t cbData = iconByte.size();
9826 if (cbData > 0)
9827 {
9828 ssize_t cchOut = RTBase64EncodedLength(cbData);
9829 Utf8Str strIconData;
9830 strIconData.reserve(cchOut+1);
9831 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9832 strIconData.mutableRaw(), strIconData.capacity(),
9833 NULL);
9834 if (RT_FAILURE(vrc))
9835 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9836 strIconData.jolt();
9837 config.machineUserData.ovIcon = strIconData;
9838 }
9839 else
9840 config.machineUserData.ovIcon.setNull();
9841
9842 if ( mData->mMachineState == MachineState_Saved
9843 || mData->mMachineState == MachineState_Restoring
9844 // when deleting a snapshot we may or may not have a saved state in the current state,
9845 // so let's not assert here please
9846 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9847 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9848 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9849 && (!mSSData->strStateFilePath.isEmpty())
9850 )
9851 )
9852 {
9853 Assert(!mSSData->strStateFilePath.isEmpty());
9854 /* try to make the file name relative to the settings file dir */
9855 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9856 }
9857 else
9858 {
9859 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9860 config.strStateFile.setNull();
9861 }
9862
9863 if (mData->mCurrentSnapshot)
9864 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9865 else
9866 config.uuidCurrentSnapshot.clear();
9867
9868 config.timeLastStateChange = mData->mLastStateChange;
9869 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9870 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9871
9872 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9873 if (FAILED(rc)) throw rc;
9874
9875 rc = i_saveStorageControllers(config.storageMachine);
9876 if (FAILED(rc)) throw rc;
9877
9878 // save machine's media registry if this is VirtualBox 4.0 or later
9879 if (config.canHaveOwnMediaRegistry())
9880 {
9881 // determine machine folder
9882 Utf8Str strMachineFolder = i_getSettingsFileFull();
9883 strMachineFolder.stripFilename();
9884 mParent->i_saveMediaRegistry(config.mediaRegistry,
9885 i_getId(), // only media with registry ID == machine UUID
9886 strMachineFolder);
9887 // this throws HRESULT
9888 }
9889
9890 // save snapshots
9891 rc = i_saveAllSnapshots(config);
9892 if (FAILED(rc)) throw rc;
9893}
9894
9895/**
9896 * Saves all snapshots of the machine into the given machine config file. Called
9897 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9898 * @param config
9899 * @return
9900 */
9901HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9902{
9903 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9904
9905 HRESULT rc = S_OK;
9906
9907 try
9908 {
9909 config.llFirstSnapshot.clear();
9910
9911 if (mData->mFirstSnapshot)
9912 {
9913 settings::Snapshot snapNew;
9914 config.llFirstSnapshot.push_back(snapNew);
9915
9916 // get reference to the fresh copy of the snapshot on the list and
9917 // work on that copy directly to avoid excessive copying later
9918 settings::Snapshot &snap = config.llFirstSnapshot.front();
9919
9920 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9921 if (FAILED(rc)) throw rc;
9922 }
9923
9924// if (mType == IsSessionMachine)
9925// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9926
9927 }
9928 catch (HRESULT err)
9929 {
9930 /* we assume that error info is set by the thrower */
9931 rc = err;
9932 }
9933 catch (...)
9934 {
9935 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9936 }
9937
9938 return rc;
9939}
9940
9941/**
9942 * Saves the VM hardware configuration. It is assumed that the
9943 * given node is empty.
9944 *
9945 * @param data Reference to the settings object for the hardware config.
9946 * @param pDbg Pointer to the settings object for the debugging config
9947 * which happens to live in mHWData.
9948 * @param pAutostart Pointer to the settings object for the autostart config
9949 * which happens to live in mHWData.
9950 */
9951HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9952 settings::Autostart *pAutostart)
9953{
9954 HRESULT rc = S_OK;
9955
9956 try
9957 {
9958 /* The hardware version attribute (optional).
9959 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9960 if ( mHWData->mHWVersion == "1"
9961 && mSSData->strStateFilePath.isEmpty()
9962 )
9963 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9964 other point needs to be found where this can be done. */
9965
9966 data.strVersion = mHWData->mHWVersion;
9967 data.uuid = mHWData->mHardwareUUID;
9968
9969 // CPU
9970 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9971 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9972 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9973 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9974 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9975 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9976 data.fPAE = !!mHWData->mPAEEnabled;
9977 data.enmLongMode = mHWData->mLongMode;
9978 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9979 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9980
9981 /* Standard and Extended CPUID leafs. */
9982 data.llCpuIdLeafs.clear();
9983 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9984 {
9985 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9986 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9987 }
9988 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9989 {
9990 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9991 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9992 }
9993
9994 data.cCPUs = mHWData->mCPUCount;
9995 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9996 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9997
9998 data.llCpus.clear();
9999 if (data.fCpuHotPlug)
10000 {
10001 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10002 {
10003 if (mHWData->mCPUAttached[idx])
10004 {
10005 settings::Cpu cpu;
10006 cpu.ulId = idx;
10007 data.llCpus.push_back(cpu);
10008 }
10009 }
10010 }
10011
10012 // memory
10013 data.ulMemorySizeMB = mHWData->mMemorySize;
10014 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10015
10016 // firmware
10017 data.firmwareType = mHWData->mFirmwareType;
10018
10019 // HID
10020 data.pointingHIDType = mHWData->mPointingHIDType;
10021 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10022
10023 // chipset
10024 data.chipsetType = mHWData->mChipsetType;
10025
10026 // paravirt
10027 data.paravirtProvider = mHWData->mParavirtProvider;
10028
10029
10030 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10031
10032 // HPET
10033 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10034
10035 // boot order
10036 data.mapBootOrder.clear();
10037 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10038 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10039
10040 // display
10041 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10042 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10043 data.cMonitors = mHWData->mMonitorCount;
10044 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10045 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10046 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10047 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10048 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10049 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10050 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10051 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10052 {
10053 if (mHWData->maVideoCaptureScreens[i])
10054 ASMBitSet(&data.u64VideoCaptureScreens, i);
10055 else
10056 ASMBitClear(&data.u64VideoCaptureScreens, i);
10057 }
10058 /* store relative video capture file if possible */
10059 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10060
10061 /* VRDEServer settings (optional) */
10062 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10063 if (FAILED(rc)) throw rc;
10064
10065 /* BIOS (required) */
10066 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10067 if (FAILED(rc)) throw rc;
10068
10069 /* USB Controller (required) */
10070 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10071 {
10072 ComObjPtr<USBController> ctrl = *it;
10073 settings::USBController settingsCtrl;
10074
10075 settingsCtrl.strName = ctrl->i_getName();
10076 settingsCtrl.enmType = ctrl->i_getControllerType();
10077
10078 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10079 }
10080
10081 /* USB device filters (required) */
10082 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10083 if (FAILED(rc)) throw rc;
10084
10085 /* Network adapters (required) */
10086 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10087 data.llNetworkAdapters.clear();
10088 /* Write out only the nominal number of network adapters for this
10089 * chipset type. Since Machine::commit() hasn't been called there
10090 * may be extra NIC settings in the vector. */
10091 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10092 {
10093 settings::NetworkAdapter nic;
10094 nic.ulSlot = (uint32_t)slot;
10095 /* paranoia check... must not be NULL, but must not crash either. */
10096 if (mNetworkAdapters[slot])
10097 {
10098 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10099 if (FAILED(rc)) throw rc;
10100
10101 data.llNetworkAdapters.push_back(nic);
10102 }
10103 }
10104
10105 /* Serial ports */
10106 data.llSerialPorts.clear();
10107 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10108 {
10109 settings::SerialPort s;
10110 s.ulSlot = slot;
10111 rc = mSerialPorts[slot]->i_saveSettings(s);
10112 if (FAILED(rc)) return rc;
10113
10114 data.llSerialPorts.push_back(s);
10115 }
10116
10117 /* Parallel ports */
10118 data.llParallelPorts.clear();
10119 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10120 {
10121 settings::ParallelPort p;
10122 p.ulSlot = slot;
10123 rc = mParallelPorts[slot]->i_saveSettings(p);
10124 if (FAILED(rc)) return rc;
10125
10126 data.llParallelPorts.push_back(p);
10127 }
10128
10129 /* Audio adapter */
10130 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10131 if (FAILED(rc)) return rc;
10132
10133 /* Shared folders */
10134 data.llSharedFolders.clear();
10135 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10136 it != mHWData->mSharedFolders.end();
10137 ++it)
10138 {
10139 SharedFolder *pSF = *it;
10140 AutoCaller sfCaller(pSF);
10141 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10142 settings::SharedFolder sf;
10143 sf.strName = pSF->i_getName();
10144 sf.strHostPath = pSF->i_getHostPath();
10145 sf.fWritable = !!pSF->i_isWritable();
10146 sf.fAutoMount = !!pSF->i_isAutoMounted();
10147
10148 data.llSharedFolders.push_back(sf);
10149 }
10150
10151 // clipboard
10152 data.clipboardMode = mHWData->mClipboardMode;
10153
10154 // drag'n'drop
10155 data.dndMode = mHWData->mDnDMode;
10156
10157 /* Guest */
10158 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10159
10160 // IO settings
10161 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10162 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10163
10164 /* BandwidthControl (required) */
10165 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10166 if (FAILED(rc)) throw rc;
10167
10168 /* Host PCI devices */
10169 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10170 it != mHWData->mPCIDeviceAssignments.end();
10171 ++it)
10172 {
10173 ComObjPtr<PCIDeviceAttachment> pda = *it;
10174 settings::HostPCIDeviceAttachment hpda;
10175
10176 rc = pda->i_saveSettings(hpda);
10177 if (FAILED(rc)) throw rc;
10178
10179 data.pciAttachments.push_back(hpda);
10180 }
10181
10182
10183 // guest properties
10184 data.llGuestProperties.clear();
10185#ifdef VBOX_WITH_GUEST_PROPS
10186 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10187 it != mHWData->mGuestProperties.end();
10188 ++it)
10189 {
10190 HWData::GuestProperty property = it->second;
10191
10192 /* Remove transient guest properties at shutdown unless we
10193 * are saving state */
10194 if ( ( mData->mMachineState == MachineState_PoweredOff
10195 || mData->mMachineState == MachineState_Aborted
10196 || mData->mMachineState == MachineState_Teleported)
10197 && ( property.mFlags & guestProp::TRANSIENT
10198 || property.mFlags & guestProp::TRANSRESET))
10199 continue;
10200 settings::GuestProperty prop;
10201 prop.strName = it->first;
10202 prop.strValue = property.strValue;
10203 prop.timestamp = property.mTimestamp;
10204 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10205 guestProp::writeFlags(property.mFlags, szFlags);
10206 prop.strFlags = szFlags;
10207
10208 data.llGuestProperties.push_back(prop);
10209 }
10210
10211 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10212 /* I presume this doesn't require a backup(). */
10213 mData->mGuestPropertiesModified = FALSE;
10214#endif /* VBOX_WITH_GUEST_PROPS defined */
10215
10216 *pDbg = mHWData->mDebugging;
10217 *pAutostart = mHWData->mAutostart;
10218
10219 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10220 }
10221 catch(std::bad_alloc &)
10222 {
10223 return E_OUTOFMEMORY;
10224 }
10225
10226 AssertComRC(rc);
10227 return rc;
10228}
10229
10230/**
10231 * Saves the storage controller configuration.
10232 *
10233 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10234 */
10235HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10236{
10237 data.llStorageControllers.clear();
10238
10239 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10240 it != mStorageControllers->end();
10241 ++it)
10242 {
10243 HRESULT rc;
10244 ComObjPtr<StorageController> pCtl = *it;
10245
10246 settings::StorageController ctl;
10247 ctl.strName = pCtl->i_getName();
10248 ctl.controllerType = pCtl->i_getControllerType();
10249 ctl.storageBus = pCtl->i_getStorageBus();
10250 ctl.ulInstance = pCtl->i_getInstance();
10251 ctl.fBootable = pCtl->i_getBootable();
10252
10253 /* Save the port count. */
10254 ULONG portCount;
10255 rc = pCtl->COMGETTER(PortCount)(&portCount);
10256 ComAssertComRCRet(rc, rc);
10257 ctl.ulPortCount = portCount;
10258
10259 /* Save fUseHostIOCache */
10260 BOOL fUseHostIOCache;
10261 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10262 ComAssertComRCRet(rc, rc);
10263 ctl.fUseHostIOCache = !!fUseHostIOCache;
10264
10265 /* Save IDE emulation settings. */
10266 if (ctl.controllerType == StorageControllerType_IntelAhci)
10267 {
10268 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10269 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10270 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10271 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10272 )
10273 ComAssertComRCRet(rc, rc);
10274 }
10275
10276 /* save the devices now. */
10277 rc = i_saveStorageDevices(pCtl, ctl);
10278 ComAssertComRCRet(rc, rc);
10279
10280 data.llStorageControllers.push_back(ctl);
10281 }
10282
10283 return S_OK;
10284}
10285
10286/**
10287 * Saves the hard disk configuration.
10288 */
10289HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10290 settings::StorageController &data)
10291{
10292 MediaData::AttachmentList atts;
10293
10294 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10295 if (FAILED(rc)) return rc;
10296
10297 data.llAttachedDevices.clear();
10298 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10299 it != atts.end();
10300 ++it)
10301 {
10302 settings::AttachedDevice dev;
10303 IMediumAttachment *iA = *it;
10304 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10305 Medium *pMedium = pAttach->i_getMedium();
10306
10307 dev.deviceType = pAttach->i_getType();
10308 dev.lPort = pAttach->i_getPort();
10309 dev.lDevice = pAttach->i_getDevice();
10310 dev.fPassThrough = pAttach->i_getPassthrough();
10311 dev.fHotPluggable = pAttach->i_getHotPluggable();
10312 if (pMedium)
10313 {
10314 if (pMedium->i_isHostDrive())
10315 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10316 else
10317 dev.uuid = pMedium->i_getId();
10318 dev.fTempEject = pAttach->i_getTempEject();
10319 dev.fNonRotational = pAttach->i_getNonRotational();
10320 dev.fDiscard = pAttach->i_getDiscard();
10321 }
10322
10323 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10324
10325 data.llAttachedDevices.push_back(dev);
10326 }
10327
10328 return S_OK;
10329}
10330
10331/**
10332 * Saves machine state settings as defined by aFlags
10333 * (SaveSTS_* values).
10334 *
10335 * @param aFlags Combination of SaveSTS_* flags.
10336 *
10337 * @note Locks objects for writing.
10338 */
10339HRESULT Machine::i_saveStateSettings(int aFlags)
10340{
10341 if (aFlags == 0)
10342 return S_OK;
10343
10344 AutoCaller autoCaller(this);
10345 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10346
10347 /* This object's write lock is also necessary to serialize file access
10348 * (prevent concurrent reads and writes) */
10349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10350
10351 HRESULT rc = S_OK;
10352
10353 Assert(mData->pMachineConfigFile);
10354
10355 try
10356 {
10357 if (aFlags & SaveSTS_CurStateModified)
10358 mData->pMachineConfigFile->fCurrentStateModified = true;
10359
10360 if (aFlags & SaveSTS_StateFilePath)
10361 {
10362 if (!mSSData->strStateFilePath.isEmpty())
10363 /* try to make the file name relative to the settings file dir */
10364 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10365 else
10366 mData->pMachineConfigFile->strStateFile.setNull();
10367 }
10368
10369 if (aFlags & SaveSTS_StateTimeStamp)
10370 {
10371 Assert( mData->mMachineState != MachineState_Aborted
10372 || mSSData->strStateFilePath.isEmpty());
10373
10374 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10375
10376 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10377//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10378 }
10379
10380 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10381 }
10382 catch (...)
10383 {
10384 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10385 }
10386
10387 return rc;
10388}
10389
10390/**
10391 * Ensures that the given medium is added to a media registry. If this machine
10392 * was created with 4.0 or later, then the machine registry is used. Otherwise
10393 * the global VirtualBox media registry is used.
10394 *
10395 * Caller must NOT hold machine lock, media tree or any medium locks!
10396 *
10397 * @param pMedium
10398 */
10399void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10400{
10401 /* Paranoia checks: do not hold machine or media tree locks. */
10402 AssertReturnVoid(!isWriteLockOnCurrentThread());
10403 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10404
10405 ComObjPtr<Medium> pBase;
10406 {
10407 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10408 pBase = pMedium->i_getBase();
10409 }
10410
10411 /* Paranoia checks: do not hold medium locks. */
10412 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10413 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10414
10415 // decide which medium registry to use now that the medium is attached:
10416 Guid uuid;
10417 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10418 // machine XML is VirtualBox 4.0 or higher:
10419 uuid = i_getId(); // machine UUID
10420 else
10421 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10422
10423 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10424 mParent->i_markRegistryModified(uuid);
10425
10426 /* For more complex hard disk structures it can happen that the base
10427 * medium isn't yet associated with any medium registry. Do that now. */
10428 if (pMedium != pBase)
10429 {
10430 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10431 mParent->i_markRegistryModified(uuid);
10432 }
10433}
10434
10435/**
10436 * Creates differencing hard disks for all normal hard disks attached to this
10437 * machine and a new set of attachments to refer to created disks.
10438 *
10439 * Used when taking a snapshot or when deleting the current state. Gets called
10440 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10441 *
10442 * This method assumes that mMediaData contains the original hard disk attachments
10443 * it needs to create diffs for. On success, these attachments will be replaced
10444 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10445 * called to delete created diffs which will also rollback mMediaData and restore
10446 * whatever was backed up before calling this method.
10447 *
10448 * Attachments with non-normal hard disks are left as is.
10449 *
10450 * If @a aOnline is @c false then the original hard disks that require implicit
10451 * diffs will be locked for reading. Otherwise it is assumed that they are
10452 * already locked for writing (when the VM was started). Note that in the latter
10453 * case it is responsibility of the caller to lock the newly created diffs for
10454 * writing if this method succeeds.
10455 *
10456 * @param aProgress Progress object to run (must contain at least as
10457 * many operations left as the number of hard disks
10458 * attached).
10459 * @param aOnline Whether the VM was online prior to this operation.
10460 *
10461 * @note The progress object is not marked as completed, neither on success nor
10462 * on failure. This is a responsibility of the caller.
10463 *
10464 * @note Locks this object and the media tree for writing.
10465 */
10466HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10467 ULONG aWeight,
10468 bool aOnline)
10469{
10470 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10471
10472 AutoCaller autoCaller(this);
10473 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10474
10475 AutoMultiWriteLock2 alock(this->lockHandle(),
10476 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10477
10478 /* must be in a protective state because we release the lock below */
10479 AssertReturn( mData->mMachineState == MachineState_Saving
10480 || mData->mMachineState == MachineState_LiveSnapshotting
10481 || mData->mMachineState == MachineState_RestoringSnapshot
10482 || mData->mMachineState == MachineState_DeletingSnapshot
10483 , E_FAIL);
10484
10485 HRESULT rc = S_OK;
10486
10487 // use appropriate locked media map (online or offline)
10488 MediumLockListMap lockedMediaOffline;
10489 MediumLockListMap *lockedMediaMap;
10490 if (aOnline)
10491 lockedMediaMap = &mData->mSession.mLockedMedia;
10492 else
10493 lockedMediaMap = &lockedMediaOffline;
10494
10495 try
10496 {
10497 if (!aOnline)
10498 {
10499 /* lock all attached hard disks early to detect "in use"
10500 * situations before creating actual diffs */
10501 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10502 it != mMediaData->mAttachments.end();
10503 ++it)
10504 {
10505 MediumAttachment* pAtt = *it;
10506 if (pAtt->i_getType() == DeviceType_HardDisk)
10507 {
10508 Medium* pMedium = pAtt->i_getMedium();
10509 Assert(pMedium);
10510
10511 MediumLockList *pMediumLockList(new MediumLockList());
10512 alock.release();
10513 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10514 false /* fMediumLockWrite */,
10515 NULL,
10516 *pMediumLockList);
10517 alock.acquire();
10518 if (FAILED(rc))
10519 {
10520 delete pMediumLockList;
10521 throw rc;
10522 }
10523 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10524 if (FAILED(rc))
10525 {
10526 throw setError(rc,
10527 tr("Collecting locking information for all attached media failed"));
10528 }
10529 }
10530 }
10531
10532 /* Now lock all media. If this fails, nothing is locked. */
10533 alock.release();
10534 rc = lockedMediaMap->Lock();
10535 alock.acquire();
10536 if (FAILED(rc))
10537 {
10538 throw setError(rc,
10539 tr("Locking of attached media failed"));
10540 }
10541 }
10542
10543 /* remember the current list (note that we don't use backup() since
10544 * mMediaData may be already backed up) */
10545 MediaData::AttachmentList atts = mMediaData->mAttachments;
10546
10547 /* start from scratch */
10548 mMediaData->mAttachments.clear();
10549
10550 /* go through remembered attachments and create diffs for normal hard
10551 * disks and attach them */
10552 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10553 it != atts.end();
10554 ++it)
10555 {
10556 MediumAttachment* pAtt = *it;
10557
10558 DeviceType_T devType = pAtt->i_getType();
10559 Medium* pMedium = pAtt->i_getMedium();
10560
10561 if ( devType != DeviceType_HardDisk
10562 || pMedium == NULL
10563 || pMedium->i_getType() != MediumType_Normal)
10564 {
10565 /* copy the attachment as is */
10566
10567 /** @todo the progress object created in Console::TakeSnaphot
10568 * only expects operations for hard disks. Later other
10569 * device types need to show up in the progress as well. */
10570 if (devType == DeviceType_HardDisk)
10571 {
10572 if (pMedium == NULL)
10573 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10574 aWeight); // weight
10575 else
10576 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10577 pMedium->i_getBase()->i_getName().c_str()).raw(),
10578 aWeight); // weight
10579 }
10580
10581 mMediaData->mAttachments.push_back(pAtt);
10582 continue;
10583 }
10584
10585 /* need a diff */
10586 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10587 pMedium->i_getBase()->i_getName().c_str()).raw(),
10588 aWeight); // weight
10589
10590 Utf8Str strFullSnapshotFolder;
10591 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10592
10593 ComObjPtr<Medium> diff;
10594 diff.createObject();
10595 // store the diff in the same registry as the parent
10596 // (this cannot fail here because we can't create implicit diffs for
10597 // unregistered images)
10598 Guid uuidRegistryParent;
10599 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10600 Assert(fInRegistry); NOREF(fInRegistry);
10601 rc = diff->init(mParent,
10602 pMedium->i_getPreferredDiffFormat(),
10603 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10604 uuidRegistryParent);
10605 if (FAILED(rc)) throw rc;
10606
10607 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10608 * the push_back? Looks like we're going to release medium with the
10609 * wrong kind of lock (general issue with if we fail anywhere at all)
10610 * and an orphaned VDI in the snapshots folder. */
10611
10612 /* update the appropriate lock list */
10613 MediumLockList *pMediumLockList;
10614 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10615 AssertComRCThrowRC(rc);
10616 if (aOnline)
10617 {
10618 alock.release();
10619 /* The currently attached medium will be read-only, change
10620 * the lock type to read. */
10621 rc = pMediumLockList->Update(pMedium, false);
10622 alock.acquire();
10623 AssertComRCThrowRC(rc);
10624 }
10625
10626 /* release the locks before the potentially lengthy operation */
10627 alock.release();
10628 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10629 pMediumLockList,
10630 NULL /* aProgress */,
10631 true /* aWait */);
10632 alock.acquire();
10633 if (FAILED(rc)) throw rc;
10634
10635 /* actual lock list update is done in Medium::commitMedia */
10636
10637 rc = diff->i_addBackReference(mData->mUuid);
10638 AssertComRCThrowRC(rc);
10639
10640 /* add a new attachment */
10641 ComObjPtr<MediumAttachment> attachment;
10642 attachment.createObject();
10643 rc = attachment->init(this,
10644 diff,
10645 pAtt->i_getControllerName(),
10646 pAtt->i_getPort(),
10647 pAtt->i_getDevice(),
10648 DeviceType_HardDisk,
10649 true /* aImplicit */,
10650 false /* aPassthrough */,
10651 false /* aTempEject */,
10652 pAtt->i_getNonRotational(),
10653 pAtt->i_getDiscard(),
10654 pAtt->i_getHotPluggable(),
10655 pAtt->i_getBandwidthGroup());
10656 if (FAILED(rc)) throw rc;
10657
10658 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10659 AssertComRCThrowRC(rc);
10660 mMediaData->mAttachments.push_back(attachment);
10661 }
10662 }
10663 catch (HRESULT aRC) { rc = aRC; }
10664
10665 /* unlock all hard disks we locked when there is no VM */
10666 if (!aOnline)
10667 {
10668 ErrorInfoKeeper eik;
10669
10670 HRESULT rc1 = lockedMediaMap->Clear();
10671 AssertComRC(rc1);
10672 }
10673
10674 return rc;
10675}
10676
10677/**
10678 * Deletes implicit differencing hard disks created either by
10679 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10680 *
10681 * Note that to delete hard disks created by #AttachDevice() this method is
10682 * called from #fixupMedia() when the changes are rolled back.
10683 *
10684 * @note Locks this object and the media tree for writing.
10685 */
10686HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10687{
10688 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10689
10690 AutoCaller autoCaller(this);
10691 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10692
10693 AutoMultiWriteLock2 alock(this->lockHandle(),
10694 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10695
10696 /* We absolutely must have backed up state. */
10697 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10698
10699 /* Check if there are any implicitly created diff images. */
10700 bool fImplicitDiffs = false;
10701 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10702 it != mMediaData->mAttachments.end();
10703 ++it)
10704 {
10705 const ComObjPtr<MediumAttachment> &pAtt = *it;
10706 if (pAtt->i_isImplicit())
10707 {
10708 fImplicitDiffs = true;
10709 break;
10710 }
10711 }
10712 /* If there is nothing to do, leave early. This saves lots of image locking
10713 * effort. It also avoids a MachineStateChanged event without real reason.
10714 * This is important e.g. when loading a VM config, because there should be
10715 * no events. Otherwise API clients can become thoroughly confused for
10716 * inaccessible VMs (the code for loading VM configs uses this method for
10717 * cleanup if the config makes no sense), as they take such events as an
10718 * indication that the VM is alive, and they would force the VM config to
10719 * be reread, leading to an endless loop. */
10720 if (!fImplicitDiffs)
10721 return S_OK;
10722
10723 HRESULT rc = S_OK;
10724 MachineState_T oldState = mData->mMachineState;
10725
10726 /* will release the lock before the potentially lengthy operation,
10727 * so protect with the special state (unless already protected) */
10728 if ( oldState != MachineState_Saving
10729 && oldState != MachineState_LiveSnapshotting
10730 && oldState != MachineState_RestoringSnapshot
10731 && oldState != MachineState_DeletingSnapshot
10732 && oldState != MachineState_DeletingSnapshotOnline
10733 && oldState != MachineState_DeletingSnapshotPaused
10734 )
10735 i_setMachineState(MachineState_SettingUp);
10736
10737 // use appropriate locked media map (online or offline)
10738 MediumLockListMap lockedMediaOffline;
10739 MediumLockListMap *lockedMediaMap;
10740 if (aOnline)
10741 lockedMediaMap = &mData->mSession.mLockedMedia;
10742 else
10743 lockedMediaMap = &lockedMediaOffline;
10744
10745 try
10746 {
10747 if (!aOnline)
10748 {
10749 /* lock all attached hard disks early to detect "in use"
10750 * situations before deleting actual diffs */
10751 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10752 it != mMediaData->mAttachments.end();
10753 ++it)
10754 {
10755 MediumAttachment* pAtt = *it;
10756 if (pAtt->i_getType() == DeviceType_HardDisk)
10757 {
10758 Medium* pMedium = pAtt->i_getMedium();
10759 Assert(pMedium);
10760
10761 MediumLockList *pMediumLockList(new MediumLockList());
10762 alock.release();
10763 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10764 false /* fMediumLockWrite */,
10765 NULL,
10766 *pMediumLockList);
10767 alock.acquire();
10768
10769 if (FAILED(rc))
10770 {
10771 delete pMediumLockList;
10772 throw rc;
10773 }
10774
10775 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10776 if (FAILED(rc))
10777 throw rc;
10778 }
10779 }
10780
10781 if (FAILED(rc))
10782 throw rc;
10783 } // end of offline
10784
10785 /* Lock lists are now up to date and include implicitly created media */
10786
10787 /* Go through remembered attachments and delete all implicitly created
10788 * diffs and fix up the attachment information */
10789 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10790 MediaData::AttachmentList implicitAtts;
10791 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10792 it != mMediaData->mAttachments.end();
10793 ++it)
10794 {
10795 ComObjPtr<MediumAttachment> pAtt = *it;
10796 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10797 if (pMedium.isNull())
10798 continue;
10799
10800 // Implicit attachments go on the list for deletion and back references are removed.
10801 if (pAtt->i_isImplicit())
10802 {
10803 /* Deassociate and mark for deletion */
10804 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10805 rc = pMedium->i_removeBackReference(mData->mUuid);
10806 if (FAILED(rc))
10807 throw rc;
10808 implicitAtts.push_back(pAtt);
10809 continue;
10810 }
10811
10812 /* Was this medium attached before? */
10813 if (!i_findAttachment(oldAtts, pMedium))
10814 {
10815 /* no: de-associate */
10816 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10817 rc = pMedium->i_removeBackReference(mData->mUuid);
10818 if (FAILED(rc))
10819 throw rc;
10820 continue;
10821 }
10822 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10823 }
10824
10825 /* If there are implicit attachments to delete, throw away the lock
10826 * map contents (which will unlock all media) since the medium
10827 * attachments will be rolled back. Below we need to completely
10828 * recreate the lock map anyway since it is infinitely complex to
10829 * do this incrementally (would need reconstructing each attachment
10830 * change, which would be extremely hairy). */
10831 if (implicitAtts.size() != 0)
10832 {
10833 ErrorInfoKeeper eik;
10834
10835 HRESULT rc1 = lockedMediaMap->Clear();
10836 AssertComRC(rc1);
10837 }
10838
10839 /* rollback hard disk changes */
10840 mMediaData.rollback();
10841
10842 MultiResult mrc(S_OK);
10843
10844 // Delete unused implicit diffs.
10845 if (implicitAtts.size() != 0)
10846 {
10847 alock.release();
10848
10849 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10850 {
10851 // Remove medium associated with this attachment.
10852 ComObjPtr<MediumAttachment> pAtt = *it;
10853 Assert(pAtt);
10854 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10855 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10856 Assert(pMedium);
10857
10858 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10859 // continue on delete failure, just collect error messages
10860 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10861 pMedium->i_getLocationFull().c_str() ));
10862 mrc = rc;
10863 }
10864
10865 alock.acquire();
10866
10867 /* if there is a VM recreate media lock map as mentioned above,
10868 * otherwise it is a waste of time and we leave things unlocked */
10869 if (aOnline)
10870 {
10871 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10872 /* must never be NULL, but better safe than sorry */
10873 if (!pMachine.isNull())
10874 {
10875 alock.release();
10876 rc = mData->mSession.mMachine->i_lockMedia();
10877 alock.acquire();
10878 if (FAILED(rc))
10879 throw rc;
10880 }
10881 }
10882 }
10883 }
10884 catch (HRESULT aRC) {rc = aRC;}
10885
10886 if (mData->mMachineState == MachineState_SettingUp)
10887 i_setMachineState(oldState);
10888
10889 /* unlock all hard disks we locked when there is no VM */
10890 if (!aOnline)
10891 {
10892 ErrorInfoKeeper eik;
10893
10894 HRESULT rc1 = lockedMediaMap->Clear();
10895 AssertComRC(rc1);
10896 }
10897
10898 return rc;
10899}
10900
10901
10902/**
10903 * Looks through the given list of media attachments for one with the given parameters
10904 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10905 * can be searched as well if needed.
10906 *
10907 * @param list
10908 * @param aControllerName
10909 * @param aControllerPort
10910 * @param aDevice
10911 * @return
10912 */
10913MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10914 IN_BSTR aControllerName,
10915 LONG aControllerPort,
10916 LONG aDevice)
10917{
10918 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10919 {
10920 MediumAttachment *pAttach = *it;
10921 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10922 return pAttach;
10923 }
10924
10925 return NULL;
10926}
10927
10928/**
10929 * Looks through the given list of media attachments for one with the given parameters
10930 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10931 * can be searched as well if needed.
10932 *
10933 * @param list
10934 * @param aControllerName
10935 * @param aControllerPort
10936 * @param aDevice
10937 * @return
10938 */
10939MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10940 ComObjPtr<Medium> pMedium)
10941{
10942 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10943 {
10944 MediumAttachment *pAttach = *it;
10945 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10946 if (pMediumThis == pMedium)
10947 return pAttach;
10948 }
10949
10950 return NULL;
10951}
10952
10953/**
10954 * Looks through the given list of media attachments for one with the given parameters
10955 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10956 * can be searched as well if needed.
10957 *
10958 * @param list
10959 * @param aControllerName
10960 * @param aControllerPort
10961 * @param aDevice
10962 * @return
10963 */
10964MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10965 Guid &id)
10966{
10967 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10968 {
10969 MediumAttachment *pAttach = *it;
10970 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10971 if (pMediumThis->i_getId() == id)
10972 return pAttach;
10973 }
10974
10975 return NULL;
10976}
10977
10978/**
10979 * Main implementation for Machine::DetachDevice. This also gets called
10980 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10981 *
10982 * @param pAttach Medium attachment to detach.
10983 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10984 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10985 * SnapshotMachine, and this must be its snapshot.
10986 * @return
10987 */
10988HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10989 AutoWriteLock &writeLock,
10990 Snapshot *pSnapshot)
10991{
10992 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10993 DeviceType_T mediumType = pAttach->i_getType();
10994
10995 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10996
10997 if (pAttach->i_isImplicit())
10998 {
10999 /* attempt to implicitly delete the implicitly created diff */
11000
11001 /// @todo move the implicit flag from MediumAttachment to Medium
11002 /// and forbid any hard disk operation when it is implicit. Or maybe
11003 /// a special media state for it to make it even more simple.
11004
11005 Assert(mMediaData.isBackedUp());
11006
11007 /* will release the lock before the potentially lengthy operation, so
11008 * protect with the special state */
11009 MachineState_T oldState = mData->mMachineState;
11010 i_setMachineState(MachineState_SettingUp);
11011
11012 writeLock.release();
11013
11014 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11015 true /*aWait*/);
11016
11017 writeLock.acquire();
11018
11019 i_setMachineState(oldState);
11020
11021 if (FAILED(rc)) return rc;
11022 }
11023
11024 i_setModified(IsModified_Storage);
11025 mMediaData.backup();
11026 mMediaData->mAttachments.remove(pAttach);
11027
11028 if (!oldmedium.isNull())
11029 {
11030 // if this is from a snapshot, do not defer detachment to commitMedia()
11031 if (pSnapshot)
11032 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11033 // else if non-hard disk media, do not defer detachment to commitMedia() either
11034 else if (mediumType != DeviceType_HardDisk)
11035 oldmedium->i_removeBackReference(mData->mUuid);
11036 }
11037
11038 return S_OK;
11039}
11040
11041/**
11042 * Goes thru all media of the given list and
11043 *
11044 * 1) calls i_detachDevice() on each of them for this machine and
11045 * 2) adds all Medium objects found in the process to the given list,
11046 * depending on cleanupMode.
11047 *
11048 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11049 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11050 * media to the list.
11051 *
11052 * This gets called from Machine::Unregister, both for the actual Machine and
11053 * the SnapshotMachine objects that might be found in the snapshots.
11054 *
11055 * Requires caller and locking. The machine lock must be passed in because it
11056 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11057 *
11058 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11059 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11060 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11061 * Full, then all media get added;
11062 * otherwise no media get added.
11063 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11064 * @return
11065 */
11066HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11067 Snapshot *pSnapshot,
11068 CleanupMode_T cleanupMode,
11069 MediaList &llMedia)
11070{
11071 Assert(isWriteLockOnCurrentThread());
11072
11073 HRESULT rc;
11074
11075 // make a temporary list because i_detachDevice invalidates iterators into
11076 // mMediaData->mAttachments
11077 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11078
11079 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11080 {
11081 ComObjPtr<MediumAttachment> &pAttach = *it;
11082 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11083
11084 if (!pMedium.isNull())
11085 {
11086 AutoCaller mac(pMedium);
11087 if (FAILED(mac.rc())) return mac.rc();
11088 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11089 DeviceType_T devType = pMedium->i_getDeviceType();
11090 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11091 && devType == DeviceType_HardDisk)
11092 || (cleanupMode == CleanupMode_Full)
11093 )
11094 {
11095 llMedia.push_back(pMedium);
11096 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11097 /*
11098 * Search for medias which are not attached to any machine, but
11099 * in the chain to an attached disk. Mediums are only consided
11100 * if they are:
11101 * - have only one child
11102 * - no references to any machines
11103 * - are of normal medium type
11104 */
11105 while (!pParent.isNull())
11106 {
11107 AutoCaller mac1(pParent);
11108 if (FAILED(mac1.rc())) return mac1.rc();
11109 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11110 if (pParent->i_getChildren().size() == 1)
11111 {
11112 if ( pParent->i_getMachineBackRefCount() == 0
11113 && pParent->i_getType() == MediumType_Normal
11114 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11115 llMedia.push_back(pParent);
11116 }
11117 else
11118 break;
11119 pParent = pParent->i_getParent();
11120 }
11121 }
11122 }
11123
11124 // real machine: then we need to use the proper method
11125 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11126
11127 if (FAILED(rc))
11128 return rc;
11129 }
11130
11131 return S_OK;
11132}
11133
11134/**
11135 * Perform deferred hard disk detachments.
11136 *
11137 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11138 * backed up).
11139 *
11140 * If @a aOnline is @c true then this method will also unlock the old hard disks
11141 * for which the new implicit diffs were created and will lock these new diffs for
11142 * writing.
11143 *
11144 * @param aOnline Whether the VM was online prior to this operation.
11145 *
11146 * @note Locks this object for writing!
11147 */
11148void Machine::i_commitMedia(bool aOnline /*= false*/)
11149{
11150 AutoCaller autoCaller(this);
11151 AssertComRCReturnVoid(autoCaller.rc());
11152
11153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11154
11155 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11156
11157 HRESULT rc = S_OK;
11158
11159 /* no attach/detach operations -- nothing to do */
11160 if (!mMediaData.isBackedUp())
11161 return;
11162
11163 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11164 bool fMediaNeedsLocking = false;
11165
11166 /* enumerate new attachments */
11167 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11168 it != mMediaData->mAttachments.end();
11169 ++it)
11170 {
11171 MediumAttachment *pAttach = *it;
11172
11173 pAttach->i_commit();
11174
11175 Medium* pMedium = pAttach->i_getMedium();
11176 bool fImplicit = pAttach->i_isImplicit();
11177
11178 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11179 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11180 fImplicit));
11181
11182 /** @todo convert all this Machine-based voodoo to MediumAttachment
11183 * based commit logic. */
11184 if (fImplicit)
11185 {
11186 /* convert implicit attachment to normal */
11187 pAttach->i_setImplicit(false);
11188
11189 if ( aOnline
11190 && pMedium
11191 && pAttach->i_getType() == DeviceType_HardDisk
11192 )
11193 {
11194 ComObjPtr<Medium> parent = pMedium->i_getParent();
11195 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11196
11197 /* update the appropriate lock list */
11198 MediumLockList *pMediumLockList;
11199 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11200 AssertComRC(rc);
11201 if (pMediumLockList)
11202 {
11203 /* unlock if there's a need to change the locking */
11204 if (!fMediaNeedsLocking)
11205 {
11206 rc = mData->mSession.mLockedMedia.Unlock();
11207 AssertComRC(rc);
11208 fMediaNeedsLocking = true;
11209 }
11210 rc = pMediumLockList->Update(parent, false);
11211 AssertComRC(rc);
11212 rc = pMediumLockList->Append(pMedium, true);
11213 AssertComRC(rc);
11214 }
11215 }
11216
11217 continue;
11218 }
11219
11220 if (pMedium)
11221 {
11222 /* was this medium attached before? */
11223 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11224 {
11225 MediumAttachment *pOldAttach = *oldIt;
11226 if (pOldAttach->i_getMedium() == pMedium)
11227 {
11228 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11229
11230 /* yes: remove from old to avoid de-association */
11231 oldAtts.erase(oldIt);
11232 break;
11233 }
11234 }
11235 }
11236 }
11237
11238 /* enumerate remaining old attachments and de-associate from the
11239 * current machine state */
11240 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11241 {
11242 MediumAttachment *pAttach = *it;
11243 Medium* pMedium = pAttach->i_getMedium();
11244
11245 /* Detach only hard disks, since DVD/floppy media is detached
11246 * instantly in MountMedium. */
11247 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11248 {
11249 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11250
11251 /* now de-associate from the current machine state */
11252 rc = pMedium->i_removeBackReference(mData->mUuid);
11253 AssertComRC(rc);
11254
11255 if (aOnline)
11256 {
11257 /* unlock since medium is not used anymore */
11258 MediumLockList *pMediumLockList;
11259 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11260 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11261 {
11262 /* this happens for online snapshots, there the attachment
11263 * is changing, but only to a diff image created under
11264 * the old one, so there is no separate lock list */
11265 Assert(!pMediumLockList);
11266 }
11267 else
11268 {
11269 AssertComRC(rc);
11270 if (pMediumLockList)
11271 {
11272 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11273 AssertComRC(rc);
11274 }
11275 }
11276 }
11277 }
11278 }
11279
11280 /* take media locks again so that the locking state is consistent */
11281 if (fMediaNeedsLocking)
11282 {
11283 Assert(aOnline);
11284 rc = mData->mSession.mLockedMedia.Lock();
11285 AssertComRC(rc);
11286 }
11287
11288 /* commit the hard disk changes */
11289 mMediaData.commit();
11290
11291 if (i_isSessionMachine())
11292 {
11293 /*
11294 * Update the parent machine to point to the new owner.
11295 * This is necessary because the stored parent will point to the
11296 * session machine otherwise and cause crashes or errors later
11297 * when the session machine gets invalid.
11298 */
11299 /** @todo Change the MediumAttachment class to behave like any other
11300 * class in this regard by creating peer MediumAttachment
11301 * objects for session machines and share the data with the peer
11302 * machine.
11303 */
11304 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11305 it != mMediaData->mAttachments.end();
11306 ++it)
11307 (*it)->i_updateParentMachine(mPeer);
11308
11309 /* attach new data to the primary machine and reshare it */
11310 mPeer->mMediaData.attach(mMediaData);
11311 }
11312
11313 return;
11314}
11315
11316/**
11317 * Perform deferred deletion of implicitly created diffs.
11318 *
11319 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11320 * backed up).
11321 *
11322 * @note Locks this object for writing!
11323 */
11324void Machine::i_rollbackMedia()
11325{
11326 AutoCaller autoCaller(this);
11327 AssertComRCReturnVoid(autoCaller.rc());
11328
11329 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11330 LogFlowThisFunc(("Entering rollbackMedia\n"));
11331
11332 HRESULT rc = S_OK;
11333
11334 /* no attach/detach operations -- nothing to do */
11335 if (!mMediaData.isBackedUp())
11336 return;
11337
11338 /* enumerate new attachments */
11339 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11340 it != mMediaData->mAttachments.end();
11341 ++it)
11342 {
11343 MediumAttachment *pAttach = *it;
11344 /* Fix up the backrefs for DVD/floppy media. */
11345 if (pAttach->i_getType() != DeviceType_HardDisk)
11346 {
11347 Medium* pMedium = pAttach->i_getMedium();
11348 if (pMedium)
11349 {
11350 rc = pMedium->i_removeBackReference(mData->mUuid);
11351 AssertComRC(rc);
11352 }
11353 }
11354
11355 (*it)->i_rollback();
11356
11357 pAttach = *it;
11358 /* Fix up the backrefs for DVD/floppy media. */
11359 if (pAttach->i_getType() != DeviceType_HardDisk)
11360 {
11361 Medium* pMedium = pAttach->i_getMedium();
11362 if (pMedium)
11363 {
11364 rc = pMedium->i_addBackReference(mData->mUuid);
11365 AssertComRC(rc);
11366 }
11367 }
11368 }
11369
11370 /** @todo convert all this Machine-based voodoo to MediumAttachment
11371 * based rollback logic. */
11372 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11373
11374 return;
11375}
11376
11377/**
11378 * Returns true if the settings file is located in the directory named exactly
11379 * as the machine; this means, among other things, that the machine directory
11380 * should be auto-renamed.
11381 *
11382 * @param aSettingsDir if not NULL, the full machine settings file directory
11383 * name will be assigned there.
11384 *
11385 * @note Doesn't lock anything.
11386 * @note Not thread safe (must be called from this object's lock).
11387 */
11388bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11389{
11390 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11391 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11392 if (aSettingsDir)
11393 *aSettingsDir = strMachineDirName;
11394 strMachineDirName.stripPath(); // vmname
11395 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11396 strConfigFileOnly.stripPath() // vmname.vbox
11397 .stripSuffix(); // vmname
11398 /** @todo hack, make somehow use of ComposeMachineFilename */
11399 if (mUserData->s.fDirectoryIncludesUUID)
11400 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11401
11402 AssertReturn(!strMachineDirName.isEmpty(), false);
11403 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11404
11405 return strMachineDirName == strConfigFileOnly;
11406}
11407
11408/**
11409 * Discards all changes to machine settings.
11410 *
11411 * @param aNotify Whether to notify the direct session about changes or not.
11412 *
11413 * @note Locks objects for writing!
11414 */
11415void Machine::i_rollback(bool aNotify)
11416{
11417 AutoCaller autoCaller(this);
11418 AssertComRCReturn(autoCaller.rc(), (void)0);
11419
11420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11421
11422 if (!mStorageControllers.isNull())
11423 {
11424 if (mStorageControllers.isBackedUp())
11425 {
11426 /* unitialize all new devices (absent in the backed up list). */
11427 StorageControllerList::const_iterator it = mStorageControllers->begin();
11428 StorageControllerList *backedList = mStorageControllers.backedUpData();
11429 while (it != mStorageControllers->end())
11430 {
11431 if ( std::find(backedList->begin(), backedList->end(), *it)
11432 == backedList->end()
11433 )
11434 {
11435 (*it)->uninit();
11436 }
11437 ++it;
11438 }
11439
11440 /* restore the list */
11441 mStorageControllers.rollback();
11442 }
11443
11444 /* rollback any changes to devices after restoring the list */
11445 if (mData->flModifications & IsModified_Storage)
11446 {
11447 StorageControllerList::const_iterator it = mStorageControllers->begin();
11448 while (it != mStorageControllers->end())
11449 {
11450 (*it)->i_rollback();
11451 ++it;
11452 }
11453 }
11454 }
11455
11456 if (!mUSBControllers.isNull())
11457 {
11458 if (mUSBControllers.isBackedUp())
11459 {
11460 /* unitialize all new devices (absent in the backed up list). */
11461 USBControllerList::const_iterator it = mUSBControllers->begin();
11462 USBControllerList *backedList = mUSBControllers.backedUpData();
11463 while (it != mUSBControllers->end())
11464 {
11465 if ( std::find(backedList->begin(), backedList->end(), *it)
11466 == backedList->end()
11467 )
11468 {
11469 (*it)->uninit();
11470 }
11471 ++it;
11472 }
11473
11474 /* restore the list */
11475 mUSBControllers.rollback();
11476 }
11477
11478 /* rollback any changes to devices after restoring the list */
11479 if (mData->flModifications & IsModified_USB)
11480 {
11481 USBControllerList::const_iterator it = mUSBControllers->begin();
11482 while (it != mUSBControllers->end())
11483 {
11484 (*it)->i_rollback();
11485 ++it;
11486 }
11487 }
11488 }
11489
11490 mUserData.rollback();
11491
11492 mHWData.rollback();
11493
11494 if (mData->flModifications & IsModified_Storage)
11495 i_rollbackMedia();
11496
11497 if (mBIOSSettings)
11498 mBIOSSettings->i_rollback();
11499
11500 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11501 mVRDEServer->i_rollback();
11502
11503 if (mAudioAdapter)
11504 mAudioAdapter->i_rollback();
11505
11506 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11507 mUSBDeviceFilters->i_rollback();
11508
11509 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11510 mBandwidthControl->i_rollback();
11511
11512 if (!mHWData.isNull())
11513 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11514 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11515 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11516 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11517
11518 if (mData->flModifications & IsModified_NetworkAdapters)
11519 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11520 if ( mNetworkAdapters[slot]
11521 && mNetworkAdapters[slot]->i_isModified())
11522 {
11523 mNetworkAdapters[slot]->i_rollback();
11524 networkAdapters[slot] = mNetworkAdapters[slot];
11525 }
11526
11527 if (mData->flModifications & IsModified_SerialPorts)
11528 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11529 if ( mSerialPorts[slot]
11530 && mSerialPorts[slot]->i_isModified())
11531 {
11532 mSerialPorts[slot]->i_rollback();
11533 serialPorts[slot] = mSerialPorts[slot];
11534 }
11535
11536 if (mData->flModifications & IsModified_ParallelPorts)
11537 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11538 if ( mParallelPorts[slot]
11539 && mParallelPorts[slot]->i_isModified())
11540 {
11541 mParallelPorts[slot]->i_rollback();
11542 parallelPorts[slot] = mParallelPorts[slot];
11543 }
11544
11545 if (aNotify)
11546 {
11547 /* inform the direct session about changes */
11548
11549 ComObjPtr<Machine> that = this;
11550 uint32_t flModifications = mData->flModifications;
11551 alock.release();
11552
11553 if (flModifications & IsModified_SharedFolders)
11554 that->i_onSharedFolderChange();
11555
11556 if (flModifications & IsModified_VRDEServer)
11557 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11558 if (flModifications & IsModified_USB)
11559 that->i_onUSBControllerChange();
11560
11561 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11562 if (networkAdapters[slot])
11563 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11564 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11565 if (serialPorts[slot])
11566 that->i_onSerialPortChange(serialPorts[slot]);
11567 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11568 if (parallelPorts[slot])
11569 that->i_onParallelPortChange(parallelPorts[slot]);
11570
11571 if (flModifications & IsModified_Storage)
11572 that->i_onStorageControllerChange();
11573
11574#if 0
11575 if (flModifications & IsModified_BandwidthControl)
11576 that->onBandwidthControlChange();
11577#endif
11578 }
11579}
11580
11581/**
11582 * Commits all the changes to machine settings.
11583 *
11584 * Note that this operation is supposed to never fail.
11585 *
11586 * @note Locks this object and children for writing.
11587 */
11588void Machine::i_commit()
11589{
11590 AutoCaller autoCaller(this);
11591 AssertComRCReturnVoid(autoCaller.rc());
11592
11593 AutoCaller peerCaller(mPeer);
11594 AssertComRCReturnVoid(peerCaller.rc());
11595
11596 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11597
11598 /*
11599 * use safe commit to ensure Snapshot machines (that share mUserData)
11600 * will still refer to a valid memory location
11601 */
11602 mUserData.commitCopy();
11603
11604 mHWData.commit();
11605
11606 if (mMediaData.isBackedUp())
11607 i_commitMedia(Global::IsOnline(mData->mMachineState));
11608
11609 mBIOSSettings->i_commit();
11610 mVRDEServer->i_commit();
11611 mAudioAdapter->i_commit();
11612 mUSBDeviceFilters->i_commit();
11613 mBandwidthControl->i_commit();
11614
11615 /* Since mNetworkAdapters is a list which might have been changed (resized)
11616 * without using the Backupable<> template we need to handle the copying
11617 * of the list entries manually, including the creation of peers for the
11618 * new objects. */
11619 bool commitNetworkAdapters = false;
11620 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11621 if (mPeer)
11622 {
11623 /* commit everything, even the ones which will go away */
11624 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11625 mNetworkAdapters[slot]->i_commit();
11626 /* copy over the new entries, creating a peer and uninit the original */
11627 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11628 for (size_t slot = 0; slot < newSize; slot++)
11629 {
11630 /* look if this adapter has a peer device */
11631 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11632 if (!peer)
11633 {
11634 /* no peer means the adapter is a newly created one;
11635 * create a peer owning data this data share it with */
11636 peer.createObject();
11637 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11638 }
11639 mPeer->mNetworkAdapters[slot] = peer;
11640 }
11641 /* uninit any no longer needed network adapters */
11642 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11643 mNetworkAdapters[slot]->uninit();
11644 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11645 {
11646 if (mPeer->mNetworkAdapters[slot])
11647 mPeer->mNetworkAdapters[slot]->uninit();
11648 }
11649 /* Keep the original network adapter count until this point, so that
11650 * discarding a chipset type change will not lose settings. */
11651 mNetworkAdapters.resize(newSize);
11652 mPeer->mNetworkAdapters.resize(newSize);
11653 }
11654 else
11655 {
11656 /* we have no peer (our parent is the newly created machine);
11657 * just commit changes to the network adapters */
11658 commitNetworkAdapters = true;
11659 }
11660 if (commitNetworkAdapters)
11661 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11662 mNetworkAdapters[slot]->i_commit();
11663
11664 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11665 mSerialPorts[slot]->i_commit();
11666 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11667 mParallelPorts[slot]->i_commit();
11668
11669 bool commitStorageControllers = false;
11670
11671 if (mStorageControllers.isBackedUp())
11672 {
11673 mStorageControllers.commit();
11674
11675 if (mPeer)
11676 {
11677 /* Commit all changes to new controllers (this will reshare data with
11678 * peers for those who have peers) */
11679 StorageControllerList *newList = new StorageControllerList();
11680 StorageControllerList::const_iterator it = mStorageControllers->begin();
11681 while (it != mStorageControllers->end())
11682 {
11683 (*it)->i_commit();
11684
11685 /* look if this controller has a peer device */
11686 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11687 if (!peer)
11688 {
11689 /* no peer means the device is a newly created one;
11690 * create a peer owning data this device share it with */
11691 peer.createObject();
11692 peer->init(mPeer, *it, true /* aReshare */);
11693 }
11694 else
11695 {
11696 /* remove peer from the old list */
11697 mPeer->mStorageControllers->remove(peer);
11698 }
11699 /* and add it to the new list */
11700 newList->push_back(peer);
11701
11702 ++it;
11703 }
11704
11705 /* uninit old peer's controllers that are left */
11706 it = mPeer->mStorageControllers->begin();
11707 while (it != mPeer->mStorageControllers->end())
11708 {
11709 (*it)->uninit();
11710 ++it;
11711 }
11712
11713 /* attach new list of controllers to our peer */
11714 mPeer->mStorageControllers.attach(newList);
11715 }
11716 else
11717 {
11718 /* we have no peer (our parent is the newly created machine);
11719 * just commit changes to devices */
11720 commitStorageControllers = true;
11721 }
11722 }
11723 else
11724 {
11725 /* the list of controllers itself is not changed,
11726 * just commit changes to controllers themselves */
11727 commitStorageControllers = true;
11728 }
11729
11730 if (commitStorageControllers)
11731 {
11732 StorageControllerList::const_iterator it = mStorageControllers->begin();
11733 while (it != mStorageControllers->end())
11734 {
11735 (*it)->i_commit();
11736 ++it;
11737 }
11738 }
11739
11740 bool commitUSBControllers = false;
11741
11742 if (mUSBControllers.isBackedUp())
11743 {
11744 mUSBControllers.commit();
11745
11746 if (mPeer)
11747 {
11748 /* Commit all changes to new controllers (this will reshare data with
11749 * peers for those who have peers) */
11750 USBControllerList *newList = new USBControllerList();
11751 USBControllerList::const_iterator it = mUSBControllers->begin();
11752 while (it != mUSBControllers->end())
11753 {
11754 (*it)->i_commit();
11755
11756 /* look if this controller has a peer device */
11757 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11758 if (!peer)
11759 {
11760 /* no peer means the device is a newly created one;
11761 * create a peer owning data this device share it with */
11762 peer.createObject();
11763 peer->init(mPeer, *it, true /* aReshare */);
11764 }
11765 else
11766 {
11767 /* remove peer from the old list */
11768 mPeer->mUSBControllers->remove(peer);
11769 }
11770 /* and add it to the new list */
11771 newList->push_back(peer);
11772
11773 ++it;
11774 }
11775
11776 /* uninit old peer's controllers that are left */
11777 it = mPeer->mUSBControllers->begin();
11778 while (it != mPeer->mUSBControllers->end())
11779 {
11780 (*it)->uninit();
11781 ++it;
11782 }
11783
11784 /* attach new list of controllers to our peer */
11785 mPeer->mUSBControllers.attach(newList);
11786 }
11787 else
11788 {
11789 /* we have no peer (our parent is the newly created machine);
11790 * just commit changes to devices */
11791 commitUSBControllers = true;
11792 }
11793 }
11794 else
11795 {
11796 /* the list of controllers itself is not changed,
11797 * just commit changes to controllers themselves */
11798 commitUSBControllers = true;
11799 }
11800
11801 if (commitUSBControllers)
11802 {
11803 USBControllerList::const_iterator it = mUSBControllers->begin();
11804 while (it != mUSBControllers->end())
11805 {
11806 (*it)->i_commit();
11807 ++it;
11808 }
11809 }
11810
11811 if (i_isSessionMachine())
11812 {
11813 /* attach new data to the primary machine and reshare it */
11814 mPeer->mUserData.attach(mUserData);
11815 mPeer->mHWData.attach(mHWData);
11816 /* mMediaData is reshared by fixupMedia */
11817 // mPeer->mMediaData.attach(mMediaData);
11818 Assert(mPeer->mMediaData.data() == mMediaData.data());
11819 }
11820}
11821
11822/**
11823 * Copies all the hardware data from the given machine.
11824 *
11825 * Currently, only called when the VM is being restored from a snapshot. In
11826 * particular, this implies that the VM is not running during this method's
11827 * call.
11828 *
11829 * @note This method must be called from under this object's lock.
11830 *
11831 * @note This method doesn't call #commit(), so all data remains backed up and
11832 * unsaved.
11833 */
11834void Machine::i_copyFrom(Machine *aThat)
11835{
11836 AssertReturnVoid(!i_isSnapshotMachine());
11837 AssertReturnVoid(aThat->i_isSnapshotMachine());
11838
11839 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11840
11841 mHWData.assignCopy(aThat->mHWData);
11842
11843 // create copies of all shared folders (mHWData after attaching a copy
11844 // contains just references to original objects)
11845 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11846 it != mHWData->mSharedFolders.end();
11847 ++it)
11848 {
11849 ComObjPtr<SharedFolder> folder;
11850 folder.createObject();
11851 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11852 AssertComRC(rc);
11853 *it = folder;
11854 }
11855
11856 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11857 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11858 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11859 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11860 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11861
11862 /* create private copies of all controllers */
11863 mStorageControllers.backup();
11864 mStorageControllers->clear();
11865 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11866 it != aThat->mStorageControllers->end();
11867 ++it)
11868 {
11869 ComObjPtr<StorageController> ctrl;
11870 ctrl.createObject();
11871 ctrl->initCopy(this, *it);
11872 mStorageControllers->push_back(ctrl);
11873 }
11874
11875 /* create private copies of all USB controllers */
11876 mUSBControllers.backup();
11877 mUSBControllers->clear();
11878 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11879 it != aThat->mUSBControllers->end();
11880 ++it)
11881 {
11882 ComObjPtr<USBController> ctrl;
11883 ctrl.createObject();
11884 ctrl->initCopy(this, *it);
11885 mUSBControllers->push_back(ctrl);
11886 }
11887
11888 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11889 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11890 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11891 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11892 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11893 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11894 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11895}
11896
11897/**
11898 * Returns whether the given storage controller is hotplug capable.
11899 *
11900 * @returns true if the controller supports hotplugging
11901 * false otherwise.
11902 * @param enmCtrlType The controller type to check for.
11903 */
11904bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11905{
11906 ComPtr<ISystemProperties> systemProperties;
11907 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11908 if (FAILED(rc))
11909 return false;
11910
11911 BOOL aHotplugCapable = FALSE;
11912 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11913
11914 return RT_BOOL(aHotplugCapable);
11915}
11916
11917#ifdef VBOX_WITH_RESOURCE_USAGE_API
11918
11919void Machine::i_getDiskList(MediaList &list)
11920{
11921 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11922 it != mMediaData->mAttachments.end();
11923 ++it)
11924 {
11925 MediumAttachment* pAttach = *it;
11926 /* just in case */
11927 AssertStmt(pAttach, continue);
11928
11929 AutoCaller localAutoCallerA(pAttach);
11930 if (FAILED(localAutoCallerA.rc())) continue;
11931
11932 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11933
11934 if (pAttach->i_getType() == DeviceType_HardDisk)
11935 list.push_back(pAttach->i_getMedium());
11936 }
11937}
11938
11939void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11940{
11941 AssertReturnVoid(isWriteLockOnCurrentThread());
11942 AssertPtrReturnVoid(aCollector);
11943
11944 pm::CollectorHAL *hal = aCollector->getHAL();
11945 /* Create sub metrics */
11946 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11947 "Percentage of processor time spent in user mode by the VM process.");
11948 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11949 "Percentage of processor time spent in kernel mode by the VM process.");
11950 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11951 "Size of resident portion of VM process in memory.");
11952 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11953 "Actual size of all VM disks combined.");
11954 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11955 "Network receive rate.");
11956 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11957 "Network transmit rate.");
11958 /* Create and register base metrics */
11959 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11960 cpuLoadUser, cpuLoadKernel);
11961 aCollector->registerBaseMetric(cpuLoad);
11962 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11963 ramUsageUsed);
11964 aCollector->registerBaseMetric(ramUsage);
11965 MediaList disks;
11966 i_getDiskList(disks);
11967 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11968 diskUsageUsed);
11969 aCollector->registerBaseMetric(diskUsage);
11970
11971 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11972 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11973 new pm::AggregateAvg()));
11974 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11975 new pm::AggregateMin()));
11976 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11977 new pm::AggregateMax()));
11978 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11979 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11980 new pm::AggregateAvg()));
11981 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11982 new pm::AggregateMin()));
11983 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11984 new pm::AggregateMax()));
11985
11986 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11987 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11988 new pm::AggregateAvg()));
11989 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11990 new pm::AggregateMin()));
11991 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11992 new pm::AggregateMax()));
11993
11994 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11995 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11996 new pm::AggregateAvg()));
11997 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11998 new pm::AggregateMin()));
11999 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12000 new pm::AggregateMax()));
12001
12002
12003 /* Guest metrics collector */
12004 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12005 aCollector->registerGuest(mCollectorGuest);
12006 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12007 this, __PRETTY_FUNCTION__, mCollectorGuest));
12008
12009 /* Create sub metrics */
12010 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12011 "Percentage of processor time spent in user mode as seen by the guest.");
12012 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12013 "Percentage of processor time spent in kernel mode as seen by the guest.");
12014 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12015 "Percentage of processor time spent idling as seen by the guest.");
12016
12017 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12018 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12019 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12020 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12021 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12022 pm::SubMetric *guestMemCache = new pm::SubMetric(
12023 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12024
12025 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12026 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12027
12028 /* Create and register base metrics */
12029 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12030 machineNetRx, machineNetTx);
12031 aCollector->registerBaseMetric(machineNetRate);
12032
12033 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12034 guestLoadUser, guestLoadKernel, guestLoadIdle);
12035 aCollector->registerBaseMetric(guestCpuLoad);
12036
12037 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12038 guestMemTotal, guestMemFree,
12039 guestMemBalloon, guestMemShared,
12040 guestMemCache, guestPagedTotal);
12041 aCollector->registerBaseMetric(guestCpuMem);
12042
12043 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12044 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12045 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12046 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12047
12048 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12049 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12050 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12051 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12052
12053 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12054 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12055 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12056 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12057
12058 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12059 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12060 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12061 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12062
12063 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12064 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12065 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12066 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12067
12068 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12069 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12071 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12072
12073 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12074 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12075 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12076 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12077
12078 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12079 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12080 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12081 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12082
12083 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12084 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12085 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12086 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12087
12088 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12089 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12090 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12091 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12092
12093 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12094 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12095 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12096 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12097}
12098
12099void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12100{
12101 AssertReturnVoid(isWriteLockOnCurrentThread());
12102
12103 if (aCollector)
12104 {
12105 aCollector->unregisterMetricsFor(aMachine);
12106 aCollector->unregisterBaseMetricsFor(aMachine);
12107 }
12108}
12109
12110#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12111
12112
12113////////////////////////////////////////////////////////////////////////////////
12114
12115DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12116
12117HRESULT SessionMachine::FinalConstruct()
12118{
12119 LogFlowThisFunc(("\n"));
12120
12121 mClientToken = NULL;
12122
12123 return BaseFinalConstruct();
12124}
12125
12126void SessionMachine::FinalRelease()
12127{
12128 LogFlowThisFunc(("\n"));
12129
12130 Assert(!mClientToken);
12131 /* paranoia, should not hang around any more */
12132 if (mClientToken)
12133 {
12134 delete mClientToken;
12135 mClientToken = NULL;
12136 }
12137
12138 uninit(Uninit::Unexpected);
12139
12140 BaseFinalRelease();
12141}
12142
12143/**
12144 * @note Must be called only by Machine::LockMachine() from its own write lock.
12145 */
12146HRESULT SessionMachine::init(Machine *aMachine)
12147{
12148 LogFlowThisFuncEnter();
12149 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12150
12151 AssertReturn(aMachine, E_INVALIDARG);
12152
12153 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12154
12155 /* Enclose the state transition NotReady->InInit->Ready */
12156 AutoInitSpan autoInitSpan(this);
12157 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12158
12159 HRESULT rc = S_OK;
12160
12161 /* create the machine client token */
12162 try
12163 {
12164 mClientToken = new ClientToken(aMachine, this);
12165 if (!mClientToken->isReady())
12166 {
12167 delete mClientToken;
12168 mClientToken = NULL;
12169 rc = E_FAIL;
12170 }
12171 }
12172 catch (std::bad_alloc &)
12173 {
12174 rc = E_OUTOFMEMORY;
12175 }
12176 if (FAILED(rc))
12177 return rc;
12178
12179 /* memorize the peer Machine */
12180 unconst(mPeer) = aMachine;
12181 /* share the parent pointer */
12182 unconst(mParent) = aMachine->mParent;
12183
12184 /* take the pointers to data to share */
12185 mData.share(aMachine->mData);
12186 mSSData.share(aMachine->mSSData);
12187
12188 mUserData.share(aMachine->mUserData);
12189 mHWData.share(aMachine->mHWData);
12190 mMediaData.share(aMachine->mMediaData);
12191
12192 mStorageControllers.allocate();
12193 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12194 it != aMachine->mStorageControllers->end();
12195 ++it)
12196 {
12197 ComObjPtr<StorageController> ctl;
12198 ctl.createObject();
12199 ctl->init(this, *it);
12200 mStorageControllers->push_back(ctl);
12201 }
12202
12203 mUSBControllers.allocate();
12204 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12205 it != aMachine->mUSBControllers->end();
12206 ++it)
12207 {
12208 ComObjPtr<USBController> ctl;
12209 ctl.createObject();
12210 ctl->init(this, *it);
12211 mUSBControllers->push_back(ctl);
12212 }
12213
12214 unconst(mBIOSSettings).createObject();
12215 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12216 /* create another VRDEServer object that will be mutable */
12217 unconst(mVRDEServer).createObject();
12218 mVRDEServer->init(this, aMachine->mVRDEServer);
12219 /* create another audio adapter object that will be mutable */
12220 unconst(mAudioAdapter).createObject();
12221 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12222 /* create a list of serial ports that will be mutable */
12223 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12224 {
12225 unconst(mSerialPorts[slot]).createObject();
12226 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12227 }
12228 /* create a list of parallel ports that will be mutable */
12229 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12230 {
12231 unconst(mParallelPorts[slot]).createObject();
12232 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12233 }
12234
12235 /* create another USB device filters object that will be mutable */
12236 unconst(mUSBDeviceFilters).createObject();
12237 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12238
12239 /* create a list of network adapters that will be mutable */
12240 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12241 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12242 {
12243 unconst(mNetworkAdapters[slot]).createObject();
12244 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12245 }
12246
12247 /* create another bandwidth control object that will be mutable */
12248 unconst(mBandwidthControl).createObject();
12249 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12250
12251 /* default is to delete saved state on Saved -> PoweredOff transition */
12252 mRemoveSavedState = true;
12253
12254 /* Confirm a successful initialization when it's the case */
12255 autoInitSpan.setSucceeded();
12256
12257 miNATNetworksStarted = 0;
12258
12259 LogFlowThisFuncLeave();
12260 return rc;
12261}
12262
12263/**
12264 * Uninitializes this session object. If the reason is other than
12265 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12266 * or the client watcher code.
12267 *
12268 * @param aReason uninitialization reason
12269 *
12270 * @note Locks mParent + this object for writing.
12271 */
12272void SessionMachine::uninit(Uninit::Reason aReason)
12273{
12274 LogFlowThisFuncEnter();
12275 LogFlowThisFunc(("reason=%d\n", aReason));
12276
12277 /*
12278 * Strongly reference ourselves to prevent this object deletion after
12279 * mData->mSession.mMachine.setNull() below (which can release the last
12280 * reference and call the destructor). Important: this must be done before
12281 * accessing any members (and before AutoUninitSpan that does it as well).
12282 * This self reference will be released as the very last step on return.
12283 */
12284 ComObjPtr<SessionMachine> selfRef = this;
12285
12286 /* Enclose the state transition Ready->InUninit->NotReady */
12287 AutoUninitSpan autoUninitSpan(this);
12288 if (autoUninitSpan.uninitDone())
12289 {
12290 LogFlowThisFunc(("Already uninitialized\n"));
12291 LogFlowThisFuncLeave();
12292 return;
12293 }
12294
12295 if (autoUninitSpan.initFailed())
12296 {
12297 /* We've been called by init() because it's failed. It's not really
12298 * necessary (nor it's safe) to perform the regular uninit sequence
12299 * below, the following is enough.
12300 */
12301 LogFlowThisFunc(("Initialization failed.\n"));
12302 /* destroy the machine client token */
12303 if (mClientToken)
12304 {
12305 delete mClientToken;
12306 mClientToken = NULL;
12307 }
12308 uninitDataAndChildObjects();
12309 mData.free();
12310 unconst(mParent) = NULL;
12311 unconst(mPeer) = NULL;
12312 LogFlowThisFuncLeave();
12313 return;
12314 }
12315
12316 MachineState_T lastState;
12317 {
12318 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12319 lastState = mData->mMachineState;
12320 }
12321 NOREF(lastState);
12322
12323#ifdef VBOX_WITH_USB
12324 // release all captured USB devices, but do this before requesting the locks below
12325 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12326 {
12327 /* Console::captureUSBDevices() is called in the VM process only after
12328 * setting the machine state to Starting or Restoring.
12329 * Console::detachAllUSBDevices() will be called upon successful
12330 * termination. So, we need to release USB devices only if there was
12331 * an abnormal termination of a running VM.
12332 *
12333 * This is identical to SessionMachine::DetachAllUSBDevices except
12334 * for the aAbnormal argument. */
12335 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12336 AssertComRC(rc);
12337 NOREF(rc);
12338
12339 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12340 if (service)
12341 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12342 }
12343#endif /* VBOX_WITH_USB */
12344
12345 // we need to lock this object in uninit() because the lock is shared
12346 // with mPeer (as well as data we modify below). mParent lock is needed
12347 // by several calls to it, and USB needs host lock.
12348 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12349
12350#ifdef VBOX_WITH_RESOURCE_USAGE_API
12351 /*
12352 * It is safe to call Machine::i_unregisterMetrics() here because
12353 * PerformanceCollector::samplerCallback no longer accesses guest methods
12354 * holding the lock.
12355 */
12356 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12357 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12358 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12359 this, __PRETTY_FUNCTION__, mCollectorGuest));
12360 if (mCollectorGuest)
12361 {
12362 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12363 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12364 mCollectorGuest = NULL;
12365 }
12366#endif
12367
12368 if (aReason == Uninit::Abnormal)
12369 {
12370 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12371 Global::IsOnlineOrTransient(lastState)));
12372
12373 /* reset the state to Aborted */
12374 if (mData->mMachineState != MachineState_Aborted)
12375 i_setMachineState(MachineState_Aborted);
12376 }
12377
12378 // any machine settings modified?
12379 if (mData->flModifications)
12380 {
12381 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12382 i_rollback(false /* aNotify */);
12383 }
12384
12385 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12386 || !mConsoleTaskData.mSnapshot);
12387 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12388 {
12389 LogWarningThisFunc(("canceling failed save state request!\n"));
12390 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12391 }
12392 else if (!mConsoleTaskData.mSnapshot.isNull())
12393 {
12394 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12395
12396 /* delete all differencing hard disks created (this will also attach
12397 * their parents back by rolling back mMediaData) */
12398 i_rollbackMedia();
12399
12400 // delete the saved state file (it might have been already created)
12401 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12402 // think it's still in use
12403 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12404 mConsoleTaskData.mSnapshot->uninit();
12405 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12406 }
12407
12408 mData->mSession.mPID = NIL_RTPROCESS;
12409
12410 if (aReason == Uninit::Unexpected)
12411 {
12412 /* Uninitialization didn't come from #checkForDeath(), so tell the
12413 * client watcher thread to update the set of machines that have open
12414 * sessions. */
12415 mParent->i_updateClientWatcher();
12416 }
12417
12418 /* uninitialize all remote controls */
12419 if (mData->mSession.mRemoteControls.size())
12420 {
12421 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12422 mData->mSession.mRemoteControls.size()));
12423
12424 Data::Session::RemoteControlList::iterator it =
12425 mData->mSession.mRemoteControls.begin();
12426 while (it != mData->mSession.mRemoteControls.end())
12427 {
12428 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12429 HRESULT rc = (*it)->Uninitialize();
12430 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12431 if (FAILED(rc))
12432 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12433 ++it;
12434 }
12435 mData->mSession.mRemoteControls.clear();
12436 }
12437
12438 /* Remove all references to the NAT network service. The service will stop
12439 * if all references (also from other VMs) are removed. */
12440 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12441 {
12442 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12443 {
12444 NetworkAttachmentType_T type;
12445 HRESULT hrc;
12446
12447 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12448 if ( SUCCEEDED(hrc)
12449 && type == NetworkAttachmentType_NATNetwork)
12450 {
12451 Bstr name;
12452 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12453 if (SUCCEEDED(hrc))
12454 {
12455 multilock.release();
12456 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12457 mUserData->s.strName.c_str(), name.raw()));
12458 mParent->i_natNetworkRefDec(name.raw());
12459 multilock.acquire();
12460 }
12461 }
12462 }
12463 }
12464
12465 /*
12466 * An expected uninitialization can come only from #checkForDeath().
12467 * Otherwise it means that something's gone really wrong (for example,
12468 * the Session implementation has released the VirtualBox reference
12469 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12470 * etc). However, it's also possible, that the client releases the IPC
12471 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12472 * but the VirtualBox release event comes first to the server process.
12473 * This case is practically possible, so we should not assert on an
12474 * unexpected uninit, just log a warning.
12475 */
12476
12477 if ((aReason == Uninit::Unexpected))
12478 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12479
12480 if (aReason != Uninit::Normal)
12481 {
12482 mData->mSession.mDirectControl.setNull();
12483 }
12484 else
12485 {
12486 /* this must be null here (see #OnSessionEnd()) */
12487 Assert(mData->mSession.mDirectControl.isNull());
12488 Assert(mData->mSession.mState == SessionState_Unlocking);
12489 Assert(!mData->mSession.mProgress.isNull());
12490 }
12491 if (mData->mSession.mProgress)
12492 {
12493 if (aReason == Uninit::Normal)
12494 mData->mSession.mProgress->i_notifyComplete(S_OK);
12495 else
12496 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12497 COM_IIDOF(ISession),
12498 getComponentName(),
12499 tr("The VM session was aborted"));
12500 mData->mSession.mProgress.setNull();
12501 }
12502
12503 /* remove the association between the peer machine and this session machine */
12504 Assert( (SessionMachine*)mData->mSession.mMachine == this
12505 || aReason == Uninit::Unexpected);
12506
12507 /* reset the rest of session data */
12508 mData->mSession.mMachine.setNull();
12509 mData->mSession.mState = SessionState_Unlocked;
12510 mData->mSession.mType.setNull();
12511
12512 /* destroy the machine client token before leaving the exclusive lock */
12513 if (mClientToken)
12514 {
12515 delete mClientToken;
12516 mClientToken = NULL;
12517 }
12518
12519 /* fire an event */
12520 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12521
12522 uninitDataAndChildObjects();
12523
12524 /* free the essential data structure last */
12525 mData.free();
12526
12527 /* release the exclusive lock before setting the below two to NULL */
12528 multilock.release();
12529
12530 unconst(mParent) = NULL;
12531 unconst(mPeer) = NULL;
12532
12533 LogFlowThisFuncLeave();
12534}
12535
12536// util::Lockable interface
12537////////////////////////////////////////////////////////////////////////////////
12538
12539/**
12540 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12541 * with the primary Machine instance (mPeer).
12542 */
12543RWLockHandle *SessionMachine::lockHandle() const
12544{
12545 AssertReturn(mPeer != NULL, NULL);
12546 return mPeer->lockHandle();
12547}
12548
12549// IInternalMachineControl methods
12550////////////////////////////////////////////////////////////////////////////////
12551
12552/**
12553 * Passes collected guest statistics to performance collector object
12554 */
12555HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12556 ULONG aCpuKernel, ULONG aCpuIdle,
12557 ULONG aMemTotal, ULONG aMemFree,
12558 ULONG aMemBalloon, ULONG aMemShared,
12559 ULONG aMemCache, ULONG aPageTotal,
12560 ULONG aAllocVMM, ULONG aFreeVMM,
12561 ULONG aBalloonedVMM, ULONG aSharedVMM,
12562 ULONG aVmNetRx, ULONG aVmNetTx)
12563{
12564#ifdef VBOX_WITH_RESOURCE_USAGE_API
12565 if (mCollectorGuest)
12566 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12567 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12568 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12569 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12570
12571 return S_OK;
12572#else
12573 NOREF(aValidStats);
12574 NOREF(aCpuUser);
12575 NOREF(aCpuKernel);
12576 NOREF(aCpuIdle);
12577 NOREF(aMemTotal);
12578 NOREF(aMemFree);
12579 NOREF(aMemBalloon);
12580 NOREF(aMemShared);
12581 NOREF(aMemCache);
12582 NOREF(aPageTotal);
12583 NOREF(aAllocVMM);
12584 NOREF(aFreeVMM);
12585 NOREF(aBalloonedVMM);
12586 NOREF(aSharedVMM);
12587 NOREF(aVmNetRx);
12588 NOREF(aVmNetTx);
12589 return E_NOTIMPL;
12590#endif
12591}
12592
12593/**
12594 * @note Locks this object for writing.
12595 */
12596HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12597{
12598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12599
12600 mRemoveSavedState = RT_BOOL(aRemove);
12601
12602 return S_OK;
12603}
12604
12605/**
12606 * @note Locks the same as #i_setMachineState() does.
12607 */
12608HRESULT SessionMachine::updateState(MachineState_T aState)
12609{
12610 return i_setMachineState(aState);
12611}
12612
12613/**
12614 * @note Locks this object for writing.
12615 */
12616HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12617{
12618 IProgress* pProgress(aProgress);
12619
12620 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12621
12622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12623
12624 if (mData->mSession.mState != SessionState_Locked)
12625 return VBOX_E_INVALID_OBJECT_STATE;
12626
12627 if (!mData->mSession.mProgress.isNull())
12628 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12629
12630 /* If we didn't reference the NAT network service yet, add a reference to
12631 * force a start */
12632 if (miNATNetworksStarted < 1)
12633 {
12634 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12635 {
12636 NetworkAttachmentType_T type;
12637 HRESULT hrc;
12638 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12639 if ( SUCCEEDED(hrc)
12640 && type == NetworkAttachmentType_NATNetwork)
12641 {
12642 Bstr name;
12643 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12644 if (SUCCEEDED(hrc))
12645 {
12646 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12647 mUserData->s.strName.c_str(), name.raw()));
12648 mPeer->lockHandle()->unlockWrite();
12649 mParent->i_natNetworkRefInc(name.raw());
12650#ifdef RT_LOCK_STRICT
12651 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12652#else
12653 mPeer->lockHandle()->lockWrite();
12654#endif
12655 }
12656 }
12657 }
12658 miNATNetworksStarted++;
12659 }
12660
12661 LogFlowThisFunc(("returns S_OK.\n"));
12662 return S_OK;
12663}
12664
12665/**
12666 * @note Locks this object for writing.
12667 */
12668HRESULT SessionMachine::endPowerUp(LONG aResult)
12669{
12670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12671
12672 if (mData->mSession.mState != SessionState_Locked)
12673 return VBOX_E_INVALID_OBJECT_STATE;
12674
12675 /* Finalize the LaunchVMProcess progress object. */
12676 if (mData->mSession.mProgress)
12677 {
12678 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12679 mData->mSession.mProgress.setNull();
12680 }
12681
12682 if (SUCCEEDED((HRESULT)aResult))
12683 {
12684#ifdef VBOX_WITH_RESOURCE_USAGE_API
12685 /* The VM has been powered up successfully, so it makes sense
12686 * now to offer the performance metrics for a running machine
12687 * object. Doing it earlier wouldn't be safe. */
12688 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12689 mData->mSession.mPID);
12690#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12691 }
12692
12693 return S_OK;
12694}
12695
12696/**
12697 * @note Locks this object for writing.
12698 */
12699HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12700{
12701 LogFlowThisFuncEnter();
12702
12703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12704
12705 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12706 E_FAIL);
12707
12708 /* create a progress object to track operation completion */
12709 ComObjPtr<Progress> pProgress;
12710 pProgress.createObject();
12711 pProgress->init(i_getVirtualBox(),
12712 static_cast<IMachine *>(this) /* aInitiator */,
12713 Bstr(tr("Stopping the virtual machine")).raw(),
12714 FALSE /* aCancelable */);
12715
12716 /* fill in the console task data */
12717 mConsoleTaskData.mLastState = mData->mMachineState;
12718 mConsoleTaskData.mProgress = pProgress;
12719
12720 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12721 i_setMachineState(MachineState_Stopping);
12722
12723 pProgress.queryInterfaceTo(aProgress.asOutParam());
12724
12725 return S_OK;
12726}
12727
12728/**
12729 * @note Locks this object for writing.
12730 */
12731HRESULT SessionMachine::endPoweringDown(LONG aResult,
12732 const com::Utf8Str &aErrMsg)
12733{
12734 LogFlowThisFuncEnter();
12735
12736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12737
12738 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12739 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12740 && mConsoleTaskData.mLastState != MachineState_Null,
12741 E_FAIL);
12742
12743 /*
12744 * On failure, set the state to the state we had when BeginPoweringDown()
12745 * was called (this is expected by Console::PowerDown() and the associated
12746 * task). On success the VM process already changed the state to
12747 * MachineState_PoweredOff, so no need to do anything.
12748 */
12749 if (FAILED(aResult))
12750 i_setMachineState(mConsoleTaskData.mLastState);
12751
12752 /* notify the progress object about operation completion */
12753 Assert(mConsoleTaskData.mProgress);
12754 if (SUCCEEDED(aResult))
12755 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12756 else
12757 {
12758 if (aErrMsg.length())
12759 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12760 COM_IIDOF(ISession),
12761 getComponentName(),
12762 aErrMsg.c_str());
12763 else
12764 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12765 }
12766
12767 /* clear out the temporary saved state data */
12768 mConsoleTaskData.mLastState = MachineState_Null;
12769 mConsoleTaskData.mProgress.setNull();
12770
12771 LogFlowThisFuncLeave();
12772 return S_OK;
12773}
12774
12775
12776/**
12777 * Goes through the USB filters of the given machine to see if the given
12778 * device matches any filter or not.
12779 *
12780 * @note Locks the same as USBController::hasMatchingFilter() does.
12781 */
12782HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12783 BOOL *aMatched,
12784 ULONG *aMaskedInterfaces)
12785{
12786 LogFlowThisFunc(("\n"));
12787
12788#ifdef VBOX_WITH_USB
12789 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12790#else
12791 NOREF(aDevice);
12792 NOREF(aMaskedInterfaces);
12793 *aMatched = FALSE;
12794#endif
12795
12796 return S_OK;
12797}
12798
12799/**
12800 * @note Locks the same as Host::captureUSBDevice() does.
12801 */
12802HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId)
12803{
12804 LogFlowThisFunc(("\n"));
12805
12806#ifdef VBOX_WITH_USB
12807 /* if captureDeviceForVM() fails, it must have set extended error info */
12808 clearError();
12809 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12810 if (FAILED(rc)) return rc;
12811
12812 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12813 AssertReturn(service, E_FAIL);
12814 return service->captureDeviceForVM(this, aId.ref());
12815#else
12816 NOREF(aId);
12817 return E_NOTIMPL;
12818#endif
12819}
12820
12821/**
12822 * @note Locks the same as Host::detachUSBDevice() does.
12823 */
12824HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12825 BOOL aDone)
12826{
12827 LogFlowThisFunc(("\n"));
12828
12829#ifdef VBOX_WITH_USB
12830 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12831 AssertReturn(service, E_FAIL);
12832 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12833#else
12834 NOREF(aId);
12835 NOREF(aDone);
12836 return E_NOTIMPL;
12837#endif
12838}
12839
12840/**
12841 * Inserts all machine filters to the USB proxy service and then calls
12842 * Host::autoCaptureUSBDevices().
12843 *
12844 * Called by Console from the VM process upon VM startup.
12845 *
12846 * @note Locks what called methods lock.
12847 */
12848HRESULT SessionMachine::autoCaptureUSBDevices()
12849{
12850 LogFlowThisFunc(("\n"));
12851
12852#ifdef VBOX_WITH_USB
12853 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12854 AssertComRC(rc);
12855 NOREF(rc);
12856
12857 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12858 AssertReturn(service, E_FAIL);
12859 return service->autoCaptureDevicesForVM(this);
12860#else
12861 return S_OK;
12862#endif
12863}
12864
12865/**
12866 * Removes all machine filters from the USB proxy service and then calls
12867 * Host::detachAllUSBDevices().
12868 *
12869 * Called by Console from the VM process upon normal VM termination or by
12870 * SessionMachine::uninit() upon abnormal VM termination (from under the
12871 * Machine/SessionMachine lock).
12872 *
12873 * @note Locks what called methods lock.
12874 */
12875HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12876{
12877 LogFlowThisFunc(("\n"));
12878
12879#ifdef VBOX_WITH_USB
12880 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12881 AssertComRC(rc);
12882 NOREF(rc);
12883
12884 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12885 AssertReturn(service, E_FAIL);
12886 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12887#else
12888 NOREF(aDone);
12889 return S_OK;
12890#endif
12891}
12892
12893/**
12894 * @note Locks this object for writing.
12895 */
12896HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12897 ComPtr<IProgress> &aProgress)
12898{
12899 LogFlowThisFuncEnter();
12900
12901 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12902 /*
12903 * We don't assert below because it might happen that a non-direct session
12904 * informs us it is closed right after we've been uninitialized -- it's ok.
12905 */
12906
12907 /* get IInternalSessionControl interface */
12908 ComPtr<IInternalSessionControl> control(aSession);
12909
12910 ComAssertRet(!control.isNull(), E_INVALIDARG);
12911
12912 /* Creating a Progress object requires the VirtualBox lock, and
12913 * thus locking it here is required by the lock order rules. */
12914 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12915
12916 if (control == mData->mSession.mDirectControl)
12917 {
12918 /* The direct session is being normally closed by the client process
12919 * ----------------------------------------------------------------- */
12920
12921 /* go to the closing state (essential for all open*Session() calls and
12922 * for #checkForDeath()) */
12923 Assert(mData->mSession.mState == SessionState_Locked);
12924 mData->mSession.mState = SessionState_Unlocking;
12925
12926 /* set direct control to NULL to release the remote instance */
12927 mData->mSession.mDirectControl.setNull();
12928 LogFlowThisFunc(("Direct control is set to NULL\n"));
12929
12930 if (mData->mSession.mProgress)
12931 {
12932 /* finalize the progress, someone might wait if a frontend
12933 * closes the session before powering on the VM. */
12934 mData->mSession.mProgress->notifyComplete(E_FAIL,
12935 COM_IIDOF(ISession),
12936 getComponentName(),
12937 tr("The VM session was closed before any attempt to power it on"));
12938 mData->mSession.mProgress.setNull();
12939 }
12940
12941 /* Create the progress object the client will use to wait until
12942 * #checkForDeath() is called to uninitialize this session object after
12943 * it releases the IPC semaphore.
12944 * Note! Because we're "reusing" mProgress here, this must be a proxy
12945 * object just like for LaunchVMProcess. */
12946 Assert(mData->mSession.mProgress.isNull());
12947 ComObjPtr<ProgressProxy> progress;
12948 progress.createObject();
12949 ComPtr<IUnknown> pPeer(mPeer);
12950 progress->init(mParent, pPeer,
12951 Bstr(tr("Closing session")).raw(),
12952 FALSE /* aCancelable */);
12953 progress.queryInterfaceTo(aProgress.asOutParam());
12954 mData->mSession.mProgress = progress;
12955 }
12956 else
12957 {
12958 /* the remote session is being normally closed */
12959 Data::Session::RemoteControlList::iterator it =
12960 mData->mSession.mRemoteControls.begin();
12961 while (it != mData->mSession.mRemoteControls.end())
12962 {
12963 if (control == *it)
12964 break;
12965 ++it;
12966 }
12967 BOOL found = it != mData->mSession.mRemoteControls.end();
12968 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12969 E_INVALIDARG);
12970 // This MUST be erase(it), not remove(*it) as the latter triggers a
12971 // very nasty use after free due to the place where the value "lives".
12972 mData->mSession.mRemoteControls.erase(it);
12973 }
12974
12975 /* signal the client watcher thread, because the client is going away */
12976 mParent->i_updateClientWatcher();
12977
12978 LogFlowThisFuncLeave();
12979 return S_OK;
12980}
12981
12982/**
12983 * @note Locks this object for writing.
12984 */
12985HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
12986 com::Utf8Str &aStateFilePath)
12987{
12988 LogFlowThisFuncEnter();
12989
12990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12991
12992 AssertReturn( mData->mMachineState == MachineState_Paused
12993 && mConsoleTaskData.mLastState == MachineState_Null
12994 && mConsoleTaskData.strStateFilePath.isEmpty(),
12995 E_FAIL);
12996
12997 /* create a progress object to track operation completion */
12998 ComObjPtr<Progress> pProgress;
12999 pProgress.createObject();
13000 pProgress->init(i_getVirtualBox(),
13001 static_cast<IMachine *>(this) /* aInitiator */,
13002 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13003 FALSE /* aCancelable */);
13004
13005 /* stateFilePath is null when the machine is not running */
13006 if (mData->mMachineState == MachineState_Paused)
13007 i_composeSavedStateFilename(aStateFilePath);
13008
13009 /* fill in the console task data */
13010 mConsoleTaskData.mLastState = mData->mMachineState;
13011 mConsoleTaskData.strStateFilePath = aStateFilePath;
13012 mConsoleTaskData.mProgress = pProgress;
13013
13014 /* set the state to Saving (this is expected by Console::SaveState()) */
13015 i_setMachineState(MachineState_Saving);
13016
13017 pProgress.queryInterfaceTo(aProgress.asOutParam());
13018
13019 return S_OK;
13020}
13021
13022/**
13023 * @note Locks mParent + this object for writing.
13024 */
13025HRESULT SessionMachine::endSavingState(LONG aResult,
13026 const com::Utf8Str &aErrMsg)
13027{
13028 LogFlowThisFunc(("\n"));
13029
13030 /* endSavingState() need mParent lock */
13031 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13032
13033 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13034 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13035 && mConsoleTaskData.mLastState != MachineState_Null
13036 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13037 E_FAIL);
13038
13039 /*
13040 * On failure, set the state to the state we had when BeginSavingState()
13041 * was called (this is expected by Console::SaveState() and the associated
13042 * task). On success the VM process already changed the state to
13043 * MachineState_Saved, so no need to do anything.
13044 */
13045 if (FAILED(aResult))
13046 i_setMachineState(mConsoleTaskData.mLastState);
13047
13048 return i_endSavingState(aResult, aErrMsg);
13049}
13050
13051/**
13052 * @note Locks this object for writing.
13053 */
13054HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13055{
13056 LogFlowThisFunc(("\n"));
13057
13058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13059
13060 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13061 || mData->mMachineState == MachineState_Teleported
13062 || mData->mMachineState == MachineState_Aborted
13063 , E_FAIL); /** @todo setError. */
13064
13065 com::Utf8Str stateFilePathFull;
13066 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13067 if (RT_FAILURE(vrc))
13068 return setError(VBOX_E_FILE_ERROR,
13069 tr("Invalid saved state file path '%s' (%Rrc)"),
13070 aSavedStateFile.c_str(),
13071 vrc);
13072
13073 mSSData->strStateFilePath = stateFilePathFull;
13074
13075 /* The below i_setMachineState() will detect the state transition and will
13076 * update the settings file */
13077
13078 return i_setMachineState(MachineState_Saved);
13079}
13080
13081HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13082 std::vector<com::Utf8Str> &aValues,
13083 std::vector<LONG64> &aTimestamps,
13084 std::vector<com::Utf8Str> &aFlags)
13085{
13086 LogFlowThisFunc(("\n"));
13087
13088#ifdef VBOX_WITH_GUEST_PROPS
13089 using namespace guestProp;
13090
13091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13092
13093 size_t cEntries = mHWData->mGuestProperties.size();
13094 aNames.resize(cEntries);
13095 aValues.resize(cEntries);
13096 aTimestamps.resize(cEntries);
13097 aFlags.resize(cEntries);
13098
13099 size_t i = 0;
13100 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13101 it != mHWData->mGuestProperties.end();
13102 ++it, ++i)
13103 {
13104 char szFlags[MAX_FLAGS_LEN + 1];
13105 aNames[i] = it->first;
13106 aValues[i] = it->second.strValue;
13107 aTimestamps[i] = it->second.mTimestamp;
13108
13109 /* If it is NULL, keep it NULL. */
13110 if (it->second.mFlags)
13111 {
13112 writeFlags(it->second.mFlags, szFlags);
13113 aFlags[i] = szFlags;
13114 }
13115 else
13116 aFlags[i] = "";
13117 }
13118 return S_OK;
13119#else
13120 ReturnComNotImplemented();
13121#endif
13122}
13123
13124HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13125 const com::Utf8Str &aValue,
13126 LONG64 aTimestamp,
13127 const com::Utf8Str &aFlags)
13128{
13129 LogFlowThisFunc(("\n"));
13130
13131#ifdef VBOX_WITH_GUEST_PROPS
13132 using namespace guestProp;
13133
13134 try
13135 {
13136 /*
13137 * Convert input up front.
13138 */
13139 uint32_t fFlags = NILFLAG;
13140 if (aFlags.length())
13141 {
13142 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13143 AssertRCReturn(vrc, E_INVALIDARG);
13144 }
13145
13146 /*
13147 * Now grab the object lock, validate the state and do the update.
13148 */
13149
13150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13151
13152 switch (mData->mMachineState)
13153 {
13154 case MachineState_Paused:
13155 case MachineState_Running:
13156 case MachineState_Teleporting:
13157 case MachineState_TeleportingPausedVM:
13158 case MachineState_LiveSnapshotting:
13159 case MachineState_DeletingSnapshotOnline:
13160 case MachineState_DeletingSnapshotPaused:
13161 case MachineState_Saving:
13162 case MachineState_Stopping:
13163 break;
13164
13165 default:
13166 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13167 VBOX_E_INVALID_VM_STATE);
13168 }
13169
13170 i_setModified(IsModified_MachineData);
13171 mHWData.backup();
13172
13173 bool fDelete = !aValue.length();
13174 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13175 if (it != mHWData->mGuestProperties.end())
13176 {
13177 if (!fDelete)
13178 {
13179 it->second.strValue = aValue;
13180 it->second.mTimestamp = aTimestamp;
13181 it->second.mFlags = fFlags;
13182 }
13183 else
13184 mHWData->mGuestProperties.erase(it);
13185
13186 mData->mGuestPropertiesModified = TRUE;
13187 }
13188 else if (!fDelete)
13189 {
13190 HWData::GuestProperty prop;
13191 prop.strValue = aValue;
13192 prop.mTimestamp = aTimestamp;
13193 prop.mFlags = fFlags;
13194
13195 mHWData->mGuestProperties[aName] = prop;
13196 mData->mGuestPropertiesModified = TRUE;
13197 }
13198
13199 /*
13200 * Send a callback notification if appropriate
13201 */
13202 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13203 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13204 RTSTR_MAX,
13205 aName.c_str(),
13206 RTSTR_MAX, NULL)
13207 )
13208 {
13209 alock.release();
13210
13211 mParent->i_onGuestPropertyChange(mData->mUuid,
13212 Bstr(aName).raw(),
13213 Bstr(aValue).raw(),
13214 Bstr(aFlags).raw());
13215 }
13216 }
13217 catch (...)
13218 {
13219 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13220 }
13221 return S_OK;
13222#else
13223 ReturnComNotImplemented();
13224#endif
13225}
13226
13227
13228HRESULT SessionMachine::lockMedia()
13229{
13230 AutoMultiWriteLock2 alock(this->lockHandle(),
13231 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13232
13233 AssertReturn( mData->mMachineState == MachineState_Starting
13234 || mData->mMachineState == MachineState_Restoring
13235 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13236
13237 clearError();
13238 alock.release();
13239 return i_lockMedia();
13240}
13241
13242HRESULT SessionMachine::unlockMedia()
13243{
13244 HRESULT hrc = i_unlockMedia();
13245 return hrc;
13246}
13247
13248HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13249 ComPtr<IMediumAttachment> &aNewAttachment)
13250{
13251 // request the host lock first, since might be calling Host methods for getting host drives;
13252 // next, protect the media tree all the while we're in here, as well as our member variables
13253 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13254 this->lockHandle(),
13255 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13256
13257 IMediumAttachment *iAttach = aAttachment;
13258 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13259
13260 Bstr ctrlName;
13261 LONG lPort;
13262 LONG lDevice;
13263 bool fTempEject;
13264 {
13265 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13266
13267 /* Need to query the details first, as the IMediumAttachment reference
13268 * might be to the original settings, which we are going to change. */
13269 ctrlName = pAttach->i_getControllerName();
13270 lPort = pAttach->i_getPort();
13271 lDevice = pAttach->i_getDevice();
13272 fTempEject = pAttach->i_getTempEject();
13273 }
13274
13275 if (!fTempEject)
13276 {
13277 /* Remember previously mounted medium. The medium before taking the
13278 * backup is not necessarily the same thing. */
13279 ComObjPtr<Medium> oldmedium;
13280 oldmedium = pAttach->i_getMedium();
13281
13282 i_setModified(IsModified_Storage);
13283 mMediaData.backup();
13284
13285 // The backup operation makes the pAttach reference point to the
13286 // old settings. Re-get the correct reference.
13287 pAttach = i_findAttachment(mMediaData->mAttachments,
13288 ctrlName.raw(),
13289 lPort,
13290 lDevice);
13291
13292 {
13293 AutoCaller autoAttachCaller(this);
13294 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13295
13296 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13297 if (!oldmedium.isNull())
13298 oldmedium->i_removeBackReference(mData->mUuid);
13299
13300 pAttach->i_updateMedium(NULL);
13301 pAttach->i_updateEjected();
13302 }
13303
13304 i_setModified(IsModified_Storage);
13305 }
13306 else
13307 {
13308 {
13309 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13310 pAttach->i_updateEjected();
13311 }
13312 }
13313
13314 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13315
13316 return S_OK;
13317}
13318
13319// public methods only for internal purposes
13320/////////////////////////////////////////////////////////////////////////////
13321
13322#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13323/**
13324 * Called from the client watcher thread to check for expected or unexpected
13325 * death of the client process that has a direct session to this machine.
13326 *
13327 * On Win32 and on OS/2, this method is called only when we've got the
13328 * mutex (i.e. the client has either died or terminated normally) so it always
13329 * returns @c true (the client is terminated, the session machine is
13330 * uninitialized).
13331 *
13332 * On other platforms, the method returns @c true if the client process has
13333 * terminated normally or abnormally and the session machine was uninitialized,
13334 * and @c false if the client process is still alive.
13335 *
13336 * @note Locks this object for writing.
13337 */
13338bool SessionMachine::i_checkForDeath()
13339{
13340 Uninit::Reason reason;
13341 bool terminated = false;
13342
13343 /* Enclose autoCaller with a block because calling uninit() from under it
13344 * will deadlock. */
13345 {
13346 AutoCaller autoCaller(this);
13347 if (!autoCaller.isOk())
13348 {
13349 /* return true if not ready, to cause the client watcher to exclude
13350 * the corresponding session from watching */
13351 LogFlowThisFunc(("Already uninitialized!\n"));
13352 return true;
13353 }
13354
13355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13356
13357 /* Determine the reason of death: if the session state is Closing here,
13358 * everything is fine. Otherwise it means that the client did not call
13359 * OnSessionEnd() before it released the IPC semaphore. This may happen
13360 * either because the client process has abnormally terminated, or
13361 * because it simply forgot to call ISession::Close() before exiting. We
13362 * threat the latter also as an abnormal termination (see
13363 * Session::uninit() for details). */
13364 reason = mData->mSession.mState == SessionState_Unlocking ?
13365 Uninit::Normal :
13366 Uninit::Abnormal;
13367
13368 if (mClientToken)
13369 terminated = mClientToken->release();
13370 } /* AutoCaller block */
13371
13372 if (terminated)
13373 uninit(reason);
13374
13375 return terminated;
13376}
13377
13378void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13379{
13380 LogFlowThisFunc(("\n"));
13381
13382 strTokenId.setNull();
13383
13384 AutoCaller autoCaller(this);
13385 AssertComRCReturnVoid(autoCaller.rc());
13386
13387 Assert(mClientToken);
13388 if (mClientToken)
13389 mClientToken->getId(strTokenId);
13390}
13391#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13392IToken *SessionMachine::i_getToken()
13393{
13394 LogFlowThisFunc(("\n"));
13395
13396 AutoCaller autoCaller(this);
13397 AssertComRCReturn(autoCaller.rc(), NULL);
13398
13399 Assert(mClientToken);
13400 if (mClientToken)
13401 return mClientToken->getToken();
13402 else
13403 return NULL;
13404}
13405#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13406
13407Machine::ClientToken *SessionMachine::i_getClientToken()
13408{
13409 LogFlowThisFunc(("\n"));
13410
13411 AutoCaller autoCaller(this);
13412 AssertComRCReturn(autoCaller.rc(), NULL);
13413
13414 return mClientToken;
13415}
13416
13417
13418/**
13419 * @note Locks this object for reading.
13420 */
13421HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13422{
13423 LogFlowThisFunc(("\n"));
13424
13425 AutoCaller autoCaller(this);
13426 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13427
13428 ComPtr<IInternalSessionControl> directControl;
13429 {
13430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13431 directControl = mData->mSession.mDirectControl;
13432 }
13433
13434 /* ignore notifications sent after #OnSessionEnd() is called */
13435 if (!directControl)
13436 return S_OK;
13437
13438 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13439}
13440
13441/**
13442 * @note Locks this object for reading.
13443 */
13444HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13445 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13446 IN_BSTR aGuestIp, LONG aGuestPort)
13447{
13448 LogFlowThisFunc(("\n"));
13449
13450 AutoCaller autoCaller(this);
13451 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13452
13453 ComPtr<IInternalSessionControl> directControl;
13454 {
13455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13456 directControl = mData->mSession.mDirectControl;
13457 }
13458
13459 /* ignore notifications sent after #OnSessionEnd() is called */
13460 if (!directControl)
13461 return S_OK;
13462 /*
13463 * instead acting like callback we ask IVirtualBox deliver corresponding event
13464 */
13465
13466 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13467 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13468 return S_OK;
13469}
13470
13471/**
13472 * @note Locks this object for reading.
13473 */
13474HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13475{
13476 LogFlowThisFunc(("\n"));
13477
13478 AutoCaller autoCaller(this);
13479 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13480
13481 ComPtr<IInternalSessionControl> directControl;
13482 {
13483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13484 directControl = mData->mSession.mDirectControl;
13485 }
13486
13487 /* ignore notifications sent after #OnSessionEnd() is called */
13488 if (!directControl)
13489 return S_OK;
13490
13491 return directControl->OnSerialPortChange(serialPort);
13492}
13493
13494/**
13495 * @note Locks this object for reading.
13496 */
13497HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13498{
13499 LogFlowThisFunc(("\n"));
13500
13501 AutoCaller autoCaller(this);
13502 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13503
13504 ComPtr<IInternalSessionControl> directControl;
13505 {
13506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13507 directControl = mData->mSession.mDirectControl;
13508 }
13509
13510 /* ignore notifications sent after #OnSessionEnd() is called */
13511 if (!directControl)
13512 return S_OK;
13513
13514 return directControl->OnParallelPortChange(parallelPort);
13515}
13516
13517/**
13518 * @note Locks this object for reading.
13519 */
13520HRESULT SessionMachine::i_onStorageControllerChange()
13521{
13522 LogFlowThisFunc(("\n"));
13523
13524 AutoCaller autoCaller(this);
13525 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13526
13527 ComPtr<IInternalSessionControl> directControl;
13528 {
13529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13530 directControl = mData->mSession.mDirectControl;
13531 }
13532
13533 /* ignore notifications sent after #OnSessionEnd() is called */
13534 if (!directControl)
13535 return S_OK;
13536
13537 return directControl->OnStorageControllerChange();
13538}
13539
13540/**
13541 * @note Locks this object for reading.
13542 */
13543HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13544{
13545 LogFlowThisFunc(("\n"));
13546
13547 AutoCaller autoCaller(this);
13548 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13549
13550 ComPtr<IInternalSessionControl> directControl;
13551 {
13552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13553 directControl = mData->mSession.mDirectControl;
13554 }
13555
13556 /* ignore notifications sent after #OnSessionEnd() is called */
13557 if (!directControl)
13558 return S_OK;
13559
13560 return directControl->OnMediumChange(aAttachment, aForce);
13561}
13562
13563/**
13564 * @note Locks this object for reading.
13565 */
13566HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13567{
13568 LogFlowThisFunc(("\n"));
13569
13570 AutoCaller autoCaller(this);
13571 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13572
13573 ComPtr<IInternalSessionControl> directControl;
13574 {
13575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13576 directControl = mData->mSession.mDirectControl;
13577 }
13578
13579 /* ignore notifications sent after #OnSessionEnd() is called */
13580 if (!directControl)
13581 return S_OK;
13582
13583 return directControl->OnCPUChange(aCPU, aRemove);
13584}
13585
13586HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13587{
13588 LogFlowThisFunc(("\n"));
13589
13590 AutoCaller autoCaller(this);
13591 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13592
13593 ComPtr<IInternalSessionControl> directControl;
13594 {
13595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13596 directControl = mData->mSession.mDirectControl;
13597 }
13598
13599 /* ignore notifications sent after #OnSessionEnd() is called */
13600 if (!directControl)
13601 return S_OK;
13602
13603 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13604}
13605
13606/**
13607 * @note Locks this object for reading.
13608 */
13609HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13610{
13611 LogFlowThisFunc(("\n"));
13612
13613 AutoCaller autoCaller(this);
13614 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13615
13616 ComPtr<IInternalSessionControl> directControl;
13617 {
13618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13619 directControl = mData->mSession.mDirectControl;
13620 }
13621
13622 /* ignore notifications sent after #OnSessionEnd() is called */
13623 if (!directControl)
13624 return S_OK;
13625
13626 return directControl->OnVRDEServerChange(aRestart);
13627}
13628
13629/**
13630 * @note Locks this object for reading.
13631 */
13632HRESULT SessionMachine::i_onVideoCaptureChange()
13633{
13634 LogFlowThisFunc(("\n"));
13635
13636 AutoCaller autoCaller(this);
13637 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13638
13639 ComPtr<IInternalSessionControl> directControl;
13640 {
13641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13642 directControl = mData->mSession.mDirectControl;
13643 }
13644
13645 /* ignore notifications sent after #OnSessionEnd() is called */
13646 if (!directControl)
13647 return S_OK;
13648
13649 return directControl->OnVideoCaptureChange();
13650}
13651
13652/**
13653 * @note Locks this object for reading.
13654 */
13655HRESULT SessionMachine::i_onUSBControllerChange()
13656{
13657 LogFlowThisFunc(("\n"));
13658
13659 AutoCaller autoCaller(this);
13660 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13661
13662 ComPtr<IInternalSessionControl> directControl;
13663 {
13664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13665 directControl = mData->mSession.mDirectControl;
13666 }
13667
13668 /* ignore notifications sent after #OnSessionEnd() is called */
13669 if (!directControl)
13670 return S_OK;
13671
13672 return directControl->OnUSBControllerChange();
13673}
13674
13675/**
13676 * @note Locks this object for reading.
13677 */
13678HRESULT SessionMachine::i_onSharedFolderChange()
13679{
13680 LogFlowThisFunc(("\n"));
13681
13682 AutoCaller autoCaller(this);
13683 AssertComRCReturnRC(autoCaller.rc());
13684
13685 ComPtr<IInternalSessionControl> directControl;
13686 {
13687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13688 directControl = mData->mSession.mDirectControl;
13689 }
13690
13691 /* ignore notifications sent after #OnSessionEnd() is called */
13692 if (!directControl)
13693 return S_OK;
13694
13695 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13696}
13697
13698/**
13699 * @note Locks this object for reading.
13700 */
13701HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13702{
13703 LogFlowThisFunc(("\n"));
13704
13705 AutoCaller autoCaller(this);
13706 AssertComRCReturnRC(autoCaller.rc());
13707
13708 ComPtr<IInternalSessionControl> directControl;
13709 {
13710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13711 directControl = mData->mSession.mDirectControl;
13712 }
13713
13714 /* ignore notifications sent after #OnSessionEnd() is called */
13715 if (!directControl)
13716 return S_OK;
13717
13718 return directControl->OnClipboardModeChange(aClipboardMode);
13719}
13720
13721/**
13722 * @note Locks this object for reading.
13723 */
13724HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13725{
13726 LogFlowThisFunc(("\n"));
13727
13728 AutoCaller autoCaller(this);
13729 AssertComRCReturnRC(autoCaller.rc());
13730
13731 ComPtr<IInternalSessionControl> directControl;
13732 {
13733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13734 directControl = mData->mSession.mDirectControl;
13735 }
13736
13737 /* ignore notifications sent after #OnSessionEnd() is called */
13738 if (!directControl)
13739 return S_OK;
13740
13741 return directControl->OnDnDModeChange(aDnDMode);
13742}
13743
13744/**
13745 * @note Locks this object for reading.
13746 */
13747HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13748{
13749 LogFlowThisFunc(("\n"));
13750
13751 AutoCaller autoCaller(this);
13752 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13753
13754 ComPtr<IInternalSessionControl> directControl;
13755 {
13756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13757 directControl = mData->mSession.mDirectControl;
13758 }
13759
13760 /* ignore notifications sent after #OnSessionEnd() is called */
13761 if (!directControl)
13762 return S_OK;
13763
13764 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13765}
13766
13767/**
13768 * @note Locks this object for reading.
13769 */
13770HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13771{
13772 LogFlowThisFunc(("\n"));
13773
13774 AutoCaller autoCaller(this);
13775 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13776
13777 ComPtr<IInternalSessionControl> directControl;
13778 {
13779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13780 directControl = mData->mSession.mDirectControl;
13781 }
13782
13783 /* ignore notifications sent after #OnSessionEnd() is called */
13784 if (!directControl)
13785 return S_OK;
13786
13787 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13788}
13789
13790/**
13791 * Returns @c true if this machine's USB controller reports it has a matching
13792 * filter for the given USB device and @c false otherwise.
13793 *
13794 * @note locks this object for reading.
13795 */
13796bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13797{
13798 AutoCaller autoCaller(this);
13799 /* silently return if not ready -- this method may be called after the
13800 * direct machine session has been called */
13801 if (!autoCaller.isOk())
13802 return false;
13803
13804#ifdef VBOX_WITH_USB
13805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13806
13807 switch (mData->mMachineState)
13808 {
13809 case MachineState_Starting:
13810 case MachineState_Restoring:
13811 case MachineState_TeleportingIn:
13812 case MachineState_Paused:
13813 case MachineState_Running:
13814 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13815 * elsewhere... */
13816 alock.release();
13817 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13818 default: break;
13819 }
13820#else
13821 NOREF(aDevice);
13822 NOREF(aMaskedIfs);
13823#endif
13824 return false;
13825}
13826
13827/**
13828 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13829 */
13830HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13831 IVirtualBoxErrorInfo *aError,
13832 ULONG aMaskedIfs)
13833{
13834 LogFlowThisFunc(("\n"));
13835
13836 AutoCaller autoCaller(this);
13837
13838 /* This notification may happen after the machine object has been
13839 * uninitialized (the session was closed), so don't assert. */
13840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13841
13842 ComPtr<IInternalSessionControl> directControl;
13843 {
13844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13845 directControl = mData->mSession.mDirectControl;
13846 }
13847
13848 /* fail on notifications sent after #OnSessionEnd() is called, it is
13849 * expected by the caller */
13850 if (!directControl)
13851 return E_FAIL;
13852
13853 /* No locks should be held at this point. */
13854 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13855 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13856
13857 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13858}
13859
13860/**
13861 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13862 */
13863HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13864 IVirtualBoxErrorInfo *aError)
13865{
13866 LogFlowThisFunc(("\n"));
13867
13868 AutoCaller autoCaller(this);
13869
13870 /* This notification may happen after the machine object has been
13871 * uninitialized (the session was closed), so don't assert. */
13872 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13873
13874 ComPtr<IInternalSessionControl> directControl;
13875 {
13876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13877 directControl = mData->mSession.mDirectControl;
13878 }
13879
13880 /* fail on notifications sent after #OnSessionEnd() is called, it is
13881 * expected by the caller */
13882 if (!directControl)
13883 return E_FAIL;
13884
13885 /* No locks should be held at this point. */
13886 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13887 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13888
13889 return directControl->OnUSBDeviceDetach(aId, aError);
13890}
13891
13892// protected methods
13893/////////////////////////////////////////////////////////////////////////////
13894
13895/**
13896 * Helper method to finalize saving the state.
13897 *
13898 * @note Must be called from under this object's lock.
13899 *
13900 * @param aRc S_OK if the snapshot has been taken successfully
13901 * @param aErrMsg human readable error message for failure
13902 *
13903 * @note Locks mParent + this objects for writing.
13904 */
13905HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13906{
13907 LogFlowThisFuncEnter();
13908
13909 AutoCaller autoCaller(this);
13910 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13911
13912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13913
13914 HRESULT rc = S_OK;
13915
13916 if (SUCCEEDED(aRc))
13917 {
13918 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13919
13920 /* save all VM settings */
13921 rc = i_saveSettings(NULL);
13922 // no need to check whether VirtualBox.xml needs saving also since
13923 // we can't have a name change pending at this point
13924 }
13925 else
13926 {
13927 // delete the saved state file (it might have been already created);
13928 // we need not check whether this is shared with a snapshot here because
13929 // we certainly created this saved state file here anew
13930 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13931 }
13932
13933 /* notify the progress object about operation completion */
13934 Assert(mConsoleTaskData.mProgress);
13935 if (SUCCEEDED(aRc))
13936 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13937 else
13938 {
13939 if (aErrMsg.length())
13940 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13941 COM_IIDOF(ISession),
13942 getComponentName(),
13943 aErrMsg.c_str());
13944 else
13945 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13946 }
13947
13948 /* clear out the temporary saved state data */
13949 mConsoleTaskData.mLastState = MachineState_Null;
13950 mConsoleTaskData.strStateFilePath.setNull();
13951 mConsoleTaskData.mProgress.setNull();
13952
13953 LogFlowThisFuncLeave();
13954 return rc;
13955}
13956
13957/**
13958 * Deletes the given file if it is no longer in use by either the current machine state
13959 * (if the machine is "saved") or any of the machine's snapshots.
13960 *
13961 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13962 * but is different for each SnapshotMachine. When calling this, the order of calling this
13963 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13964 * is therefore critical. I know, it's all rather messy.
13965 *
13966 * @param strStateFile
13967 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13968 * the test for whether the saved state file is in use.
13969 */
13970void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13971 Snapshot *pSnapshotToIgnore)
13972{
13973 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13974 if ( (strStateFile.isNotEmpty())
13975 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13976 )
13977 // ... and it must also not be shared with other snapshots
13978 if ( !mData->mFirstSnapshot
13979 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13980 // this checks the SnapshotMachine's state file paths
13981 )
13982 RTFileDelete(strStateFile.c_str());
13983}
13984
13985/**
13986 * Locks the attached media.
13987 *
13988 * All attached hard disks are locked for writing and DVD/floppy are locked for
13989 * reading. Parents of attached hard disks (if any) are locked for reading.
13990 *
13991 * This method also performs accessibility check of all media it locks: if some
13992 * media is inaccessible, the method will return a failure and a bunch of
13993 * extended error info objects per each inaccessible medium.
13994 *
13995 * Note that this method is atomic: if it returns a success, all media are
13996 * locked as described above; on failure no media is locked at all (all
13997 * succeeded individual locks will be undone).
13998 *
13999 * The caller is responsible for doing the necessary state sanity checks.
14000 *
14001 * The locks made by this method must be undone by calling #unlockMedia() when
14002 * no more needed.
14003 */
14004HRESULT SessionMachine::i_lockMedia()
14005{
14006 AutoCaller autoCaller(this);
14007 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14008
14009 AutoMultiWriteLock2 alock(this->lockHandle(),
14010 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14011
14012 /* bail out if trying to lock things with already set up locking */
14013 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14014
14015 MultiResult mrc(S_OK);
14016
14017 /* Collect locking information for all medium objects attached to the VM. */
14018 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14019 it != mMediaData->mAttachments.end();
14020 ++it)
14021 {
14022 MediumAttachment* pAtt = *it;
14023 DeviceType_T devType = pAtt->i_getType();
14024 Medium *pMedium = pAtt->i_getMedium();
14025
14026 MediumLockList *pMediumLockList(new MediumLockList());
14027 // There can be attachments without a medium (floppy/dvd), and thus
14028 // it's impossible to create a medium lock list. It still makes sense
14029 // to have the empty medium lock list in the map in case a medium is
14030 // attached later.
14031 if (pMedium != NULL)
14032 {
14033 MediumType_T mediumType = pMedium->i_getType();
14034 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14035 || mediumType == MediumType_Shareable;
14036 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14037
14038 alock.release();
14039 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14040 !fIsReadOnlyLock /* fMediumLockWrite */,
14041 NULL,
14042 *pMediumLockList);
14043 alock.acquire();
14044 if (FAILED(mrc))
14045 {
14046 delete pMediumLockList;
14047 mData->mSession.mLockedMedia.Clear();
14048 break;
14049 }
14050 }
14051
14052 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14053 if (FAILED(rc))
14054 {
14055 mData->mSession.mLockedMedia.Clear();
14056 mrc = setError(rc,
14057 tr("Collecting locking information for all attached media failed"));
14058 break;
14059 }
14060 }
14061
14062 if (SUCCEEDED(mrc))
14063 {
14064 /* Now lock all media. If this fails, nothing is locked. */
14065 alock.release();
14066 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14067 alock.acquire();
14068 if (FAILED(rc))
14069 {
14070 mrc = setError(rc,
14071 tr("Locking of attached media failed"));
14072 }
14073 }
14074
14075 return mrc;
14076}
14077
14078/**
14079 * Undoes the locks made by by #lockMedia().
14080 */
14081HRESULT SessionMachine::i_unlockMedia()
14082{
14083 AutoCaller autoCaller(this);
14084 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14085
14086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14087
14088 /* we may be holding important error info on the current thread;
14089 * preserve it */
14090 ErrorInfoKeeper eik;
14091
14092 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14093 AssertComRC(rc);
14094 return rc;
14095}
14096
14097/**
14098 * Helper to change the machine state (reimplementation).
14099 *
14100 * @note Locks this object for writing.
14101 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14102 * it can cause crashes in random places due to unexpectedly committing
14103 * the current settings. The caller is responsible for that. The call
14104 * to saveStateSettings is fine, because this method does not commit.
14105 */
14106HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14107{
14108 LogFlowThisFuncEnter();
14109 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14110
14111 AutoCaller autoCaller(this);
14112 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14113
14114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14115
14116 MachineState_T oldMachineState = mData->mMachineState;
14117
14118 AssertMsgReturn(oldMachineState != aMachineState,
14119 ("oldMachineState=%s, aMachineState=%s\n",
14120 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14121 E_FAIL);
14122
14123 HRESULT rc = S_OK;
14124
14125 int stsFlags = 0;
14126 bool deleteSavedState = false;
14127
14128 /* detect some state transitions */
14129
14130 if ( ( oldMachineState == MachineState_Saved
14131 && aMachineState == MachineState_Restoring)
14132 || ( ( oldMachineState == MachineState_PoweredOff
14133 || oldMachineState == MachineState_Teleported
14134 || oldMachineState == MachineState_Aborted
14135 )
14136 && ( aMachineState == MachineState_TeleportingIn
14137 || aMachineState == MachineState_Starting
14138 )
14139 )
14140 )
14141 {
14142 /* The EMT thread is about to start */
14143
14144 /* Nothing to do here for now... */
14145
14146 /// @todo NEWMEDIA don't let mDVDDrive and other children
14147 /// change anything when in the Starting/Restoring state
14148 }
14149 else if ( ( oldMachineState == MachineState_Running
14150 || oldMachineState == MachineState_Paused
14151 || oldMachineState == MachineState_Teleporting
14152 || oldMachineState == MachineState_LiveSnapshotting
14153 || oldMachineState == MachineState_Stuck
14154 || oldMachineState == MachineState_Starting
14155 || oldMachineState == MachineState_Stopping
14156 || oldMachineState == MachineState_Saving
14157 || oldMachineState == MachineState_Restoring
14158 || oldMachineState == MachineState_TeleportingPausedVM
14159 || oldMachineState == MachineState_TeleportingIn
14160 )
14161 && ( aMachineState == MachineState_PoweredOff
14162 || aMachineState == MachineState_Saved
14163 || aMachineState == MachineState_Teleported
14164 || aMachineState == MachineState_Aborted
14165 )
14166 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14167 * snapshot */
14168 && ( mConsoleTaskData.mSnapshot.isNull()
14169 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14170 )
14171 )
14172 {
14173 /* The EMT thread has just stopped, unlock attached media. Note that as
14174 * opposed to locking that is done from Console, we do unlocking here
14175 * because the VM process may have aborted before having a chance to
14176 * properly unlock all media it locked. */
14177
14178 unlockMedia();
14179 }
14180
14181 if (oldMachineState == MachineState_Restoring)
14182 {
14183 if (aMachineState != MachineState_Saved)
14184 {
14185 /*
14186 * delete the saved state file once the machine has finished
14187 * restoring from it (note that Console sets the state from
14188 * Restoring to Saved if the VM couldn't restore successfully,
14189 * to give the user an ability to fix an error and retry --
14190 * we keep the saved state file in this case)
14191 */
14192 deleteSavedState = true;
14193 }
14194 }
14195 else if ( oldMachineState == MachineState_Saved
14196 && ( aMachineState == MachineState_PoweredOff
14197 || aMachineState == MachineState_Aborted
14198 || aMachineState == MachineState_Teleported
14199 )
14200 )
14201 {
14202 /*
14203 * delete the saved state after Console::ForgetSavedState() is called
14204 * or if the VM process (owning a direct VM session) crashed while the
14205 * VM was Saved
14206 */
14207
14208 /// @todo (dmik)
14209 // Not sure that deleting the saved state file just because of the
14210 // client death before it attempted to restore the VM is a good
14211 // thing. But when it crashes we need to go to the Aborted state
14212 // which cannot have the saved state file associated... The only
14213 // way to fix this is to make the Aborted condition not a VM state
14214 // but a bool flag: i.e., when a crash occurs, set it to true and
14215 // change the state to PoweredOff or Saved depending on the
14216 // saved state presence.
14217
14218 deleteSavedState = true;
14219 mData->mCurrentStateModified = TRUE;
14220 stsFlags |= SaveSTS_CurStateModified;
14221 }
14222
14223 if ( aMachineState == MachineState_Starting
14224 || aMachineState == MachineState_Restoring
14225 || aMachineState == MachineState_TeleportingIn
14226 )
14227 {
14228 /* set the current state modified flag to indicate that the current
14229 * state is no more identical to the state in the
14230 * current snapshot */
14231 if (!mData->mCurrentSnapshot.isNull())
14232 {
14233 mData->mCurrentStateModified = TRUE;
14234 stsFlags |= SaveSTS_CurStateModified;
14235 }
14236 }
14237
14238 if (deleteSavedState)
14239 {
14240 if (mRemoveSavedState)
14241 {
14242 Assert(!mSSData->strStateFilePath.isEmpty());
14243
14244 // it is safe to delete the saved state file if ...
14245 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14246 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14247 // ... none of the snapshots share the saved state file
14248 )
14249 RTFileDelete(mSSData->strStateFilePath.c_str());
14250 }
14251
14252 mSSData->strStateFilePath.setNull();
14253 stsFlags |= SaveSTS_StateFilePath;
14254 }
14255
14256 /* redirect to the underlying peer machine */
14257 mPeer->i_setMachineState(aMachineState);
14258
14259 if ( aMachineState == MachineState_PoweredOff
14260 || aMachineState == MachineState_Teleported
14261 || aMachineState == MachineState_Aborted
14262 || aMachineState == MachineState_Saved)
14263 {
14264 /* the machine has stopped execution
14265 * (or the saved state file was adopted) */
14266 stsFlags |= SaveSTS_StateTimeStamp;
14267 }
14268
14269 if ( ( oldMachineState == MachineState_PoweredOff
14270 || oldMachineState == MachineState_Aborted
14271 || oldMachineState == MachineState_Teleported
14272 )
14273 && aMachineState == MachineState_Saved)
14274 {
14275 /* the saved state file was adopted */
14276 Assert(!mSSData->strStateFilePath.isEmpty());
14277 stsFlags |= SaveSTS_StateFilePath;
14278 }
14279
14280#ifdef VBOX_WITH_GUEST_PROPS
14281 if ( aMachineState == MachineState_PoweredOff
14282 || aMachineState == MachineState_Aborted
14283 || aMachineState == MachineState_Teleported)
14284 {
14285 /* Make sure any transient guest properties get removed from the
14286 * property store on shutdown. */
14287
14288 HWData::GuestPropertyMap::const_iterator it;
14289 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14290 if (!fNeedsSaving)
14291 for (it = mHWData->mGuestProperties.begin();
14292 it != mHWData->mGuestProperties.end(); ++it)
14293 if ( (it->second.mFlags & guestProp::TRANSIENT)
14294 || (it->second.mFlags & guestProp::TRANSRESET))
14295 {
14296 fNeedsSaving = true;
14297 break;
14298 }
14299 if (fNeedsSaving)
14300 {
14301 mData->mCurrentStateModified = TRUE;
14302 stsFlags |= SaveSTS_CurStateModified;
14303 }
14304 }
14305#endif
14306
14307 rc = i_saveStateSettings(stsFlags);
14308
14309 if ( ( oldMachineState != MachineState_PoweredOff
14310 && oldMachineState != MachineState_Aborted
14311 && oldMachineState != MachineState_Teleported
14312 )
14313 && ( aMachineState == MachineState_PoweredOff
14314 || aMachineState == MachineState_Aborted
14315 || aMachineState == MachineState_Teleported
14316 )
14317 )
14318 {
14319 /* we've been shut down for any reason */
14320 /* no special action so far */
14321 }
14322
14323 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14324 LogFlowThisFuncLeave();
14325 return rc;
14326}
14327
14328/**
14329 * Sends the current machine state value to the VM process.
14330 *
14331 * @note Locks this object for reading, then calls a client process.
14332 */
14333HRESULT SessionMachine::i_updateMachineStateOnClient()
14334{
14335 AutoCaller autoCaller(this);
14336 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14337
14338 ComPtr<IInternalSessionControl> directControl;
14339 {
14340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14341 AssertReturn(!!mData, E_FAIL);
14342 directControl = mData->mSession.mDirectControl;
14343
14344 /* directControl may be already set to NULL here in #OnSessionEnd()
14345 * called too early by the direct session process while there is still
14346 * some operation (like deleting the snapshot) in progress. The client
14347 * process in this case is waiting inside Session::close() for the
14348 * "end session" process object to complete, while #uninit() called by
14349 * #checkForDeath() on the Watcher thread is waiting for the pending
14350 * operation to complete. For now, we accept this inconsistent behavior
14351 * and simply do nothing here. */
14352
14353 if (mData->mSession.mState == SessionState_Unlocking)
14354 return S_OK;
14355
14356 AssertReturn(!directControl.isNull(), E_FAIL);
14357 }
14358
14359 return directControl->UpdateMachineState(mData->mMachineState);
14360}
14361
14362HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14363{
14364 NOREF(aRemove);
14365 ReturnComNotImplemented();
14366}
14367
14368HRESULT Machine::updateState(MachineState_T aState)
14369{
14370 NOREF(aState);
14371 ReturnComNotImplemented();
14372}
14373
14374HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14375{
14376 NOREF(aProgress);
14377 ReturnComNotImplemented();
14378}
14379
14380HRESULT Machine::endPowerUp(LONG aResult)
14381{
14382 NOREF(aResult);
14383 ReturnComNotImplemented();
14384}
14385
14386HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14387{
14388 NOREF(aProgress);
14389 ReturnComNotImplemented();
14390}
14391
14392HRESULT Machine::endPoweringDown(LONG aResult,
14393 const com::Utf8Str &aErrMsg)
14394{
14395 NOREF(aResult);
14396 NOREF(aErrMsg);
14397 ReturnComNotImplemented();
14398}
14399
14400HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14401 BOOL *aMatched,
14402 ULONG *aMaskedInterfaces)
14403{
14404 NOREF(aDevice);
14405 NOREF(aMatched);
14406 NOREF(aMaskedInterfaces);
14407 ReturnComNotImplemented();
14408
14409}
14410
14411HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14412{
14413 NOREF(aId);
14414 ReturnComNotImplemented();
14415}
14416
14417HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14418 BOOL aDone)
14419{
14420 NOREF(aId);
14421 NOREF(aDone);
14422 ReturnComNotImplemented();
14423}
14424
14425HRESULT Machine::autoCaptureUSBDevices()
14426{
14427 ReturnComNotImplemented();
14428}
14429
14430HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14431{
14432 NOREF(aDone);
14433 ReturnComNotImplemented();
14434}
14435
14436HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14437 ComPtr<IProgress> &aProgress)
14438{
14439 NOREF(aSession);
14440 NOREF(aProgress);
14441 ReturnComNotImplemented();
14442}
14443
14444HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14445 com::Utf8Str &aStateFilePath)
14446{
14447 NOREF(aProgress);
14448 NOREF(aStateFilePath);
14449 ReturnComNotImplemented();
14450}
14451
14452HRESULT Machine::endSavingState(LONG aResult,
14453 const com::Utf8Str &aErrMsg)
14454{
14455 NOREF(aResult);
14456 NOREF(aErrMsg);
14457 ReturnComNotImplemented();
14458}
14459
14460HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14461{
14462 NOREF(aSavedStateFile);
14463 ReturnComNotImplemented();
14464}
14465
14466HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14467 const com::Utf8Str &aName,
14468 const com::Utf8Str &aDescription,
14469 const ComPtr<IProgress> &aConsoleProgress,
14470 BOOL aFTakingSnapshotOnline,
14471 com::Utf8Str &aStateFilePath)
14472{
14473 NOREF(aInitiator);
14474 NOREF(aName);
14475 NOREF(aDescription);
14476 NOREF(aConsoleProgress);
14477 NOREF(aFTakingSnapshotOnline);
14478 NOREF(aStateFilePath);
14479 ReturnComNotImplemented();
14480}
14481
14482HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14483{
14484 NOREF(aSuccess);
14485 ReturnComNotImplemented();
14486}
14487
14488HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14489 const com::Guid &aStartId,
14490 const com::Guid &aEndId,
14491 BOOL aDeleteAllChildren,
14492 MachineState_T *aMachineState,
14493 ComPtr<IProgress> &aProgress)
14494{
14495 NOREF(aInitiator);
14496 NOREF(aStartId);
14497 NOREF(aEndId);
14498 NOREF(aDeleteAllChildren);
14499 NOREF(aMachineState);
14500 NOREF(aProgress);
14501 ReturnComNotImplemented();
14502}
14503
14504HRESULT Machine::finishOnlineMergeMedium()
14505{
14506 ReturnComNotImplemented();
14507}
14508
14509HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14510 const ComPtr<ISnapshot> &aSnapshot,
14511 MachineState_T *aMachineState,
14512 ComPtr<IProgress> &aProgress)
14513{
14514 NOREF(aInitiator);
14515 NOREF(aSnapshot);
14516 NOREF(aMachineState);
14517 NOREF(aProgress);
14518 ReturnComNotImplemented();
14519}
14520
14521HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14522 std::vector<com::Utf8Str> &aValues,
14523 std::vector<LONG64> &aTimestamps,
14524 std::vector<com::Utf8Str> &aFlags)
14525{
14526 NOREF(aNames);
14527 NOREF(aValues);
14528 NOREF(aTimestamps);
14529 NOREF(aFlags);
14530 ReturnComNotImplemented();
14531}
14532
14533HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14534 const com::Utf8Str &aValue,
14535 LONG64 aTimestamp,
14536 const com::Utf8Str &aFlags)
14537{
14538 NOREF(aName);
14539 NOREF(aValue);
14540 NOREF(aTimestamp);
14541 NOREF(aFlags);
14542 ReturnComNotImplemented();
14543}
14544
14545HRESULT Machine::lockMedia()
14546{
14547 ReturnComNotImplemented();
14548}
14549
14550HRESULT Machine::unlockMedia()
14551{
14552 ReturnComNotImplemented();
14553}
14554
14555HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14556 ComPtr<IMediumAttachment> &aNewAttachment)
14557{
14558 NOREF(aAttachment);
14559 NOREF(aNewAttachment);
14560 ReturnComNotImplemented();
14561}
14562
14563HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14564 ULONG aCpuUser,
14565 ULONG aCpuKernel,
14566 ULONG aCpuIdle,
14567 ULONG aMemTotal,
14568 ULONG aMemFree,
14569 ULONG aMemBalloon,
14570 ULONG aMemShared,
14571 ULONG aMemCache,
14572 ULONG aPagedTotal,
14573 ULONG aMemAllocTotal,
14574 ULONG aMemFreeTotal,
14575 ULONG aMemBalloonTotal,
14576 ULONG aMemSharedTotal,
14577 ULONG aVmNetRx,
14578 ULONG aVmNetTx)
14579{
14580 NOREF(aValidStats);
14581 NOREF(aCpuUser);
14582 NOREF(aCpuKernel);
14583 NOREF(aCpuIdle);
14584 NOREF(aMemTotal);
14585 NOREF(aMemFree);
14586 NOREF(aMemBalloon);
14587 NOREF(aMemShared);
14588 NOREF(aMemCache);
14589 NOREF(aPagedTotal);
14590 NOREF(aMemAllocTotal);
14591 NOREF(aMemFreeTotal);
14592 NOREF(aMemBalloonTotal);
14593 NOREF(aMemSharedTotal);
14594 NOREF(aVmNetRx);
14595 NOREF(aVmNetTx);
14596 ReturnComNotImplemented();
14597}
Note: See TracBrowser for help on using the repository browser.

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