VirtualBox

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

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

Encryption/Main: Allow encryption of differencing media as long as there are no branches in the image chain and no image is used by multiple virtual machines

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 499.9 KB
Line 
1/* $Id: MachineImpl.cpp 54645 2015-03-05 12:12:41Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#ifdef VBOX_WITH_DTRACE_R3_MAIN
90# include "dtrace/VBoxAPI.h"
91#endif
92
93#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
94# define HOSTSUFF_EXE ".exe"
95#else /* !RT_OS_WINDOWS */
96# define HOSTSUFF_EXE ""
97#endif /* !RT_OS_WINDOWS */
98
99// defines / prototypes
100/////////////////////////////////////////////////////////////////////////////
101
102/////////////////////////////////////////////////////////////////////////////
103// Machine::Data structure
104/////////////////////////////////////////////////////////////////////////////
105
106Machine::Data::Data()
107{
108 mRegistered = FALSE;
109 pMachineConfigFile = NULL;
110 /* Contains hints on what has changed when the user is using the VM (config
111 * changes, running the VM, ...). This is used to decide if a config needs
112 * to be written to disk. */
113 flModifications = 0;
114 /* VM modification usually also trigger setting the current state to
115 * "Modified". Although this is not always the case. An e.g. is the VM
116 * initialization phase or when snapshot related data is changed. The
117 * actually behavior is controlled by the following flag. */
118 m_fAllowStateModification = false;
119 mAccessible = FALSE;
120 /* mUuid is initialized in Machine::init() */
121
122 mMachineState = MachineState_PoweredOff;
123 RTTimeNow(&mLastStateChange);
124
125 mMachineStateDeps = 0;
126 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
127 mMachineStateChangePending = 0;
128
129 mCurrentStateModified = TRUE;
130 mGuestPropertiesModified = FALSE;
131
132 mSession.mPID = NIL_RTPROCESS;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mTripleFaultReset = false;
197 mHPETEnabled = false;
198
199 /* default boot order: floppy - DVD - HDD */
200 mBootOrder[0] = DeviceType_Floppy;
201 mBootOrder[1] = DeviceType_DVD;
202 mBootOrder[2] = DeviceType_HardDisk;
203 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
204 mBootOrder[i] = DeviceType_Null;
205
206 mClipboardMode = ClipboardMode_Disabled;
207 mDnDMode = DnDMode_Disabled;
208 mGuestPropertyNotificationPatterns = "";
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222
223 /* Maximum CPU execution cap by default. */
224 mCpuExecutionCap = 100;
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HDData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::MediaData::MediaData()
236{
237}
238
239Machine::MediaData::~MediaData()
240{
241}
242
243/////////////////////////////////////////////////////////////////////////////
244// Machine class
245/////////////////////////////////////////////////////////////////////////////
246
247// constructor / destructor
248/////////////////////////////////////////////////////////////////////////////
249
250Machine::Machine() :
251#ifdef VBOX_WITH_RESOURCE_USAGE_API
252 mCollectorGuest(NULL),
253#endif
254 mPeer(NULL),
255 mParent(NULL),
256 mSerialPorts(),
257 mParallelPorts(),
258 uRegistryNeedsSaving(0)
259{}
260
261Machine::~Machine()
262{}
263
264HRESULT Machine::FinalConstruct()
265{
266 LogFlowThisFunc(("\n"));
267 return BaseFinalConstruct();
268}
269
270void Machine::FinalRelease()
271{
272 LogFlowThisFunc(("\n"));
273 uninit();
274 BaseFinalRelease();
275}
276
277/**
278 * Initializes a new machine instance; this init() variant creates a new, empty machine.
279 * This gets called from VirtualBox::CreateMachine().
280 *
281 * @param aParent Associated parent object
282 * @param strConfigFile Local file system path to the VM settings file (can
283 * be relative to the VirtualBox config directory).
284 * @param strName name for the machine
285 * @param llGroups list of groups for the machine
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Apply BIOS defaults */
349 mBIOSSettings->i_applyDefaults(aOsType);
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Let the OS type select 64-bit ness. */
360 mHWData->mLongMode = aOsType->i_is64Bit()
361 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 }
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 i_allowStateModification();
367
368 /* commit all changes made during the initialization */
369 i_commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param aConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = i_registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 i_allowStateModification();
469
470 i_commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->i_unregisterMachineMedia(i_getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent
607 * @return
608 */
609HRESULT Machine::initImpl(VirtualBox *aParent,
610 const Utf8Str &strConfigFile)
611{
612 LogFlowThisFuncEnter();
613
614 AssertReturn(aParent, E_INVALIDARG);
615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
616
617 HRESULT rc = S_OK;
618
619 /* share the parent weakly */
620 unconst(mParent) = aParent;
621
622 /* allocate the essential machine data structure (the rest will be
623 * allocated later by initDataAndChildObjects() */
624 mData.allocate();
625
626 /* memorize the config file name (as provided) */
627 mData->m_strConfigFile = strConfigFile;
628
629 /* get the full file name */
630 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
631 if (RT_FAILURE(vrc1))
632 return setError(VBOX_E_FILE_ERROR,
633 tr("Invalid machine settings file name '%s' (%Rrc)"),
634 strConfigFile.c_str(),
635 vrc1);
636
637 LogFlowThisFuncLeave();
638
639 return rc;
640}
641
642/**
643 * Tries to create a machine settings file in the path stored in the machine
644 * instance data. Used when a new machine is created to fail gracefully if
645 * the settings file could not be written (e.g. because machine dir is read-only).
646 * @return
647 */
648HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
649{
650 HRESULT rc = S_OK;
651
652 // when we create a new machine, we must be able to create the settings file
653 RTFILE f = NIL_RTFILE;
654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
655 if ( RT_SUCCESS(vrc)
656 || vrc == VERR_SHARING_VIOLATION
657 )
658 {
659 if (RT_SUCCESS(vrc))
660 RTFileClose(f);
661 if (!fForceOverwrite)
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Machine settings file '%s' already exists"),
664 mData->m_strConfigFileFull.c_str());
665 else
666 {
667 /* try to delete the config file, as otherwise the creation
668 * of a new settings file will fail. */
669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
670 if (RT_FAILURE(vrc2))
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Could not delete the existing settings file '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(), vrc2);
674 }
675 }
676 else if ( vrc != VERR_FILE_NOT_FOUND
677 && vrc != VERR_PATH_NOT_FOUND
678 )
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Invalid machine settings file name '%s' (%Rrc)"),
681 mData->m_strConfigFileFull.c_str(),
682 vrc);
683 return rc;
684}
685
686/**
687 * Initializes the registered machine by loading the settings file.
688 * This method is separated from #init() in order to make it possible to
689 * retry the operation after VirtualBox startup instead of refusing to
690 * startup the whole VirtualBox server in case if the settings file of some
691 * registered VM is invalid or inaccessible.
692 *
693 * @note Must be always called from this object's write lock
694 * (unless called from #init() that doesn't need any locking).
695 * @note Locks the mUSBController method for writing.
696 * @note Subclasses must not call this method.
697 */
698HRESULT Machine::i_registeredInit()
699{
700 AssertReturn(!i_isSessionMachine(), E_FAIL);
701 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
702 AssertReturn(mData->mUuid.isValid(), E_FAIL);
703 AssertReturn(!mData->mAccessible, E_FAIL);
704
705 HRESULT rc = initDataAndChildObjects();
706
707 if (SUCCEEDED(rc))
708 {
709 /* Temporarily reset the registered flag in order to let setters
710 * potentially called from loadSettings() succeed (isMutable() used in
711 * all setters will return FALSE for a Machine instance if mRegistered
712 * is TRUE). */
713 mData->mRegistered = FALSE;
714
715 try
716 {
717 // load and parse machine XML; this will throw on XML or logic errors
718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
719
720 if (mData->mUuid != mData->pMachineConfigFile->uuid)
721 throw setError(E_FAIL,
722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
723 mData->pMachineConfigFile->uuid.raw(),
724 mData->m_strConfigFileFull.c_str(),
725 mData->mUuid.toString().c_str(),
726 mParent->i_settingsFilePath().c_str());
727
728 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
729 NULL /* const Guid *puuidRegistry */);
730 if (FAILED(rc)) throw rc;
731 }
732 catch (HRESULT err)
733 {
734 /* we assume that error info is set by the thrower */
735 rc = err;
736 }
737 catch (...)
738 {
739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
740 }
741
742 /* Restore the registered flag (even on failure) */
743 mData->mRegistered = TRUE;
744 }
745
746 if (SUCCEEDED(rc))
747 {
748 /* Set mAccessible to TRUE only if we successfully locked and loaded
749 * the settings file */
750 mData->mAccessible = TRUE;
751
752 /* commit all changes made during loading the settings file */
753 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
754 /// @todo r=klaus for some reason the settings loading logic backs up
755 // the settings, and therefore a commit is needed. Should probably be changed.
756 }
757 else
758 {
759 /* If the machine is registered, then, instead of returning a
760 * failure, we mark it as inaccessible and set the result to
761 * success to give it a try later */
762
763 /* fetch the current error info */
764 mData->mAccessError = com::ErrorInfo();
765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
766 mData->mUuid.raw(),
767 mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 i_rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->i_unregisterMachineMedia(i_getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 i_saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!i_isSnapshotMachine());
820 Assert(!i_isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it does
835 * VirtualBox::addCaller() for the duration of the
836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 LogWarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(i_getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->i_unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
879 i_rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// Wrapped IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
894{
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 aParent = pVirtualBox;
898
899 return S_OK;
900}
901
902
903HRESULT Machine::getAccessible(BOOL *aAccessible)
904{
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->i_dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = i_registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->i_onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
959 {
960 /* return shortly */
961 aAccessError = NULL;
962 return S_OK;
963 }
964
965 HRESULT rc = S_OK;
966
967 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
968 rc = errorInfo.createObject();
969 if (SUCCEEDED(rc))
970 {
971 errorInfo->init(mData->mAccessError.getResultCode(),
972 mData->mAccessError.getInterfaceID().ref(),
973 Utf8Str(mData->mAccessError.getComponent()).c_str(),
974 Utf8Str(mData->mAccessError.getText()));
975 aAccessError = errorInfo;
976 }
977
978 return rc;
979}
980
981HRESULT Machine::getName(com::Utf8Str &aName)
982{
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 aName = mUserData->s.strName;
986
987 return S_OK;
988}
989
990HRESULT Machine::setName(const com::Utf8Str &aName)
991{
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995
996 if (test.isValid())
997 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
998
999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 HRESULT rc = i_checkStateDependency(MutableStateDep);
1002 if (FAILED(rc)) return rc;
1003
1004 i_setModified(IsModified_MachineData);
1005 mUserData.backup();
1006 mUserData->s.strName = aName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1012{
1013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 aDescription = mUserData->s.strDescription;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1021{
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 // this can be done in principle in any state as it doesn't affect the VM
1025 // significantly, but play safe by not messing around while complex
1026 // activities are going on
1027 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1028 if (FAILED(rc)) return rc;
1029
1030 i_setModified(IsModified_MachineData);
1031 mUserData.backup();
1032 mUserData->s.strDescription = aDescription;
1033
1034 return S_OK;
1035}
1036
1037HRESULT Machine::getId(com::Guid &aId)
1038{
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 aId = mData->mUuid;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049 aGroups.resize(mUserData->s.llGroups.size());
1050 size_t i = 0;
1051 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1052 it != mUserData->s.llGroups.end(); ++it, ++i)
1053 aGroups[i] = (*it);
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1059{
1060 StringsList llGroups;
1061 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1062 if (FAILED(rc))
1063 return rc;
1064
1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1066
1067 // changing machine groups is possible while the VM is offline
1068 rc = i_checkStateDependency(OfflineStateDep);
1069 if (FAILED(rc)) return rc;
1070
1071 i_setModified(IsModified_MachineData);
1072 mUserData.backup();
1073 mUserData->s.llGroups = llGroups;
1074
1075 return S_OK;
1076}
1077
1078HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1079{
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 aOSTypeId = mUserData->s.strOsType;
1083
1084 return S_OK;
1085}
1086
1087HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1088{
1089 /* look up the object by Id to check it is valid */
1090 ComPtr<IGuestOSType> guestOSType;
1091 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1092 if (FAILED(rc)) return rc;
1093
1094 /* when setting, always use the "etalon" value for consistency -- lookup
1095 * by ID is case-insensitive and the input value may have different case */
1096 Bstr osTypeId;
1097 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1098 if (FAILED(rc)) return rc;
1099
1100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1101
1102 rc = i_checkStateDependency(MutableStateDep);
1103 if (FAILED(rc)) return rc;
1104
1105 i_setModified(IsModified_MachineData);
1106 mUserData.backup();
1107 mUserData->s.strOsType = osTypeId;
1108
1109 return S_OK;
1110}
1111
1112HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1113{
1114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1115
1116 *aFirmwareType = mHWData->mFirmwareType;
1117
1118 return S_OK;
1119}
1120
1121HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1122{
1123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1124
1125 HRESULT rc = i_checkStateDependency(MutableStateDep);
1126 if (FAILED(rc)) return rc;
1127
1128 i_setModified(IsModified_MachineData);
1129 mHWData.backup();
1130 mHWData->mFirmwareType = aFirmwareType;
1131
1132 return S_OK;
1133}
1134
1135HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1136{
1137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1138
1139 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1140
1141 return S_OK;
1142}
1143
1144HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1145{
1146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 HRESULT rc = i_checkStateDependency(MutableStateDep);
1149 if (FAILED(rc)) return rc;
1150
1151 i_setModified(IsModified_MachineData);
1152 mHWData.backup();
1153 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aPointingHIDType = mHWData->mPointingHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mPointingHIDType = aPointingHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aChipsetType = mHWData->mChipsetType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 if (aChipsetType != mHWData->mChipsetType)
1198 {
1199 i_setModified(IsModified_MachineData);
1200 mHWData.backup();
1201 mHWData->mChipsetType = aChipsetType;
1202
1203 // Resize network adapter array, to be finalized on commit/rollback.
1204 // We must not throw away entries yet, otherwise settings are lost
1205 // without a way to roll back.
1206 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1207 size_t oldCount = mNetworkAdapters.size();
1208 if (newCount > oldCount)
1209 {
1210 mNetworkAdapters.resize(newCount);
1211 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1212 {
1213 unconst(mNetworkAdapters[slot]).createObject();
1214 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1215 }
1216 }
1217 }
1218
1219 return S_OK;
1220}
1221
1222HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1223{
1224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1225
1226 *aParavirtProvider = mHWData->mParavirtProvider;
1227
1228 return S_OK;
1229}
1230
1231HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1232{
1233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 HRESULT rc = i_checkStateDependency(MutableStateDep);
1236 if (FAILED(rc)) return rc;
1237
1238 if (aParavirtProvider != mHWData->mParavirtProvider)
1239 {
1240 i_setModified(IsModified_MachineData);
1241 mHWData.backup();
1242 mHWData->mParavirtProvider = aParavirtProvider;
1243 }
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1249{
1250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 *aParavirtProvider = mHWData->mParavirtProvider;
1253 switch (mHWData->mParavirtProvider)
1254 {
1255 case ParavirtProvider_None:
1256 case ParavirtProvider_HyperV:
1257 case ParavirtProvider_Minimal:
1258 break;
1259
1260 /* Resolve dynamic provider types to the effective types. */
1261 default:
1262 {
1263 ComPtr<IGuestOSType> ptrGuestOSType;
1264 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1266
1267 Bstr guestTypeFamilyId;
1268 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1269 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1270 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1271
1272 switch (mHWData->mParavirtProvider)
1273 {
1274 case ParavirtProvider_Legacy:
1275 {
1276 if (fOsXGuest)
1277 *aParavirtProvider = ParavirtProvider_Minimal;
1278 else
1279 *aParavirtProvider = ParavirtProvider_None;
1280 break;
1281 }
1282
1283 case ParavirtProvider_Default:
1284 {
1285 if (fOsXGuest)
1286 *aParavirtProvider = ParavirtProvider_Minimal;
1287 else if ( mUserData->s.strOsType == "Windows10"
1288 || mUserData->s.strOsType == "Windows10_64"
1289 || mUserData->s.strOsType == "Windows81"
1290 || mUserData->s.strOsType == "Windows81_64"
1291 || mUserData->s.strOsType == "Windows8"
1292 || mUserData->s.strOsType == "Windows8_64"
1293 || mUserData->s.strOsType == "Windows7"
1294 || mUserData->s.strOsType == "Windows7_64"
1295 || mUserData->s.strOsType == "WindowsVista"
1296 || mUserData->s.strOsType == "WindowsVista_64"
1297 || mUserData->s.strOsType == "Windows2012"
1298 || mUserData->s.strOsType == "Windows2012_64"
1299 || mUserData->s.strOsType == "Windows2008"
1300 || mUserData->s.strOsType == "Windows2008_64")
1301 {
1302 *aParavirtProvider = ParavirtProvider_HyperV;
1303 }
1304 else
1305 *aParavirtProvider = ParavirtProvider_None;
1306 break;
1307 }
1308 }
1309 break;
1310 }
1311 }
1312
1313 Assert( *aParavirtProvider == ParavirtProvider_None
1314 || *aParavirtProvider == ParavirtProvider_Minimal
1315 || *aParavirtProvider == ParavirtProvider_HyperV);
1316 return S_OK;
1317}
1318
1319HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1320{
1321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1322
1323 aHardwareVersion = mHWData->mHWVersion;
1324
1325 return S_OK;
1326}
1327
1328HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1329{
1330 /* check known version */
1331 Utf8Str hwVersion = aHardwareVersion;
1332 if ( hwVersion.compare("1") != 0
1333 && hwVersion.compare("2") != 0)
1334 return setError(E_INVALIDARG,
1335 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1336
1337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1338
1339 HRESULT rc = i_checkStateDependency(MutableStateDep);
1340 if (FAILED(rc)) return rc;
1341
1342 i_setModified(IsModified_MachineData);
1343 mHWData.backup();
1344 mHWData->mHWVersion = aHardwareVersion;
1345
1346 return S_OK;
1347}
1348
1349HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1350{
1351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 if (!mHWData->mHardwareUUID.isZero())
1354 aHardwareUUID = mHWData->mHardwareUUID;
1355 else
1356 aHardwareUUID = mData->mUuid;
1357
1358 return S_OK;
1359}
1360
1361HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1362{
1363 if (!aHardwareUUID.isValid())
1364 return E_INVALIDARG;
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT rc = i_checkStateDependency(MutableStateDep);
1369 if (FAILED(rc)) return rc;
1370
1371 i_setModified(IsModified_MachineData);
1372 mHWData.backup();
1373 if (aHardwareUUID == mData->mUuid)
1374 mHWData->mHardwareUUID.clear();
1375 else
1376 mHWData->mHardwareUUID = aHardwareUUID;
1377
1378 return S_OK;
1379}
1380
1381HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1382{
1383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1384
1385 *aMemorySize = mHWData->mMemorySize;
1386
1387 return S_OK;
1388}
1389
1390HRESULT Machine::setMemorySize(ULONG aMemorySize)
1391{
1392 /* check RAM limits */
1393 if ( aMemorySize < MM_RAM_MIN_IN_MB
1394 || aMemorySize > MM_RAM_MAX_IN_MB
1395 )
1396 return setError(E_INVALIDARG,
1397 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1398 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1399
1400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1401
1402 HRESULT rc = i_checkStateDependency(MutableStateDep);
1403 if (FAILED(rc)) return rc;
1404
1405 i_setModified(IsModified_MachineData);
1406 mHWData.backup();
1407 mHWData->mMemorySize = aMemorySize;
1408
1409 return S_OK;
1410}
1411
1412HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1413{
1414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 *aCPUCount = mHWData->mCPUCount;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::setCPUCount(ULONG aCPUCount)
1422{
1423 /* check CPU limits */
1424 if ( aCPUCount < SchemaDefs::MinCPUCount
1425 || aCPUCount > SchemaDefs::MaxCPUCount
1426 )
1427 return setError(E_INVALIDARG,
1428 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1429 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1430
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1434 if (mHWData->mCPUHotPlugEnabled)
1435 {
1436 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1437 {
1438 if (mHWData->mCPUAttached[idx])
1439 return setError(E_INVALIDARG,
1440 tr("There is still a CPU attached to socket %lu."
1441 "Detach the CPU before removing the socket"),
1442 aCPUCount, idx+1);
1443 }
1444 }
1445
1446 HRESULT rc = i_checkStateDependency(MutableStateDep);
1447 if (FAILED(rc)) return rc;
1448
1449 i_setModified(IsModified_MachineData);
1450 mHWData.backup();
1451 mHWData->mCPUCount = aCPUCount;
1452
1453 return S_OK;
1454}
1455
1456HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1457{
1458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1461
1462 return S_OK;
1463}
1464
1465HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1466{
1467 HRESULT rc = S_OK;
1468
1469 /* check throttle limits */
1470 if ( aCPUExecutionCap < 1
1471 || aCPUExecutionCap > 100
1472 )
1473 return setError(E_INVALIDARG,
1474 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1475 aCPUExecutionCap, 1, 100);
1476
1477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1478
1479 alock.release();
1480 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1481 alock.acquire();
1482 if (FAILED(rc)) return rc;
1483
1484 i_setModified(IsModified_MachineData);
1485 mHWData.backup();
1486 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1487
1488 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1489 if (Global::IsOnline(mData->mMachineState))
1490 i_saveSettings(NULL);
1491
1492 return S_OK;
1493}
1494
1495HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1496{
1497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1498
1499 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1500
1501 return S_OK;
1502}
1503
1504HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1505{
1506 HRESULT rc = S_OK;
1507
1508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1509
1510 rc = i_checkStateDependency(MutableStateDep);
1511 if (FAILED(rc)) return rc;
1512
1513 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1514 {
1515 if (aCPUHotPlugEnabled)
1516 {
1517 i_setModified(IsModified_MachineData);
1518 mHWData.backup();
1519
1520 /* Add the amount of CPUs currently attached */
1521 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1522 mHWData->mCPUAttached[i] = true;
1523 }
1524 else
1525 {
1526 /*
1527 * We can disable hotplug only if the amount of maximum CPUs is equal
1528 * to the amount of attached CPUs
1529 */
1530 unsigned cCpusAttached = 0;
1531 unsigned iHighestId = 0;
1532
1533 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1534 {
1535 if (mHWData->mCPUAttached[i])
1536 {
1537 cCpusAttached++;
1538 iHighestId = i;
1539 }
1540 }
1541
1542 if ( (cCpusAttached != mHWData->mCPUCount)
1543 || (iHighestId >= mHWData->mCPUCount))
1544 return setError(E_INVALIDARG,
1545 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1546
1547 i_setModified(IsModified_MachineData);
1548 mHWData.backup();
1549 }
1550 }
1551
1552 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1553
1554 return rc;
1555}
1556
1557HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1558{
1559#ifdef VBOX_WITH_USB_CARDREADER
1560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1561
1562 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1563
1564 return S_OK;
1565#else
1566 NOREF(aEmulatedUSBCardReaderEnabled);
1567 return E_NOTIMPL;
1568#endif
1569}
1570
1571HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1572{
1573#ifdef VBOX_WITH_USB_CARDREADER
1574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1575
1576 HRESULT rc = i_checkStateDependency(MutableStateDep);
1577 if (FAILED(rc)) return rc;
1578
1579 i_setModified(IsModified_MachineData);
1580 mHWData.backup();
1581 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1582
1583 return S_OK;
1584#else
1585 NOREF(aEmulatedUSBCardReaderEnabled);
1586 return E_NOTIMPL;
1587#endif
1588}
1589
1590HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1591{
1592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1593
1594 *aHPETEnabled = mHWData->mHPETEnabled;
1595
1596 return S_OK;
1597}
1598
1599HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1600{
1601 HRESULT rc = S_OK;
1602
1603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1604
1605 rc = i_checkStateDependency(MutableStateDep);
1606 if (FAILED(rc)) return rc;
1607
1608 i_setModified(IsModified_MachineData);
1609 mHWData.backup();
1610
1611 mHWData->mHPETEnabled = aHPETEnabled;
1612
1613 return rc;
1614}
1615
1616HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1617{
1618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1619
1620 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1621 return S_OK;
1622}
1623
1624HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1625{
1626 HRESULT rc = S_OK;
1627
1628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1633
1634 alock.release();
1635 rc = i_onVideoCaptureChange();
1636 alock.acquire();
1637 if (FAILED(rc))
1638 {
1639 /*
1640 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1641 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1642 * determine if it should start or stop capturing. Therefore we need to manually
1643 * undo change.
1644 */
1645 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1646 return rc;
1647 }
1648
1649 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1650 if (Global::IsOnline(mData->mMachineState))
1651 i_saveSettings(NULL);
1652
1653 return rc;
1654}
1655
1656HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1657{
1658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1659 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1660 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1661 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1662 return S_OK;
1663}
1664
1665HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1666{
1667 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1668 bool fChanged = false;
1669
1670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1671
1672 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1673 {
1674 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1675 {
1676 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1677 fChanged = true;
1678 }
1679 }
1680 if (fChanged)
1681 {
1682 alock.release();
1683 HRESULT rc = i_onVideoCaptureChange();
1684 alock.acquire();
1685 if (FAILED(rc)) return rc;
1686 i_setModified(IsModified_MachineData);
1687
1688 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1689 if (Global::IsOnline(mData->mMachineState))
1690 i_saveSettings(NULL);
1691 }
1692
1693 return S_OK;
1694}
1695
1696HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699 if (mHWData->mVideoCaptureFile.isEmpty())
1700 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1701 else
1702 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1703 return S_OK;
1704}
1705
1706HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1707{
1708 Utf8Str strFile(aVideoCaptureFile);
1709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1710
1711 if ( Global::IsOnline(mData->mMachineState)
1712 && mHWData->mVideoCaptureEnabled)
1713 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1714
1715 if (!RTPathStartsWithRoot(strFile.c_str()))
1716 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1717
1718 if (!strFile.isEmpty())
1719 {
1720 Utf8Str defaultFile;
1721 i_getDefaultVideoCaptureFile(defaultFile);
1722 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1723 strFile.setNull();
1724 }
1725
1726 i_setModified(IsModified_MachineData);
1727 mHWData.backup();
1728 mHWData->mVideoCaptureFile = strFile;
1729
1730 return S_OK;
1731}
1732
1733HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1734{
1735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1736 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1737 return S_OK;
1738}
1739
1740HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1741{
1742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1743
1744 if ( Global::IsOnline(mData->mMachineState)
1745 && mHWData->mVideoCaptureEnabled)
1746 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1747
1748 i_setModified(IsModified_MachineData);
1749 mHWData.backup();
1750 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1751
1752 return S_OK;
1753}
1754
1755HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1756{
1757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1758 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1759 return S_OK;
1760}
1761
1762HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1763{
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 if ( Global::IsOnline(mData->mMachineState)
1767 && mHWData->mVideoCaptureEnabled)
1768 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1769
1770 i_setModified(IsModified_MachineData);
1771 mHWData.backup();
1772 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1773
1774 return S_OK;
1775}
1776
1777HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1778{
1779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1780 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1781 return S_OK;
1782}
1783
1784HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1785{
1786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1787
1788 if ( Global::IsOnline(mData->mMachineState)
1789 && mHWData->mVideoCaptureEnabled)
1790 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1791
1792 i_setModified(IsModified_MachineData);
1793 mHWData.backup();
1794 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1795
1796 return S_OK;
1797}
1798
1799HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1800{
1801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1802 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1803 return S_OK;
1804}
1805
1806HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1807{
1808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1809
1810 if ( Global::IsOnline(mData->mMachineState)
1811 && mHWData->mVideoCaptureEnabled)
1812 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1813
1814 i_setModified(IsModified_MachineData);
1815 mHWData.backup();
1816 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1817
1818 return S_OK;
1819}
1820
1821HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1822{
1823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1824 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1825 return S_OK;
1826}
1827
1828HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1829{
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831
1832 if ( Global::IsOnline(mData->mMachineState)
1833 && mHWData->mVideoCaptureEnabled)
1834 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1835
1836 i_setModified(IsModified_MachineData);
1837 mHWData.backup();
1838 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1839
1840 return S_OK;
1841}
1842
1843HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1844{
1845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1846 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1847 return S_OK;
1848}
1849
1850HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1851{
1852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1853
1854 if ( Global::IsOnline(mData->mMachineState)
1855 && mHWData->mVideoCaptureEnabled)
1856 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1857
1858 i_setModified(IsModified_MachineData);
1859 mHWData.backup();
1860 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1861
1862 return S_OK;
1863}
1864
1865HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1866{
1867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1868
1869 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1870 return S_OK;
1871}
1872
1873HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1874{
1875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 if ( Global::IsOnline(mData->mMachineState)
1878 && mHWData->mVideoCaptureEnabled)
1879 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1880
1881 i_setModified(IsModified_MachineData);
1882 mHWData.backup();
1883 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1884
1885 return S_OK;
1886}
1887
1888HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1889{
1890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1893
1894 return S_OK;
1895}
1896
1897HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1898{
1899 switch (aGraphicsControllerType)
1900 {
1901 case GraphicsControllerType_Null:
1902 case GraphicsControllerType_VBoxVGA:
1903#ifdef VBOX_WITH_VMSVGA
1904 case GraphicsControllerType_VMSVGA:
1905#endif
1906 break;
1907 default:
1908 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1909 }
1910
1911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1912
1913 HRESULT rc = i_checkStateDependency(MutableStateDep);
1914 if (FAILED(rc)) return rc;
1915
1916 i_setModified(IsModified_MachineData);
1917 mHWData.backup();
1918 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1919
1920 return S_OK;
1921}
1922
1923HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1924{
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 *aVRAMSize = mHWData->mVRAMSize;
1928
1929 return S_OK;
1930}
1931
1932HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1933{
1934 /* check VRAM limits */
1935 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1936 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1937 return setError(E_INVALIDARG,
1938 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1939 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1940
1941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1942
1943 HRESULT rc = i_checkStateDependency(MutableStateDep);
1944 if (FAILED(rc)) return rc;
1945
1946 i_setModified(IsModified_MachineData);
1947 mHWData.backup();
1948 mHWData->mVRAMSize = aVRAMSize;
1949
1950 return S_OK;
1951}
1952
1953/** @todo this method should not be public */
1954HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1955{
1956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1959
1960 return S_OK;
1961}
1962
1963/**
1964 * Set the memory balloon size.
1965 *
1966 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1967 * we have to make sure that we never call IGuest from here.
1968 */
1969HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1970{
1971 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1972#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1973 /* check limits */
1974 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1975 return setError(E_INVALIDARG,
1976 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1977 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1978
1979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1980
1981 i_setModified(IsModified_MachineData);
1982 mHWData.backup();
1983 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1984
1985 return S_OK;
1986#else
1987 NOREF(aMemoryBalloonSize);
1988 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1989#endif
1990}
1991
1992HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1993{
1994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
1996 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1997 return S_OK;
1998}
1999
2000HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2001{
2002#ifdef VBOX_WITH_PAGE_SHARING
2003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2004
2005 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2006 i_setModified(IsModified_MachineData);
2007 mHWData.backup();
2008 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2009 return S_OK;
2010#else
2011 NOREF(aPageFusionEnabled);
2012 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2013#endif
2014}
2015
2016HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2017{
2018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2019
2020 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2021
2022 return S_OK;
2023}
2024
2025HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2026{
2027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2028
2029 HRESULT rc = i_checkStateDependency(MutableStateDep);
2030 if (FAILED(rc)) return rc;
2031
2032 /** @todo check validity! */
2033
2034 i_setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2037
2038 return S_OK;
2039}
2040
2041
2042HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2043{
2044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2047
2048 return S_OK;
2049}
2050
2051HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2052{
2053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2054
2055 HRESULT rc = i_checkStateDependency(MutableStateDep);
2056 if (FAILED(rc)) return rc;
2057
2058 /** @todo check validity! */
2059 i_setModified(IsModified_MachineData);
2060 mHWData.backup();
2061 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2062
2063 return S_OK;
2064}
2065
2066HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2067{
2068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2069
2070 *aMonitorCount = mHWData->mMonitorCount;
2071
2072 return S_OK;
2073}
2074
2075HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2076{
2077 /* make sure monitor count is a sensible number */
2078 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2079 return setError(E_INVALIDARG,
2080 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2081 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2082
2083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 HRESULT rc = i_checkStateDependency(MutableStateDep);
2086 if (FAILED(rc)) return rc;
2087
2088 i_setModified(IsModified_MachineData);
2089 mHWData.backup();
2090 mHWData->mMonitorCount = aMonitorCount;
2091
2092 return S_OK;
2093}
2094
2095HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2096{
2097 /* mBIOSSettings is constant during life time, no need to lock */
2098 aBIOSSettings = mBIOSSettings;
2099
2100 return S_OK;
2101}
2102
2103HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2104{
2105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2106
2107 switch (aProperty)
2108 {
2109 case CPUPropertyType_PAE:
2110 *aValue = mHWData->mPAEEnabled;
2111 break;
2112
2113 case CPUPropertyType_Synthetic:
2114 *aValue = mHWData->mSyntheticCpu;
2115 break;
2116
2117 case CPUPropertyType_LongMode:
2118 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2119 *aValue = TRUE;
2120 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2121 *aValue = FALSE;
2122#if HC_ARCH_BITS == 64
2123 else
2124 *aValue = TRUE;
2125#else
2126 else
2127 {
2128 *aValue = FALSE;
2129
2130 ComPtr<IGuestOSType> ptrGuestOSType;
2131 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2132 if (SUCCEEDED(hrc2))
2133 {
2134 BOOL fIs64Bit = FALSE;
2135 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2136 if (SUCCEEDED(hrc2) && fIs64Bit)
2137 {
2138 ComObjPtr<Host> ptrHost = mParent->i_host();
2139 alock.release();
2140
2141 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2142 if (FAILED(hrc2))
2143 *aValue = FALSE;
2144 }
2145 }
2146 }
2147#endif
2148 break;
2149
2150 case CPUPropertyType_TripleFaultReset:
2151 *aValue = mHWData->mTripleFaultReset;
2152 break;
2153
2154 default:
2155 return E_INVALIDARG;
2156 }
2157 return S_OK;
2158}
2159
2160HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2161{
2162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2163
2164 HRESULT rc = i_checkStateDependency(MutableStateDep);
2165 if (FAILED(rc)) return rc;
2166
2167 switch (aProperty)
2168 {
2169 case CPUPropertyType_PAE:
2170 i_setModified(IsModified_MachineData);
2171 mHWData.backup();
2172 mHWData->mPAEEnabled = !!aValue;
2173 break;
2174
2175 case CPUPropertyType_Synthetic:
2176 i_setModified(IsModified_MachineData);
2177 mHWData.backup();
2178 mHWData->mSyntheticCpu = !!aValue;
2179 break;
2180
2181 case CPUPropertyType_LongMode:
2182 i_setModified(IsModified_MachineData);
2183 mHWData.backup();
2184 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2185 break;
2186
2187 case CPUPropertyType_TripleFaultReset:
2188 i_setModified(IsModified_MachineData);
2189 mHWData.backup();
2190 mHWData->mTripleFaultReset = !!aValue;
2191 break;
2192
2193 default:
2194 return E_INVALIDARG;
2195 }
2196 return S_OK;
2197}
2198
2199HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2200{
2201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2202
2203 switch(aId)
2204 {
2205 case 0x0:
2206 case 0x1:
2207 case 0x2:
2208 case 0x3:
2209 case 0x4:
2210 case 0x5:
2211 case 0x6:
2212 case 0x7:
2213 case 0x8:
2214 case 0x9:
2215 case 0xA:
2216 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2217 return E_INVALIDARG;
2218
2219 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2220 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2221 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2222 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2223 break;
2224
2225 case 0x80000000:
2226 case 0x80000001:
2227 case 0x80000002:
2228 case 0x80000003:
2229 case 0x80000004:
2230 case 0x80000005:
2231 case 0x80000006:
2232 case 0x80000007:
2233 case 0x80000008:
2234 case 0x80000009:
2235 case 0x8000000A:
2236 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2237 return E_INVALIDARG;
2238
2239 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2240 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2241 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2242 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2243 break;
2244
2245 default:
2246 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2247 }
2248 return S_OK;
2249}
2250
2251
2252HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2253{
2254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2255
2256 HRESULT rc = i_checkStateDependency(MutableStateDep);
2257 if (FAILED(rc)) return rc;
2258
2259 switch(aId)
2260 {
2261 case 0x0:
2262 case 0x1:
2263 case 0x2:
2264 case 0x3:
2265 case 0x4:
2266 case 0x5:
2267 case 0x6:
2268 case 0x7:
2269 case 0x8:
2270 case 0x9:
2271 case 0xA:
2272 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2273 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2274 i_setModified(IsModified_MachineData);
2275 mHWData.backup();
2276 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2277 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2278 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2279 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2280 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2281 break;
2282
2283 case 0x80000000:
2284 case 0x80000001:
2285 case 0x80000002:
2286 case 0x80000003:
2287 case 0x80000004:
2288 case 0x80000005:
2289 case 0x80000006:
2290 case 0x80000007:
2291 case 0x80000008:
2292 case 0x80000009:
2293 case 0x8000000A:
2294 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2295 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2299 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2300 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2301 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2302 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2303 break;
2304
2305 default:
2306 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2307 }
2308 return S_OK;
2309}
2310
2311HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2312{
2313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2314
2315 HRESULT rc = i_checkStateDependency(MutableStateDep);
2316 if (FAILED(rc)) return rc;
2317
2318 switch(aId)
2319 {
2320 case 0x0:
2321 case 0x1:
2322 case 0x2:
2323 case 0x3:
2324 case 0x4:
2325 case 0x5:
2326 case 0x6:
2327 case 0x7:
2328 case 0x8:
2329 case 0x9:
2330 case 0xA:
2331 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2332 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 /* Invalidate leaf. */
2336 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2337 break;
2338
2339 case 0x80000000:
2340 case 0x80000001:
2341 case 0x80000002:
2342 case 0x80000003:
2343 case 0x80000004:
2344 case 0x80000005:
2345 case 0x80000006:
2346 case 0x80000007:
2347 case 0x80000008:
2348 case 0x80000009:
2349 case 0x8000000A:
2350 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2351 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2352 i_setModified(IsModified_MachineData);
2353 mHWData.backup();
2354 /* Invalidate leaf. */
2355 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2356 break;
2357
2358 default:
2359 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2360 }
2361 return S_OK;
2362}
2363
2364HRESULT Machine::removeAllCPUIDLeaves()
2365{
2366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2367
2368 HRESULT rc = i_checkStateDependency(MutableStateDep);
2369 if (FAILED(rc)) return rc;
2370
2371 i_setModified(IsModified_MachineData);
2372 mHWData.backup();
2373
2374 /* Invalidate all standard leafs. */
2375 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2376 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2377
2378 /* Invalidate all extended leafs. */
2379 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2380 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2381
2382 return S_OK;
2383}
2384HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2385{
2386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2387
2388 switch(aProperty)
2389 {
2390 case HWVirtExPropertyType_Enabled:
2391 *aValue = mHWData->mHWVirtExEnabled;
2392 break;
2393
2394 case HWVirtExPropertyType_VPID:
2395 *aValue = mHWData->mHWVirtExVPIDEnabled;
2396 break;
2397
2398 case HWVirtExPropertyType_NestedPaging:
2399 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2400 break;
2401
2402 case HWVirtExPropertyType_UnrestrictedExecution:
2403 *aValue = mHWData->mHWVirtExUXEnabled;
2404 break;
2405
2406 case HWVirtExPropertyType_LargePages:
2407 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2408#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2409 *aValue = FALSE;
2410#endif
2411 break;
2412
2413 case HWVirtExPropertyType_Force:
2414 *aValue = mHWData->mHWVirtExForceEnabled;
2415 break;
2416
2417 default:
2418 return E_INVALIDARG;
2419 }
2420 return S_OK;
2421}
2422
2423HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2424{
2425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2426
2427 HRESULT rc = i_checkStateDependency(MutableStateDep);
2428 if (FAILED(rc)) return rc;
2429
2430 switch(aProperty)
2431 {
2432 case HWVirtExPropertyType_Enabled:
2433 i_setModified(IsModified_MachineData);
2434 mHWData.backup();
2435 mHWData->mHWVirtExEnabled = !!aValue;
2436 break;
2437
2438 case HWVirtExPropertyType_VPID:
2439 i_setModified(IsModified_MachineData);
2440 mHWData.backup();
2441 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2442 break;
2443
2444 case HWVirtExPropertyType_NestedPaging:
2445 i_setModified(IsModified_MachineData);
2446 mHWData.backup();
2447 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2448 break;
2449
2450 case HWVirtExPropertyType_UnrestrictedExecution:
2451 i_setModified(IsModified_MachineData);
2452 mHWData.backup();
2453 mHWData->mHWVirtExUXEnabled = !!aValue;
2454 break;
2455
2456 case HWVirtExPropertyType_LargePages:
2457 i_setModified(IsModified_MachineData);
2458 mHWData.backup();
2459 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2460 break;
2461
2462 case HWVirtExPropertyType_Force:
2463 i_setModified(IsModified_MachineData);
2464 mHWData.backup();
2465 mHWData->mHWVirtExForceEnabled = !!aValue;
2466 break;
2467
2468 default:
2469 return E_INVALIDARG;
2470 }
2471
2472 return S_OK;
2473}
2474
2475HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2476{
2477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2480
2481 return S_OK;
2482}
2483
2484HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2485{
2486 /* @todo (r=dmik):
2487 * 1. Allow to change the name of the snapshot folder containing snapshots
2488 * 2. Rename the folder on disk instead of just changing the property
2489 * value (to be smart and not to leave garbage). Note that it cannot be
2490 * done here because the change may be rolled back. Thus, the right
2491 * place is #saveSettings().
2492 */
2493
2494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 HRESULT rc = i_checkStateDependency(MutableStateDep);
2497 if (FAILED(rc)) return rc;
2498
2499 if (!mData->mCurrentSnapshot.isNull())
2500 return setError(E_FAIL,
2501 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2502
2503 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2504
2505 if (strSnapshotFolder.isEmpty())
2506 strSnapshotFolder = "Snapshots";
2507 int vrc = i_calculateFullPath(strSnapshotFolder,
2508 strSnapshotFolder);
2509 if (RT_FAILURE(vrc))
2510 return setError(E_FAIL,
2511 tr("Invalid snapshot folder '%s' (%Rrc)"),
2512 strSnapshotFolder.c_str(), vrc);
2513
2514 i_setModified(IsModified_MachineData);
2515 mUserData.backup();
2516
2517 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2518
2519 return S_OK;
2520}
2521
2522HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2523{
2524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2525
2526 aMediumAttachments.resize(mMediaData->mAttachments.size());
2527 size_t i = 0;
2528 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2529 it != mMediaData->mAttachments.end(); ++it, ++i)
2530 aMediumAttachments[i] = *it;
2531
2532 return S_OK;
2533}
2534
2535HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2536{
2537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2538
2539 Assert(!!mVRDEServer);
2540
2541 aVRDEServer = mVRDEServer;
2542
2543 return S_OK;
2544}
2545
2546HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2547{
2548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 aAudioAdapter = mAudioAdapter;
2551
2552 return S_OK;
2553}
2554
2555HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2556{
2557#ifdef VBOX_WITH_VUSB
2558 clearError();
2559 MultiResult rc(S_OK);
2560
2561# ifdef VBOX_WITH_USB
2562 rc = mParent->i_host()->i_checkUSBProxyService();
2563 if (FAILED(rc)) return rc;
2564# endif
2565
2566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568 USBControllerList data = *mUSBControllers.data();
2569 aUSBControllers.resize(data.size());
2570 size_t i = 0;
2571 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2572 aUSBControllers[i] = *it;
2573
2574 return S_OK;
2575#else
2576 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2577 * extended error info to indicate that USB is simply not available
2578 * (w/o treating it as a failure), for example, as in OSE */
2579 NOREF(aUSBControllers);
2580 ReturnComNotImplemented();
2581#endif /* VBOX_WITH_VUSB */
2582}
2583
2584HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2585{
2586#ifdef VBOX_WITH_VUSB
2587 clearError();
2588 MultiResult rc(S_OK);
2589
2590# ifdef VBOX_WITH_USB
2591 rc = mParent->i_host()->i_checkUSBProxyService();
2592 if (FAILED(rc)) return rc;
2593# endif
2594
2595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597 aUSBDeviceFilters = mUSBDeviceFilters;
2598 return rc;
2599#else
2600 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2601 * extended error info to indicate that USB is simply not available
2602 * (w/o treating it as a failure), for example, as in OSE */
2603 NOREF(aUSBDeviceFilters);
2604 ReturnComNotImplemented();
2605#endif /* VBOX_WITH_VUSB */
2606}
2607
2608HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2609{
2610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 aSettingsFilePath = mData->m_strConfigFileFull;
2613
2614 return S_OK;
2615}
2616
2617HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2618{
2619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2620
2621 HRESULT rc = i_checkStateDependency(MutableStateDep);
2622 if (FAILED(rc)) return rc;
2623
2624 if (!mData->pMachineConfigFile->fileExists())
2625 // this is a new machine, and no config file exists yet:
2626 *aSettingsModified = TRUE;
2627 else
2628 *aSettingsModified = (mData->flModifications != 0);
2629
2630 return S_OK;
2631}
2632
2633HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2634{
2635
2636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2637
2638 *aSessionState = mData->mSession.mState;
2639
2640 return S_OK;
2641}
2642
2643HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2644{
2645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2646
2647 aSessionType = mData->mSession.mType;
2648
2649 return S_OK;
2650}
2651
2652HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2653{
2654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 *aSessionPID = mData->mSession.mPID;
2657
2658 return S_OK;
2659}
2660
2661HRESULT Machine::getState(MachineState_T *aState)
2662{
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 *aState = mData->mMachineState;
2666
2667 return S_OK;
2668}
2669
2670HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2671{
2672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2673
2674 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2680{
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 aStateFilePath = mSSData->strStateFilePath;
2684
2685 return S_OK;
2686}
2687
2688HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2689{
2690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 i_getLogFolder(aLogFolder);
2693
2694 return S_OK;
2695}
2696
2697HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2698{
2699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2700
2701 aCurrentSnapshot = mData->mCurrentSnapshot;
2702
2703 return S_OK;
2704}
2705
2706HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2707{
2708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2709
2710 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2711 ? 0
2712 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2713
2714 return S_OK;
2715}
2716
2717HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2718{
2719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2720
2721 /* Note: for machines with no snapshots, we always return FALSE
2722 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2723 * reasons :) */
2724
2725 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2726 ? FALSE
2727 : mData->mCurrentStateModified;
2728
2729 return S_OK;
2730}
2731
2732HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2733{
2734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 aSharedFolders.resize(mHWData->mSharedFolders.size());
2737 size_t i = 0;
2738 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2739 it != mHWData->mSharedFolders.end(); ++i, ++it)
2740 aSharedFolders[i] = *it;
2741
2742 return S_OK;
2743}
2744
2745HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2746{
2747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2748
2749 *aClipboardMode = mHWData->mClipboardMode;
2750
2751 return S_OK;
2752}
2753
2754HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2755{
2756 HRESULT rc = S_OK;
2757
2758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 alock.release();
2761 rc = i_onClipboardModeChange(aClipboardMode);
2762 alock.acquire();
2763 if (FAILED(rc)) return rc;
2764
2765 i_setModified(IsModified_MachineData);
2766 mHWData.backup();
2767 mHWData->mClipboardMode = aClipboardMode;
2768
2769 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2770 if (Global::IsOnline(mData->mMachineState))
2771 i_saveSettings(NULL);
2772
2773 return S_OK;
2774}
2775
2776HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2777{
2778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2779
2780 *aDnDMode = mHWData->mDnDMode;
2781
2782 return S_OK;
2783}
2784
2785HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2786{
2787 HRESULT rc = S_OK;
2788
2789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 alock.release();
2792 rc = i_onDnDModeChange(aDnDMode);
2793
2794 alock.acquire();
2795 if (FAILED(rc)) return rc;
2796
2797 i_setModified(IsModified_MachineData);
2798 mHWData.backup();
2799 mHWData->mDnDMode = aDnDMode;
2800
2801 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2802 if (Global::IsOnline(mData->mMachineState))
2803 i_saveSettings(NULL);
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 try
2813 {
2814 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2815 }
2816 catch (...)
2817 {
2818 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2819 }
2820
2821 return S_OK;
2822}
2823
2824HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2825{
2826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 HRESULT rc = i_checkStateDependency(MutableStateDep);
2829 if (FAILED(rc)) return rc;
2830
2831 i_setModified(IsModified_MachineData);
2832 mHWData.backup();
2833 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2834 return rc;
2835}
2836
2837HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2838{
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840 StorageControllerList data = *mStorageControllers.data();
2841 size_t i = 0;
2842 aStorageControllers.resize(data.size());
2843 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2844 aStorageControllers[i] = *it;
2845 return S_OK;
2846}
2847
2848HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2849{
2850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 *aEnabled = mUserData->s.fTeleporterEnabled;
2853
2854 return S_OK;
2855}
2856
2857HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2858{
2859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 /* Only allow it to be set to true when PoweredOff or Aborted.
2862 (Clearing it is always permitted.) */
2863 if ( aTeleporterEnabled
2864 && mData->mRegistered
2865 && ( !i_isSessionMachine()
2866 || ( mData->mMachineState != MachineState_PoweredOff
2867 && mData->mMachineState != MachineState_Teleported
2868 && mData->mMachineState != MachineState_Aborted
2869 )
2870 )
2871 )
2872 return setError(VBOX_E_INVALID_VM_STATE,
2873 tr("The machine is not powered off (state is %s)"),
2874 Global::stringifyMachineState(mData->mMachineState));
2875
2876 i_setModified(IsModified_MachineData);
2877 mUserData.backup();
2878 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2879
2880 return S_OK;
2881}
2882
2883HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2884{
2885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2886
2887 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2888
2889 return S_OK;
2890}
2891
2892HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2893{
2894 if (aTeleporterPort >= _64K)
2895 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2896
2897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 HRESULT rc = i_checkStateDependency(MutableStateDep);
2900 if (FAILED(rc)) return rc;
2901
2902 i_setModified(IsModified_MachineData);
2903 mUserData.backup();
2904 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2905
2906 return S_OK;
2907}
2908
2909HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2910{
2911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2912
2913 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2914
2915 return S_OK;
2916}
2917
2918HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2919{
2920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 HRESULT rc = i_checkStateDependency(MutableStateDep);
2923 if (FAILED(rc)) return rc;
2924
2925 i_setModified(IsModified_MachineData);
2926 mUserData.backup();
2927 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2928
2929 return S_OK;
2930}
2931
2932HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2933{
2934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2935 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2936
2937 return S_OK;
2938}
2939
2940HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2941{
2942 /*
2943 * Hash the password first.
2944 */
2945 com::Utf8Str aT = aTeleporterPassword;
2946
2947 if (!aT.isEmpty())
2948 {
2949 if (VBoxIsPasswordHashed(&aT))
2950 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2951 VBoxHashPassword(&aT);
2952 }
2953
2954 /*
2955 * Do the update.
2956 */
2957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2958 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2959 if (SUCCEEDED(hrc))
2960 {
2961 i_setModified(IsModified_MachineData);
2962 mUserData.backup();
2963 mUserData->s.strTeleporterPassword = aT;
2964 }
2965
2966 return hrc;
2967}
2968
2969HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2970{
2971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2972
2973 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2974 return S_OK;
2975}
2976
2977HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2978{
2979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 /* @todo deal with running state change. */
2982 HRESULT rc = i_checkStateDependency(MutableStateDep);
2983 if (FAILED(rc)) return rc;
2984
2985 i_setModified(IsModified_MachineData);
2986 mUserData.backup();
2987 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2988 return S_OK;
2989}
2990
2991HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2992{
2993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2994
2995 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2996 return S_OK;
2997}
2998
2999HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3000{
3001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 /* @todo deal with running state change. */
3004 HRESULT rc = i_checkStateDependency(MutableStateDep);
3005 if (FAILED(rc)) return rc;
3006
3007 i_setModified(IsModified_MachineData);
3008 mUserData.backup();
3009 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3010 return S_OK;
3011}
3012
3013HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3014{
3015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3016
3017 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3018 return S_OK;
3019}
3020
3021HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3022{
3023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 /* @todo deal with running state change. */
3026 HRESULT rc = i_checkStateDependency(MutableStateDep);
3027 if (FAILED(rc)) return rc;
3028
3029 i_setModified(IsModified_MachineData);
3030 mUserData.backup();
3031 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3032 return S_OK;
3033}
3034
3035HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3036{
3037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3038
3039 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3040
3041 return S_OK;
3042}
3043
3044HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3045{
3046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3047
3048 /* @todo deal with running state change. */
3049 HRESULT rc = i_checkStateDependency(MutableStateDep);
3050 if (FAILED(rc)) return rc;
3051
3052 i_setModified(IsModified_MachineData);
3053 mUserData.backup();
3054 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3055
3056 return S_OK;
3057}
3058
3059HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3060{
3061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3064 return S_OK;
3065}
3066
3067HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3068{
3069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3070
3071 /* @todo deal with running state change. */
3072 HRESULT rc = i_checkStateDependency(MutableStateDep);
3073 if (FAILED(rc)) return rc;
3074
3075 i_setModified(IsModified_MachineData);
3076 mUserData.backup();
3077 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3078 return S_OK;
3079}
3080
3081HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3082{
3083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
3085 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3086
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3091{
3092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 /* Only allow it to be set to true when PoweredOff or Aborted.
3095 (Clearing it is always permitted.) */
3096 if ( aRTCUseUTC
3097 && mData->mRegistered
3098 && ( !i_isSessionMachine()
3099 || ( mData->mMachineState != MachineState_PoweredOff
3100 && mData->mMachineState != MachineState_Teleported
3101 && mData->mMachineState != MachineState_Aborted
3102 )
3103 )
3104 )
3105 return setError(VBOX_E_INVALID_VM_STATE,
3106 tr("The machine is not powered off (state is %s)"),
3107 Global::stringifyMachineState(mData->mMachineState));
3108
3109 i_setModified(IsModified_MachineData);
3110 mUserData.backup();
3111 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3112
3113 return S_OK;
3114}
3115
3116HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3117{
3118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3119
3120 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3121
3122 return S_OK;
3123}
3124
3125HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3126{
3127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3128
3129 HRESULT rc = i_checkStateDependency(MutableStateDep);
3130 if (FAILED(rc)) return rc;
3131
3132 i_setModified(IsModified_MachineData);
3133 mHWData.backup();
3134 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3135
3136 return S_OK;
3137}
3138
3139HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3140{
3141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3142
3143 *aIOCacheSize = mHWData->mIOCacheSize;
3144
3145 return S_OK;
3146}
3147
3148HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3149{
3150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3151
3152 HRESULT rc = i_checkStateDependency(MutableStateDep);
3153 if (FAILED(rc)) return rc;
3154
3155 i_setModified(IsModified_MachineData);
3156 mHWData.backup();
3157 mHWData->mIOCacheSize = aIOCacheSize;
3158
3159 return S_OK;
3160}
3161
3162
3163/**
3164 * @note Locks objects!
3165 */
3166HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3167 LockType_T aLockType)
3168
3169{
3170 /* check the session state */
3171 SessionState_T state;
3172 HRESULT rc = aSession->COMGETTER(State)(&state);
3173 if (FAILED(rc)) return rc;
3174
3175 if (state != SessionState_Unlocked)
3176 return setError(VBOX_E_INVALID_OBJECT_STATE,
3177 tr("The given session is busy"));
3178
3179 // get the client's IInternalSessionControl interface
3180 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3181 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3182 E_INVALIDARG);
3183
3184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3185
3186 if (!mData->mRegistered)
3187 return setError(E_UNEXPECTED,
3188 tr("The machine '%s' is not registered"),
3189 mUserData->s.strName.c_str());
3190
3191 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3192
3193 SessionState_T oldState = mData->mSession.mState;
3194 /* Hack: in case the session is closing and there is a progress object
3195 * which allows waiting for the session to be closed, take the opportunity
3196 * and do a limited wait (max. 1 second). This helps a lot when the system
3197 * is busy and thus session closing can take a little while. */
3198 if ( mData->mSession.mState == SessionState_Unlocking
3199 && mData->mSession.mProgress)
3200 {
3201 alock.release();
3202 mData->mSession.mProgress->WaitForCompletion(1000);
3203 alock.acquire();
3204 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3205 }
3206
3207 // try again now
3208 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3209 // (i.e. session machine exists)
3210 && (aLockType == LockType_Shared) // caller wants a shared link to the
3211 // existing session that holds the write lock:
3212 )
3213 {
3214 // OK, share the session... we are now dealing with three processes:
3215 // 1) VBoxSVC (where this code runs);
3216 // 2) process C: the caller's client process (who wants a shared session);
3217 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3218
3219 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3220 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3221 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3222 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3223 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3224
3225 /*
3226 * Release the lock before calling the client process. It's safe here
3227 * since the only thing to do after we get the lock again is to add
3228 * the remote control to the list (which doesn't directly influence
3229 * anything).
3230 */
3231 alock.release();
3232
3233 // get the console of the session holding the write lock (this is a remote call)
3234 ComPtr<IConsole> pConsoleW;
3235 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3236 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3237 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3238 if (FAILED(rc))
3239 // the failure may occur w/o any error info (from RPC), so provide one
3240 return setError(VBOX_E_VM_ERROR,
3241 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3242
3243 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3244
3245 // share the session machine and W's console with the caller's session
3246 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3247 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3248 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3249
3250 if (FAILED(rc))
3251 // the failure may occur w/o any error info (from RPC), so provide one
3252 return setError(VBOX_E_VM_ERROR,
3253 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3254 alock.acquire();
3255
3256 // need to revalidate the state after acquiring the lock again
3257 if (mData->mSession.mState != SessionState_Locked)
3258 {
3259 pSessionControl->Uninitialize();
3260 return setError(VBOX_E_INVALID_SESSION_STATE,
3261 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3262 mUserData->s.strName.c_str());
3263 }
3264
3265 // add the caller's session to the list
3266 mData->mSession.mRemoteControls.push_back(pSessionControl);
3267 }
3268 else if ( mData->mSession.mState == SessionState_Locked
3269 || mData->mSession.mState == SessionState_Unlocking
3270 )
3271 {
3272 // sharing not permitted, or machine still unlocking:
3273 return setError(VBOX_E_INVALID_OBJECT_STATE,
3274 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3275 mUserData->s.strName.c_str());
3276 }
3277 else
3278 {
3279 // machine is not locked: then write-lock the machine (create the session machine)
3280
3281 // must not be busy
3282 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3283
3284 // get the caller's session PID
3285 RTPROCESS pid = NIL_RTPROCESS;
3286 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3287 pSessionControl->GetPID((ULONG*)&pid);
3288 Assert(pid != NIL_RTPROCESS);
3289
3290 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3291
3292 if (fLaunchingVMProcess)
3293 {
3294 if (mData->mSession.mPID == NIL_RTPROCESS)
3295 {
3296 // two or more clients racing for a lock, the one which set the
3297 // session state to Spawning will win, the others will get an
3298 // error as we can't decide here if waiting a little would help
3299 // (only for shared locks this would avoid an error)
3300 return setError(VBOX_E_INVALID_OBJECT_STATE,
3301 tr("The machine '%s' already has a lock request pending"),
3302 mUserData->s.strName.c_str());
3303 }
3304
3305 // this machine is awaiting for a spawning session to be opened:
3306 // then the calling process must be the one that got started by
3307 // LaunchVMProcess()
3308
3309 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3310 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3311
3312#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3313 /* Hardened windows builds spawns three processes when a VM is
3314 launched, the 3rd one is the one that will end up here. */
3315 RTPROCESS ppid;
3316 int rc = RTProcQueryParent(pid, &ppid);
3317 if (RT_SUCCESS(rc))
3318 rc = RTProcQueryParent(ppid, &ppid);
3319 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3320 || rc == VERR_ACCESS_DENIED)
3321 {
3322 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3323 mData->mSession.mPID = pid;
3324 }
3325#endif
3326
3327 if (mData->mSession.mPID != pid)
3328 return setError(E_ACCESSDENIED,
3329 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3330 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3331 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3332 }
3333
3334 // create the mutable SessionMachine from the current machine
3335 ComObjPtr<SessionMachine> sessionMachine;
3336 sessionMachine.createObject();
3337 rc = sessionMachine->init(this);
3338 AssertComRC(rc);
3339
3340 /* NOTE: doing return from this function after this point but
3341 * before the end is forbidden since it may call SessionMachine::uninit()
3342 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3343 * lock while still holding the Machine lock in alock so that a deadlock
3344 * is possible due to the wrong lock order. */
3345
3346 if (SUCCEEDED(rc))
3347 {
3348 /*
3349 * Set the session state to Spawning to protect against subsequent
3350 * attempts to open a session and to unregister the machine after
3351 * we release the lock.
3352 */
3353 SessionState_T origState = mData->mSession.mState;
3354 mData->mSession.mState = SessionState_Spawning;
3355
3356#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3357 /* Get the client token ID to be passed to the client process */
3358 Utf8Str strTokenId;
3359 sessionMachine->i_getTokenId(strTokenId);
3360 Assert(!strTokenId.isEmpty());
3361#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3362 /* Get the client token to be passed to the client process */
3363 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3364 /* The token is now "owned" by pToken, fix refcount */
3365 if (!pToken.isNull())
3366 pToken->Release();
3367#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3368
3369 /*
3370 * Release the lock before calling the client process -- it will call
3371 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3372 * because the state is Spawning, so that LaunchVMProcess() and
3373 * LockMachine() calls will fail. This method, called before we
3374 * acquire the lock again, will fail because of the wrong PID.
3375 *
3376 * Note that mData->mSession.mRemoteControls accessed outside
3377 * the lock may not be modified when state is Spawning, so it's safe.
3378 */
3379 alock.release();
3380
3381 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3382#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3383 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3384#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3385 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3386 /* Now the token is owned by the client process. */
3387 pToken.setNull();
3388#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3389 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3390
3391 /* The failure may occur w/o any error info (from RPC), so provide one */
3392 if (FAILED(rc))
3393 setError(VBOX_E_VM_ERROR,
3394 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3395
3396 if ( SUCCEEDED(rc)
3397 && fLaunchingVMProcess
3398 )
3399 {
3400 /* complete the remote session initialization */
3401
3402 /* get the console from the direct session */
3403 ComPtr<IConsole> console;
3404 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3405 ComAssertComRC(rc);
3406
3407 if (SUCCEEDED(rc) && !console)
3408 {
3409 ComAssert(!!console);
3410 rc = E_FAIL;
3411 }
3412
3413 /* assign machine & console to the remote session */
3414 if (SUCCEEDED(rc))
3415 {
3416 /*
3417 * after LaunchVMProcess(), the first and the only
3418 * entry in remoteControls is that remote session
3419 */
3420 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3421 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3422 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3423
3424 /* The failure may occur w/o any error info (from RPC), so provide one */
3425 if (FAILED(rc))
3426 setError(VBOX_E_VM_ERROR,
3427 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3428 }
3429
3430 if (FAILED(rc))
3431 pSessionControl->Uninitialize();
3432 }
3433
3434 /* acquire the lock again */
3435 alock.acquire();
3436
3437 /* Restore the session state */
3438 mData->mSession.mState = origState;
3439 }
3440
3441 // finalize spawning anyway (this is why we don't return on errors above)
3442 if (fLaunchingVMProcess)
3443 {
3444 /* Note that the progress object is finalized later */
3445 /** @todo Consider checking mData->mSession.mProgress for cancellation
3446 * around here. */
3447
3448 /* We don't reset mSession.mPID here because it is necessary for
3449 * SessionMachine::uninit() to reap the child process later. */
3450
3451 if (FAILED(rc))
3452 {
3453 /* Close the remote session, remove the remote control from the list
3454 * and reset session state to Closed (@note keep the code in sync
3455 * with the relevant part in checkForSpawnFailure()). */
3456
3457 Assert(mData->mSession.mRemoteControls.size() == 1);
3458 if (mData->mSession.mRemoteControls.size() == 1)
3459 {
3460 ErrorInfoKeeper eik;
3461 mData->mSession.mRemoteControls.front()->Uninitialize();
3462 }
3463
3464 mData->mSession.mRemoteControls.clear();
3465 mData->mSession.mState = SessionState_Unlocked;
3466 }
3467 }
3468 else
3469 {
3470 /* memorize PID of the directly opened session */
3471 if (SUCCEEDED(rc))
3472 mData->mSession.mPID = pid;
3473 }
3474
3475 if (SUCCEEDED(rc))
3476 {
3477 /* memorize the direct session control and cache IUnknown for it */
3478 mData->mSession.mDirectControl = pSessionControl;
3479 mData->mSession.mState = SessionState_Locked;
3480 /* associate the SessionMachine with this Machine */
3481 mData->mSession.mMachine = sessionMachine;
3482
3483 /* request an IUnknown pointer early from the remote party for later
3484 * identity checks (it will be internally cached within mDirectControl
3485 * at least on XPCOM) */
3486 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3487 NOREF(unk);
3488 }
3489
3490 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3491 * would break the lock order */
3492 alock.release();
3493
3494 /* uninitialize the created session machine on failure */
3495 if (FAILED(rc))
3496 sessionMachine->uninit();
3497
3498 }
3499
3500 if (SUCCEEDED(rc))
3501 {
3502 /*
3503 * tell the client watcher thread to update the set of
3504 * machines that have open sessions
3505 */
3506 mParent->i_updateClientWatcher();
3507
3508 if (oldState != SessionState_Locked)
3509 /* fire an event */
3510 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3511 }
3512
3513 return rc;
3514}
3515
3516/**
3517 * @note Locks objects!
3518 */
3519HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3520 const com::Utf8Str &aType,
3521 const com::Utf8Str &aEnvironment,
3522 ComPtr<IProgress> &aProgress)
3523{
3524 Utf8Str strFrontend(aType);
3525 /* "emergencystop" doesn't need the session, so skip the checks/interface
3526 * retrieval. This code doesn't quite fit in here, but introducing a
3527 * special API method would be even more effort, and would require explicit
3528 * support by every API client. It's better to hide the feature a bit. */
3529 if (strFrontend != "emergencystop")
3530 CheckComArgNotNull(aSession);
3531
3532 HRESULT rc = S_OK;
3533 if (strFrontend.isEmpty())
3534 {
3535 Bstr bstrFrontend;
3536 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3537 if (FAILED(rc))
3538 return rc;
3539 strFrontend = bstrFrontend;
3540 if (strFrontend.isEmpty())
3541 {
3542 ComPtr<ISystemProperties> systemProperties;
3543 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3544 if (FAILED(rc))
3545 return rc;
3546 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3547 if (FAILED(rc))
3548 return rc;
3549 strFrontend = bstrFrontend;
3550 }
3551 /* paranoia - emergencystop is not a valid default */
3552 if (strFrontend == "emergencystop")
3553 strFrontend = Utf8Str::Empty;
3554 }
3555 /* default frontend: Qt GUI */
3556 if (strFrontend.isEmpty())
3557 strFrontend = "GUI/Qt";
3558
3559 if (strFrontend != "emergencystop")
3560 {
3561 /* check the session state */
3562 SessionState_T state;
3563 rc = aSession->COMGETTER(State)(&state);
3564 if (FAILED(rc))
3565 return rc;
3566
3567 if (state != SessionState_Unlocked)
3568 return setError(VBOX_E_INVALID_OBJECT_STATE,
3569 tr("The given session is busy"));
3570
3571 /* get the IInternalSessionControl interface */
3572 ComPtr<IInternalSessionControl> control(aSession);
3573 ComAssertMsgRet(!control.isNull(),
3574 ("No IInternalSessionControl interface"),
3575 E_INVALIDARG);
3576
3577 /* get the teleporter enable state for the progress object init. */
3578 BOOL fTeleporterEnabled;
3579 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3580 if (FAILED(rc))
3581 return rc;
3582
3583 /* create a progress object */
3584 ComObjPtr<ProgressProxy> progress;
3585 progress.createObject();
3586 rc = progress->init(mParent,
3587 static_cast<IMachine*>(this),
3588 Bstr(tr("Starting VM")).raw(),
3589 TRUE /* aCancelable */,
3590 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3591 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3592 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3593 2 /* uFirstOperationWeight */,
3594 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3595
3596 if (SUCCEEDED(rc))
3597 {
3598 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3599 if (SUCCEEDED(rc))
3600 {
3601 aProgress = progress;
3602
3603 /* signal the client watcher thread */
3604 mParent->i_updateClientWatcher();
3605
3606 /* fire an event */
3607 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3608 }
3609 }
3610 }
3611 else
3612 {
3613 /* no progress object - either instant success or failure */
3614 aProgress = NULL;
3615
3616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3617
3618 if (mData->mSession.mState != SessionState_Locked)
3619 return setError(VBOX_E_INVALID_OBJECT_STATE,
3620 tr("The machine '%s' is not locked by a session"),
3621 mUserData->s.strName.c_str());
3622
3623 /* must have a VM process associated - do not kill normal API clients
3624 * with an open session */
3625 if (!Global::IsOnline(mData->mMachineState))
3626 return setError(VBOX_E_INVALID_OBJECT_STATE,
3627 tr("The machine '%s' does not have a VM process"),
3628 mUserData->s.strName.c_str());
3629
3630 /* forcibly terminate the VM process */
3631 if (mData->mSession.mPID != NIL_RTPROCESS)
3632 RTProcTerminate(mData->mSession.mPID);
3633
3634 /* signal the client watcher thread, as most likely the client has
3635 * been terminated */
3636 mParent->i_updateClientWatcher();
3637 }
3638
3639 return rc;
3640}
3641
3642HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3643{
3644 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3645 return setError(E_INVALIDARG,
3646 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3647 aPosition, SchemaDefs::MaxBootPosition);
3648
3649 if (aDevice == DeviceType_USB)
3650 return setError(E_NOTIMPL,
3651 tr("Booting from USB device is currently not supported"));
3652
3653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3654
3655 HRESULT rc = i_checkStateDependency(MutableStateDep);
3656 if (FAILED(rc)) return rc;
3657
3658 i_setModified(IsModified_MachineData);
3659 mHWData.backup();
3660 mHWData->mBootOrder[aPosition - 1] = aDevice;
3661
3662 return S_OK;
3663}
3664
3665HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3666{
3667 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3668 return setError(E_INVALIDARG,
3669 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3670 aPosition, SchemaDefs::MaxBootPosition);
3671
3672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3673
3674 *aDevice = mHWData->mBootOrder[aPosition - 1];
3675
3676 return S_OK;
3677}
3678
3679HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3680 LONG aControllerPort,
3681 LONG aDevice,
3682 DeviceType_T aType,
3683 const ComPtr<IMedium> &aMedium)
3684{
3685 IMedium *aM = aMedium;
3686 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3687 aName.c_str(), aControllerPort, aDevice, aType, aM));
3688
3689 // request the host lock first, since might be calling Host methods for getting host drives;
3690 // next, protect the media tree all the while we're in here, as well as our member variables
3691 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3692 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3693
3694 HRESULT rc = i_checkStateDependency(MutableStateDep);
3695 if (FAILED(rc)) return rc;
3696
3697 /// @todo NEWMEDIA implicit machine registration
3698 if (!mData->mRegistered)
3699 return setError(VBOX_E_INVALID_OBJECT_STATE,
3700 tr("Cannot attach storage devices to an unregistered machine"));
3701
3702 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3703
3704 /* Check for an existing controller. */
3705 ComObjPtr<StorageController> ctl;
3706 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3707 if (FAILED(rc)) return rc;
3708
3709 StorageControllerType_T ctrlType;
3710 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3711 if (FAILED(rc))
3712 return setError(E_FAIL,
3713 tr("Could not get type of controller '%s'"),
3714 aName.c_str());
3715
3716 bool fSilent = false;
3717 Utf8Str strReconfig;
3718
3719 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3720 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3721 if ( mData->mMachineState == MachineState_Paused
3722 && strReconfig == "1")
3723 fSilent = true;
3724
3725 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3726 bool fHotplug = false;
3727 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3728 fHotplug = true;
3729
3730 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3731 return setError(VBOX_E_INVALID_VM_STATE,
3732 tr("Controller '%s' does not support hotplugging"),
3733 aName.c_str());
3734
3735 // check that the port and device are not out of range
3736 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3737 if (FAILED(rc)) return rc;
3738
3739 /* check if the device slot is already busy */
3740 MediumAttachment *pAttachTemp;
3741 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3742 Bstr(aName).raw(),
3743 aControllerPort,
3744 aDevice)))
3745 {
3746 Medium *pMedium = pAttachTemp->i_getMedium();
3747 if (pMedium)
3748 {
3749 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3750 return setError(VBOX_E_OBJECT_IN_USE,
3751 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3752 pMedium->i_getLocationFull().c_str(),
3753 aControllerPort,
3754 aDevice,
3755 aName.c_str());
3756 }
3757 else
3758 return setError(VBOX_E_OBJECT_IN_USE,
3759 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3760 aControllerPort, aDevice, aName.c_str());
3761 }
3762
3763 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3764 if (aMedium && medium.isNull())
3765 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3766
3767 AutoCaller mediumCaller(medium);
3768 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3769
3770 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3771
3772 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3773 && !medium.isNull()
3774 )
3775 return setError(VBOX_E_OBJECT_IN_USE,
3776 tr("Medium '%s' is already attached to this virtual machine"),
3777 medium->i_getLocationFull().c_str());
3778
3779 if (!medium.isNull())
3780 {
3781 MediumType_T mtype = medium->i_getType();
3782 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3783 // For DVDs it's not written to the config file, so needs no global config
3784 // version bump. For floppies it's a new attribute "type", which is ignored
3785 // by older VirtualBox version, so needs no global config version bump either.
3786 // For hard disks this type is not accepted.
3787 if (mtype == MediumType_MultiAttach)
3788 {
3789 // This type is new with VirtualBox 4.0 and therefore requires settings
3790 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3791 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3792 // two reasons: The medium type is a property of the media registry tree, which
3793 // can reside in the global config file (for pre-4.0 media); we would therefore
3794 // possibly need to bump the global config version. We don't want to do that though
3795 // because that might make downgrading to pre-4.0 impossible.
3796 // As a result, we can only use these two new types if the medium is NOT in the
3797 // global registry:
3798 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3799 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3800 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3801 )
3802 return setError(VBOX_E_INVALID_OBJECT_STATE,
3803 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3804 "to machines that were created with VirtualBox 4.0 or later"),
3805 medium->i_getLocationFull().c_str());
3806 }
3807 }
3808
3809 bool fIndirect = false;
3810 if (!medium.isNull())
3811 fIndirect = medium->i_isReadOnly();
3812 bool associate = true;
3813
3814 do
3815 {
3816 if ( aType == DeviceType_HardDisk
3817 && mMediaData.isBackedUp())
3818 {
3819 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3820
3821 /* check if the medium was attached to the VM before we started
3822 * changing attachments in which case the attachment just needs to
3823 * be restored */
3824 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3825 {
3826 AssertReturn(!fIndirect, E_FAIL);
3827
3828 /* see if it's the same bus/channel/device */
3829 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3830 {
3831 /* the simplest case: restore the whole attachment
3832 * and return, nothing else to do */
3833 mMediaData->mAttachments.push_back(pAttachTemp);
3834
3835 /* Reattach the medium to the VM. */
3836 if (fHotplug || fSilent)
3837 {
3838 mediumLock.release();
3839 treeLock.release();
3840 alock.release();
3841
3842 MediumLockList *pMediumLockList(new MediumLockList());
3843
3844 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3845 true /* fMediumLockWrite */,
3846 false /* fMediumLockWriteAll */,
3847 NULL,
3848 *pMediumLockList);
3849 alock.acquire();
3850 if (FAILED(rc))
3851 delete pMediumLockList;
3852 else
3853 {
3854 mData->mSession.mLockedMedia.Unlock();
3855 alock.release();
3856 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3857 mData->mSession.mLockedMedia.Lock();
3858 alock.acquire();
3859 }
3860 alock.release();
3861
3862 if (SUCCEEDED(rc))
3863 {
3864 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3865 /* Remove lock list in case of error. */
3866 if (FAILED(rc))
3867 {
3868 mData->mSession.mLockedMedia.Unlock();
3869 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3870 mData->mSession.mLockedMedia.Lock();
3871 }
3872 }
3873 }
3874
3875 return S_OK;
3876 }
3877
3878 /* bus/channel/device differ; we need a new attachment object,
3879 * but don't try to associate it again */
3880 associate = false;
3881 break;
3882 }
3883 }
3884
3885 /* go further only if the attachment is to be indirect */
3886 if (!fIndirect)
3887 break;
3888
3889 /* perform the so called smart attachment logic for indirect
3890 * attachments. Note that smart attachment is only applicable to base
3891 * hard disks. */
3892
3893 if (medium->i_getParent().isNull())
3894 {
3895 /* first, investigate the backup copy of the current hard disk
3896 * attachments to make it possible to re-attach existing diffs to
3897 * another device slot w/o losing their contents */
3898 if (mMediaData.isBackedUp())
3899 {
3900 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3901
3902 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3903 uint32_t foundLevel = 0;
3904
3905 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3906 {
3907 uint32_t level = 0;
3908 MediumAttachment *pAttach = *it;
3909 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3910 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3911 if (pMedium.isNull())
3912 continue;
3913
3914 if (pMedium->i_getBase(&level) == medium)
3915 {
3916 /* skip the hard disk if its currently attached (we
3917 * cannot attach the same hard disk twice) */
3918 if (i_findAttachment(mMediaData->mAttachments,
3919 pMedium))
3920 continue;
3921
3922 /* matched device, channel and bus (i.e. attached to the
3923 * same place) will win and immediately stop the search;
3924 * otherwise the attachment that has the youngest
3925 * descendant of medium will be used
3926 */
3927 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3928 {
3929 /* the simplest case: restore the whole attachment
3930 * and return, nothing else to do */
3931 mMediaData->mAttachments.push_back(*it);
3932
3933 /* Reattach the medium to the VM. */
3934 if (fHotplug || fSilent)
3935 {
3936 mediumLock.release();
3937 treeLock.release();
3938 alock.release();
3939
3940 MediumLockList *pMediumLockList(new MediumLockList());
3941
3942 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3943 true /* fMediumLockWrite */,
3944 false /* fMediumLockWriteAll */,
3945 NULL,
3946 *pMediumLockList);
3947 alock.acquire();
3948 if (FAILED(rc))
3949 delete pMediumLockList;
3950 else
3951 {
3952 mData->mSession.mLockedMedia.Unlock();
3953 alock.release();
3954 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3955 mData->mSession.mLockedMedia.Lock();
3956 alock.acquire();
3957 }
3958 alock.release();
3959
3960 if (SUCCEEDED(rc))
3961 {
3962 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3963 /* Remove lock list in case of error. */
3964 if (FAILED(rc))
3965 {
3966 mData->mSession.mLockedMedia.Unlock();
3967 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3968 mData->mSession.mLockedMedia.Lock();
3969 }
3970 }
3971 }
3972
3973 return S_OK;
3974 }
3975 else if ( foundIt == oldAtts.end()
3976 || level > foundLevel /* prefer younger */
3977 )
3978 {
3979 foundIt = it;
3980 foundLevel = level;
3981 }
3982 }
3983 }
3984
3985 if (foundIt != oldAtts.end())
3986 {
3987 /* use the previously attached hard disk */
3988 medium = (*foundIt)->i_getMedium();
3989 mediumCaller.attach(medium);
3990 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3991 mediumLock.attach(medium);
3992 /* not implicit, doesn't require association with this VM */
3993 fIndirect = false;
3994 associate = false;
3995 /* go right to the MediumAttachment creation */
3996 break;
3997 }
3998 }
3999
4000 /* must give up the medium lock and medium tree lock as below we
4001 * go over snapshots, which needs a lock with higher lock order. */
4002 mediumLock.release();
4003 treeLock.release();
4004
4005 /* then, search through snapshots for the best diff in the given
4006 * hard disk's chain to base the new diff on */
4007
4008 ComObjPtr<Medium> base;
4009 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4010 while (snap)
4011 {
4012 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4013
4014 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4015
4016 MediumAttachment *pAttachFound = NULL;
4017 uint32_t foundLevel = 0;
4018
4019 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4020 {
4021 MediumAttachment *pAttach = *it;
4022 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4023 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4024 if (pMedium.isNull())
4025 continue;
4026
4027 uint32_t level = 0;
4028 if (pMedium->i_getBase(&level) == medium)
4029 {
4030 /* matched device, channel and bus (i.e. attached to the
4031 * same place) will win and immediately stop the search;
4032 * otherwise the attachment that has the youngest
4033 * descendant of medium will be used
4034 */
4035 if ( pAttach->i_getDevice() == aDevice
4036 && pAttach->i_getPort() == aControllerPort
4037 && pAttach->i_getControllerName() == aName
4038 )
4039 {
4040 pAttachFound = pAttach;
4041 break;
4042 }
4043 else if ( !pAttachFound
4044 || level > foundLevel /* prefer younger */
4045 )
4046 {
4047 pAttachFound = pAttach;
4048 foundLevel = level;
4049 }
4050 }
4051 }
4052
4053 if (pAttachFound)
4054 {
4055 base = pAttachFound->i_getMedium();
4056 break;
4057 }
4058
4059 snap = snap->i_getParent();
4060 }
4061
4062 /* re-lock medium tree and the medium, as we need it below */
4063 treeLock.acquire();
4064 mediumLock.acquire();
4065
4066 /* found a suitable diff, use it as a base */
4067 if (!base.isNull())
4068 {
4069 medium = base;
4070 mediumCaller.attach(medium);
4071 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4072 mediumLock.attach(medium);
4073 }
4074 }
4075
4076 Utf8Str strFullSnapshotFolder;
4077 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4078
4079 ComObjPtr<Medium> diff;
4080 diff.createObject();
4081 // store this diff in the same registry as the parent
4082 Guid uuidRegistryParent;
4083 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4084 {
4085 // parent image has no registry: this can happen if we're attaching a new immutable
4086 // image that has not yet been attached (medium then points to the base and we're
4087 // creating the diff image for the immutable, and the parent is not yet registered);
4088 // put the parent in the machine registry then
4089 mediumLock.release();
4090 treeLock.release();
4091 alock.release();
4092 i_addMediumToRegistry(medium);
4093 alock.acquire();
4094 treeLock.acquire();
4095 mediumLock.acquire();
4096 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4097 }
4098 rc = diff->init(mParent,
4099 medium->i_getPreferredDiffFormat(),
4100 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4101 uuidRegistryParent,
4102 DeviceType_HardDisk);
4103 if (FAILED(rc)) return rc;
4104
4105 /* Apply the normal locking logic to the entire chain. */
4106 MediumLockList *pMediumLockList(new MediumLockList());
4107 mediumLock.release();
4108 treeLock.release();
4109 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4110 true /* fMediumLockWrite */,
4111 false /* fMediumLockWriteAll */,
4112 medium,
4113 *pMediumLockList);
4114 treeLock.acquire();
4115 mediumLock.acquire();
4116 if (SUCCEEDED(rc))
4117 {
4118 mediumLock.release();
4119 treeLock.release();
4120 rc = pMediumLockList->Lock();
4121 treeLock.acquire();
4122 mediumLock.acquire();
4123 if (FAILED(rc))
4124 setError(rc,
4125 tr("Could not lock medium when creating diff '%s'"),
4126 diff->i_getLocationFull().c_str());
4127 else
4128 {
4129 /* will release the lock before the potentially lengthy
4130 * operation, so protect with the special state */
4131 MachineState_T oldState = mData->mMachineState;
4132 i_setMachineState(MachineState_SettingUp);
4133
4134 mediumLock.release();
4135 treeLock.release();
4136 alock.release();
4137
4138 rc = medium->i_createDiffStorage(diff,
4139 MediumVariant_Standard,
4140 pMediumLockList,
4141 NULL /* aProgress */,
4142 true /* aWait */);
4143
4144 alock.acquire();
4145 treeLock.acquire();
4146 mediumLock.acquire();
4147
4148 i_setMachineState(oldState);
4149 }
4150 }
4151
4152 /* Unlock the media and free the associated memory. */
4153 delete pMediumLockList;
4154
4155 if (FAILED(rc)) return rc;
4156
4157 /* use the created diff for the actual attachment */
4158 medium = diff;
4159 mediumCaller.attach(medium);
4160 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4161 mediumLock.attach(medium);
4162 }
4163 while (0);
4164
4165 ComObjPtr<MediumAttachment> attachment;
4166 attachment.createObject();
4167 rc = attachment->init(this,
4168 medium,
4169 aName,
4170 aControllerPort,
4171 aDevice,
4172 aType,
4173 fIndirect,
4174 false /* fPassthrough */,
4175 false /* fTempEject */,
4176 false /* fNonRotational */,
4177 false /* fDiscard */,
4178 fHotplug /* fHotPluggable */,
4179 Utf8Str::Empty);
4180 if (FAILED(rc)) return rc;
4181
4182 if (associate && !medium.isNull())
4183 {
4184 // as the last step, associate the medium to the VM
4185 rc = medium->i_addBackReference(mData->mUuid);
4186 // here we can fail because of Deleting, or being in process of creating a Diff
4187 if (FAILED(rc)) return rc;
4188
4189 mediumLock.release();
4190 treeLock.release();
4191 alock.release();
4192 i_addMediumToRegistry(medium);
4193 alock.acquire();
4194 treeLock.acquire();
4195 mediumLock.acquire();
4196 }
4197
4198 /* success: finally remember the attachment */
4199 i_setModified(IsModified_Storage);
4200 mMediaData.backup();
4201 mMediaData->mAttachments.push_back(attachment);
4202
4203 mediumLock.release();
4204 treeLock.release();
4205 alock.release();
4206
4207 if (fHotplug || fSilent)
4208 {
4209 if (!medium.isNull())
4210 {
4211 MediumLockList *pMediumLockList(new MediumLockList());
4212
4213 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4214 true /* fMediumLockWrite */,
4215 false /* fMediumLockWriteAll */,
4216 NULL,
4217 *pMediumLockList);
4218 alock.acquire();
4219 if (FAILED(rc))
4220 delete pMediumLockList;
4221 else
4222 {
4223 mData->mSession.mLockedMedia.Unlock();
4224 alock.release();
4225 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4226 mData->mSession.mLockedMedia.Lock();
4227 alock.acquire();
4228 }
4229 alock.release();
4230 }
4231
4232 if (SUCCEEDED(rc))
4233 {
4234 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4235 /* Remove lock list in case of error. */
4236 if (FAILED(rc))
4237 {
4238 mData->mSession.mLockedMedia.Unlock();
4239 mData->mSession.mLockedMedia.Remove(attachment);
4240 mData->mSession.mLockedMedia.Lock();
4241 }
4242 }
4243 }
4244
4245 mParent->i_saveModifiedRegistries();
4246
4247 return rc;
4248}
4249
4250HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4251 LONG aDevice)
4252{
4253 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4254 aName.c_str(), aControllerPort, aDevice));
4255
4256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4257
4258 HRESULT rc = i_checkStateDependency(MutableStateDep);
4259 if (FAILED(rc)) return rc;
4260
4261 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4262
4263 /* Check for an existing controller. */
4264 ComObjPtr<StorageController> ctl;
4265 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4266 if (FAILED(rc)) return rc;
4267
4268 StorageControllerType_T ctrlType;
4269 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4270 if (FAILED(rc))
4271 return setError(E_FAIL,
4272 tr("Could not get type of controller '%s'"),
4273 aName.c_str());
4274
4275 bool fSilent = false;
4276 Utf8Str strReconfig;
4277
4278 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4279 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4280 if ( mData->mMachineState == MachineState_Paused
4281 && strReconfig == "1")
4282 fSilent = true;
4283
4284 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4285 bool fHotplug = false;
4286 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4287 fHotplug = true;
4288
4289 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4290 return setError(VBOX_E_INVALID_VM_STATE,
4291 tr("Controller '%s' does not support hotplugging"),
4292 aName.c_str());
4293
4294 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4295 Bstr(aName).raw(),
4296 aControllerPort,
4297 aDevice);
4298 if (!pAttach)
4299 return setError(VBOX_E_OBJECT_NOT_FOUND,
4300 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4301 aDevice, aControllerPort, aName.c_str());
4302
4303 if (fHotplug && !pAttach->i_getHotPluggable())
4304 return setError(VBOX_E_NOT_SUPPORTED,
4305 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4306 aDevice, aControllerPort, aName.c_str());
4307
4308 /*
4309 * The VM has to detach the device before we delete any implicit diffs.
4310 * If this fails we can roll back without loosing data.
4311 */
4312 if (fHotplug || fSilent)
4313 {
4314 alock.release();
4315 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4316 alock.acquire();
4317 }
4318 if (FAILED(rc)) return rc;
4319
4320 /* If we are here everything went well and we can delete the implicit now. */
4321 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4322
4323 alock.release();
4324
4325 mParent->i_saveModifiedRegistries();
4326
4327 return rc;
4328}
4329
4330HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4331 LONG aDevice, BOOL aPassthrough)
4332{
4333 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4334 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4335
4336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4337
4338 HRESULT rc = i_checkStateDependency(MutableStateDep);
4339 if (FAILED(rc)) return rc;
4340
4341 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4342
4343 if (Global::IsOnlineOrTransient(mData->mMachineState))
4344 return setError(VBOX_E_INVALID_VM_STATE,
4345 tr("Invalid machine state: %s"),
4346 Global::stringifyMachineState(mData->mMachineState));
4347
4348 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4349 Bstr(aName).raw(),
4350 aControllerPort,
4351 aDevice);
4352 if (!pAttach)
4353 return setError(VBOX_E_OBJECT_NOT_FOUND,
4354 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4355 aDevice, aControllerPort, aName.c_str());
4356
4357
4358 i_setModified(IsModified_Storage);
4359 mMediaData.backup();
4360
4361 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4362
4363 if (pAttach->i_getType() != DeviceType_DVD)
4364 return setError(E_INVALIDARG,
4365 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4366 aDevice, aControllerPort, aName.c_str());
4367 pAttach->i_updatePassthrough(!!aPassthrough);
4368
4369 return S_OK;
4370}
4371
4372HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4373 LONG aDevice, BOOL aTemporaryEject)
4374{
4375
4376 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4377 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4378
4379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4380
4381 HRESULT rc = i_checkStateDependency(MutableStateDep);
4382 if (FAILED(rc)) return rc;
4383
4384 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4385 Bstr(aName).raw(),
4386 aControllerPort,
4387 aDevice);
4388 if (!pAttach)
4389 return setError(VBOX_E_OBJECT_NOT_FOUND,
4390 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4391 aDevice, aControllerPort, aName.c_str());
4392
4393
4394 i_setModified(IsModified_Storage);
4395 mMediaData.backup();
4396
4397 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4398
4399 if (pAttach->i_getType() != DeviceType_DVD)
4400 return setError(E_INVALIDARG,
4401 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4402 aDevice, aControllerPort, aName.c_str());
4403 pAttach->i_updateTempEject(!!aTemporaryEject);
4404
4405 return S_OK;
4406}
4407
4408HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4409 LONG aDevice, BOOL aNonRotational)
4410{
4411
4412 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4413 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4414
4415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4416
4417 HRESULT rc = i_checkStateDependency(MutableStateDep);
4418 if (FAILED(rc)) return rc;
4419
4420 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4421
4422 if (Global::IsOnlineOrTransient(mData->mMachineState))
4423 return setError(VBOX_E_INVALID_VM_STATE,
4424 tr("Invalid machine state: %s"),
4425 Global::stringifyMachineState(mData->mMachineState));
4426
4427 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4428 Bstr(aName).raw(),
4429 aControllerPort,
4430 aDevice);
4431 if (!pAttach)
4432 return setError(VBOX_E_OBJECT_NOT_FOUND,
4433 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4434 aDevice, aControllerPort, aName.c_str());
4435
4436
4437 i_setModified(IsModified_Storage);
4438 mMediaData.backup();
4439
4440 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4441
4442 if (pAttach->i_getType() != DeviceType_HardDisk)
4443 return setError(E_INVALIDARG,
4444 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"),
4445 aDevice, aControllerPort, aName.c_str());
4446 pAttach->i_updateNonRotational(!!aNonRotational);
4447
4448 return S_OK;
4449}
4450
4451HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4452 LONG aDevice, BOOL aDiscard)
4453{
4454
4455 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4456 aName.c_str(), aControllerPort, aDevice, aDiscard));
4457
4458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4459
4460 HRESULT rc = i_checkStateDependency(MutableStateDep);
4461 if (FAILED(rc)) return rc;
4462
4463 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4464
4465 if (Global::IsOnlineOrTransient(mData->mMachineState))
4466 return setError(VBOX_E_INVALID_VM_STATE,
4467 tr("Invalid machine state: %s"),
4468 Global::stringifyMachineState(mData->mMachineState));
4469
4470 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4471 Bstr(aName).raw(),
4472 aControllerPort,
4473 aDevice);
4474 if (!pAttach)
4475 return setError(VBOX_E_OBJECT_NOT_FOUND,
4476 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4477 aDevice, aControllerPort, aName.c_str());
4478
4479
4480 i_setModified(IsModified_Storage);
4481 mMediaData.backup();
4482
4483 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4484
4485 if (pAttach->i_getType() != DeviceType_HardDisk)
4486 return setError(E_INVALIDARG,
4487 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"),
4488 aDevice, aControllerPort, aName.c_str());
4489 pAttach->i_updateDiscard(!!aDiscard);
4490
4491 return S_OK;
4492}
4493
4494HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4495 LONG aDevice, BOOL aHotPluggable)
4496{
4497 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4498 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4499
4500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4501
4502 HRESULT rc = i_checkStateDependency(MutableStateDep);
4503 if (FAILED(rc)) return rc;
4504
4505 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4506
4507 if (Global::IsOnlineOrTransient(mData->mMachineState))
4508 return setError(VBOX_E_INVALID_VM_STATE,
4509 tr("Invalid machine state: %s"),
4510 Global::stringifyMachineState(mData->mMachineState));
4511
4512 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4513 Bstr(aName).raw(),
4514 aControllerPort,
4515 aDevice);
4516 if (!pAttach)
4517 return setError(VBOX_E_OBJECT_NOT_FOUND,
4518 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4519 aDevice, aControllerPort, aName.c_str());
4520
4521 /* Check for an existing controller. */
4522 ComObjPtr<StorageController> ctl;
4523 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4524 if (FAILED(rc)) return rc;
4525
4526 StorageControllerType_T ctrlType;
4527 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4528 if (FAILED(rc))
4529 return setError(E_FAIL,
4530 tr("Could not get type of controller '%s'"),
4531 aName.c_str());
4532
4533 if (!i_isControllerHotplugCapable(ctrlType))
4534 return setError(VBOX_E_NOT_SUPPORTED,
4535 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4536 aName.c_str());
4537
4538 i_setModified(IsModified_Storage);
4539 mMediaData.backup();
4540
4541 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4542
4543 if (pAttach->i_getType() == DeviceType_Floppy)
4544 return setError(E_INVALIDARG,
4545 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"),
4546 aDevice, aControllerPort, aName.c_str());
4547 pAttach->i_updateHotPluggable(!!aHotPluggable);
4548
4549 return S_OK;
4550}
4551
4552HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4553 LONG aDevice)
4554{
4555 int rc = S_OK;
4556 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4557 aName.c_str(), aControllerPort, aDevice));
4558
4559 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4560
4561 return rc;
4562}
4563
4564HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4565 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4566{
4567 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4568 aName.c_str(), aControllerPort, aDevice));
4569
4570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4571
4572 HRESULT rc = i_checkStateDependency(MutableStateDep);
4573 if (FAILED(rc)) return rc;
4574
4575 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4576
4577 if (Global::IsOnlineOrTransient(mData->mMachineState))
4578 return setError(VBOX_E_INVALID_VM_STATE,
4579 tr("Invalid machine state: %s"),
4580 Global::stringifyMachineState(mData->mMachineState));
4581
4582 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4583 Bstr(aName).raw(),
4584 aControllerPort,
4585 aDevice);
4586 if (!pAttach)
4587 return setError(VBOX_E_OBJECT_NOT_FOUND,
4588 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4589 aDevice, aControllerPort, aName.c_str());
4590
4591
4592 i_setModified(IsModified_Storage);
4593 mMediaData.backup();
4594
4595 IBandwidthGroup *iB = aBandwidthGroup;
4596 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4597 if (aBandwidthGroup && group.isNull())
4598 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4599
4600 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4601
4602 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4603 if (strBandwidthGroupOld.isNotEmpty())
4604 {
4605 /* Get the bandwidth group object and release it - this must not fail. */
4606 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4607 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4608 Assert(SUCCEEDED(rc));
4609
4610 pBandwidthGroupOld->i_release();
4611 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4612 }
4613
4614 if (!group.isNull())
4615 {
4616 group->i_reference();
4617 pAttach->i_updateBandwidthGroup(group->i_getName());
4618 }
4619
4620 return S_OK;
4621}
4622
4623HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4624 LONG aControllerPort,
4625 LONG aDevice,
4626 DeviceType_T aType)
4627{
4628 HRESULT rc = S_OK;
4629
4630 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4631 aName.c_str(), aControllerPort, aDevice, aType));
4632
4633 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4634
4635 return rc;
4636}
4637
4638
4639HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4640 LONG aControllerPort,
4641 LONG aDevice,
4642 BOOL aForce)
4643{
4644 int rc = S_OK;
4645 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4646 aName.c_str(), aControllerPort, aForce));
4647
4648 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4649
4650 return rc;
4651}
4652
4653HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4654 LONG aControllerPort,
4655 LONG aDevice,
4656 const ComPtr<IMedium> &aMedium,
4657 BOOL aForce)
4658{
4659 int rc = S_OK;
4660 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4661 aName.c_str(), aControllerPort, aDevice, aForce));
4662
4663 // request the host lock first, since might be calling Host methods for getting host drives;
4664 // next, protect the media tree all the while we're in here, as well as our member variables
4665 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4666 this->lockHandle(),
4667 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4668
4669 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4670 Bstr(aName).raw(),
4671 aControllerPort,
4672 aDevice);
4673 if (pAttach.isNull())
4674 return setError(VBOX_E_OBJECT_NOT_FOUND,
4675 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4676 aDevice, aControllerPort, aName.c_str());
4677
4678 /* Remember previously mounted medium. The medium before taking the
4679 * backup is not necessarily the same thing. */
4680 ComObjPtr<Medium> oldmedium;
4681 oldmedium = pAttach->i_getMedium();
4682
4683 IMedium *iM = aMedium;
4684 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4685 if (aMedium && pMedium.isNull())
4686 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4687
4688 AutoCaller mediumCaller(pMedium);
4689 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4690
4691 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4692 if (pMedium)
4693 {
4694 DeviceType_T mediumType = pAttach->i_getType();
4695 switch (mediumType)
4696 {
4697 case DeviceType_DVD:
4698 case DeviceType_Floppy:
4699 break;
4700
4701 default:
4702 return setError(VBOX_E_INVALID_OBJECT_STATE,
4703 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4704 aControllerPort,
4705 aDevice,
4706 aName.c_str());
4707 }
4708 }
4709
4710 i_setModified(IsModified_Storage);
4711 mMediaData.backup();
4712
4713 {
4714 // The backup operation makes the pAttach reference point to the
4715 // old settings. Re-get the correct reference.
4716 pAttach = i_findAttachment(mMediaData->mAttachments,
4717 Bstr(aName).raw(),
4718 aControllerPort,
4719 aDevice);
4720 if (!oldmedium.isNull())
4721 oldmedium->i_removeBackReference(mData->mUuid);
4722 if (!pMedium.isNull())
4723 {
4724 pMedium->i_addBackReference(mData->mUuid);
4725
4726 mediumLock.release();
4727 multiLock.release();
4728 i_addMediumToRegistry(pMedium);
4729 multiLock.acquire();
4730 mediumLock.acquire();
4731 }
4732
4733 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4734 pAttach->i_updateMedium(pMedium);
4735 }
4736
4737 i_setModified(IsModified_Storage);
4738
4739 mediumLock.release();
4740 multiLock.release();
4741 rc = i_onMediumChange(pAttach, aForce);
4742 multiLock.acquire();
4743 mediumLock.acquire();
4744
4745 /* On error roll back this change only. */
4746 if (FAILED(rc))
4747 {
4748 if (!pMedium.isNull())
4749 pMedium->i_removeBackReference(mData->mUuid);
4750 pAttach = i_findAttachment(mMediaData->mAttachments,
4751 Bstr(aName).raw(),
4752 aControllerPort,
4753 aDevice);
4754 /* If the attachment is gone in the meantime, bail out. */
4755 if (pAttach.isNull())
4756 return rc;
4757 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4758 if (!oldmedium.isNull())
4759 oldmedium->i_addBackReference(mData->mUuid);
4760 pAttach->i_updateMedium(oldmedium);
4761 }
4762
4763 mediumLock.release();
4764 multiLock.release();
4765
4766 mParent->i_saveModifiedRegistries();
4767
4768 return rc;
4769}
4770HRESULT Machine::getMedium(const com::Utf8Str &aName,
4771 LONG aControllerPort,
4772 LONG aDevice,
4773 ComPtr<IMedium> &aMedium)
4774{
4775 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4776 aName.c_str(), aControllerPort, aDevice));
4777
4778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4779
4780 aMedium = NULL;
4781
4782 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4783 Bstr(aName).raw(),
4784 aControllerPort,
4785 aDevice);
4786 if (pAttach.isNull())
4787 return setError(VBOX_E_OBJECT_NOT_FOUND,
4788 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4789 aDevice, aControllerPort, aName.c_str());
4790
4791 aMedium = pAttach->i_getMedium();
4792
4793 return S_OK;
4794}
4795
4796HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4797{
4798
4799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4800
4801 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4802
4803 return S_OK;
4804}
4805
4806HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4807{
4808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4809
4810 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4811
4812 return S_OK;
4813}
4814
4815HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4816{
4817 /* Do not assert if slot is out of range, just return the advertised
4818 status. testdriver/vbox.py triggers this in logVmInfo. */
4819 if (aSlot >= mNetworkAdapters.size())
4820 return setError(E_INVALIDARG,
4821 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4822 aSlot, mNetworkAdapters.size());
4823
4824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4825
4826 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4827
4828 return S_OK;
4829}
4830
4831HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4832{
4833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4834
4835 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4836 size_t i = 0;
4837 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4838 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4839 ++it, ++i)
4840 aKeys[i] = it->first;
4841
4842 return S_OK;
4843}
4844
4845 /**
4846 * @note Locks this object for reading.
4847 */
4848HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4849 com::Utf8Str &aValue)
4850{
4851 /* start with nothing found */
4852 aValue = "";
4853
4854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4855
4856 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4857 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4858 // found:
4859 aValue = it->second; // source is a Utf8Str
4860
4861 /* return the result to caller (may be empty) */
4862 return S_OK;
4863}
4864
4865 /**
4866 * @note Locks mParent for writing + this object for writing.
4867 */
4868HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4869{
4870 Utf8Str strOldValue; // empty
4871
4872 // locking note: we only hold the read lock briefly to look up the old value,
4873 // then release it and call the onExtraCanChange callbacks. There is a small
4874 // chance of a race insofar as the callback might be called twice if two callers
4875 // change the same key at the same time, but that's a much better solution
4876 // than the deadlock we had here before. The actual changing of the extradata
4877 // is then performed under the write lock and race-free.
4878
4879 // look up the old value first; if nothing has changed then we need not do anything
4880 {
4881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4882 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4883 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4884 strOldValue = it->second;
4885 }
4886
4887 bool fChanged;
4888 if ((fChanged = (strOldValue != aValue)))
4889 {
4890 // ask for permission from all listeners outside the locks;
4891 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4892 // lock to copy the list of callbacks to invoke
4893 Bstr error;
4894 Bstr bstrValue(aValue);
4895
4896 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4897 {
4898 const char *sep = error.isEmpty() ? "" : ": ";
4899 CBSTR err = error.raw();
4900 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4901 sep, err));
4902 return setError(E_ACCESSDENIED,
4903 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4904 aKey.c_str(),
4905 aValue.c_str(),
4906 sep,
4907 err);
4908 }
4909
4910 // data is changing and change not vetoed: then write it out under the lock
4911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4912
4913 if (i_isSnapshotMachine())
4914 {
4915 HRESULT rc = i_checkStateDependency(MutableStateDep);
4916 if (FAILED(rc)) return rc;
4917 }
4918
4919 if (aValue.isEmpty())
4920 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4921 else
4922 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4923 // creates a new key if needed
4924
4925 bool fNeedsGlobalSaveSettings = false;
4926 // This saving of settings is tricky: there is no "old state" for the
4927 // extradata items at all (unlike all other settings), so the old/new
4928 // settings comparison would give a wrong result!
4929 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4930
4931 if (fNeedsGlobalSaveSettings)
4932 {
4933 // save the global settings; for that we should hold only the VirtualBox lock
4934 alock.release();
4935 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4936 mParent->i_saveSettings();
4937 }
4938 }
4939
4940 // fire notification outside the lock
4941 if (fChanged)
4942 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4943
4944 return S_OK;
4945}
4946
4947HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4948{
4949 aProgress = NULL;
4950 NOREF(aSettingsFilePath);
4951 ReturnComNotImplemented();
4952}
4953
4954HRESULT Machine::saveSettings()
4955{
4956 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4957
4958 /* when there was auto-conversion, we want to save the file even if
4959 * the VM is saved */
4960 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4961 if (FAILED(rc)) return rc;
4962
4963 /* the settings file path may never be null */
4964 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4965
4966 /* save all VM data excluding snapshots */
4967 bool fNeedsGlobalSaveSettings = false;
4968 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4969 mlock.release();
4970
4971 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4972 {
4973 // save the global settings; for that we should hold only the VirtualBox lock
4974 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4975 rc = mParent->i_saveSettings();
4976 }
4977
4978 return rc;
4979}
4980
4981
4982HRESULT Machine::discardSettings()
4983{
4984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4985
4986 HRESULT rc = i_checkStateDependency(MutableStateDep);
4987 if (FAILED(rc)) return rc;
4988
4989 /*
4990 * during this rollback, the session will be notified if data has
4991 * been actually changed
4992 */
4993 i_rollback(true /* aNotify */);
4994
4995 return S_OK;
4996}
4997
4998/** @note Locks objects! */
4999HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5000 std::vector<ComPtr<IMedium> > &aMedia)
5001{
5002 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5003 AutoLimitedCaller autoCaller(this);
5004 AssertComRCReturnRC(autoCaller.rc());
5005
5006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5007
5008 Guid id(i_getId());
5009
5010 if (mData->mSession.mState != SessionState_Unlocked)
5011 return setError(VBOX_E_INVALID_OBJECT_STATE,
5012 tr("Cannot unregister the machine '%s' while it is locked"),
5013 mUserData->s.strName.c_str());
5014
5015 // wait for state dependents to drop to zero
5016 i_ensureNoStateDependencies();
5017
5018 if (!mData->mAccessible)
5019 {
5020 // inaccessible maschines can only be unregistered; uninitialize ourselves
5021 // here because currently there may be no unregistered that are inaccessible
5022 // (this state combination is not supported). Note releasing the caller and
5023 // leaving the lock before calling uninit()
5024 alock.release();
5025 autoCaller.release();
5026
5027 uninit();
5028
5029 mParent->i_unregisterMachine(this, id);
5030 // calls VirtualBox::i_saveSettings()
5031
5032 return S_OK;
5033 }
5034
5035 HRESULT rc = S_OK;
5036
5037 // discard saved state
5038 if (mData->mMachineState == MachineState_Saved)
5039 {
5040 // add the saved state file to the list of files the caller should delete
5041 Assert(!mSSData->strStateFilePath.isEmpty());
5042 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5043
5044 mSSData->strStateFilePath.setNull();
5045
5046 // unconditionally set the machine state to powered off, we now
5047 // know no session has locked the machine
5048 mData->mMachineState = MachineState_PoweredOff;
5049 }
5050
5051 size_t cSnapshots = 0;
5052 if (mData->mFirstSnapshot)
5053 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5054 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5055 // fail now before we start detaching media
5056 return setError(VBOX_E_INVALID_OBJECT_STATE,
5057 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5058 mUserData->s.strName.c_str(), cSnapshots);
5059
5060 // This list collects the medium objects from all medium attachments
5061 // which we will detach from the machine and its snapshots, in a specific
5062 // order which allows for closing all media without getting "media in use"
5063 // errors, simply by going through the list from the front to the back:
5064 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5065 // and must be closed before the parent media from the snapshots, or closing the parents
5066 // will fail because they still have children);
5067 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5068 // the root ("first") snapshot of the machine.
5069 MediaList llMedia;
5070
5071 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5072 && mMediaData->mAttachments.size()
5073 )
5074 {
5075 // we have media attachments: detach them all and add the Medium objects to our list
5076 if (aCleanupMode != CleanupMode_UnregisterOnly)
5077 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5078 else
5079 return setError(VBOX_E_INVALID_OBJECT_STATE,
5080 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5081 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5082 }
5083
5084 if (cSnapshots)
5085 {
5086 // autoCleanup must be true here, or we would have failed above
5087
5088 // add the media from the medium attachments of the snapshots to llMedia
5089 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5090 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5091 // into the children first
5092
5093 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5094 MachineState_T oldState = mData->mMachineState;
5095 mData->mMachineState = MachineState_DeletingSnapshot;
5096
5097 // make a copy of the first snapshot so the refcount does not drop to 0
5098 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5099 // because of the AutoCaller voodoo)
5100 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5101
5102 // GO!
5103 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5104
5105 mData->mMachineState = oldState;
5106 }
5107
5108 if (FAILED(rc))
5109 {
5110 i_rollbackMedia();
5111 return rc;
5112 }
5113
5114 // commit all the media changes made above
5115 i_commitMedia();
5116
5117 mData->mRegistered = false;
5118
5119 // machine lock no longer needed
5120 alock.release();
5121
5122 // return media to caller
5123 size_t i = 0;
5124 aMedia.resize(llMedia.size());
5125 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5126 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5127
5128 mParent->i_unregisterMachine(this, id);
5129 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5130
5131 return S_OK;
5132}
5133
5134struct Machine::DeleteTask
5135{
5136 ComObjPtr<Machine> pMachine;
5137 RTCList<ComPtr<IMedium> > llMediums;
5138 StringsList llFilesToDelete;
5139 ComObjPtr<Progress> pProgress;
5140};
5141
5142HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5143{
5144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5145
5146 HRESULT rc = i_checkStateDependency(MutableStateDep);
5147 if (FAILED(rc)) return rc;
5148
5149 if (mData->mRegistered)
5150 return setError(VBOX_E_INVALID_VM_STATE,
5151 tr("Cannot delete settings of a registered machine"));
5152
5153 DeleteTask *pTask = new DeleteTask;
5154 pTask->pMachine = this;
5155
5156 // collect files to delete
5157 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5158
5159 for (size_t i = 0; i < aMedia.size(); ++i)
5160 {
5161 IMedium *pIMedium(aMedia[i]);
5162 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5163 if (pMedium.isNull())
5164 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5165 SafeArray<BSTR> ids;
5166 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5167 if (FAILED(rc)) return rc;
5168 /* At this point the medium should not have any back references
5169 * anymore. If it has it is attached to another VM and *must* not
5170 * deleted. */
5171 if (ids.size() < 1)
5172 pTask->llMediums.append(pMedium);
5173 }
5174 if (mData->pMachineConfigFile->fileExists())
5175 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5176
5177 pTask->pProgress.createObject();
5178 pTask->pProgress->init(i_getVirtualBox(),
5179 static_cast<IMachine*>(this) /* aInitiator */,
5180 Bstr(tr("Deleting files")).raw(),
5181 true /* fCancellable */,
5182 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5183 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5184
5185 int vrc = RTThreadCreate(NULL,
5186 Machine::deleteThread,
5187 (void*)pTask,
5188 0,
5189 RTTHREADTYPE_MAIN_WORKER,
5190 0,
5191 "MachineDelete");
5192
5193 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5194
5195 if (RT_FAILURE(vrc))
5196 {
5197 delete pTask;
5198 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5199 }
5200
5201 LogFlowFuncLeave();
5202
5203 return S_OK;
5204}
5205
5206/**
5207 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5208 * calls Machine::deleteTaskWorker() on the actual machine object.
5209 * @param Thread
5210 * @param pvUser
5211 * @return
5212 */
5213/*static*/
5214DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5215{
5216 LogFlowFuncEnter();
5217
5218 DeleteTask *pTask = (DeleteTask*)pvUser;
5219 Assert(pTask);
5220 Assert(pTask->pMachine);
5221 Assert(pTask->pProgress);
5222
5223 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5224 pTask->pProgress->i_notifyComplete(rc);
5225
5226 delete pTask;
5227
5228 LogFlowFuncLeave();
5229
5230 NOREF(Thread);
5231
5232 return VINF_SUCCESS;
5233}
5234
5235/**
5236 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5237 * @param task
5238 * @return
5239 */
5240HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5241{
5242 AutoCaller autoCaller(this);
5243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5244
5245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5246
5247 HRESULT rc = S_OK;
5248
5249 try
5250 {
5251 ULONG uLogHistoryCount = 3;
5252 ComPtr<ISystemProperties> systemProperties;
5253 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5254 if (FAILED(rc)) throw rc;
5255
5256 if (!systemProperties.isNull())
5257 {
5258 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5259 if (FAILED(rc)) throw rc;
5260 }
5261
5262 MachineState_T oldState = mData->mMachineState;
5263 i_setMachineState(MachineState_SettingUp);
5264 alock.release();
5265 for (size_t i = 0; i < task.llMediums.size(); ++i)
5266 {
5267 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5268 {
5269 AutoCaller mac(pMedium);
5270 if (FAILED(mac.rc())) throw mac.rc();
5271 Utf8Str strLocation = pMedium->i_getLocationFull();
5272 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5273 if (FAILED(rc)) throw rc;
5274 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5275 }
5276 if (pMedium->i_isMediumFormatFile())
5277 {
5278 ComPtr<IProgress> pProgress2;
5279 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5280 if (FAILED(rc)) throw rc;
5281 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5282 if (FAILED(rc)) throw rc;
5283 /* Check the result of the asynchronous process. */
5284 LONG iRc;
5285 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5286 if (FAILED(rc)) throw rc;
5287 /* If the thread of the progress object has an error, then
5288 * retrieve the error info from there, or it'll be lost. */
5289 if (FAILED(iRc))
5290 throw setError(ProgressErrorInfo(pProgress2));
5291 }
5292
5293 /* Close the medium, deliberately without checking the return
5294 * code, and without leaving any trace in the error info, as
5295 * a failure here is a very minor issue, which shouldn't happen
5296 * as above we even managed to delete the medium. */
5297 {
5298 ErrorInfoKeeper eik;
5299 pMedium->Close();
5300 }
5301 }
5302 i_setMachineState(oldState);
5303 alock.acquire();
5304
5305 // delete the files pushed on the task list by Machine::Delete()
5306 // (this includes saved states of the machine and snapshots and
5307 // medium storage files from the IMedium list passed in, and the
5308 // machine XML file)
5309 StringsList::const_iterator it = task.llFilesToDelete.begin();
5310 while (it != task.llFilesToDelete.end())
5311 {
5312 const Utf8Str &strFile = *it;
5313 LogFunc(("Deleting file %s\n", strFile.c_str()));
5314 int vrc = RTFileDelete(strFile.c_str());
5315 if (RT_FAILURE(vrc))
5316 throw setError(VBOX_E_IPRT_ERROR,
5317 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5318
5319 ++it;
5320 if (it == task.llFilesToDelete.end())
5321 {
5322 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5323 if (FAILED(rc)) throw rc;
5324 break;
5325 }
5326
5327 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5328 if (FAILED(rc)) throw rc;
5329 }
5330
5331 /* delete the settings only when the file actually exists */
5332 if (mData->pMachineConfigFile->fileExists())
5333 {
5334 /* Delete any backup or uncommitted XML files. Ignore failures.
5335 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5336 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5337 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5338 RTFileDelete(otherXml.c_str());
5339 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5340 RTFileDelete(otherXml.c_str());
5341
5342 /* delete the Logs folder, nothing important should be left
5343 * there (we don't check for errors because the user might have
5344 * some private files there that we don't want to delete) */
5345 Utf8Str logFolder;
5346 getLogFolder(logFolder);
5347 Assert(logFolder.length());
5348 if (RTDirExists(logFolder.c_str()))
5349 {
5350 /* Delete all VBox.log[.N] files from the Logs folder
5351 * (this must be in sync with the rotation logic in
5352 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5353 * files that may have been created by the GUI. */
5354 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5355 logFolder.c_str(), RTPATH_DELIMITER);
5356 RTFileDelete(log.c_str());
5357 log = Utf8StrFmt("%s%cVBox.png",
5358 logFolder.c_str(), RTPATH_DELIMITER);
5359 RTFileDelete(log.c_str());
5360 for (int i = uLogHistoryCount; i > 0; i--)
5361 {
5362 log = Utf8StrFmt("%s%cVBox.log.%d",
5363 logFolder.c_str(), RTPATH_DELIMITER, i);
5364 RTFileDelete(log.c_str());
5365 log = Utf8StrFmt("%s%cVBox.png.%d",
5366 logFolder.c_str(), RTPATH_DELIMITER, i);
5367 RTFileDelete(log.c_str());
5368 }
5369#if defined(RT_OS_WINDOWS)
5370 log = Utf8StrFmt("%s%cVBoxStartup.log",
5371 logFolder.c_str(), RTPATH_DELIMITER);
5372 RTFileDelete(log.c_str());
5373#endif
5374
5375 RTDirRemove(logFolder.c_str());
5376 }
5377
5378 /* delete the Snapshots folder, nothing important should be left
5379 * there (we don't check for errors because the user might have
5380 * some private files there that we don't want to delete) */
5381 Utf8Str strFullSnapshotFolder;
5382 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5383 Assert(!strFullSnapshotFolder.isEmpty());
5384 if (RTDirExists(strFullSnapshotFolder.c_str()))
5385 RTDirRemove(strFullSnapshotFolder.c_str());
5386
5387 // delete the directory that contains the settings file, but only
5388 // if it matches the VM name
5389 Utf8Str settingsDir;
5390 if (i_isInOwnDir(&settingsDir))
5391 RTDirRemove(settingsDir.c_str());
5392 }
5393
5394 alock.release();
5395
5396 mParent->i_saveModifiedRegistries();
5397 }
5398 catch (HRESULT aRC) { rc = aRC; }
5399
5400 return rc;
5401}
5402
5403HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5404{
5405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5406
5407 ComObjPtr<Snapshot> pSnapshot;
5408 HRESULT rc;
5409
5410 if (aNameOrId.isEmpty())
5411 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5412 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5413 else
5414 {
5415 Guid uuid(aNameOrId);
5416 if (uuid.isValid())
5417 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5418 else
5419 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5420 }
5421 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5422
5423 return rc;
5424}
5425
5426HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5427{
5428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5429
5430 HRESULT rc = i_checkStateDependency(MutableStateDep);
5431 if (FAILED(rc)) return rc;
5432
5433 ComObjPtr<SharedFolder> sharedFolder;
5434 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5435 if (SUCCEEDED(rc))
5436 return setError(VBOX_E_OBJECT_IN_USE,
5437 tr("Shared folder named '%s' already exists"),
5438 aName.c_str());
5439
5440 sharedFolder.createObject();
5441 rc = sharedFolder->init(i_getMachine(),
5442 aName,
5443 aHostPath,
5444 !!aWritable,
5445 !!aAutomount,
5446 true /* fFailOnError */);
5447 if (FAILED(rc)) return rc;
5448
5449 i_setModified(IsModified_SharedFolders);
5450 mHWData.backup();
5451 mHWData->mSharedFolders.push_back(sharedFolder);
5452
5453 /* inform the direct session if any */
5454 alock.release();
5455 i_onSharedFolderChange();
5456
5457 return S_OK;
5458}
5459
5460HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5461{
5462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5463
5464 HRESULT rc = i_checkStateDependency(MutableStateDep);
5465 if (FAILED(rc)) return rc;
5466
5467 ComObjPtr<SharedFolder> sharedFolder;
5468 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5469 if (FAILED(rc)) return rc;
5470
5471 i_setModified(IsModified_SharedFolders);
5472 mHWData.backup();
5473 mHWData->mSharedFolders.remove(sharedFolder);
5474
5475 /* inform the direct session if any */
5476 alock.release();
5477 i_onSharedFolderChange();
5478
5479 return S_OK;
5480}
5481
5482HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5483{
5484 /* start with No */
5485 *aCanShow = FALSE;
5486
5487 ComPtr<IInternalSessionControl> directControl;
5488 {
5489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5490
5491 if (mData->mSession.mState != SessionState_Locked)
5492 return setError(VBOX_E_INVALID_VM_STATE,
5493 tr("Machine is not locked for session (session state: %s)"),
5494 Global::stringifySessionState(mData->mSession.mState));
5495
5496 directControl = mData->mSession.mDirectControl;
5497 }
5498
5499 /* ignore calls made after #OnSessionEnd() is called */
5500 if (!directControl)
5501 return S_OK;
5502
5503 LONG64 dummy;
5504 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5505}
5506
5507HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5508{
5509 ComPtr<IInternalSessionControl> directControl;
5510 {
5511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5512
5513 if (mData->mSession.mState != SessionState_Locked)
5514 return setError(E_FAIL,
5515 tr("Machine is not locked for session (session state: %s)"),
5516 Global::stringifySessionState(mData->mSession.mState));
5517
5518 directControl = mData->mSession.mDirectControl;
5519 }
5520
5521 /* ignore calls made after #OnSessionEnd() is called */
5522 if (!directControl)
5523 return S_OK;
5524
5525 BOOL dummy;
5526 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5527}
5528
5529#ifdef VBOX_WITH_GUEST_PROPS
5530/**
5531 * Look up a guest property in VBoxSVC's internal structures.
5532 */
5533HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5534 com::Utf8Str &aValue,
5535 LONG64 *aTimestamp,
5536 com::Utf8Str &aFlags) const
5537{
5538 using namespace guestProp;
5539
5540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5541 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5542
5543 if (it != mHWData->mGuestProperties.end())
5544 {
5545 char szFlags[MAX_FLAGS_LEN + 1];
5546 aValue = it->second.strValue;
5547 *aTimestamp = it->second.mTimestamp;
5548 writeFlags(it->second.mFlags, szFlags);
5549 aFlags = Utf8Str(szFlags);
5550 }
5551
5552 return S_OK;
5553}
5554
5555/**
5556 * Query the VM that a guest property belongs to for the property.
5557 * @returns E_ACCESSDENIED if the VM process is not available or not
5558 * currently handling queries and the lookup should then be done in
5559 * VBoxSVC.
5560 */
5561HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5562 com::Utf8Str &aValue,
5563 LONG64 *aTimestamp,
5564 com::Utf8Str &aFlags) const
5565{
5566 HRESULT rc = S_OK;
5567 BSTR bValue = NULL;
5568 BSTR bFlags = NULL;
5569
5570 ComPtr<IInternalSessionControl> directControl;
5571 directControl = mData->mSession.mDirectControl;
5572
5573 /* fail if we were called after #OnSessionEnd() is called. This is a
5574 * silly race condition. */
5575
5576 /** @todo This code is bothering API clients (like python script clients) with
5577 * the AccessGuestProperty call, creating unncessary IPC. Need to
5578 * have a way of figuring out which kind of direct session it is... */
5579 if (!directControl)
5580 rc = E_ACCESSDENIED;
5581 else
5582 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5583 0 /* accessMode */,
5584 &bValue, aTimestamp, &bFlags);
5585
5586 aValue = bValue;
5587 aFlags = bFlags;
5588
5589 return rc;
5590}
5591#endif // VBOX_WITH_GUEST_PROPS
5592
5593HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5594 com::Utf8Str &aValue,
5595 LONG64 *aTimestamp,
5596 com::Utf8Str &aFlags)
5597{
5598#ifndef VBOX_WITH_GUEST_PROPS
5599 ReturnComNotImplemented();
5600#else // VBOX_WITH_GUEST_PROPS
5601
5602 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5603
5604 if (rc == E_ACCESSDENIED)
5605 /* The VM is not running or the service is not (yet) accessible */
5606 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5607 return rc;
5608#endif // VBOX_WITH_GUEST_PROPS
5609}
5610
5611HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5612{
5613 LONG64 dummyTimestamp;
5614 com::Utf8Str dummyFlags;
5615 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5616 return rc;
5617
5618}
5619HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5620{
5621 com::Utf8Str dummyFlags;
5622 com::Utf8Str dummyValue;
5623 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5624 return rc;
5625}
5626
5627#ifdef VBOX_WITH_GUEST_PROPS
5628/**
5629 * Set a guest property in VBoxSVC's internal structures.
5630 */
5631HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5632 const com::Utf8Str &aFlags, bool fDelete)
5633{
5634 using namespace guestProp;
5635
5636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5637 HRESULT rc = S_OK;
5638
5639 rc = i_checkStateDependency(MutableStateDep);
5640 if (FAILED(rc)) return rc;
5641
5642 try
5643 {
5644 uint32_t fFlags = NILFLAG;
5645 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5646 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5647
5648 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5649 if (it == mHWData->mGuestProperties.end())
5650 {
5651 if (!fDelete)
5652 {
5653 i_setModified(IsModified_MachineData);
5654 mHWData.backupEx();
5655
5656 RTTIMESPEC time;
5657 HWData::GuestProperty prop;
5658 prop.strValue = Bstr(aValue).raw();
5659 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5660 prop.mFlags = fFlags;
5661 mHWData->mGuestProperties[aName] = prop;
5662 }
5663 }
5664 else
5665 {
5666 if (it->second.mFlags & (RDONLYHOST))
5667 {
5668 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5669 }
5670 else
5671 {
5672 i_setModified(IsModified_MachineData);
5673 mHWData.backupEx();
5674
5675 /* The backupEx() operation invalidates our iterator,
5676 * so get a new one. */
5677 it = mHWData->mGuestProperties.find(aName);
5678 Assert(it != mHWData->mGuestProperties.end());
5679
5680 if (!fDelete)
5681 {
5682 RTTIMESPEC time;
5683 it->second.strValue = aValue;
5684 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5685 it->second.mFlags = fFlags;
5686 }
5687 else
5688 mHWData->mGuestProperties.erase(it);
5689 }
5690 }
5691
5692 if ( SUCCEEDED(rc)
5693 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5694 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5695 RTSTR_MAX,
5696 aName.c_str(),
5697 RTSTR_MAX,
5698 NULL)
5699 )
5700 )
5701 {
5702 alock.release();
5703
5704 mParent->i_onGuestPropertyChange(mData->mUuid,
5705 Bstr(aName).raw(),
5706 Bstr(aValue).raw(),
5707 Bstr(aFlags).raw());
5708 }
5709 }
5710 catch (std::bad_alloc &)
5711 {
5712 rc = E_OUTOFMEMORY;
5713 }
5714
5715 return rc;
5716}
5717
5718/**
5719 * Set a property on the VM that that property belongs to.
5720 * @returns E_ACCESSDENIED if the VM process is not available or not
5721 * currently handling queries and the setting should then be done in
5722 * VBoxSVC.
5723 */
5724HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5725 const com::Utf8Str &aFlags, bool fDelete)
5726{
5727 HRESULT rc;
5728
5729 try
5730 {
5731 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5732
5733 BSTR dummy = NULL; /* will not be changed (setter) */
5734 LONG64 dummy64;
5735 if (!directControl)
5736 rc = E_ACCESSDENIED;
5737 else
5738 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5739 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5740 fDelete? 2: 1 /* accessMode */,
5741 &dummy, &dummy64, &dummy);
5742 }
5743 catch (std::bad_alloc &)
5744 {
5745 rc = E_OUTOFMEMORY;
5746 }
5747
5748 return rc;
5749}
5750#endif // VBOX_WITH_GUEST_PROPS
5751
5752HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5753 const com::Utf8Str &aFlags)
5754{
5755#ifndef VBOX_WITH_GUEST_PROPS
5756 ReturnComNotImplemented();
5757#else // VBOX_WITH_GUEST_PROPS
5758 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5759 if (rc == E_ACCESSDENIED)
5760 /* The VM is not running or the service is not (yet) accessible */
5761 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5762 return rc;
5763#endif // VBOX_WITH_GUEST_PROPS
5764}
5765
5766HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5767{
5768 return setGuestProperty(aProperty, aValue, "");
5769}
5770
5771HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5772{
5773#ifndef VBOX_WITH_GUEST_PROPS
5774 ReturnComNotImplemented();
5775#else // VBOX_WITH_GUEST_PROPS
5776 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5777 if (rc == E_ACCESSDENIED)
5778 /* The VM is not running or the service is not (yet) accessible */
5779 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5780 return rc;
5781#endif // VBOX_WITH_GUEST_PROPS
5782}
5783
5784#ifdef VBOX_WITH_GUEST_PROPS
5785/**
5786 * Enumerate the guest properties in VBoxSVC's internal structures.
5787 */
5788HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5789 std::vector<com::Utf8Str> &aNames,
5790 std::vector<com::Utf8Str> &aValues,
5791 std::vector<LONG64> &aTimestamps,
5792 std::vector<com::Utf8Str> &aFlags)
5793{
5794 using namespace guestProp;
5795
5796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5797 Utf8Str strPatterns(aPatterns);
5798
5799 HWData::GuestPropertyMap propMap;
5800
5801 /*
5802 * Look for matching patterns and build up a list.
5803 */
5804 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5805 while (it != mHWData->mGuestProperties.end())
5806 {
5807 if ( strPatterns.isEmpty()
5808 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5809 RTSTR_MAX,
5810 it->first.c_str(),
5811 RTSTR_MAX,
5812 NULL)
5813 )
5814 propMap.insert(*it);
5815 it++;
5816 }
5817
5818 alock.release();
5819
5820 /*
5821 * And build up the arrays for returning the property information.
5822 */
5823 size_t cEntries = propMap.size();
5824
5825 aNames.resize(cEntries);
5826 aValues.resize(cEntries);
5827 aTimestamps.resize(cEntries);
5828 aFlags.resize(cEntries);
5829
5830 char szFlags[MAX_FLAGS_LEN + 1];
5831 size_t i= 0;
5832 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5833 {
5834 aNames[i] = it->first;
5835 aValues[i] = it->second.strValue;
5836 aTimestamps[i] = it->second.mTimestamp;
5837 writeFlags(it->second.mFlags, szFlags);
5838 aFlags[i] = Utf8Str(szFlags);
5839 }
5840
5841 return S_OK;
5842}
5843
5844/**
5845 * Enumerate the properties managed by a VM.
5846 * @returns E_ACCESSDENIED if the VM process is not available or not
5847 * currently handling queries and the setting should then be done in
5848 * VBoxSVC.
5849 */
5850HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5851 std::vector<com::Utf8Str> &aNames,
5852 std::vector<com::Utf8Str> &aValues,
5853 std::vector<LONG64> &aTimestamps,
5854 std::vector<com::Utf8Str> &aFlags)
5855{
5856 HRESULT rc;
5857 ComPtr<IInternalSessionControl> directControl;
5858 directControl = mData->mSession.mDirectControl;
5859
5860
5861 com::SafeArray<BSTR> bNames;
5862 com::SafeArray<BSTR> bValues;
5863 com::SafeArray<LONG64> bTimestamps;
5864 com::SafeArray<BSTR> bFlags;
5865
5866 if (!directControl)
5867 rc = E_ACCESSDENIED;
5868 else
5869 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5870 ComSafeArrayAsOutParam(bNames),
5871 ComSafeArrayAsOutParam(bValues),
5872 ComSafeArrayAsOutParam(bTimestamps),
5873 ComSafeArrayAsOutParam(bFlags));
5874 size_t i;
5875 aNames.resize(bNames.size());
5876 for (i = 0; i < bNames.size(); ++i)
5877 aNames[i] = Utf8Str(bNames[i]);
5878 aValues.resize(bValues.size());
5879 for (i = 0; i < bValues.size(); ++i)
5880 aValues[i] = Utf8Str(bValues[i]);
5881 aTimestamps.resize(bTimestamps.size());
5882 for (i = 0; i < bTimestamps.size(); ++i)
5883 aTimestamps[i] = bTimestamps[i];
5884 aFlags.resize(bFlags.size());
5885 for (i = 0; i < bFlags.size(); ++i)
5886 aFlags[i] = Utf8Str(bFlags[i]);
5887
5888 return rc;
5889}
5890#endif // VBOX_WITH_GUEST_PROPS
5891HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5892 std::vector<com::Utf8Str> &aNames,
5893 std::vector<com::Utf8Str> &aValues,
5894 std::vector<LONG64> &aTimestamps,
5895 std::vector<com::Utf8Str> &aFlags)
5896{
5897#ifndef VBOX_WITH_GUEST_PROPS
5898 ReturnComNotImplemented();
5899#else // VBOX_WITH_GUEST_PROPS
5900
5901 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5902
5903 if (rc == E_ACCESSDENIED)
5904 /* The VM is not running or the service is not (yet) accessible */
5905 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5906 return rc;
5907#endif // VBOX_WITH_GUEST_PROPS
5908}
5909
5910HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5911 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5912{
5913 MediaData::AttachmentList atts;
5914
5915 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5916 if (FAILED(rc)) return rc;
5917
5918 size_t i = 0;
5919 aMediumAttachments.resize(atts.size());
5920 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5921 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5922
5923 return S_OK;
5924}
5925
5926HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5927 LONG aControllerPort,
5928 LONG aDevice,
5929 ComPtr<IMediumAttachment> &aAttachment)
5930{
5931 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5932 aName.c_str(), aControllerPort, aDevice));
5933
5934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5935
5936 aAttachment = NULL;
5937
5938 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5939 Bstr(aName).raw(),
5940 aControllerPort,
5941 aDevice);
5942 if (pAttach.isNull())
5943 return setError(VBOX_E_OBJECT_NOT_FOUND,
5944 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5945 aDevice, aControllerPort, aName.c_str());
5946
5947 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5948
5949 return S_OK;
5950}
5951
5952
5953HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5954 StorageBus_T aConnectionType,
5955 ComPtr<IStorageController> &aController)
5956{
5957 if ( (aConnectionType <= StorageBus_Null)
5958 || (aConnectionType > StorageBus_USB))
5959 return setError(E_INVALIDARG,
5960 tr("Invalid connection type: %d"),
5961 aConnectionType);
5962
5963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5964
5965 HRESULT rc = i_checkStateDependency(MutableStateDep);
5966 if (FAILED(rc)) return rc;
5967
5968 /* try to find one with the name first. */
5969 ComObjPtr<StorageController> ctrl;
5970
5971 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5972 if (SUCCEEDED(rc))
5973 return setError(VBOX_E_OBJECT_IN_USE,
5974 tr("Storage controller named '%s' already exists"),
5975 aName.c_str());
5976
5977 ctrl.createObject();
5978
5979 /* get a new instance number for the storage controller */
5980 ULONG ulInstance = 0;
5981 bool fBootable = true;
5982 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5983 it != mStorageControllers->end();
5984 ++it)
5985 {
5986 if ((*it)->i_getStorageBus() == aConnectionType)
5987 {
5988 ULONG ulCurInst = (*it)->i_getInstance();
5989
5990 if (ulCurInst >= ulInstance)
5991 ulInstance = ulCurInst + 1;
5992
5993 /* Only one controller of each type can be marked as bootable. */
5994 if ((*it)->i_getBootable())
5995 fBootable = false;
5996 }
5997 }
5998
5999 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6000 if (FAILED(rc)) return rc;
6001
6002 i_setModified(IsModified_Storage);
6003 mStorageControllers.backup();
6004 mStorageControllers->push_back(ctrl);
6005
6006 ctrl.queryInterfaceTo(aController.asOutParam());
6007
6008 /* inform the direct session if any */
6009 alock.release();
6010 i_onStorageControllerChange();
6011
6012 return S_OK;
6013}
6014
6015HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6016 ComPtr<IStorageController> &aStorageController)
6017{
6018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6019
6020 ComObjPtr<StorageController> ctrl;
6021
6022 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6023 if (SUCCEEDED(rc))
6024 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6025
6026 return rc;
6027}
6028
6029HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6030 ComPtr<IStorageController> &aStorageController)
6031{
6032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6033
6034 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6035 it != mStorageControllers->end();
6036 ++it)
6037 {
6038 if ((*it)->i_getInstance() == aInstance)
6039 {
6040 (*it).queryInterfaceTo(aStorageController.asOutParam());
6041 return S_OK;
6042 }
6043 }
6044
6045 return setError(VBOX_E_OBJECT_NOT_FOUND,
6046 tr("Could not find a storage controller with instance number '%lu'"),
6047 aInstance);
6048}
6049
6050HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6051{
6052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6053
6054 HRESULT rc = i_checkStateDependency(MutableStateDep);
6055 if (FAILED(rc)) return rc;
6056
6057 ComObjPtr<StorageController> ctrl;
6058
6059 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6060 if (SUCCEEDED(rc))
6061 {
6062 /* Ensure that only one controller of each type is marked as bootable. */
6063 if (aBootable == TRUE)
6064 {
6065 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6066 it != mStorageControllers->end();
6067 ++it)
6068 {
6069 ComObjPtr<StorageController> aCtrl = (*it);
6070
6071 if ( (aCtrl->i_getName() != aName)
6072 && aCtrl->i_getBootable() == TRUE
6073 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6074 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6075 {
6076 aCtrl->i_setBootable(FALSE);
6077 break;
6078 }
6079 }
6080 }
6081
6082 if (SUCCEEDED(rc))
6083 {
6084 ctrl->i_setBootable(aBootable);
6085 i_setModified(IsModified_Storage);
6086 }
6087 }
6088
6089 if (SUCCEEDED(rc))
6090 {
6091 /* inform the direct session if any */
6092 alock.release();
6093 i_onStorageControllerChange();
6094 }
6095
6096 return rc;
6097}
6098
6099HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6100{
6101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6102
6103 HRESULT rc = i_checkStateDependency(MutableStateDep);
6104 if (FAILED(rc)) return rc;
6105
6106 ComObjPtr<StorageController> ctrl;
6107 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6108 if (FAILED(rc)) return rc;
6109
6110 {
6111 /* find all attached devices to the appropriate storage controller and detach them all */
6112 // make a temporary list because detachDevice invalidates iterators into
6113 // mMediaData->mAttachments
6114 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6115
6116 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6117 it != llAttachments2.end();
6118 ++it)
6119 {
6120 MediumAttachment *pAttachTemp = *it;
6121
6122 AutoCaller localAutoCaller(pAttachTemp);
6123 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6124
6125 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6126
6127 if (pAttachTemp->i_getControllerName() == aName)
6128 {
6129 rc = i_detachDevice(pAttachTemp, alock, NULL);
6130 if (FAILED(rc)) return rc;
6131 }
6132 }
6133 }
6134
6135 /* We can remove it now. */
6136 i_setModified(IsModified_Storage);
6137 mStorageControllers.backup();
6138
6139 ctrl->i_unshare();
6140
6141 mStorageControllers->remove(ctrl);
6142
6143 /* inform the direct session if any */
6144 alock.release();
6145 i_onStorageControllerChange();
6146
6147 return S_OK;
6148}
6149
6150HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6151 ComPtr<IUSBController> &aController)
6152{
6153 if ( (aType <= USBControllerType_Null)
6154 || (aType >= USBControllerType_Last))
6155 return setError(E_INVALIDARG,
6156 tr("Invalid USB controller type: %d"),
6157 aType);
6158
6159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6160
6161 HRESULT rc = i_checkStateDependency(MutableStateDep);
6162 if (FAILED(rc)) return rc;
6163
6164 /* try to find one with the same type first. */
6165 ComObjPtr<USBController> ctrl;
6166
6167 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6168 if (SUCCEEDED(rc))
6169 return setError(VBOX_E_OBJECT_IN_USE,
6170 tr("USB controller named '%s' already exists"),
6171 aName.c_str());
6172
6173 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6174 ULONG maxInstances;
6175 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6176 if (FAILED(rc))
6177 return rc;
6178
6179 ULONG cInstances = i_getUSBControllerCountByType(aType);
6180 if (cInstances >= maxInstances)
6181 return setError(E_INVALIDARG,
6182 tr("Too many USB controllers of this type"));
6183
6184 ctrl.createObject();
6185
6186 rc = ctrl->init(this, aName, aType);
6187 if (FAILED(rc)) return rc;
6188
6189 i_setModified(IsModified_USB);
6190 mUSBControllers.backup();
6191 mUSBControllers->push_back(ctrl);
6192
6193 ctrl.queryInterfaceTo(aController.asOutParam());
6194
6195 /* inform the direct session if any */
6196 alock.release();
6197 i_onUSBControllerChange();
6198
6199 return S_OK;
6200}
6201
6202HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6203{
6204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6205
6206 ComObjPtr<USBController> ctrl;
6207
6208 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6209 if (SUCCEEDED(rc))
6210 ctrl.queryInterfaceTo(aController.asOutParam());
6211
6212 return rc;
6213}
6214
6215HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6216 ULONG *aControllers)
6217{
6218 if ( (aType <= USBControllerType_Null)
6219 || (aType >= USBControllerType_Last))
6220 return setError(E_INVALIDARG,
6221 tr("Invalid USB controller type: %d"),
6222 aType);
6223
6224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 ComObjPtr<USBController> ctrl;
6227
6228 *aControllers = i_getUSBControllerCountByType(aType);
6229
6230 return S_OK;
6231}
6232
6233HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6234{
6235
6236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6237
6238 HRESULT rc = i_checkStateDependency(MutableStateDep);
6239 if (FAILED(rc)) return rc;
6240
6241 ComObjPtr<USBController> ctrl;
6242 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6243 if (FAILED(rc)) return rc;
6244
6245 i_setModified(IsModified_USB);
6246 mUSBControllers.backup();
6247
6248 ctrl->i_unshare();
6249
6250 mUSBControllers->remove(ctrl);
6251
6252 /* inform the direct session if any */
6253 alock.release();
6254 i_onUSBControllerChange();
6255
6256 return S_OK;
6257}
6258
6259HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6260 ULONG *aOriginX,
6261 ULONG *aOriginY,
6262 ULONG *aWidth,
6263 ULONG *aHeight,
6264 BOOL *aEnabled)
6265{
6266 uint32_t u32OriginX= 0;
6267 uint32_t u32OriginY= 0;
6268 uint32_t u32Width = 0;
6269 uint32_t u32Height = 0;
6270 uint16_t u16Flags = 0;
6271
6272 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6273 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6274 if (RT_FAILURE(vrc))
6275 {
6276#ifdef RT_OS_WINDOWS
6277 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6278 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6279 * So just assign fEnable to TRUE again.
6280 * The right fix would be to change GUI API wrappers to make sure that parameters
6281 * are changed only if API succeeds.
6282 */
6283 *aEnabled = TRUE;
6284#endif
6285 return setError(VBOX_E_IPRT_ERROR,
6286 tr("Saved guest size is not available (%Rrc)"),
6287 vrc);
6288 }
6289
6290 *aOriginX = u32OriginX;
6291 *aOriginY = u32OriginY;
6292 *aWidth = u32Width;
6293 *aHeight = u32Height;
6294 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6295
6296 return S_OK;
6297}
6298
6299HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6300{
6301 if (aScreenId != 0)
6302 return E_NOTIMPL;
6303
6304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6305
6306 uint8_t *pu8Data = NULL;
6307 uint32_t cbData = 0;
6308 uint32_t u32Width = 0;
6309 uint32_t u32Height = 0;
6310
6311 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6312
6313 if (RT_FAILURE(vrc))
6314 return setError(VBOX_E_IPRT_ERROR,
6315 tr("Saved screenshot data is not available (%Rrc)"),
6316 vrc);
6317
6318 *aSize = cbData;
6319 *aWidth = u32Width;
6320 *aHeight = u32Height;
6321
6322 freeSavedDisplayScreenshot(pu8Data);
6323
6324 return S_OK;
6325}
6326
6327HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6328 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6329{
6330 if (aScreenId != 0)
6331 return E_NOTIMPL;
6332
6333 if ( aBitmapFormat != BitmapFormat_BGR0
6334 && aBitmapFormat != BitmapFormat_BGRA
6335 && aBitmapFormat != BitmapFormat_RGBA
6336 && aBitmapFormat != BitmapFormat_PNG)
6337 return setError(E_NOTIMPL,
6338 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6339
6340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6341
6342 uint8_t *pu8Data = NULL;
6343 uint32_t cbData = 0;
6344 uint32_t u32Width = 0;
6345 uint32_t u32Height = 0;
6346
6347 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6348
6349 if (RT_FAILURE(vrc))
6350 return setError(VBOX_E_IPRT_ERROR,
6351 tr("Saved thumbnail data is not available (%Rrc)"),
6352 vrc);
6353
6354 HRESULT hr = S_OK;
6355
6356 *aWidth = u32Width;
6357 *aHeight = u32Height;
6358
6359 if (cbData > 0)
6360 {
6361 /* Convert pixels to the format expected by the API caller. */
6362 if (aBitmapFormat == BitmapFormat_BGR0)
6363 {
6364 /* [0] B, [1] G, [2] R, [3] 0. */
6365 aData.resize(cbData);
6366 memcpy(&aData.front(), pu8Data, cbData);
6367 }
6368 else if (aBitmapFormat == BitmapFormat_BGRA)
6369 {
6370 /* [0] B, [1] G, [2] R, [3] A. */
6371 aData.resize(cbData);
6372 for (uint32_t i = 0; i < cbData; i += 4)
6373 {
6374 aData[i] = pu8Data[i];
6375 aData[i + 1] = pu8Data[i + 1];
6376 aData[i + 2] = pu8Data[i + 2];
6377 aData[i + 3] = 0xff;
6378 }
6379 }
6380 else if (aBitmapFormat == BitmapFormat_RGBA)
6381 {
6382 /* [0] R, [1] G, [2] B, [3] A. */
6383 aData.resize(cbData);
6384 for (uint32_t i = 0; i < cbData; i += 4)
6385 {
6386 aData[i] = pu8Data[i + 2];
6387 aData[i + 1] = pu8Data[i + 1];
6388 aData[i + 2] = pu8Data[i];
6389 aData[i + 3] = 0xff;
6390 }
6391 }
6392 else if (aBitmapFormat == BitmapFormat_PNG)
6393 {
6394 uint8_t *pu8PNG = NULL;
6395 uint32_t cbPNG = 0;
6396 uint32_t cxPNG = 0;
6397 uint32_t cyPNG = 0;
6398
6399 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6400
6401 if (RT_SUCCESS(vrc))
6402 {
6403 aData.resize(cbPNG);
6404 if (cbPNG)
6405 memcpy(&aData.front(), pu8PNG, cbPNG);
6406 }
6407 else
6408 hr = setError(VBOX_E_IPRT_ERROR,
6409 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6410 vrc);
6411
6412 RTMemFree(pu8PNG);
6413 }
6414 }
6415
6416 freeSavedDisplayScreenshot(pu8Data);
6417
6418 return hr;
6419}
6420
6421HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6422{
6423 if (aScreenId != 0)
6424 return E_NOTIMPL;
6425
6426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6427
6428 uint8_t *pu8Data = NULL;
6429 uint32_t cbData = 0;
6430 uint32_t u32Width = 0;
6431 uint32_t u32Height = 0;
6432
6433 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6434
6435 if (RT_FAILURE(vrc))
6436 return setError(VBOX_E_IPRT_ERROR,
6437 tr("Saved screenshot data is not available (%Rrc)"),
6438 vrc);
6439
6440 *aSize = cbData;
6441 *aWidth = u32Width;
6442 *aHeight = u32Height;
6443
6444 freeSavedDisplayScreenshot(pu8Data);
6445
6446 return S_OK;
6447}
6448
6449HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6450{
6451 if (aScreenId != 0)
6452 return E_NOTIMPL;
6453
6454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6455
6456 uint8_t *pu8Data = NULL;
6457 uint32_t cbData = 0;
6458 uint32_t u32Width = 0;
6459 uint32_t u32Height = 0;
6460
6461 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6462
6463 if (RT_FAILURE(vrc))
6464 return setError(VBOX_E_IPRT_ERROR,
6465 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6466 vrc);
6467
6468 *aWidth = u32Width;
6469 *aHeight = u32Height;
6470
6471 aData.resize(cbData);
6472 if (cbData)
6473 memcpy(&aData.front(), pu8Data, cbData);
6474
6475 freeSavedDisplayScreenshot(pu8Data);
6476
6477 return S_OK;
6478}
6479
6480HRESULT Machine::hotPlugCPU(ULONG aCpu)
6481{
6482 HRESULT rc = S_OK;
6483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6484
6485 if (!mHWData->mCPUHotPlugEnabled)
6486 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6487
6488 if (aCpu >= mHWData->mCPUCount)
6489 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6490
6491 if (mHWData->mCPUAttached[aCpu])
6492 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6493
6494 alock.release();
6495 rc = i_onCPUChange(aCpu, false);
6496 alock.acquire();
6497 if (FAILED(rc)) return rc;
6498
6499 i_setModified(IsModified_MachineData);
6500 mHWData.backup();
6501 mHWData->mCPUAttached[aCpu] = true;
6502
6503 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6504 if (Global::IsOnline(mData->mMachineState))
6505 i_saveSettings(NULL);
6506
6507 return S_OK;
6508}
6509
6510HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6511{
6512 HRESULT rc = S_OK;
6513
6514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6515
6516 if (!mHWData->mCPUHotPlugEnabled)
6517 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6518
6519 if (aCpu >= SchemaDefs::MaxCPUCount)
6520 return setError(E_INVALIDARG,
6521 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6522 SchemaDefs::MaxCPUCount);
6523
6524 if (!mHWData->mCPUAttached[aCpu])
6525 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6526
6527 /* CPU 0 can't be detached */
6528 if (aCpu == 0)
6529 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6530
6531 alock.release();
6532 rc = i_onCPUChange(aCpu, true);
6533 alock.acquire();
6534 if (FAILED(rc)) return rc;
6535
6536 i_setModified(IsModified_MachineData);
6537 mHWData.backup();
6538 mHWData->mCPUAttached[aCpu] = false;
6539
6540 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6541 if (Global::IsOnline(mData->mMachineState))
6542 i_saveSettings(NULL);
6543
6544 return S_OK;
6545}
6546
6547HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6548{
6549 *aAttached = false;
6550
6551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6552
6553 /* If hotplug is enabled the CPU is always enabled. */
6554 if (!mHWData->mCPUHotPlugEnabled)
6555 {
6556 if (aCpu < mHWData->mCPUCount)
6557 *aAttached = true;
6558 }
6559 else
6560 {
6561 if (aCpu < SchemaDefs::MaxCPUCount)
6562 *aAttached = mHWData->mCPUAttached[aCpu];
6563 }
6564
6565 return S_OK;
6566}
6567
6568HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6569{
6570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6571
6572 Utf8Str log = i_queryLogFilename(aIdx);
6573 if (!RTFileExists(log.c_str()))
6574 log.setNull();
6575 aFilename = log;
6576
6577 return S_OK;
6578}
6579
6580HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6581{
6582 if (aSize < 0)
6583 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6584
6585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6586
6587 HRESULT rc = S_OK;
6588 Utf8Str log = i_queryLogFilename(aIdx);
6589
6590 /* do not unnecessarily hold the lock while doing something which does
6591 * not need the lock and potentially takes a long time. */
6592 alock.release();
6593
6594 /* Limit the chunk size to 32K for now, as that gives better performance
6595 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6596 * One byte expands to approx. 25 bytes of breathtaking XML. */
6597 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6598 aData.resize(cbData);
6599
6600 RTFILE LogFile;
6601 int vrc = RTFileOpen(&LogFile, log.c_str(),
6602 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6603 if (RT_SUCCESS(vrc))
6604 {
6605 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6606 if (RT_SUCCESS(vrc))
6607 aData.resize(cbData);
6608 else
6609 rc = setError(VBOX_E_IPRT_ERROR,
6610 tr("Could not read log file '%s' (%Rrc)"),
6611 log.c_str(), vrc);
6612 RTFileClose(LogFile);
6613 }
6614 else
6615 rc = setError(VBOX_E_IPRT_ERROR,
6616 tr("Could not open log file '%s' (%Rrc)"),
6617 log.c_str(), vrc);
6618
6619 if (FAILED(rc))
6620 aData.resize(0);
6621
6622 return rc;
6623}
6624
6625
6626/**
6627 * Currently this method doesn't attach device to the running VM,
6628 * just makes sure it's plugged on next VM start.
6629 */
6630HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6631{
6632 // lock scope
6633 {
6634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6635
6636 HRESULT rc = i_checkStateDependency(MutableStateDep);
6637 if (FAILED(rc)) return rc;
6638
6639 ChipsetType_T aChipset = ChipsetType_PIIX3;
6640 COMGETTER(ChipsetType)(&aChipset);
6641
6642 if (aChipset != ChipsetType_ICH9)
6643 {
6644 return setError(E_INVALIDARG,
6645 tr("Host PCI attachment only supported with ICH9 chipset"));
6646 }
6647
6648 // check if device with this host PCI address already attached
6649 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6650 it != mHWData->mPCIDeviceAssignments.end();
6651 ++it)
6652 {
6653 LONG iHostAddress = -1;
6654 ComPtr<PCIDeviceAttachment> pAttach;
6655 pAttach = *it;
6656 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6657 if (iHostAddress == aHostAddress)
6658 return setError(E_INVALIDARG,
6659 tr("Device with host PCI address already attached to this VM"));
6660 }
6661
6662 ComObjPtr<PCIDeviceAttachment> pda;
6663 char name[32];
6664
6665 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6666 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6667 Bstr bname(name);
6668 pda.createObject();
6669 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6670 i_setModified(IsModified_MachineData);
6671 mHWData.backup();
6672 mHWData->mPCIDeviceAssignments.push_back(pda);
6673 }
6674
6675 return S_OK;
6676}
6677
6678/**
6679 * Currently this method doesn't detach device from the running VM,
6680 * just makes sure it's not plugged on next VM start.
6681 */
6682HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6683{
6684 ComObjPtr<PCIDeviceAttachment> pAttach;
6685 bool fRemoved = false;
6686 HRESULT rc;
6687
6688 // lock scope
6689 {
6690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6691
6692 rc = i_checkStateDependency(MutableStateDep);
6693 if (FAILED(rc)) return rc;
6694
6695 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6696 it != mHWData->mPCIDeviceAssignments.end();
6697 ++it)
6698 {
6699 LONG iHostAddress = -1;
6700 pAttach = *it;
6701 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6702 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6703 {
6704 i_setModified(IsModified_MachineData);
6705 mHWData.backup();
6706 mHWData->mPCIDeviceAssignments.remove(pAttach);
6707 fRemoved = true;
6708 break;
6709 }
6710 }
6711 }
6712
6713
6714 /* Fire event outside of the lock */
6715 if (fRemoved)
6716 {
6717 Assert(!pAttach.isNull());
6718 ComPtr<IEventSource> es;
6719 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6720 Assert(SUCCEEDED(rc));
6721 Bstr mid;
6722 rc = this->COMGETTER(Id)(mid.asOutParam());
6723 Assert(SUCCEEDED(rc));
6724 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6725 }
6726
6727 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6728 tr("No host PCI device %08x attached"),
6729 aHostAddress
6730 );
6731}
6732
6733HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6734{
6735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6736
6737 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6738
6739 size_t i = 0;
6740 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6741 it != mHWData->mPCIDeviceAssignments.end();
6742 ++i, ++it)
6743 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6744
6745 return S_OK;
6746}
6747
6748HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6749{
6750 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6751
6752 return S_OK;
6753}
6754
6755HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6756{
6757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6758
6759 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6760
6761 return S_OK;
6762}
6763
6764HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6765{
6766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6767 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6768 if (SUCCEEDED(hrc))
6769 {
6770 hrc = mHWData.backupEx();
6771 if (SUCCEEDED(hrc))
6772 {
6773 i_setModified(IsModified_MachineData);
6774 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6775 }
6776 }
6777 return hrc;
6778}
6779
6780HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6781{
6782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6783 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6784 return S_OK;
6785}
6786
6787HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6788{
6789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6790 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6791 if (SUCCEEDED(hrc))
6792 {
6793 hrc = mHWData.backupEx();
6794 if (SUCCEEDED(hrc))
6795 {
6796 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6797 if (SUCCEEDED(hrc))
6798 i_setModified(IsModified_MachineData);
6799 }
6800 }
6801 return hrc;
6802}
6803
6804HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6805{
6806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6807
6808 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6809
6810 return S_OK;
6811}
6812
6813HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6814{
6815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6816 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6817 if (SUCCEEDED(hrc))
6818 {
6819 hrc = mHWData.backupEx();
6820 if (SUCCEEDED(hrc))
6821 {
6822 i_setModified(IsModified_MachineData);
6823 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6824 }
6825 }
6826 return hrc;
6827}
6828
6829HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6830{
6831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6832
6833 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6834
6835 return S_OK;
6836}
6837
6838HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6839{
6840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6841
6842 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6843 if ( SUCCEEDED(hrc)
6844 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6845 {
6846 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6847 int vrc;
6848
6849 if (aAutostartEnabled)
6850 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6851 else
6852 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6853
6854 if (RT_SUCCESS(vrc))
6855 {
6856 hrc = mHWData.backupEx();
6857 if (SUCCEEDED(hrc))
6858 {
6859 i_setModified(IsModified_MachineData);
6860 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6861 }
6862 }
6863 else if (vrc == VERR_NOT_SUPPORTED)
6864 hrc = setError(VBOX_E_NOT_SUPPORTED,
6865 tr("The VM autostart feature is not supported on this platform"));
6866 else if (vrc == VERR_PATH_NOT_FOUND)
6867 hrc = setError(E_FAIL,
6868 tr("The path to the autostart database is not set"));
6869 else
6870 hrc = setError(E_UNEXPECTED,
6871 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6872 aAutostartEnabled ? "Adding" : "Removing",
6873 mUserData->s.strName.c_str(), vrc);
6874 }
6875 return hrc;
6876}
6877
6878HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6879{
6880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6881
6882 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6883
6884 return S_OK;
6885}
6886
6887HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6888{
6889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6890 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6891 if (SUCCEEDED(hrc))
6892 {
6893 hrc = mHWData.backupEx();
6894 if (SUCCEEDED(hrc))
6895 {
6896 i_setModified(IsModified_MachineData);
6897 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6898 }
6899 }
6900 return hrc;
6901}
6902
6903HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6904{
6905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6906
6907 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6908
6909 return S_OK;
6910}
6911
6912HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6913{
6914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6915 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6916 if ( SUCCEEDED(hrc)
6917 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6918 {
6919 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6920 int vrc;
6921
6922 if (aAutostopType != AutostopType_Disabled)
6923 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6924 else
6925 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6926
6927 if (RT_SUCCESS(vrc))
6928 {
6929 hrc = mHWData.backupEx();
6930 if (SUCCEEDED(hrc))
6931 {
6932 i_setModified(IsModified_MachineData);
6933 mHWData->mAutostart.enmAutostopType = aAutostopType;
6934 }
6935 }
6936 else if (vrc == VERR_NOT_SUPPORTED)
6937 hrc = setError(VBOX_E_NOT_SUPPORTED,
6938 tr("The VM autostop feature is not supported on this platform"));
6939 else if (vrc == VERR_PATH_NOT_FOUND)
6940 hrc = setError(E_FAIL,
6941 tr("The path to the autostart database is not set"));
6942 else
6943 hrc = setError(E_UNEXPECTED,
6944 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6945 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6946 mUserData->s.strName.c_str(), vrc);
6947 }
6948 return hrc;
6949}
6950
6951HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6952{
6953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6954
6955 aDefaultFrontend = mHWData->mDefaultFrontend;
6956
6957 return S_OK;
6958}
6959
6960HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6961{
6962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6963 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6964 if (SUCCEEDED(hrc))
6965 {
6966 hrc = mHWData.backupEx();
6967 if (SUCCEEDED(hrc))
6968 {
6969 i_setModified(IsModified_MachineData);
6970 mHWData->mDefaultFrontend = aDefaultFrontend;
6971 }
6972 }
6973 return hrc;
6974}
6975
6976HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6977{
6978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6979 size_t cbIcon = mUserData->mIcon.size();
6980 aIcon.resize(cbIcon);
6981 if (cbIcon)
6982 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
6983 return S_OK;
6984}
6985
6986HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6987{
6988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6989 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6990 if (SUCCEEDED(hrc))
6991 {
6992 i_setModified(IsModified_MachineData);
6993 mUserData.backup();
6994 size_t cbIcon = aIcon.size();
6995 mUserData->mIcon.resize(cbIcon);
6996 if (cbIcon)
6997 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
6998 }
6999 return hrc;
7000}
7001
7002HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7003{
7004#ifdef VBOX_WITH_USB
7005 *aUSBProxyAvailable = true;
7006#else
7007 *aUSBProxyAvailable = false;
7008#endif
7009 return S_OK;
7010}
7011
7012HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7013 ComPtr<IProgress> &aProgress)
7014{
7015 ComObjPtr<Progress> pP;
7016 Progress *ppP = pP;
7017 IProgress *iP = static_cast<IProgress *>(ppP);
7018 IProgress **pProgress = &iP;
7019
7020 IMachine *pTarget = aTarget;
7021
7022 /* Convert the options. */
7023 RTCList<CloneOptions_T> optList;
7024 if (aOptions.size())
7025 for (size_t i = 0; i < aOptions.size(); ++i)
7026 optList.append(aOptions[i]);
7027
7028 if (optList.contains(CloneOptions_Link))
7029 {
7030 if (!i_isSnapshotMachine())
7031 return setError(E_INVALIDARG,
7032 tr("Linked clone can only be created from a snapshot"));
7033 if (aMode != CloneMode_MachineState)
7034 return setError(E_INVALIDARG,
7035 tr("Linked clone can only be created for a single machine state"));
7036 }
7037 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7038
7039 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7040
7041 HRESULT rc = pWorker->start(pProgress);
7042
7043 pP = static_cast<Progress *>(*pProgress);
7044 pP.queryInterfaceTo(aProgress.asOutParam());
7045
7046 return rc;
7047
7048}
7049
7050// public methods for internal purposes
7051/////////////////////////////////////////////////////////////////////////////
7052
7053/**
7054 * Adds the given IsModified_* flag to the dirty flags of the machine.
7055 * This must be called either during i_loadSettings or under the machine write lock.
7056 * @param fl
7057 */
7058void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7059{
7060 mData->flModifications |= fl;
7061 if (fAllowStateModification && i_isStateModificationAllowed())
7062 mData->mCurrentStateModified = true;
7063}
7064
7065/**
7066 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7067 * care of the write locking.
7068 *
7069 * @param fModifications The flag to add.
7070 */
7071void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7072{
7073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7074 i_setModified(fModification, fAllowStateModification);
7075}
7076
7077/**
7078 * Saves the registry entry of this machine to the given configuration node.
7079 *
7080 * @param aEntryNode Node to save the registry entry to.
7081 *
7082 * @note locks this object for reading.
7083 */
7084HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7085{
7086 AutoLimitedCaller autoCaller(this);
7087 AssertComRCReturnRC(autoCaller.rc());
7088
7089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7090
7091 data.uuid = mData->mUuid;
7092 data.strSettingsFile = mData->m_strConfigFile;
7093
7094 return S_OK;
7095}
7096
7097/**
7098 * Calculates the absolute path of the given path taking the directory of the
7099 * machine settings file as the current directory.
7100 *
7101 * @param aPath Path to calculate the absolute path for.
7102 * @param aResult Where to put the result (used only on success, can be the
7103 * same Utf8Str instance as passed in @a aPath).
7104 * @return IPRT result.
7105 *
7106 * @note Locks this object for reading.
7107 */
7108int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7109{
7110 AutoCaller autoCaller(this);
7111 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7112
7113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7114
7115 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7116
7117 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7118
7119 strSettingsDir.stripFilename();
7120 char folder[RTPATH_MAX];
7121 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7122 if (RT_SUCCESS(vrc))
7123 aResult = folder;
7124
7125 return vrc;
7126}
7127
7128/**
7129 * Copies strSource to strTarget, making it relative to the machine folder
7130 * if it is a subdirectory thereof, or simply copying it otherwise.
7131 *
7132 * @param strSource Path to evaluate and copy.
7133 * @param strTarget Buffer to receive target path.
7134 *
7135 * @note Locks this object for reading.
7136 */
7137void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7138 Utf8Str &strTarget)
7139{
7140 AutoCaller autoCaller(this);
7141 AssertComRCReturn(autoCaller.rc(), (void)0);
7142
7143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7144
7145 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7146 // use strTarget as a temporary buffer to hold the machine settings dir
7147 strTarget = mData->m_strConfigFileFull;
7148 strTarget.stripFilename();
7149 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7150 {
7151 // is relative: then append what's left
7152 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7153 // for empty paths (only possible for subdirs) use "." to avoid
7154 // triggering default settings for not present config attributes.
7155 if (strTarget.isEmpty())
7156 strTarget = ".";
7157 }
7158 else
7159 // is not relative: then overwrite
7160 strTarget = strSource;
7161}
7162
7163/**
7164 * Returns the full path to the machine's log folder in the
7165 * \a aLogFolder argument.
7166 */
7167void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7168{
7169 AutoCaller autoCaller(this);
7170 AssertComRCReturnVoid(autoCaller.rc());
7171
7172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7173
7174 char szTmp[RTPATH_MAX];
7175 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7176 if (RT_SUCCESS(vrc))
7177 {
7178 if (szTmp[0] && !mUserData.isNull())
7179 {
7180 char szTmp2[RTPATH_MAX];
7181 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7182 if (RT_SUCCESS(vrc))
7183 aLogFolder = BstrFmt("%s%c%s",
7184 szTmp2,
7185 RTPATH_DELIMITER,
7186 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7187 }
7188 else
7189 vrc = VERR_PATH_IS_RELATIVE;
7190 }
7191
7192 if (RT_FAILURE(vrc))
7193 {
7194 // fallback if VBOX_USER_LOGHOME is not set or invalid
7195 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7196 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7197 aLogFolder.append(RTPATH_DELIMITER);
7198 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7199 }
7200}
7201
7202/**
7203 * Returns the full path to the machine's log file for an given index.
7204 */
7205Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7206 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7207{
7208 Utf8Str logFolder;
7209 getLogFolder(logFolder);
7210 Assert(logFolder.length());
7211 Utf8Str log;
7212 if (idx == 0)
7213 log = Utf8StrFmt("%s%cVBox.log",
7214 logFolder.c_str(), RTPATH_DELIMITER);
7215 else
7216 log = Utf8StrFmt("%s%cVBox.log.%d",
7217 logFolder.c_str(), RTPATH_DELIMITER, idx);
7218 return log;
7219}
7220
7221/**
7222 * Returns the full path to the machine's (hardened) startup log file.
7223 */
7224Utf8Str Machine::i_getStartupLogFilename(void)
7225{
7226 Utf8Str strFilename;
7227 getLogFolder(strFilename);
7228 Assert(strFilename.length());
7229 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7230 return strFilename;
7231}
7232
7233
7234/**
7235 * Composes a unique saved state filename based on the current system time. The filename is
7236 * granular to the second so this will work so long as no more than one snapshot is taken on
7237 * a machine per second.
7238 *
7239 * Before version 4.1, we used this formula for saved state files:
7240 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7241 * which no longer works because saved state files can now be shared between the saved state of the
7242 * "saved" machine and an online snapshot, and the following would cause problems:
7243 * 1) save machine
7244 * 2) create online snapshot from that machine state --> reusing saved state file
7245 * 3) save machine again --> filename would be reused, breaking the online snapshot
7246 *
7247 * So instead we now use a timestamp.
7248 *
7249 * @param str
7250 */
7251
7252void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7253{
7254 AutoCaller autoCaller(this);
7255 AssertComRCReturnVoid(autoCaller.rc());
7256
7257 {
7258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7259 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7260 }
7261
7262 RTTIMESPEC ts;
7263 RTTimeNow(&ts);
7264 RTTIME time;
7265 RTTimeExplode(&time, &ts);
7266
7267 strStateFilePath += RTPATH_DELIMITER;
7268 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7269 time.i32Year, time.u8Month, time.u8MonthDay,
7270 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7271}
7272
7273/**
7274 * Returns the full path to the default video capture file.
7275 */
7276void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7277{
7278 AutoCaller autoCaller(this);
7279 AssertComRCReturnVoid(autoCaller.rc());
7280
7281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7282
7283 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7284 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7285 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7286}
7287
7288/**
7289 * Returns whether at least one USB controller is present for the VM.
7290 */
7291bool Machine::i_isUSBControllerPresent()
7292{
7293 AutoCaller autoCaller(this);
7294 AssertComRCReturn(autoCaller.rc(), false);
7295
7296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7297
7298 return (mUSBControllers->size() > 0);
7299}
7300
7301/**
7302 * @note Locks this object for writing, calls the client process
7303 * (inside the lock).
7304 */
7305HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7306 const Utf8Str &strFrontend,
7307 const Utf8Str &strEnvironment,
7308 ProgressProxy *aProgress)
7309{
7310 LogFlowThisFuncEnter();
7311
7312 AssertReturn(aControl, E_FAIL);
7313 AssertReturn(aProgress, E_FAIL);
7314 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7315
7316 AutoCaller autoCaller(this);
7317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7318
7319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7320
7321 if (!mData->mRegistered)
7322 return setError(E_UNEXPECTED,
7323 tr("The machine '%s' is not registered"),
7324 mUserData->s.strName.c_str());
7325
7326 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7327
7328 if ( mData->mSession.mState == SessionState_Locked
7329 || mData->mSession.mState == SessionState_Spawning
7330 || mData->mSession.mState == SessionState_Unlocking)
7331 return setError(VBOX_E_INVALID_OBJECT_STATE,
7332 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7333 mUserData->s.strName.c_str());
7334
7335 /* may not be busy */
7336 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7337
7338 /* get the path to the executable */
7339 char szPath[RTPATH_MAX];
7340 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7341 size_t cchBufLeft = strlen(szPath);
7342 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7343 szPath[cchBufLeft] = 0;
7344 char *pszNamePart = szPath + cchBufLeft;
7345 cchBufLeft = sizeof(szPath) - cchBufLeft;
7346
7347 int vrc = VINF_SUCCESS;
7348 RTPROCESS pid = NIL_RTPROCESS;
7349
7350 RTENV env = RTENV_DEFAULT;
7351
7352 if (!strEnvironment.isEmpty())
7353 {
7354 char *newEnvStr = NULL;
7355
7356 do
7357 {
7358 /* clone the current environment */
7359 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7360 AssertRCBreakStmt(vrc2, vrc = vrc2);
7361
7362 newEnvStr = RTStrDup(strEnvironment.c_str());
7363 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7364
7365 /* put new variables to the environment
7366 * (ignore empty variable names here since RTEnv API
7367 * intentionally doesn't do that) */
7368 char *var = newEnvStr;
7369 for (char *p = newEnvStr; *p; ++p)
7370 {
7371 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7372 {
7373 *p = '\0';
7374 if (*var)
7375 {
7376 char *val = strchr(var, '=');
7377 if (val)
7378 {
7379 *val++ = '\0';
7380 vrc2 = RTEnvSetEx(env, var, val);
7381 }
7382 else
7383 vrc2 = RTEnvUnsetEx(env, var);
7384 if (RT_FAILURE(vrc2))
7385 break;
7386 }
7387 var = p + 1;
7388 }
7389 }
7390 if (RT_SUCCESS(vrc2) && *var)
7391 vrc2 = RTEnvPutEx(env, var);
7392
7393 AssertRCBreakStmt(vrc2, vrc = vrc2);
7394 }
7395 while (0);
7396
7397 if (newEnvStr != NULL)
7398 RTStrFree(newEnvStr);
7399 }
7400
7401 /* Hardened startup logging */
7402#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7403 Utf8Str strSupStartLogArg("--sup-startup-log=");
7404 {
7405 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7406 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7407 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7408 {
7409 Utf8Str strStartupLogDir = strStartupLogFile;
7410 strStartupLogDir.stripFilename();
7411 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7412 file without stripping the file. */
7413 }
7414 strSupStartLogArg.append(strStartupLogFile);
7415 }
7416 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7417#else
7418 const char *pszSupStartupLogArg = NULL;
7419#endif
7420
7421
7422#ifdef VBOX_WITH_QTGUI
7423 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7424 {
7425# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7426 /* Modify the base path so that we don't need to use ".." below. */
7427 RTPathStripTrailingSlash(szPath);
7428 RTPathStripFilename(szPath);
7429 cchBufLeft = strlen(szPath);
7430 pszNamePart = szPath + cchBufLeft;
7431 cchBufLeft = sizeof(szPath) - cchBufLeft;
7432
7433# define OSX_APP_NAME "VirtualBoxVM"
7434# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7435
7436 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7437 if ( strAppOverride.contains(".")
7438 || strAppOverride.contains("/")
7439 || strAppOverride.contains("\\")
7440 || strAppOverride.contains(":"))
7441 strAppOverride.setNull();
7442 Utf8Str strAppPath;
7443 if (!strAppOverride.isEmpty())
7444 {
7445 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7446 Utf8Str strFullPath(szPath);
7447 strFullPath.append(strAppPath);
7448 /* there is a race, but people using this deserve the failure */
7449 if (!RTFileExists(strFullPath.c_str()))
7450 strAppOverride.setNull();
7451 }
7452 if (strAppOverride.isEmpty())
7453 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7454 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7455 strcpy(pszNamePart, strAppPath.c_str());
7456# else
7457 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7458 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7459 strcpy(pszNamePart, s_szVirtualBox_exe);
7460# endif
7461
7462 Utf8Str idStr = mData->mUuid.toString();
7463 const char *apszArgs[] =
7464 {
7465 szPath,
7466 "--comment", mUserData->s.strName.c_str(),
7467 "--startvm", idStr.c_str(),
7468 "--no-startvm-errormsgbox",
7469 pszSupStartupLogArg,
7470 NULL
7471 };
7472 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7473 }
7474#else /* !VBOX_WITH_QTGUI */
7475 if (0)
7476 ;
7477#endif /* VBOX_WITH_QTGUI */
7478
7479 else
7480
7481#ifdef VBOX_WITH_VBOXSDL
7482 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7483 {
7484 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7485 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7486 strcpy(pszNamePart, s_szVBoxSDL_exe);
7487
7488 Utf8Str idStr = mData->mUuid.toString();
7489 const char *apszArgs[] =
7490 {
7491 szPath,
7492 "--comment", mUserData->s.strName.c_str(),
7493 "--startvm", idStr.c_str(),
7494 pszSupStartupLogArg,
7495 NULL
7496 };
7497 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7498 }
7499#else /* !VBOX_WITH_VBOXSDL */
7500 if (0)
7501 ;
7502#endif /* !VBOX_WITH_VBOXSDL */
7503
7504 else
7505
7506#ifdef VBOX_WITH_HEADLESS
7507 if ( strFrontend == "headless"
7508 || strFrontend == "capture"
7509 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7510 )
7511 {
7512 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7513 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7514 * and a VM works even if the server has not been installed.
7515 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7516 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7517 * differently in 4.0 and 3.x.
7518 */
7519 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7520 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7521 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7522
7523 Utf8Str idStr = mData->mUuid.toString();
7524 const char *apszArgs[] =
7525 {
7526 szPath,
7527 "--comment", mUserData->s.strName.c_str(),
7528 "--startvm", idStr.c_str(),
7529 "--vrde", "config",
7530 0, /* For "--capture". */
7531 0, /* For "--sup-startup-log". */
7532 0
7533 };
7534 unsigned iArg = 7;
7535 if (strFrontend == "capture")
7536 apszArgs[iArg++] = "--capture";
7537 apszArgs[iArg++] = pszSupStartupLogArg;
7538
7539# ifdef RT_OS_WINDOWS
7540 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7541# else
7542 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7543# endif
7544 }
7545#else /* !VBOX_WITH_HEADLESS */
7546 if (0)
7547 ;
7548#endif /* !VBOX_WITH_HEADLESS */
7549 else
7550 {
7551 RTEnvDestroy(env);
7552 return setError(E_INVALIDARG,
7553 tr("Invalid frontend name: '%s'"),
7554 strFrontend.c_str());
7555 }
7556
7557 RTEnvDestroy(env);
7558
7559 if (RT_FAILURE(vrc))
7560 return setError(VBOX_E_IPRT_ERROR,
7561 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7562 mUserData->s.strName.c_str(), vrc);
7563
7564 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7565
7566 /*
7567 * Note that we don't release the lock here before calling the client,
7568 * because it doesn't need to call us back if called with a NULL argument.
7569 * Releasing the lock here is dangerous because we didn't prepare the
7570 * launch data yet, but the client we've just started may happen to be
7571 * too fast and call LockMachine() that will fail (because of PID, etc.),
7572 * so that the Machine will never get out of the Spawning session state.
7573 */
7574
7575 /* inform the session that it will be a remote one */
7576 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7577#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7578 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7579#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7580 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7581#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7582 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7583
7584 if (FAILED(rc))
7585 {
7586 /* restore the session state */
7587 mData->mSession.mState = SessionState_Unlocked;
7588 alock.release();
7589 mParent->i_addProcessToReap(pid);
7590 /* The failure may occur w/o any error info (from RPC), so provide one */
7591 return setError(VBOX_E_VM_ERROR,
7592 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7593 }
7594
7595 /* attach launch data to the machine */
7596 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7597 mData->mSession.mRemoteControls.push_back(aControl);
7598 mData->mSession.mProgress = aProgress;
7599 mData->mSession.mPID = pid;
7600 mData->mSession.mState = SessionState_Spawning;
7601 mData->mSession.mType = strFrontend;
7602
7603 alock.release();
7604 mParent->i_addProcessToReap(pid);
7605
7606 LogFlowThisFuncLeave();
7607 return S_OK;
7608}
7609
7610/**
7611 * Returns @c true if the given session machine instance has an open direct
7612 * session (and optionally also for direct sessions which are closing) and
7613 * returns the session control machine instance if so.
7614 *
7615 * Note that when the method returns @c false, the arguments remain unchanged.
7616 *
7617 * @param aMachine Session machine object.
7618 * @param aControl Direct session control object (optional).
7619 * @param aAllowClosing If true then additionally a session which is currently
7620 * being closed will also be allowed.
7621 *
7622 * @note locks this object for reading.
7623 */
7624bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7625 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7626 bool aAllowClosing /*= false*/)
7627{
7628 AutoLimitedCaller autoCaller(this);
7629 AssertComRCReturn(autoCaller.rc(), false);
7630
7631 /* just return false for inaccessible machines */
7632 if (getObjectState().getState() != ObjectState::Ready)
7633 return false;
7634
7635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7636
7637 if ( mData->mSession.mState == SessionState_Locked
7638 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7639 )
7640 {
7641 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7642
7643 aMachine = mData->mSession.mMachine;
7644
7645 if (aControl != NULL)
7646 *aControl = mData->mSession.mDirectControl;
7647
7648 return true;
7649 }
7650
7651 return false;
7652}
7653
7654/**
7655 * Returns @c true if the given machine has an spawning direct session.
7656 *
7657 * @note locks this object for reading.
7658 */
7659bool Machine::i_isSessionSpawning()
7660{
7661 AutoLimitedCaller autoCaller(this);
7662 AssertComRCReturn(autoCaller.rc(), false);
7663
7664 /* just return false for inaccessible machines */
7665 if (getObjectState().getState() != ObjectState::Ready)
7666 return false;
7667
7668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7669
7670 if (mData->mSession.mState == SessionState_Spawning)
7671 return true;
7672
7673 return false;
7674}
7675
7676/**
7677 * Called from the client watcher thread to check for unexpected client process
7678 * death during Session_Spawning state (e.g. before it successfully opened a
7679 * direct session).
7680 *
7681 * On Win32 and on OS/2, this method is called only when we've got the
7682 * direct client's process termination notification, so it always returns @c
7683 * true.
7684 *
7685 * On other platforms, this method returns @c true if the client process is
7686 * terminated and @c false if it's still alive.
7687 *
7688 * @note Locks this object for writing.
7689 */
7690bool Machine::i_checkForSpawnFailure()
7691{
7692 AutoCaller autoCaller(this);
7693 if (!autoCaller.isOk())
7694 {
7695 /* nothing to do */
7696 LogFlowThisFunc(("Already uninitialized!\n"));
7697 return true;
7698 }
7699
7700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7701
7702 if (mData->mSession.mState != SessionState_Spawning)
7703 {
7704 /* nothing to do */
7705 LogFlowThisFunc(("Not spawning any more!\n"));
7706 return true;
7707 }
7708
7709 HRESULT rc = S_OK;
7710
7711 /* PID not yet initialized, skip check. */
7712 if (mData->mSession.mPID == NIL_RTPROCESS)
7713 return false;
7714
7715 RTPROCSTATUS status;
7716 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7717
7718 if (vrc != VERR_PROCESS_RUNNING)
7719 {
7720 Utf8Str strExtraInfo;
7721
7722#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7723 /* If the startup logfile exists and is of non-zero length, tell the
7724 user to look there for more details to encourage them to attach it
7725 when reporting startup issues. */
7726 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7727 uint64_t cbStartupLogFile = 0;
7728 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7729 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7730 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7731#endif
7732
7733 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7734 rc = setError(E_FAIL,
7735 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7736 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7737 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7738 rc = setError(E_FAIL,
7739 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7740 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7741 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7742 rc = setError(E_FAIL,
7743 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7744 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7745 else
7746 rc = setError(E_FAIL,
7747 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7748 i_getName().c_str(), vrc, strExtraInfo.c_str());
7749 }
7750
7751 if (FAILED(rc))
7752 {
7753 /* Close the remote session, remove the remote control from the list
7754 * and reset session state to Closed (@note keep the code in sync with
7755 * the relevant part in LockMachine()). */
7756
7757 Assert(mData->mSession.mRemoteControls.size() == 1);
7758 if (mData->mSession.mRemoteControls.size() == 1)
7759 {
7760 ErrorInfoKeeper eik;
7761 mData->mSession.mRemoteControls.front()->Uninitialize();
7762 }
7763
7764 mData->mSession.mRemoteControls.clear();
7765 mData->mSession.mState = SessionState_Unlocked;
7766
7767 /* finalize the progress after setting the state */
7768 if (!mData->mSession.mProgress.isNull())
7769 {
7770 mData->mSession.mProgress->notifyComplete(rc);
7771 mData->mSession.mProgress.setNull();
7772 }
7773
7774 mData->mSession.mPID = NIL_RTPROCESS;
7775
7776 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7777 return true;
7778 }
7779
7780 return false;
7781}
7782
7783/**
7784 * Checks whether the machine can be registered. If so, commits and saves
7785 * all settings.
7786 *
7787 * @note Must be called from mParent's write lock. Locks this object and
7788 * children for writing.
7789 */
7790HRESULT Machine::i_prepareRegister()
7791{
7792 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7793
7794 AutoLimitedCaller autoCaller(this);
7795 AssertComRCReturnRC(autoCaller.rc());
7796
7797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7798
7799 /* wait for state dependents to drop to zero */
7800 i_ensureNoStateDependencies();
7801
7802 if (!mData->mAccessible)
7803 return setError(VBOX_E_INVALID_OBJECT_STATE,
7804 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7805 mUserData->s.strName.c_str(),
7806 mData->mUuid.toString().c_str());
7807
7808 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7809
7810 if (mData->mRegistered)
7811 return setError(VBOX_E_INVALID_OBJECT_STATE,
7812 tr("The machine '%s' with UUID {%s} is already registered"),
7813 mUserData->s.strName.c_str(),
7814 mData->mUuid.toString().c_str());
7815
7816 HRESULT rc = S_OK;
7817
7818 // Ensure the settings are saved. If we are going to be registered and
7819 // no config file exists yet, create it by calling i_saveSettings() too.
7820 if ( (mData->flModifications)
7821 || (!mData->pMachineConfigFile->fileExists())
7822 )
7823 {
7824 rc = i_saveSettings(NULL);
7825 // no need to check whether VirtualBox.xml needs saving too since
7826 // we can't have a machine XML file rename pending
7827 if (FAILED(rc)) return rc;
7828 }
7829
7830 /* more config checking goes here */
7831
7832 if (SUCCEEDED(rc))
7833 {
7834 /* we may have had implicit modifications we want to fix on success */
7835 i_commit();
7836
7837 mData->mRegistered = true;
7838 }
7839 else
7840 {
7841 /* we may have had implicit modifications we want to cancel on failure*/
7842 i_rollback(false /* aNotify */);
7843 }
7844
7845 return rc;
7846}
7847
7848/**
7849 * Increases the number of objects dependent on the machine state or on the
7850 * registered state. Guarantees that these two states will not change at least
7851 * until #releaseStateDependency() is called.
7852 *
7853 * Depending on the @a aDepType value, additional state checks may be made.
7854 * These checks will set extended error info on failure. See
7855 * #checkStateDependency() for more info.
7856 *
7857 * If this method returns a failure, the dependency is not added and the caller
7858 * is not allowed to rely on any particular machine state or registration state
7859 * value and may return the failed result code to the upper level.
7860 *
7861 * @param aDepType Dependency type to add.
7862 * @param aState Current machine state (NULL if not interested).
7863 * @param aRegistered Current registered state (NULL if not interested).
7864 *
7865 * @note Locks this object for writing.
7866 */
7867HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7868 MachineState_T *aState /* = NULL */,
7869 BOOL *aRegistered /* = NULL */)
7870{
7871 AutoCaller autoCaller(this);
7872 AssertComRCReturnRC(autoCaller.rc());
7873
7874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7875
7876 HRESULT rc = i_checkStateDependency(aDepType);
7877 if (FAILED(rc)) return rc;
7878
7879 {
7880 if (mData->mMachineStateChangePending != 0)
7881 {
7882 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7883 * drop to zero so don't add more. It may make sense to wait a bit
7884 * and retry before reporting an error (since the pending state
7885 * transition should be really quick) but let's just assert for
7886 * now to see if it ever happens on practice. */
7887
7888 AssertFailed();
7889
7890 return setError(E_ACCESSDENIED,
7891 tr("Machine state change is in progress. Please retry the operation later."));
7892 }
7893
7894 ++mData->mMachineStateDeps;
7895 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7896 }
7897
7898 if (aState)
7899 *aState = mData->mMachineState;
7900 if (aRegistered)
7901 *aRegistered = mData->mRegistered;
7902
7903 return S_OK;
7904}
7905
7906/**
7907 * Decreases the number of objects dependent on the machine state.
7908 * Must always complete the #addStateDependency() call after the state
7909 * dependency is no more necessary.
7910 */
7911void Machine::i_releaseStateDependency()
7912{
7913 AutoCaller autoCaller(this);
7914 AssertComRCReturnVoid(autoCaller.rc());
7915
7916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7917
7918 /* releaseStateDependency() w/o addStateDependency()? */
7919 AssertReturnVoid(mData->mMachineStateDeps != 0);
7920 -- mData->mMachineStateDeps;
7921
7922 if (mData->mMachineStateDeps == 0)
7923 {
7924 /* inform i_ensureNoStateDependencies() that there are no more deps */
7925 if (mData->mMachineStateChangePending != 0)
7926 {
7927 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7928 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7929 }
7930 }
7931}
7932
7933Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7934{
7935 /* start with nothing found */
7936 Utf8Str strResult("");
7937
7938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7939
7940 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7941 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7942 // found:
7943 strResult = it->second; // source is a Utf8Str
7944
7945 return strResult;
7946}
7947
7948// protected methods
7949/////////////////////////////////////////////////////////////////////////////
7950
7951/**
7952 * Performs machine state checks based on the @a aDepType value. If a check
7953 * fails, this method will set extended error info, otherwise it will return
7954 * S_OK. It is supposed, that on failure, the caller will immediately return
7955 * the return value of this method to the upper level.
7956 *
7957 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7958 *
7959 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7960 * current state of this machine object allows to change settings of the
7961 * machine (i.e. the machine is not registered, or registered but not running
7962 * and not saved). It is useful to call this method from Machine setters
7963 * before performing any change.
7964 *
7965 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7966 * as for MutableStateDep except that if the machine is saved, S_OK is also
7967 * returned. This is useful in setters which allow changing machine
7968 * properties when it is in the saved state.
7969 *
7970 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7971 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7972 * Aborted).
7973 *
7974 * @param aDepType Dependency type to check.
7975 *
7976 * @note Non Machine based classes should use #addStateDependency() and
7977 * #releaseStateDependency() methods or the smart AutoStateDependency
7978 * template.
7979 *
7980 * @note This method must be called from under this object's read or write
7981 * lock.
7982 */
7983HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7984{
7985 switch (aDepType)
7986 {
7987 case AnyStateDep:
7988 {
7989 break;
7990 }
7991 case MutableStateDep:
7992 {
7993 if ( mData->mRegistered
7994 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7995 Paused should actually be included here... (Live Migration) */
7996 || ( mData->mMachineState != MachineState_Paused
7997 && mData->mMachineState != MachineState_Running
7998 && mData->mMachineState != MachineState_Aborted
7999 && mData->mMachineState != MachineState_Teleported
8000 && mData->mMachineState != MachineState_PoweredOff
8001 )
8002 )
8003 )
8004 return setError(VBOX_E_INVALID_VM_STATE,
8005 tr("The machine is not mutable (state is %s)"),
8006 Global::stringifyMachineState(mData->mMachineState));
8007 break;
8008 }
8009 case MutableOrSavedStateDep:
8010 {
8011 if ( mData->mRegistered
8012 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
8013 Paused should actually be included here... (Live Migration) */
8014 || ( mData->mMachineState != MachineState_Paused
8015 && mData->mMachineState != MachineState_Running
8016 && mData->mMachineState != MachineState_Aborted
8017 && mData->mMachineState != MachineState_Teleported
8018 && mData->mMachineState != MachineState_Saved
8019 && mData->mMachineState != MachineState_PoweredOff
8020 )
8021 )
8022 )
8023 return setError(VBOX_E_INVALID_VM_STATE,
8024 tr("The machine is not mutable (state is %s)"),
8025 Global::stringifyMachineState(mData->mMachineState));
8026 break;
8027 }
8028 case OfflineStateDep:
8029 {
8030 if ( mData->mRegistered
8031 && ( !i_isSessionMachine()
8032 || ( mData->mMachineState != MachineState_PoweredOff
8033 && mData->mMachineState != MachineState_Saved
8034 && mData->mMachineState != MachineState_Aborted
8035 && mData->mMachineState != MachineState_Teleported
8036 )
8037 )
8038 )
8039 return setError(VBOX_E_INVALID_VM_STATE,
8040 tr("The machine is not offline (state is %s)"),
8041 Global::stringifyMachineState(mData->mMachineState));
8042 break;
8043 }
8044 }
8045
8046 return S_OK;
8047}
8048
8049/**
8050 * Helper to initialize all associated child objects and allocate data
8051 * structures.
8052 *
8053 * This method must be called as a part of the object's initialization procedure
8054 * (usually done in the #init() method).
8055 *
8056 * @note Must be called only from #init() or from #registeredInit().
8057 */
8058HRESULT Machine::initDataAndChildObjects()
8059{
8060 AutoCaller autoCaller(this);
8061 AssertComRCReturnRC(autoCaller.rc());
8062 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8063 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8064
8065 AssertReturn(!mData->mAccessible, E_FAIL);
8066
8067 /* allocate data structures */
8068 mSSData.allocate();
8069 mUserData.allocate();
8070 mHWData.allocate();
8071 mMediaData.allocate();
8072 mStorageControllers.allocate();
8073 mUSBControllers.allocate();
8074
8075 /* initialize mOSTypeId */
8076 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8077
8078 /* create associated BIOS settings object */
8079 unconst(mBIOSSettings).createObject();
8080 mBIOSSettings->init(this);
8081
8082 /* create an associated VRDE object (default is disabled) */
8083 unconst(mVRDEServer).createObject();
8084 mVRDEServer->init(this);
8085
8086 /* create associated serial port objects */
8087 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8088 {
8089 unconst(mSerialPorts[slot]).createObject();
8090 mSerialPorts[slot]->init(this, slot);
8091 }
8092
8093 /* create associated parallel port objects */
8094 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8095 {
8096 unconst(mParallelPorts[slot]).createObject();
8097 mParallelPorts[slot]->init(this, slot);
8098 }
8099
8100 /* create the audio adapter object (always present, default is disabled) */
8101 unconst(mAudioAdapter).createObject();
8102 mAudioAdapter->init(this);
8103
8104 /* create the USB device filters object (always present) */
8105 unconst(mUSBDeviceFilters).createObject();
8106 mUSBDeviceFilters->init(this);
8107
8108 /* create associated network adapter objects */
8109 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8110 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8111 {
8112 unconst(mNetworkAdapters[slot]).createObject();
8113 mNetworkAdapters[slot]->init(this, slot);
8114 }
8115
8116 /* create the bandwidth control */
8117 unconst(mBandwidthControl).createObject();
8118 mBandwidthControl->init(this);
8119
8120 return S_OK;
8121}
8122
8123/**
8124 * Helper to uninitialize all associated child objects and to free all data
8125 * structures.
8126 *
8127 * This method must be called as a part of the object's uninitialization
8128 * procedure (usually done in the #uninit() method).
8129 *
8130 * @note Must be called only from #uninit() or from #registeredInit().
8131 */
8132void Machine::uninitDataAndChildObjects()
8133{
8134 AutoCaller autoCaller(this);
8135 AssertComRCReturnVoid(autoCaller.rc());
8136 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8137 || getObjectState().getState() == ObjectState::Limited);
8138
8139 /* tell all our other child objects we've been uninitialized */
8140 if (mBandwidthControl)
8141 {
8142 mBandwidthControl->uninit();
8143 unconst(mBandwidthControl).setNull();
8144 }
8145
8146 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8147 {
8148 if (mNetworkAdapters[slot])
8149 {
8150 mNetworkAdapters[slot]->uninit();
8151 unconst(mNetworkAdapters[slot]).setNull();
8152 }
8153 }
8154
8155 if (mUSBDeviceFilters)
8156 {
8157 mUSBDeviceFilters->uninit();
8158 unconst(mUSBDeviceFilters).setNull();
8159 }
8160
8161 if (mAudioAdapter)
8162 {
8163 mAudioAdapter->uninit();
8164 unconst(mAudioAdapter).setNull();
8165 }
8166
8167 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8168 {
8169 if (mParallelPorts[slot])
8170 {
8171 mParallelPorts[slot]->uninit();
8172 unconst(mParallelPorts[slot]).setNull();
8173 }
8174 }
8175
8176 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8177 {
8178 if (mSerialPorts[slot])
8179 {
8180 mSerialPorts[slot]->uninit();
8181 unconst(mSerialPorts[slot]).setNull();
8182 }
8183 }
8184
8185 if (mVRDEServer)
8186 {
8187 mVRDEServer->uninit();
8188 unconst(mVRDEServer).setNull();
8189 }
8190
8191 if (mBIOSSettings)
8192 {
8193 mBIOSSettings->uninit();
8194 unconst(mBIOSSettings).setNull();
8195 }
8196
8197 /* Deassociate media (only when a real Machine or a SnapshotMachine
8198 * instance is uninitialized; SessionMachine instances refer to real
8199 * Machine media). This is necessary for a clean re-initialization of
8200 * the VM after successfully re-checking the accessibility state. Note
8201 * that in case of normal Machine or SnapshotMachine uninitialization (as
8202 * a result of unregistering or deleting the snapshot), outdated media
8203 * attachments will already be uninitialized and deleted, so this
8204 * code will not affect them. */
8205 if ( !!mMediaData
8206 && (!i_isSessionMachine())
8207 )
8208 {
8209 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8210 it != mMediaData->mAttachments.end();
8211 ++it)
8212 {
8213 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8214 if (pMedium.isNull())
8215 continue;
8216 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8217 AssertComRC(rc);
8218 }
8219 }
8220
8221 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8222 {
8223 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8224 if (mData->mFirstSnapshot)
8225 {
8226 // snapshots tree is protected by machine write lock; strictly
8227 // this isn't necessary here since we're deleting the entire
8228 // machine, but otherwise we assert in Snapshot::uninit()
8229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8230 mData->mFirstSnapshot->uninit();
8231 mData->mFirstSnapshot.setNull();
8232 }
8233
8234 mData->mCurrentSnapshot.setNull();
8235 }
8236
8237 /* free data structures (the essential mData structure is not freed here
8238 * since it may be still in use) */
8239 mMediaData.free();
8240 mStorageControllers.free();
8241 mUSBControllers.free();
8242 mHWData.free();
8243 mUserData.free();
8244 mSSData.free();
8245}
8246
8247/**
8248 * Returns a pointer to the Machine object for this machine that acts like a
8249 * parent for complex machine data objects such as shared folders, etc.
8250 *
8251 * For primary Machine objects and for SnapshotMachine objects, returns this
8252 * object's pointer itself. For SessionMachine objects, returns the peer
8253 * (primary) machine pointer.
8254 */
8255Machine* Machine::i_getMachine()
8256{
8257 if (i_isSessionMachine())
8258 return (Machine*)mPeer;
8259 return this;
8260}
8261
8262/**
8263 * Makes sure that there are no machine state dependents. If necessary, waits
8264 * for the number of dependents to drop to zero.
8265 *
8266 * Make sure this method is called from under this object's write lock to
8267 * guarantee that no new dependents may be added when this method returns
8268 * control to the caller.
8269 *
8270 * @note Locks this object for writing. The lock will be released while waiting
8271 * (if necessary).
8272 *
8273 * @warning To be used only in methods that change the machine state!
8274 */
8275void Machine::i_ensureNoStateDependencies()
8276{
8277 AssertReturnVoid(isWriteLockOnCurrentThread());
8278
8279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8280
8281 /* Wait for all state dependents if necessary */
8282 if (mData->mMachineStateDeps != 0)
8283 {
8284 /* lazy semaphore creation */
8285 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8286 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8287
8288 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8289 mData->mMachineStateDeps));
8290
8291 ++mData->mMachineStateChangePending;
8292
8293 /* reset the semaphore before waiting, the last dependent will signal
8294 * it */
8295 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8296
8297 alock.release();
8298
8299 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8300
8301 alock.acquire();
8302
8303 -- mData->mMachineStateChangePending;
8304 }
8305}
8306
8307/**
8308 * Changes the machine state and informs callbacks.
8309 *
8310 * This method is not intended to fail so it either returns S_OK or asserts (and
8311 * returns a failure).
8312 *
8313 * @note Locks this object for writing.
8314 */
8315HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8316{
8317 LogFlowThisFuncEnter();
8318 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8319
8320 AutoCaller autoCaller(this);
8321 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8322
8323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8324
8325 /* wait for state dependents to drop to zero */
8326 i_ensureNoStateDependencies();
8327
8328 MachineState_T const enmOldState = mData->mMachineState;
8329 if (enmOldState != aMachineState)
8330 {
8331 mData->mMachineState = aMachineState;
8332 RTTimeNow(&mData->mLastStateChange);
8333
8334#ifdef VBOX_WITH_DTRACE_R3_MAIN
8335 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8336#endif
8337 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8338 }
8339
8340 LogFlowThisFuncLeave();
8341 return S_OK;
8342}
8343
8344/**
8345 * Searches for a shared folder with the given logical name
8346 * in the collection of shared folders.
8347 *
8348 * @param aName logical name of the shared folder
8349 * @param aSharedFolder where to return the found object
8350 * @param aSetError whether to set the error info if the folder is
8351 * not found
8352 * @return
8353 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8354 *
8355 * @note
8356 * must be called from under the object's lock!
8357 */
8358HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8359 ComObjPtr<SharedFolder> &aSharedFolder,
8360 bool aSetError /* = false */)
8361{
8362 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8363 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8364 it != mHWData->mSharedFolders.end();
8365 ++it)
8366 {
8367 SharedFolder *pSF = *it;
8368 AutoCaller autoCaller(pSF);
8369 if (pSF->i_getName() == aName)
8370 {
8371 aSharedFolder = pSF;
8372 rc = S_OK;
8373 break;
8374 }
8375 }
8376
8377 if (aSetError && FAILED(rc))
8378 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8379
8380 return rc;
8381}
8382
8383/**
8384 * Initializes all machine instance data from the given settings structures
8385 * from XML. The exception is the machine UUID which needs special handling
8386 * depending on the caller's use case, so the caller needs to set that herself.
8387 *
8388 * This gets called in several contexts during machine initialization:
8389 *
8390 * -- When machine XML exists on disk already and needs to be loaded into memory,
8391 * for example, from registeredInit() to load all registered machines on
8392 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8393 * attached to the machine should be part of some media registry already.
8394 *
8395 * -- During OVF import, when a machine config has been constructed from an
8396 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8397 * ensure that the media listed as attachments in the config (which have
8398 * been imported from the OVF) receive the correct registry ID.
8399 *
8400 * -- During VM cloning.
8401 *
8402 * @param config Machine settings from XML.
8403 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8404 * for each attached medium in the config.
8405 * @return
8406 */
8407HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8408 const Guid *puuidRegistry)
8409{
8410 // copy name, description, OS type, teleporter, UTC etc.
8411 mUserData->s = config.machineUserData;
8412
8413 // Decode the Icon overide data from config userdata and set onto Machine.
8414 #define DECODE_STR_MAX _1M
8415 const char* pszStr = config.machineUserData.ovIcon.c_str();
8416 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8417 if (cbOut > DECODE_STR_MAX)
8418 return setError(E_FAIL,
8419 tr("Icon Data too long.'%d' > '%d'"),
8420 cbOut,
8421 DECODE_STR_MAX);
8422 mUserData->mIcon.resize(cbOut);
8423 int vrc = VINF_SUCCESS;
8424 if (cbOut)
8425 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8426 if (RT_FAILURE(vrc))
8427 {
8428 mUserData->mIcon.resize(0);
8429 return setError(E_FAIL,
8430 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8431 pszStr,
8432 vrc);
8433 }
8434
8435 // look up the object by Id to check it is valid
8436 ComPtr<IGuestOSType> guestOSType;
8437 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8438 guestOSType.asOutParam());
8439 if (FAILED(rc)) return rc;
8440
8441 // stateFile (optional)
8442 if (config.strStateFile.isEmpty())
8443 mSSData->strStateFilePath.setNull();
8444 else
8445 {
8446 Utf8Str stateFilePathFull(config.strStateFile);
8447 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8448 if (RT_FAILURE(vrc))
8449 return setError(E_FAIL,
8450 tr("Invalid saved state file path '%s' (%Rrc)"),
8451 config.strStateFile.c_str(),
8452 vrc);
8453 mSSData->strStateFilePath = stateFilePathFull;
8454 }
8455
8456 // snapshot folder needs special processing so set it again
8457 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8458 if (FAILED(rc)) return rc;
8459
8460 /* Copy the extra data items (Not in any case config is already the same as
8461 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8462 * make sure the extra data map is copied). */
8463 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8464
8465 /* currentStateModified (optional, default is true) */
8466 mData->mCurrentStateModified = config.fCurrentStateModified;
8467
8468 mData->mLastStateChange = config.timeLastStateChange;
8469
8470 /*
8471 * note: all mUserData members must be assigned prior this point because
8472 * we need to commit changes in order to let mUserData be shared by all
8473 * snapshot machine instances.
8474 */
8475 mUserData.commitCopy();
8476
8477 // machine registry, if present (must be loaded before snapshots)
8478 if (config.canHaveOwnMediaRegistry())
8479 {
8480 // determine machine folder
8481 Utf8Str strMachineFolder = i_getSettingsFileFull();
8482 strMachineFolder.stripFilename();
8483 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8484 config.mediaRegistry,
8485 strMachineFolder);
8486 if (FAILED(rc)) return rc;
8487 }
8488
8489 /* Snapshot node (optional) */
8490 size_t cRootSnapshots;
8491 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8492 {
8493 // there must be only one root snapshot
8494 Assert(cRootSnapshots == 1);
8495
8496 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8497
8498 rc = i_loadSnapshot(snap,
8499 config.uuidCurrentSnapshot,
8500 NULL); // no parent == first snapshot
8501 if (FAILED(rc)) return rc;
8502 }
8503
8504 // hardware data
8505 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8506 if (FAILED(rc)) return rc;
8507
8508 // load storage controllers
8509 rc = i_loadStorageControllers(config.storageMachine,
8510 puuidRegistry,
8511 NULL /* puuidSnapshot */);
8512 if (FAILED(rc)) return rc;
8513
8514 /*
8515 * NOTE: the assignment below must be the last thing to do,
8516 * otherwise it will be not possible to change the settings
8517 * somewhere in the code above because all setters will be
8518 * blocked by i_checkStateDependency(MutableStateDep).
8519 */
8520
8521 /* set the machine state to Aborted or Saved when appropriate */
8522 if (config.fAborted)
8523 {
8524 mSSData->strStateFilePath.setNull();
8525
8526 /* no need to use i_setMachineState() during init() */
8527 mData->mMachineState = MachineState_Aborted;
8528 }
8529 else if (!mSSData->strStateFilePath.isEmpty())
8530 {
8531 /* no need to use i_setMachineState() during init() */
8532 mData->mMachineState = MachineState_Saved;
8533 }
8534
8535 // after loading settings, we are no longer different from the XML on disk
8536 mData->flModifications = 0;
8537
8538 return S_OK;
8539}
8540
8541/**
8542 * Recursively loads all snapshots starting from the given.
8543 *
8544 * @param aNode <Snapshot> node.
8545 * @param aCurSnapshotId Current snapshot ID from the settings file.
8546 * @param aParentSnapshot Parent snapshot.
8547 */
8548HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8549 const Guid &aCurSnapshotId,
8550 Snapshot *aParentSnapshot)
8551{
8552 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8553 AssertReturn(!i_isSessionMachine(), E_FAIL);
8554
8555 HRESULT rc = S_OK;
8556
8557 Utf8Str strStateFile;
8558 if (!data.strStateFile.isEmpty())
8559 {
8560 /* optional */
8561 strStateFile = data.strStateFile;
8562 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8563 if (RT_FAILURE(vrc))
8564 return setError(E_FAIL,
8565 tr("Invalid saved state file path '%s' (%Rrc)"),
8566 strStateFile.c_str(),
8567 vrc);
8568 }
8569
8570 /* create a snapshot machine object */
8571 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8572 pSnapshotMachine.createObject();
8573 rc = pSnapshotMachine->initFromSettings(this,
8574 data.hardware,
8575 &data.debugging,
8576 &data.autostart,
8577 data.storage,
8578 data.uuid.ref(),
8579 strStateFile);
8580 if (FAILED(rc)) return rc;
8581
8582 /* create a snapshot object */
8583 ComObjPtr<Snapshot> pSnapshot;
8584 pSnapshot.createObject();
8585 /* initialize the snapshot */
8586 rc = pSnapshot->init(mParent, // VirtualBox object
8587 data.uuid,
8588 data.strName,
8589 data.strDescription,
8590 data.timestamp,
8591 pSnapshotMachine,
8592 aParentSnapshot);
8593 if (FAILED(rc)) return rc;
8594
8595 /* memorize the first snapshot if necessary */
8596 if (!mData->mFirstSnapshot)
8597 mData->mFirstSnapshot = pSnapshot;
8598
8599 /* memorize the current snapshot when appropriate */
8600 if ( !mData->mCurrentSnapshot
8601 && pSnapshot->i_getId() == aCurSnapshotId
8602 )
8603 mData->mCurrentSnapshot = pSnapshot;
8604
8605 // now create the children
8606 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8607 it != data.llChildSnapshots.end();
8608 ++it)
8609 {
8610 const settings::Snapshot &childData = *it;
8611 // recurse
8612 rc = i_loadSnapshot(childData,
8613 aCurSnapshotId,
8614 pSnapshot); // parent = the one we created above
8615 if (FAILED(rc)) return rc;
8616 }
8617
8618 return rc;
8619}
8620
8621/**
8622 * Loads settings into mHWData.
8623 *
8624 * @param data Reference to the hardware settings.
8625 * @param pDbg Pointer to the debugging settings.
8626 * @param pAutostart Pointer to the autostart settings.
8627 */
8628HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8629 const settings::Autostart *pAutostart)
8630{
8631 AssertReturn(!i_isSessionMachine(), E_FAIL);
8632
8633 HRESULT rc = S_OK;
8634
8635 try
8636 {
8637 /* The hardware version attribute (optional). */
8638 mHWData->mHWVersion = data.strVersion;
8639 mHWData->mHardwareUUID = data.uuid;
8640
8641 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8642 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8643 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8644 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8645 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8646 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8647 mHWData->mPAEEnabled = data.fPAE;
8648 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8649 mHWData->mLongMode = data.enmLongMode;
8650 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8651 mHWData->mCPUCount = data.cCPUs;
8652 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8653 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8654
8655 // cpu
8656 if (mHWData->mCPUHotPlugEnabled)
8657 {
8658 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8659 it != data.llCpus.end();
8660 ++it)
8661 {
8662 const settings::Cpu &cpu = *it;
8663
8664 mHWData->mCPUAttached[cpu.ulId] = true;
8665 }
8666 }
8667
8668 // cpuid leafs
8669 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8670 it != data.llCpuIdLeafs.end();
8671 ++it)
8672 {
8673 const settings::CpuIdLeaf &leaf = *it;
8674
8675 switch (leaf.ulId)
8676 {
8677 case 0x0:
8678 case 0x1:
8679 case 0x2:
8680 case 0x3:
8681 case 0x4:
8682 case 0x5:
8683 case 0x6:
8684 case 0x7:
8685 case 0x8:
8686 case 0x9:
8687 case 0xA:
8688 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8689 break;
8690
8691 case 0x80000000:
8692 case 0x80000001:
8693 case 0x80000002:
8694 case 0x80000003:
8695 case 0x80000004:
8696 case 0x80000005:
8697 case 0x80000006:
8698 case 0x80000007:
8699 case 0x80000008:
8700 case 0x80000009:
8701 case 0x8000000A:
8702 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8703 break;
8704
8705 default:
8706 /* just ignore */
8707 break;
8708 }
8709 }
8710
8711 mHWData->mMemorySize = data.ulMemorySizeMB;
8712 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8713
8714 // boot order
8715 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8716 {
8717 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8718 if (it == data.mapBootOrder.end())
8719 mHWData->mBootOrder[i] = DeviceType_Null;
8720 else
8721 mHWData->mBootOrder[i] = it->second;
8722 }
8723
8724 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8725 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8726 mHWData->mMonitorCount = data.cMonitors;
8727 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8728 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8729 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8730 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8731 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8732 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8733 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8734 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8735 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8736 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8737 if (!data.strVideoCaptureFile.isEmpty())
8738 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8739 else
8740 mHWData->mVideoCaptureFile.setNull();
8741 mHWData->mFirmwareType = data.firmwareType;
8742 mHWData->mPointingHIDType = data.pointingHIDType;
8743 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8744 mHWData->mChipsetType = data.chipsetType;
8745 mHWData->mParavirtProvider = data.paravirtProvider;
8746 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8747 mHWData->mHPETEnabled = data.fHPETEnabled;
8748
8749 /* VRDEServer */
8750 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8751 if (FAILED(rc)) return rc;
8752
8753 /* BIOS */
8754 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8755 if (FAILED(rc)) return rc;
8756
8757 // Bandwidth control (must come before network adapters)
8758 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8759 if (FAILED(rc)) return rc;
8760
8761 /* Shared folders */
8762 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8763 it != data.usbSettings.llUSBControllers.end();
8764 ++it)
8765 {
8766 const settings::USBController &settingsCtrl = *it;
8767 ComObjPtr<USBController> newCtrl;
8768
8769 newCtrl.createObject();
8770 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8771 mUSBControllers->push_back(newCtrl);
8772 }
8773
8774 /* USB device filters */
8775 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8776 if (FAILED(rc)) return rc;
8777
8778 // network adapters
8779 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8780 size_t oldCount = mNetworkAdapters.size();
8781 if (newCount > oldCount)
8782 {
8783 mNetworkAdapters.resize(newCount);
8784 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8785 {
8786 unconst(mNetworkAdapters[slot]).createObject();
8787 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8788 }
8789 }
8790 else if (newCount < oldCount)
8791 mNetworkAdapters.resize(newCount);
8792 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8793 it != data.llNetworkAdapters.end();
8794 ++it)
8795 {
8796 const settings::NetworkAdapter &nic = *it;
8797
8798 /* slot unicity is guaranteed by XML Schema */
8799 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8800 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8801 if (FAILED(rc)) return rc;
8802 }
8803
8804 // serial ports
8805 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8806 it != data.llSerialPorts.end();
8807 ++it)
8808 {
8809 const settings::SerialPort &s = *it;
8810
8811 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8812 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8813 if (FAILED(rc)) return rc;
8814 }
8815
8816 // parallel ports (optional)
8817 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8818 it != data.llParallelPorts.end();
8819 ++it)
8820 {
8821 const settings::ParallelPort &p = *it;
8822
8823 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8824 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8825 if (FAILED(rc)) return rc;
8826 }
8827
8828 /* AudioAdapter */
8829 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8830 if (FAILED(rc)) return rc;
8831
8832 /* Shared folders */
8833 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8834 it != data.llSharedFolders.end();
8835 ++it)
8836 {
8837 const settings::SharedFolder &sf = *it;
8838
8839 ComObjPtr<SharedFolder> sharedFolder;
8840 /* Check for double entries. Not allowed! */
8841 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8842 if (SUCCEEDED(rc))
8843 return setError(VBOX_E_OBJECT_IN_USE,
8844 tr("Shared folder named '%s' already exists"),
8845 sf.strName.c_str());
8846
8847 /* Create the new shared folder. Don't break on error. This will be
8848 * reported when the machine starts. */
8849 sharedFolder.createObject();
8850 rc = sharedFolder->init(i_getMachine(),
8851 sf.strName,
8852 sf.strHostPath,
8853 RT_BOOL(sf.fWritable),
8854 RT_BOOL(sf.fAutoMount),
8855 false /* fFailOnError */);
8856 if (FAILED(rc)) return rc;
8857 mHWData->mSharedFolders.push_back(sharedFolder);
8858 }
8859
8860 // Clipboard
8861 mHWData->mClipboardMode = data.clipboardMode;
8862
8863 // drag'n'drop
8864 mHWData->mDnDMode = data.dndMode;
8865
8866 // guest settings
8867 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8868
8869 // IO settings
8870 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8871 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8872
8873 // Host PCI devices
8874 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8875 it != data.pciAttachments.end();
8876 ++it)
8877 {
8878 const settings::HostPCIDeviceAttachment &hpda = *it;
8879 ComObjPtr<PCIDeviceAttachment> pda;
8880
8881 pda.createObject();
8882 pda->i_loadSettings(this, hpda);
8883 mHWData->mPCIDeviceAssignments.push_back(pda);
8884 }
8885
8886 /*
8887 * (The following isn't really real hardware, but it lives in HWData
8888 * for reasons of convenience.)
8889 */
8890
8891#ifdef VBOX_WITH_GUEST_PROPS
8892 /* Guest properties (optional) */
8893 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8894 it != data.llGuestProperties.end();
8895 ++it)
8896 {
8897 const settings::GuestProperty &prop = *it;
8898 uint32_t fFlags = guestProp::NILFLAG;
8899 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8900 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8901 mHWData->mGuestProperties[prop.strName] = property;
8902 }
8903
8904 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8905#endif /* VBOX_WITH_GUEST_PROPS defined */
8906
8907 rc = i_loadDebugging(pDbg);
8908 if (FAILED(rc))
8909 return rc;
8910
8911 mHWData->mAutostart = *pAutostart;
8912
8913 /* default frontend */
8914 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8915 }
8916 catch(std::bad_alloc &)
8917 {
8918 return E_OUTOFMEMORY;
8919 }
8920
8921 AssertComRC(rc);
8922 return rc;
8923}
8924
8925/**
8926 * Called from Machine::loadHardware() to load the debugging settings of the
8927 * machine.
8928 *
8929 * @param pDbg Pointer to the settings.
8930 */
8931HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8932{
8933 mHWData->mDebugging = *pDbg;
8934 /* no more processing currently required, this will probably change. */
8935 return S_OK;
8936}
8937
8938/**
8939 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8940 *
8941 * @param data
8942 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8943 * @param puuidSnapshot
8944 * @return
8945 */
8946HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8947 const Guid *puuidRegistry,
8948 const Guid *puuidSnapshot)
8949{
8950 AssertReturn(!i_isSessionMachine(), E_FAIL);
8951
8952 HRESULT rc = S_OK;
8953
8954 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8955 it != data.llStorageControllers.end();
8956 ++it)
8957 {
8958 const settings::StorageController &ctlData = *it;
8959
8960 ComObjPtr<StorageController> pCtl;
8961 /* Try to find one with the name first. */
8962 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8963 if (SUCCEEDED(rc))
8964 return setError(VBOX_E_OBJECT_IN_USE,
8965 tr("Storage controller named '%s' already exists"),
8966 ctlData.strName.c_str());
8967
8968 pCtl.createObject();
8969 rc = pCtl->init(this,
8970 ctlData.strName,
8971 ctlData.storageBus,
8972 ctlData.ulInstance,
8973 ctlData.fBootable);
8974 if (FAILED(rc)) return rc;
8975
8976 mStorageControllers->push_back(pCtl);
8977
8978 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8979 if (FAILED(rc)) return rc;
8980
8981 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8982 if (FAILED(rc)) return rc;
8983
8984 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8985 if (FAILED(rc)) return rc;
8986
8987 /* Set IDE emulation settings (only for AHCI controller). */
8988 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8989 {
8990 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8991 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8992 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8993 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8994 )
8995 return rc;
8996 }
8997
8998 /* Load the attached devices now. */
8999 rc = i_loadStorageDevices(pCtl,
9000 ctlData,
9001 puuidRegistry,
9002 puuidSnapshot);
9003 if (FAILED(rc)) return rc;
9004 }
9005
9006 return S_OK;
9007}
9008
9009/**
9010 * Called from i_loadStorageControllers for a controller's devices.
9011 *
9012 * @param aStorageController
9013 * @param data
9014 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9015 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9016 * @return
9017 */
9018HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9019 const settings::StorageController &data,
9020 const Guid *puuidRegistry,
9021 const Guid *puuidSnapshot)
9022{
9023 HRESULT rc = S_OK;
9024
9025 /* paranoia: detect duplicate attachments */
9026 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9027 it != data.llAttachedDevices.end();
9028 ++it)
9029 {
9030 const settings::AttachedDevice &ad = *it;
9031
9032 for (settings::AttachedDevicesList::const_iterator it2 = it;
9033 it2 != data.llAttachedDevices.end();
9034 ++it2)
9035 {
9036 if (it == it2)
9037 continue;
9038
9039 const settings::AttachedDevice &ad2 = *it2;
9040
9041 if ( ad.lPort == ad2.lPort
9042 && ad.lDevice == ad2.lDevice)
9043 {
9044 return setError(E_FAIL,
9045 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9046 aStorageController->i_getName().c_str(),
9047 ad.lPort,
9048 ad.lDevice,
9049 mUserData->s.strName.c_str());
9050 }
9051 }
9052 }
9053
9054 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9055 it != data.llAttachedDevices.end();
9056 ++it)
9057 {
9058 const settings::AttachedDevice &dev = *it;
9059 ComObjPtr<Medium> medium;
9060
9061 switch (dev.deviceType)
9062 {
9063 case DeviceType_Floppy:
9064 case DeviceType_DVD:
9065 if (dev.strHostDriveSrc.isNotEmpty())
9066 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9067 false /* fRefresh */, medium);
9068 else
9069 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9070 dev.uuid,
9071 false /* fRefresh */,
9072 false /* aSetError */,
9073 medium);
9074 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9075 // This is not an error. The host drive or UUID might have vanished, so just go
9076 // ahead without this removeable medium attachment
9077 rc = S_OK;
9078 break;
9079
9080 case DeviceType_HardDisk:
9081 {
9082 /* find a hard disk by UUID */
9083 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9084 if (FAILED(rc))
9085 {
9086 if (i_isSnapshotMachine())
9087 {
9088 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9089 // so the user knows that the bad disk is in a snapshot somewhere
9090 com::ErrorInfo info;
9091 return setError(E_FAIL,
9092 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9093 puuidSnapshot->raw(),
9094 info.getText().raw());
9095 }
9096 else
9097 return rc;
9098 }
9099
9100 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9101
9102 if (medium->i_getType() == MediumType_Immutable)
9103 {
9104 if (i_isSnapshotMachine())
9105 return setError(E_FAIL,
9106 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9107 "of the virtual machine '%s' ('%s')"),
9108 medium->i_getLocationFull().c_str(),
9109 dev.uuid.raw(),
9110 puuidSnapshot->raw(),
9111 mUserData->s.strName.c_str(),
9112 mData->m_strConfigFileFull.c_str());
9113
9114 return setError(E_FAIL,
9115 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9116 medium->i_getLocationFull().c_str(),
9117 dev.uuid.raw(),
9118 mUserData->s.strName.c_str(),
9119 mData->m_strConfigFileFull.c_str());
9120 }
9121
9122 if (medium->i_getType() == MediumType_MultiAttach)
9123 {
9124 if (i_isSnapshotMachine())
9125 return setError(E_FAIL,
9126 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9127 "of the virtual machine '%s' ('%s')"),
9128 medium->i_getLocationFull().c_str(),
9129 dev.uuid.raw(),
9130 puuidSnapshot->raw(),
9131 mUserData->s.strName.c_str(),
9132 mData->m_strConfigFileFull.c_str());
9133
9134 return setError(E_FAIL,
9135 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9136 medium->i_getLocationFull().c_str(),
9137 dev.uuid.raw(),
9138 mUserData->s.strName.c_str(),
9139 mData->m_strConfigFileFull.c_str());
9140 }
9141
9142 if ( !i_isSnapshotMachine()
9143 && medium->i_getChildren().size() != 0
9144 )
9145 return setError(E_FAIL,
9146 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9147 "because it has %d differencing child hard disks"),
9148 medium->i_getLocationFull().c_str(),
9149 dev.uuid.raw(),
9150 mUserData->s.strName.c_str(),
9151 mData->m_strConfigFileFull.c_str(),
9152 medium->i_getChildren().size());
9153
9154 if (i_findAttachment(mMediaData->mAttachments,
9155 medium))
9156 return setError(E_FAIL,
9157 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9158 medium->i_getLocationFull().c_str(),
9159 dev.uuid.raw(),
9160 mUserData->s.strName.c_str(),
9161 mData->m_strConfigFileFull.c_str());
9162
9163 break;
9164 }
9165
9166 default:
9167 return setError(E_FAIL,
9168 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9169 medium->i_getLocationFull().c_str(),
9170 mUserData->s.strName.c_str(),
9171 mData->m_strConfigFileFull.c_str());
9172 }
9173
9174 if (FAILED(rc))
9175 break;
9176
9177 /* Bandwidth groups are loaded at this point. */
9178 ComObjPtr<BandwidthGroup> pBwGroup;
9179
9180 if (!dev.strBwGroup.isEmpty())
9181 {
9182 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9183 if (FAILED(rc))
9184 return setError(E_FAIL,
9185 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9186 medium->i_getLocationFull().c_str(),
9187 dev.strBwGroup.c_str(),
9188 mUserData->s.strName.c_str(),
9189 mData->m_strConfigFileFull.c_str());
9190 pBwGroup->i_reference();
9191 }
9192
9193 const Bstr controllerName = aStorageController->i_getName();
9194 ComObjPtr<MediumAttachment> pAttachment;
9195 pAttachment.createObject();
9196 rc = pAttachment->init(this,
9197 medium,
9198 controllerName,
9199 dev.lPort,
9200 dev.lDevice,
9201 dev.deviceType,
9202 false,
9203 dev.fPassThrough,
9204 dev.fTempEject,
9205 dev.fNonRotational,
9206 dev.fDiscard,
9207 dev.fHotPluggable,
9208 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9209 if (FAILED(rc)) break;
9210
9211 /* associate the medium with this machine and snapshot */
9212 if (!medium.isNull())
9213 {
9214 AutoCaller medCaller(medium);
9215 if (FAILED(medCaller.rc())) return medCaller.rc();
9216 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9217
9218 if (i_isSnapshotMachine())
9219 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9220 else
9221 rc = medium->i_addBackReference(mData->mUuid);
9222 /* If the medium->addBackReference fails it sets an appropriate
9223 * error message, so no need to do any guesswork here. */
9224
9225 if (puuidRegistry)
9226 // caller wants registry ID to be set on all attached media (OVF import case)
9227 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9228 }
9229
9230 if (FAILED(rc))
9231 break;
9232
9233 /* back up mMediaData to let registeredInit() properly rollback on failure
9234 * (= limited accessibility) */
9235 i_setModified(IsModified_Storage);
9236 mMediaData.backup();
9237 mMediaData->mAttachments.push_back(pAttachment);
9238 }
9239
9240 return rc;
9241}
9242
9243/**
9244 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9245 *
9246 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9247 * @param aSnapshot where to return the found snapshot
9248 * @param aSetError true to set extended error info on failure
9249 */
9250HRESULT Machine::i_findSnapshotById(const Guid &aId,
9251 ComObjPtr<Snapshot> &aSnapshot,
9252 bool aSetError /* = false */)
9253{
9254 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9255
9256 if (!mData->mFirstSnapshot)
9257 {
9258 if (aSetError)
9259 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9260 return E_FAIL;
9261 }
9262
9263 if (aId.isZero())
9264 aSnapshot = mData->mFirstSnapshot;
9265 else
9266 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9267
9268 if (!aSnapshot)
9269 {
9270 if (aSetError)
9271 return setError(E_FAIL,
9272 tr("Could not find a snapshot with UUID {%s}"),
9273 aId.toString().c_str());
9274 return E_FAIL;
9275 }
9276
9277 return S_OK;
9278}
9279
9280/**
9281 * Returns the snapshot with the given name or fails of no such snapshot.
9282 *
9283 * @param aName snapshot name to find
9284 * @param aSnapshot where to return the found snapshot
9285 * @param aSetError true to set extended error info on failure
9286 */
9287HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9288 ComObjPtr<Snapshot> &aSnapshot,
9289 bool aSetError /* = false */)
9290{
9291 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9292
9293 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9294
9295 if (!mData->mFirstSnapshot)
9296 {
9297 if (aSetError)
9298 return setError(VBOX_E_OBJECT_NOT_FOUND,
9299 tr("This machine does not have any snapshots"));
9300 return VBOX_E_OBJECT_NOT_FOUND;
9301 }
9302
9303 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9304
9305 if (!aSnapshot)
9306 {
9307 if (aSetError)
9308 return setError(VBOX_E_OBJECT_NOT_FOUND,
9309 tr("Could not find a snapshot named '%s'"), strName.c_str());
9310 return VBOX_E_OBJECT_NOT_FOUND;
9311 }
9312
9313 return S_OK;
9314}
9315
9316/**
9317 * Returns a storage controller object with the given name.
9318 *
9319 * @param aName storage controller name to find
9320 * @param aStorageController where to return the found storage controller
9321 * @param aSetError true to set extended error info on failure
9322 */
9323HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9324 ComObjPtr<StorageController> &aStorageController,
9325 bool aSetError /* = false */)
9326{
9327 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9328
9329 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9330 it != mStorageControllers->end();
9331 ++it)
9332 {
9333 if ((*it)->i_getName() == aName)
9334 {
9335 aStorageController = (*it);
9336 return S_OK;
9337 }
9338 }
9339
9340 if (aSetError)
9341 return setError(VBOX_E_OBJECT_NOT_FOUND,
9342 tr("Could not find a storage controller named '%s'"),
9343 aName.c_str());
9344 return VBOX_E_OBJECT_NOT_FOUND;
9345}
9346
9347/**
9348 * Returns a USB controller object with the given name.
9349 *
9350 * @param aName USB controller name to find
9351 * @param aUSBController where to return the found USB controller
9352 * @param aSetError true to set extended error info on failure
9353 */
9354HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9355 ComObjPtr<USBController> &aUSBController,
9356 bool aSetError /* = false */)
9357{
9358 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9359
9360 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9361 it != mUSBControllers->end();
9362 ++it)
9363 {
9364 if ((*it)->i_getName() == aName)
9365 {
9366 aUSBController = (*it);
9367 return S_OK;
9368 }
9369 }
9370
9371 if (aSetError)
9372 return setError(VBOX_E_OBJECT_NOT_FOUND,
9373 tr("Could not find a storage controller named '%s'"),
9374 aName.c_str());
9375 return VBOX_E_OBJECT_NOT_FOUND;
9376}
9377
9378/**
9379 * Returns the number of USB controller instance of the given type.
9380 *
9381 * @param enmType USB controller type.
9382 */
9383ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9384{
9385 ULONG cCtrls = 0;
9386
9387 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9388 it != mUSBControllers->end();
9389 ++it)
9390 {
9391 if ((*it)->i_getControllerType() == enmType)
9392 cCtrls++;
9393 }
9394
9395 return cCtrls;
9396}
9397
9398HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9399 MediaData::AttachmentList &atts)
9400{
9401 AutoCaller autoCaller(this);
9402 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9403
9404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9405
9406 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9407 it != mMediaData->mAttachments.end();
9408 ++it)
9409 {
9410 const ComObjPtr<MediumAttachment> &pAtt = *it;
9411 // should never happen, but deal with NULL pointers in the list.
9412 AssertStmt(!pAtt.isNull(), continue);
9413
9414 // getControllerName() needs caller+read lock
9415 AutoCaller autoAttCaller(pAtt);
9416 if (FAILED(autoAttCaller.rc()))
9417 {
9418 atts.clear();
9419 return autoAttCaller.rc();
9420 }
9421 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9422
9423 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9424 atts.push_back(pAtt);
9425 }
9426
9427 return S_OK;
9428}
9429
9430
9431/**
9432 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9433 * file if the machine name was changed and about creating a new settings file
9434 * if this is a new machine.
9435 *
9436 * @note Must be never called directly but only from #saveSettings().
9437 */
9438HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9439{
9440 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9441
9442 HRESULT rc = S_OK;
9443
9444 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9445
9446 /// @todo need to handle primary group change, too
9447
9448 /* attempt to rename the settings file if machine name is changed */
9449 if ( mUserData->s.fNameSync
9450 && mUserData.isBackedUp()
9451 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9452 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9453 )
9454 {
9455 bool dirRenamed = false;
9456 bool fileRenamed = false;
9457
9458 Utf8Str configFile, newConfigFile;
9459 Utf8Str configFilePrev, newConfigFilePrev;
9460 Utf8Str configDir, newConfigDir;
9461
9462 do
9463 {
9464 int vrc = VINF_SUCCESS;
9465
9466 Utf8Str name = mUserData.backedUpData()->s.strName;
9467 Utf8Str newName = mUserData->s.strName;
9468 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9469 if (group == "/")
9470 group.setNull();
9471 Utf8Str newGroup = mUserData->s.llGroups.front();
9472 if (newGroup == "/")
9473 newGroup.setNull();
9474
9475 configFile = mData->m_strConfigFileFull;
9476
9477 /* first, rename the directory if it matches the group and machine name */
9478 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9479 group.c_str(), RTPATH_DELIMITER, name.c_str());
9480 /** @todo hack, make somehow use of ComposeMachineFilename */
9481 if (mUserData->s.fDirectoryIncludesUUID)
9482 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9483 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9484 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9485 /** @todo hack, make somehow use of ComposeMachineFilename */
9486 if (mUserData->s.fDirectoryIncludesUUID)
9487 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9488 configDir = configFile;
9489 configDir.stripFilename();
9490 newConfigDir = configDir;
9491 if ( configDir.length() >= groupPlusName.length()
9492 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9493 groupPlusName.c_str()))
9494 {
9495 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9496 Utf8Str newConfigBaseDir(newConfigDir);
9497 newConfigDir.append(newGroupPlusName);
9498 /* consistency: use \ if appropriate on the platform */
9499 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9500 /* new dir and old dir cannot be equal here because of 'if'
9501 * above and because name != newName */
9502 Assert(configDir != newConfigDir);
9503 if (!fSettingsFileIsNew)
9504 {
9505 /* perform real rename only if the machine is not new */
9506 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9507 if ( vrc == VERR_FILE_NOT_FOUND
9508 || vrc == VERR_PATH_NOT_FOUND)
9509 {
9510 /* create the parent directory, then retry renaming */
9511 Utf8Str parent(newConfigDir);
9512 parent.stripFilename();
9513 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9514 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9515 }
9516 if (RT_FAILURE(vrc))
9517 {
9518 rc = setError(E_FAIL,
9519 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9520 configDir.c_str(),
9521 newConfigDir.c_str(),
9522 vrc);
9523 break;
9524 }
9525 /* delete subdirectories which are no longer needed */
9526 Utf8Str dir(configDir);
9527 dir.stripFilename();
9528 while (dir != newConfigBaseDir && dir != ".")
9529 {
9530 vrc = RTDirRemove(dir.c_str());
9531 if (RT_FAILURE(vrc))
9532 break;
9533 dir.stripFilename();
9534 }
9535 dirRenamed = true;
9536 }
9537 }
9538
9539 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9540 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9541
9542 /* then try to rename the settings file itself */
9543 if (newConfigFile != configFile)
9544 {
9545 /* get the path to old settings file in renamed directory */
9546 configFile = Utf8StrFmt("%s%c%s",
9547 newConfigDir.c_str(),
9548 RTPATH_DELIMITER,
9549 RTPathFilename(configFile.c_str()));
9550 if (!fSettingsFileIsNew)
9551 {
9552 /* perform real rename only if the machine is not new */
9553 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9554 if (RT_FAILURE(vrc))
9555 {
9556 rc = setError(E_FAIL,
9557 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9558 configFile.c_str(),
9559 newConfigFile.c_str(),
9560 vrc);
9561 break;
9562 }
9563 fileRenamed = true;
9564 configFilePrev = configFile;
9565 configFilePrev += "-prev";
9566 newConfigFilePrev = newConfigFile;
9567 newConfigFilePrev += "-prev";
9568 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9569 }
9570 }
9571
9572 // update m_strConfigFileFull amd mConfigFile
9573 mData->m_strConfigFileFull = newConfigFile;
9574 // compute the relative path too
9575 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9576
9577 // store the old and new so that VirtualBox::i_saveSettings() can update
9578 // the media registry
9579 if ( mData->mRegistered
9580 && (configDir != newConfigDir || configFile != newConfigFile))
9581 {
9582 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9583
9584 if (pfNeedsGlobalSaveSettings)
9585 *pfNeedsGlobalSaveSettings = true;
9586 }
9587
9588 // in the saved state file path, replace the old directory with the new directory
9589 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9590 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9591
9592 // and do the same thing for the saved state file paths of all the online snapshots
9593 if (mData->mFirstSnapshot)
9594 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9595 newConfigDir.c_str());
9596 }
9597 while (0);
9598
9599 if (FAILED(rc))
9600 {
9601 /* silently try to rename everything back */
9602 if (fileRenamed)
9603 {
9604 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9605 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9606 }
9607 if (dirRenamed)
9608 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9609 }
9610
9611 if (FAILED(rc)) return rc;
9612 }
9613
9614 if (fSettingsFileIsNew)
9615 {
9616 /* create a virgin config file */
9617 int vrc = VINF_SUCCESS;
9618
9619 /* ensure the settings directory exists */
9620 Utf8Str path(mData->m_strConfigFileFull);
9621 path.stripFilename();
9622 if (!RTDirExists(path.c_str()))
9623 {
9624 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9625 if (RT_FAILURE(vrc))
9626 {
9627 return setError(E_FAIL,
9628 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9629 path.c_str(),
9630 vrc);
9631 }
9632 }
9633
9634 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9635 path = Utf8Str(mData->m_strConfigFileFull);
9636 RTFILE f = NIL_RTFILE;
9637 vrc = RTFileOpen(&f, path.c_str(),
9638 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9639 if (RT_FAILURE(vrc))
9640 return setError(E_FAIL,
9641 tr("Could not create the settings file '%s' (%Rrc)"),
9642 path.c_str(),
9643 vrc);
9644 RTFileClose(f);
9645 }
9646
9647 return rc;
9648}
9649
9650/**
9651 * Saves and commits machine data, user data and hardware data.
9652 *
9653 * Note that on failure, the data remains uncommitted.
9654 *
9655 * @a aFlags may combine the following flags:
9656 *
9657 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9658 * Used when saving settings after an operation that makes them 100%
9659 * correspond to the settings from the current snapshot.
9660 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9661 * #isReallyModified() returns false. This is necessary for cases when we
9662 * change machine data directly, not through the backup()/commit() mechanism.
9663 * - SaveS_Force: settings will be saved without doing a deep compare of the
9664 * settings structures. This is used when this is called because snapshots
9665 * have changed to avoid the overhead of the deep compare.
9666 *
9667 * @note Must be called from under this object's write lock. Locks children for
9668 * writing.
9669 *
9670 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9671 * initialized to false and that will be set to true by this function if
9672 * the caller must invoke VirtualBox::i_saveSettings() because the global
9673 * settings have changed. This will happen if a machine rename has been
9674 * saved and the global machine and media registries will therefore need
9675 * updating.
9676 */
9677HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9678 int aFlags /*= 0*/)
9679{
9680 LogFlowThisFuncEnter();
9681
9682 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9683
9684 /* make sure child objects are unable to modify the settings while we are
9685 * saving them */
9686 i_ensureNoStateDependencies();
9687
9688 AssertReturn(!i_isSnapshotMachine(),
9689 E_FAIL);
9690
9691 HRESULT rc = S_OK;
9692 bool fNeedsWrite = false;
9693
9694 /* First, prepare to save settings. It will care about renaming the
9695 * settings directory and file if the machine name was changed and about
9696 * creating a new settings file if this is a new machine. */
9697 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9698 if (FAILED(rc)) return rc;
9699
9700 // keep a pointer to the current settings structures
9701 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9702 settings::MachineConfigFile *pNewConfig = NULL;
9703
9704 try
9705 {
9706 // make a fresh one to have everyone write stuff into
9707 pNewConfig = new settings::MachineConfigFile(NULL);
9708 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9709
9710 // now go and copy all the settings data from COM to the settings structures
9711 // (this calles i_saveSettings() on all the COM objects in the machine)
9712 i_copyMachineDataToSettings(*pNewConfig);
9713
9714 if (aFlags & SaveS_ResetCurStateModified)
9715 {
9716 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9717 mData->mCurrentStateModified = FALSE;
9718 fNeedsWrite = true; // always, no need to compare
9719 }
9720 else if (aFlags & SaveS_Force)
9721 {
9722 fNeedsWrite = true; // always, no need to compare
9723 }
9724 else
9725 {
9726 if (!mData->mCurrentStateModified)
9727 {
9728 // do a deep compare of the settings that we just saved with the settings
9729 // previously stored in the config file; this invokes MachineConfigFile::operator==
9730 // which does a deep compare of all the settings, which is expensive but less expensive
9731 // than writing out XML in vain
9732 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9733
9734 // could still be modified if any settings changed
9735 mData->mCurrentStateModified = fAnySettingsChanged;
9736
9737 fNeedsWrite = fAnySettingsChanged;
9738 }
9739 else
9740 fNeedsWrite = true;
9741 }
9742
9743 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9744
9745 if (fNeedsWrite)
9746 // now spit it all out!
9747 pNewConfig->write(mData->m_strConfigFileFull);
9748
9749 mData->pMachineConfigFile = pNewConfig;
9750 delete pOldConfig;
9751 i_commit();
9752
9753 // after saving settings, we are no longer different from the XML on disk
9754 mData->flModifications = 0;
9755 }
9756 catch (HRESULT err)
9757 {
9758 // we assume that error info is set by the thrower
9759 rc = err;
9760
9761 // restore old config
9762 delete pNewConfig;
9763 mData->pMachineConfigFile = pOldConfig;
9764 }
9765 catch (...)
9766 {
9767 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9768 }
9769
9770 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9771 {
9772 /* Fire the data change event, even on failure (since we've already
9773 * committed all data). This is done only for SessionMachines because
9774 * mutable Machine instances are always not registered (i.e. private
9775 * to the client process that creates them) and thus don't need to
9776 * inform callbacks. */
9777 if (i_isSessionMachine())
9778 mParent->i_onMachineDataChange(mData->mUuid);
9779 }
9780
9781 LogFlowThisFunc(("rc=%08X\n", rc));
9782 LogFlowThisFuncLeave();
9783 return rc;
9784}
9785
9786/**
9787 * Implementation for saving the machine settings into the given
9788 * settings::MachineConfigFile instance. This copies machine extradata
9789 * from the previous machine config file in the instance data, if any.
9790 *
9791 * This gets called from two locations:
9792 *
9793 * -- Machine::i_saveSettings(), during the regular XML writing;
9794 *
9795 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9796 * exported to OVF and we write the VirtualBox proprietary XML
9797 * into a <vbox:Machine> tag.
9798 *
9799 * This routine fills all the fields in there, including snapshots, *except*
9800 * for the following:
9801 *
9802 * -- fCurrentStateModified. There is some special logic associated with that.
9803 *
9804 * The caller can then call MachineConfigFile::write() or do something else
9805 * with it.
9806 *
9807 * Caller must hold the machine lock!
9808 *
9809 * This throws XML errors and HRESULT, so the caller must have a catch block!
9810 */
9811void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9812{
9813 // deep copy extradata
9814 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9815
9816 config.uuid = mData->mUuid;
9817
9818 // copy name, description, OS type, teleport, UTC etc.
9819 config.machineUserData = mUserData->s;
9820
9821 // Encode the Icon Override data from Machine and store on config userdata.
9822 std::vector<BYTE> iconByte;
9823 getIcon(iconByte);
9824 ssize_t cbData = iconByte.size();
9825 if (cbData > 0)
9826 {
9827 ssize_t cchOut = RTBase64EncodedLength(cbData);
9828 Utf8Str strIconData;
9829 strIconData.reserve(cchOut+1);
9830 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9831 strIconData.mutableRaw(), strIconData.capacity(),
9832 NULL);
9833 if (RT_FAILURE(vrc))
9834 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9835 strIconData.jolt();
9836 config.machineUserData.ovIcon = strIconData;
9837 }
9838 else
9839 config.machineUserData.ovIcon.setNull();
9840
9841 if ( mData->mMachineState == MachineState_Saved
9842 || mData->mMachineState == MachineState_Restoring
9843 // when deleting a snapshot we may or may not have a saved state in the current state,
9844 // so let's not assert here please
9845 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9846 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9847 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9848 && (!mSSData->strStateFilePath.isEmpty())
9849 )
9850 )
9851 {
9852 Assert(!mSSData->strStateFilePath.isEmpty());
9853 /* try to make the file name relative to the settings file dir */
9854 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9855 }
9856 else
9857 {
9858 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9859 config.strStateFile.setNull();
9860 }
9861
9862 if (mData->mCurrentSnapshot)
9863 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9864 else
9865 config.uuidCurrentSnapshot.clear();
9866
9867 config.timeLastStateChange = mData->mLastStateChange;
9868 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9869 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9870
9871 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9872 if (FAILED(rc)) throw rc;
9873
9874 rc = i_saveStorageControllers(config.storageMachine);
9875 if (FAILED(rc)) throw rc;
9876
9877 // save machine's media registry if this is VirtualBox 4.0 or later
9878 if (config.canHaveOwnMediaRegistry())
9879 {
9880 // determine machine folder
9881 Utf8Str strMachineFolder = i_getSettingsFileFull();
9882 strMachineFolder.stripFilename();
9883 mParent->i_saveMediaRegistry(config.mediaRegistry,
9884 i_getId(), // only media with registry ID == machine UUID
9885 strMachineFolder);
9886 // this throws HRESULT
9887 }
9888
9889 // save snapshots
9890 rc = i_saveAllSnapshots(config);
9891 if (FAILED(rc)) throw rc;
9892}
9893
9894/**
9895 * Saves all snapshots of the machine into the given machine config file. Called
9896 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9897 * @param config
9898 * @return
9899 */
9900HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9901{
9902 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9903
9904 HRESULT rc = S_OK;
9905
9906 try
9907 {
9908 config.llFirstSnapshot.clear();
9909
9910 if (mData->mFirstSnapshot)
9911 {
9912 settings::Snapshot snapNew;
9913 config.llFirstSnapshot.push_back(snapNew);
9914
9915 // get reference to the fresh copy of the snapshot on the list and
9916 // work on that copy directly to avoid excessive copying later
9917 settings::Snapshot &snap = config.llFirstSnapshot.front();
9918
9919 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9920 if (FAILED(rc)) throw rc;
9921 }
9922
9923// if (mType == IsSessionMachine)
9924// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9925
9926 }
9927 catch (HRESULT err)
9928 {
9929 /* we assume that error info is set by the thrower */
9930 rc = err;
9931 }
9932 catch (...)
9933 {
9934 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9935 }
9936
9937 return rc;
9938}
9939
9940/**
9941 * Saves the VM hardware configuration. It is assumed that the
9942 * given node is empty.
9943 *
9944 * @param data Reference to the settings object for the hardware config.
9945 * @param pDbg Pointer to the settings object for the debugging config
9946 * which happens to live in mHWData.
9947 * @param pAutostart Pointer to the settings object for the autostart config
9948 * which happens to live in mHWData.
9949 */
9950HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9951 settings::Autostart *pAutostart)
9952{
9953 HRESULT rc = S_OK;
9954
9955 try
9956 {
9957 /* The hardware version attribute (optional).
9958 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9959 if ( mHWData->mHWVersion == "1"
9960 && mSSData->strStateFilePath.isEmpty()
9961 )
9962 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9963 other point needs to be found where this can be done. */
9964
9965 data.strVersion = mHWData->mHWVersion;
9966 data.uuid = mHWData->mHardwareUUID;
9967
9968 // CPU
9969 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9970 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9971 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9972 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9973 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9974 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9975 data.fPAE = !!mHWData->mPAEEnabled;
9976 data.enmLongMode = mHWData->mLongMode;
9977 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9978 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9979
9980 /* Standard and Extended CPUID leafs. */
9981 data.llCpuIdLeafs.clear();
9982 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9983 {
9984 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9985 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9986 }
9987 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9988 {
9989 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9990 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9991 }
9992
9993 data.cCPUs = mHWData->mCPUCount;
9994 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9995 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9996
9997 data.llCpus.clear();
9998 if (data.fCpuHotPlug)
9999 {
10000 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10001 {
10002 if (mHWData->mCPUAttached[idx])
10003 {
10004 settings::Cpu cpu;
10005 cpu.ulId = idx;
10006 data.llCpus.push_back(cpu);
10007 }
10008 }
10009 }
10010
10011 // memory
10012 data.ulMemorySizeMB = mHWData->mMemorySize;
10013 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10014
10015 // firmware
10016 data.firmwareType = mHWData->mFirmwareType;
10017
10018 // HID
10019 data.pointingHIDType = mHWData->mPointingHIDType;
10020 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10021
10022 // chipset
10023 data.chipsetType = mHWData->mChipsetType;
10024
10025 // paravirt
10026 data.paravirtProvider = mHWData->mParavirtProvider;
10027
10028
10029 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10030
10031 // HPET
10032 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10033
10034 // boot order
10035 data.mapBootOrder.clear();
10036 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10037 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10038
10039 // display
10040 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10041 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10042 data.cMonitors = mHWData->mMonitorCount;
10043 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10044 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10045 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10046 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10047 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10048 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10049 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10050 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10051 {
10052 if (mHWData->maVideoCaptureScreens[i])
10053 ASMBitSet(&data.u64VideoCaptureScreens, i);
10054 else
10055 ASMBitClear(&data.u64VideoCaptureScreens, i);
10056 }
10057 /* store relative video capture file if possible */
10058 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10059
10060 /* VRDEServer settings (optional) */
10061 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10062 if (FAILED(rc)) throw rc;
10063
10064 /* BIOS (required) */
10065 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10066 if (FAILED(rc)) throw rc;
10067
10068 /* USB Controller (required) */
10069 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10070 {
10071 ComObjPtr<USBController> ctrl = *it;
10072 settings::USBController settingsCtrl;
10073
10074 settingsCtrl.strName = ctrl->i_getName();
10075 settingsCtrl.enmType = ctrl->i_getControllerType();
10076
10077 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10078 }
10079
10080 /* USB device filters (required) */
10081 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10082 if (FAILED(rc)) throw rc;
10083
10084 /* Network adapters (required) */
10085 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10086 data.llNetworkAdapters.clear();
10087 /* Write out only the nominal number of network adapters for this
10088 * chipset type. Since Machine::commit() hasn't been called there
10089 * may be extra NIC settings in the vector. */
10090 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10091 {
10092 settings::NetworkAdapter nic;
10093 nic.ulSlot = (uint32_t)slot;
10094 /* paranoia check... must not be NULL, but must not crash either. */
10095 if (mNetworkAdapters[slot])
10096 {
10097 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10098 if (FAILED(rc)) throw rc;
10099
10100 data.llNetworkAdapters.push_back(nic);
10101 }
10102 }
10103
10104 /* Serial ports */
10105 data.llSerialPorts.clear();
10106 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10107 {
10108 settings::SerialPort s;
10109 s.ulSlot = slot;
10110 rc = mSerialPorts[slot]->i_saveSettings(s);
10111 if (FAILED(rc)) return rc;
10112
10113 data.llSerialPorts.push_back(s);
10114 }
10115
10116 /* Parallel ports */
10117 data.llParallelPorts.clear();
10118 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10119 {
10120 settings::ParallelPort p;
10121 p.ulSlot = slot;
10122 rc = mParallelPorts[slot]->i_saveSettings(p);
10123 if (FAILED(rc)) return rc;
10124
10125 data.llParallelPorts.push_back(p);
10126 }
10127
10128 /* Audio adapter */
10129 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10130 if (FAILED(rc)) return rc;
10131
10132 /* Shared folders */
10133 data.llSharedFolders.clear();
10134 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10135 it != mHWData->mSharedFolders.end();
10136 ++it)
10137 {
10138 SharedFolder *pSF = *it;
10139 AutoCaller sfCaller(pSF);
10140 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10141 settings::SharedFolder sf;
10142 sf.strName = pSF->i_getName();
10143 sf.strHostPath = pSF->i_getHostPath();
10144 sf.fWritable = !!pSF->i_isWritable();
10145 sf.fAutoMount = !!pSF->i_isAutoMounted();
10146
10147 data.llSharedFolders.push_back(sf);
10148 }
10149
10150 // clipboard
10151 data.clipboardMode = mHWData->mClipboardMode;
10152
10153 // drag'n'drop
10154 data.dndMode = mHWData->mDnDMode;
10155
10156 /* Guest */
10157 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10158
10159 // IO settings
10160 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10161 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10162
10163 /* BandwidthControl (required) */
10164 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10165 if (FAILED(rc)) throw rc;
10166
10167 /* Host PCI devices */
10168 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10169 it != mHWData->mPCIDeviceAssignments.end();
10170 ++it)
10171 {
10172 ComObjPtr<PCIDeviceAttachment> pda = *it;
10173 settings::HostPCIDeviceAttachment hpda;
10174
10175 rc = pda->i_saveSettings(hpda);
10176 if (FAILED(rc)) throw rc;
10177
10178 data.pciAttachments.push_back(hpda);
10179 }
10180
10181
10182 // guest properties
10183 data.llGuestProperties.clear();
10184#ifdef VBOX_WITH_GUEST_PROPS
10185 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10186 it != mHWData->mGuestProperties.end();
10187 ++it)
10188 {
10189 HWData::GuestProperty property = it->second;
10190
10191 /* Remove transient guest properties at shutdown unless we
10192 * are saving state */
10193 if ( ( mData->mMachineState == MachineState_PoweredOff
10194 || mData->mMachineState == MachineState_Aborted
10195 || mData->mMachineState == MachineState_Teleported)
10196 && ( property.mFlags & guestProp::TRANSIENT
10197 || property.mFlags & guestProp::TRANSRESET))
10198 continue;
10199 settings::GuestProperty prop;
10200 prop.strName = it->first;
10201 prop.strValue = property.strValue;
10202 prop.timestamp = property.mTimestamp;
10203 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10204 guestProp::writeFlags(property.mFlags, szFlags);
10205 prop.strFlags = szFlags;
10206
10207 data.llGuestProperties.push_back(prop);
10208 }
10209
10210 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10211 /* I presume this doesn't require a backup(). */
10212 mData->mGuestPropertiesModified = FALSE;
10213#endif /* VBOX_WITH_GUEST_PROPS defined */
10214
10215 *pDbg = mHWData->mDebugging;
10216 *pAutostart = mHWData->mAutostart;
10217
10218 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10219 }
10220 catch(std::bad_alloc &)
10221 {
10222 return E_OUTOFMEMORY;
10223 }
10224
10225 AssertComRC(rc);
10226 return rc;
10227}
10228
10229/**
10230 * Saves the storage controller configuration.
10231 *
10232 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10233 */
10234HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10235{
10236 data.llStorageControllers.clear();
10237
10238 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10239 it != mStorageControllers->end();
10240 ++it)
10241 {
10242 HRESULT rc;
10243 ComObjPtr<StorageController> pCtl = *it;
10244
10245 settings::StorageController ctl;
10246 ctl.strName = pCtl->i_getName();
10247 ctl.controllerType = pCtl->i_getControllerType();
10248 ctl.storageBus = pCtl->i_getStorageBus();
10249 ctl.ulInstance = pCtl->i_getInstance();
10250 ctl.fBootable = pCtl->i_getBootable();
10251
10252 /* Save the port count. */
10253 ULONG portCount;
10254 rc = pCtl->COMGETTER(PortCount)(&portCount);
10255 ComAssertComRCRet(rc, rc);
10256 ctl.ulPortCount = portCount;
10257
10258 /* Save fUseHostIOCache */
10259 BOOL fUseHostIOCache;
10260 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10261 ComAssertComRCRet(rc, rc);
10262 ctl.fUseHostIOCache = !!fUseHostIOCache;
10263
10264 /* Save IDE emulation settings. */
10265 if (ctl.controllerType == StorageControllerType_IntelAhci)
10266 {
10267 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10268 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10269 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10270 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10271 )
10272 ComAssertComRCRet(rc, rc);
10273 }
10274
10275 /* save the devices now. */
10276 rc = i_saveStorageDevices(pCtl, ctl);
10277 ComAssertComRCRet(rc, rc);
10278
10279 data.llStorageControllers.push_back(ctl);
10280 }
10281
10282 return S_OK;
10283}
10284
10285/**
10286 * Saves the hard disk configuration.
10287 */
10288HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10289 settings::StorageController &data)
10290{
10291 MediaData::AttachmentList atts;
10292
10293 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10294 if (FAILED(rc)) return rc;
10295
10296 data.llAttachedDevices.clear();
10297 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10298 it != atts.end();
10299 ++it)
10300 {
10301 settings::AttachedDevice dev;
10302 IMediumAttachment *iA = *it;
10303 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10304 Medium *pMedium = pAttach->i_getMedium();
10305
10306 dev.deviceType = pAttach->i_getType();
10307 dev.lPort = pAttach->i_getPort();
10308 dev.lDevice = pAttach->i_getDevice();
10309 dev.fPassThrough = pAttach->i_getPassthrough();
10310 dev.fHotPluggable = pAttach->i_getHotPluggable();
10311 if (pMedium)
10312 {
10313 if (pMedium->i_isHostDrive())
10314 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10315 else
10316 dev.uuid = pMedium->i_getId();
10317 dev.fTempEject = pAttach->i_getTempEject();
10318 dev.fNonRotational = pAttach->i_getNonRotational();
10319 dev.fDiscard = pAttach->i_getDiscard();
10320 }
10321
10322 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10323
10324 data.llAttachedDevices.push_back(dev);
10325 }
10326
10327 return S_OK;
10328}
10329
10330/**
10331 * Saves machine state settings as defined by aFlags
10332 * (SaveSTS_* values).
10333 *
10334 * @param aFlags Combination of SaveSTS_* flags.
10335 *
10336 * @note Locks objects for writing.
10337 */
10338HRESULT Machine::i_saveStateSettings(int aFlags)
10339{
10340 if (aFlags == 0)
10341 return S_OK;
10342
10343 AutoCaller autoCaller(this);
10344 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10345
10346 /* This object's write lock is also necessary to serialize file access
10347 * (prevent concurrent reads and writes) */
10348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10349
10350 HRESULT rc = S_OK;
10351
10352 Assert(mData->pMachineConfigFile);
10353
10354 try
10355 {
10356 if (aFlags & SaveSTS_CurStateModified)
10357 mData->pMachineConfigFile->fCurrentStateModified = true;
10358
10359 if (aFlags & SaveSTS_StateFilePath)
10360 {
10361 if (!mSSData->strStateFilePath.isEmpty())
10362 /* try to make the file name relative to the settings file dir */
10363 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10364 else
10365 mData->pMachineConfigFile->strStateFile.setNull();
10366 }
10367
10368 if (aFlags & SaveSTS_StateTimeStamp)
10369 {
10370 Assert( mData->mMachineState != MachineState_Aborted
10371 || mSSData->strStateFilePath.isEmpty());
10372
10373 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10374
10375 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10376//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10377 }
10378
10379 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10380 }
10381 catch (...)
10382 {
10383 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10384 }
10385
10386 return rc;
10387}
10388
10389/**
10390 * Ensures that the given medium is added to a media registry. If this machine
10391 * was created with 4.0 or later, then the machine registry is used. Otherwise
10392 * the global VirtualBox media registry is used.
10393 *
10394 * Caller must NOT hold machine lock, media tree or any medium locks!
10395 *
10396 * @param pMedium
10397 */
10398void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10399{
10400 /* Paranoia checks: do not hold machine or media tree locks. */
10401 AssertReturnVoid(!isWriteLockOnCurrentThread());
10402 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10403
10404 ComObjPtr<Medium> pBase;
10405 {
10406 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10407 pBase = pMedium->i_getBase();
10408 }
10409
10410 /* Paranoia checks: do not hold medium locks. */
10411 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10412 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10413
10414 // decide which medium registry to use now that the medium is attached:
10415 Guid uuid;
10416 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10417 // machine XML is VirtualBox 4.0 or higher:
10418 uuid = i_getId(); // machine UUID
10419 else
10420 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10421
10422 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10423 mParent->i_markRegistryModified(uuid);
10424
10425 /* For more complex hard disk structures it can happen that the base
10426 * medium isn't yet associated with any medium registry. Do that now. */
10427 if (pMedium != pBase)
10428 {
10429 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10430 mParent->i_markRegistryModified(uuid);
10431 }
10432}
10433
10434/**
10435 * Creates differencing hard disks for all normal hard disks attached to this
10436 * machine and a new set of attachments to refer to created disks.
10437 *
10438 * Used when taking a snapshot or when deleting the current state. Gets called
10439 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10440 *
10441 * This method assumes that mMediaData contains the original hard disk attachments
10442 * it needs to create diffs for. On success, these attachments will be replaced
10443 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10444 * called to delete created diffs which will also rollback mMediaData and restore
10445 * whatever was backed up before calling this method.
10446 *
10447 * Attachments with non-normal hard disks are left as is.
10448 *
10449 * If @a aOnline is @c false then the original hard disks that require implicit
10450 * diffs will be locked for reading. Otherwise it is assumed that they are
10451 * already locked for writing (when the VM was started). Note that in the latter
10452 * case it is responsibility of the caller to lock the newly created diffs for
10453 * writing if this method succeeds.
10454 *
10455 * @param aProgress Progress object to run (must contain at least as
10456 * many operations left as the number of hard disks
10457 * attached).
10458 * @param aOnline Whether the VM was online prior to this operation.
10459 *
10460 * @note The progress object is not marked as completed, neither on success nor
10461 * on failure. This is a responsibility of the caller.
10462 *
10463 * @note Locks this object and the media tree for writing.
10464 */
10465HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10466 ULONG aWeight,
10467 bool aOnline)
10468{
10469 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10470
10471 AutoCaller autoCaller(this);
10472 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10473
10474 AutoMultiWriteLock2 alock(this->lockHandle(),
10475 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10476
10477 /* must be in a protective state because we release the lock below */
10478 AssertReturn( mData->mMachineState == MachineState_Saving
10479 || mData->mMachineState == MachineState_LiveSnapshotting
10480 || mData->mMachineState == MachineState_RestoringSnapshot
10481 || mData->mMachineState == MachineState_DeletingSnapshot
10482 , E_FAIL);
10483
10484 HRESULT rc = S_OK;
10485
10486 // use appropriate locked media map (online or offline)
10487 MediumLockListMap lockedMediaOffline;
10488 MediumLockListMap *lockedMediaMap;
10489 if (aOnline)
10490 lockedMediaMap = &mData->mSession.mLockedMedia;
10491 else
10492 lockedMediaMap = &lockedMediaOffline;
10493
10494 try
10495 {
10496 if (!aOnline)
10497 {
10498 /* lock all attached hard disks early to detect "in use"
10499 * situations before creating actual diffs */
10500 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10501 it != mMediaData->mAttachments.end();
10502 ++it)
10503 {
10504 MediumAttachment* pAtt = *it;
10505 if (pAtt->i_getType() == DeviceType_HardDisk)
10506 {
10507 Medium* pMedium = pAtt->i_getMedium();
10508 Assert(pMedium);
10509
10510 MediumLockList *pMediumLockList(new MediumLockList());
10511 alock.release();
10512 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10513 false /* fMediumLockWrite */,
10514 false /* fMediumLockWriteAll */,
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 DeviceType_HardDisk);
10606 if (FAILED(rc)) throw rc;
10607
10608 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10609 * the push_back? Looks like we're going to release medium with the
10610 * wrong kind of lock (general issue with if we fail anywhere at all)
10611 * and an orphaned VDI in the snapshots folder. */
10612
10613 /* update the appropriate lock list */
10614 MediumLockList *pMediumLockList;
10615 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10616 AssertComRCThrowRC(rc);
10617 if (aOnline)
10618 {
10619 alock.release();
10620 /* The currently attached medium will be read-only, change
10621 * the lock type to read. */
10622 rc = pMediumLockList->Update(pMedium, false);
10623 alock.acquire();
10624 AssertComRCThrowRC(rc);
10625 }
10626
10627 /* release the locks before the potentially lengthy operation */
10628 alock.release();
10629 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10630 pMediumLockList,
10631 NULL /* aProgress */,
10632 true /* aWait */);
10633 alock.acquire();
10634 if (FAILED(rc)) throw rc;
10635
10636 /* actual lock list update is done in Medium::commitMedia */
10637
10638 rc = diff->i_addBackReference(mData->mUuid);
10639 AssertComRCThrowRC(rc);
10640
10641 /* add a new attachment */
10642 ComObjPtr<MediumAttachment> attachment;
10643 attachment.createObject();
10644 rc = attachment->init(this,
10645 diff,
10646 pAtt->i_getControllerName(),
10647 pAtt->i_getPort(),
10648 pAtt->i_getDevice(),
10649 DeviceType_HardDisk,
10650 true /* aImplicit */,
10651 false /* aPassthrough */,
10652 false /* aTempEject */,
10653 pAtt->i_getNonRotational(),
10654 pAtt->i_getDiscard(),
10655 pAtt->i_getHotPluggable(),
10656 pAtt->i_getBandwidthGroup());
10657 if (FAILED(rc)) throw rc;
10658
10659 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10660 AssertComRCThrowRC(rc);
10661 mMediaData->mAttachments.push_back(attachment);
10662 }
10663 }
10664 catch (HRESULT aRC) { rc = aRC; }
10665
10666 /* unlock all hard disks we locked when there is no VM */
10667 if (!aOnline)
10668 {
10669 ErrorInfoKeeper eik;
10670
10671 HRESULT rc1 = lockedMediaMap->Clear();
10672 AssertComRC(rc1);
10673 }
10674
10675 return rc;
10676}
10677
10678/**
10679 * Deletes implicit differencing hard disks created either by
10680 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10681 *
10682 * Note that to delete hard disks created by #AttachDevice() this method is
10683 * called from #fixupMedia() when the changes are rolled back.
10684 *
10685 * @note Locks this object and the media tree for writing.
10686 */
10687HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10688{
10689 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10690
10691 AutoCaller autoCaller(this);
10692 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10693
10694 AutoMultiWriteLock2 alock(this->lockHandle(),
10695 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10696
10697 /* We absolutely must have backed up state. */
10698 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10699
10700 /* Check if there are any implicitly created diff images. */
10701 bool fImplicitDiffs = false;
10702 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10703 it != mMediaData->mAttachments.end();
10704 ++it)
10705 {
10706 const ComObjPtr<MediumAttachment> &pAtt = *it;
10707 if (pAtt->i_isImplicit())
10708 {
10709 fImplicitDiffs = true;
10710 break;
10711 }
10712 }
10713 /* If there is nothing to do, leave early. This saves lots of image locking
10714 * effort. It also avoids a MachineStateChanged event without real reason.
10715 * This is important e.g. when loading a VM config, because there should be
10716 * no events. Otherwise API clients can become thoroughly confused for
10717 * inaccessible VMs (the code for loading VM configs uses this method for
10718 * cleanup if the config makes no sense), as they take such events as an
10719 * indication that the VM is alive, and they would force the VM config to
10720 * be reread, leading to an endless loop. */
10721 if (!fImplicitDiffs)
10722 return S_OK;
10723
10724 HRESULT rc = S_OK;
10725 MachineState_T oldState = mData->mMachineState;
10726
10727 /* will release the lock before the potentially lengthy operation,
10728 * so protect with the special state (unless already protected) */
10729 if ( oldState != MachineState_Saving
10730 && oldState != MachineState_LiveSnapshotting
10731 && oldState != MachineState_RestoringSnapshot
10732 && oldState != MachineState_DeletingSnapshot
10733 && oldState != MachineState_DeletingSnapshotOnline
10734 && oldState != MachineState_DeletingSnapshotPaused
10735 )
10736 i_setMachineState(MachineState_SettingUp);
10737
10738 // use appropriate locked media map (online or offline)
10739 MediumLockListMap lockedMediaOffline;
10740 MediumLockListMap *lockedMediaMap;
10741 if (aOnline)
10742 lockedMediaMap = &mData->mSession.mLockedMedia;
10743 else
10744 lockedMediaMap = &lockedMediaOffline;
10745
10746 try
10747 {
10748 if (!aOnline)
10749 {
10750 /* lock all attached hard disks early to detect "in use"
10751 * situations before deleting actual diffs */
10752 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10753 it != mMediaData->mAttachments.end();
10754 ++it)
10755 {
10756 MediumAttachment* pAtt = *it;
10757 if (pAtt->i_getType() == DeviceType_HardDisk)
10758 {
10759 Medium* pMedium = pAtt->i_getMedium();
10760 Assert(pMedium);
10761
10762 MediumLockList *pMediumLockList(new MediumLockList());
10763 alock.release();
10764 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10765 false /* fMediumLockWrite */,
10766 false /* fMediumLockWriteAll */,
10767 NULL,
10768 *pMediumLockList);
10769 alock.acquire();
10770
10771 if (FAILED(rc))
10772 {
10773 delete pMediumLockList;
10774 throw rc;
10775 }
10776
10777 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10778 if (FAILED(rc))
10779 throw rc;
10780 }
10781 }
10782
10783 if (FAILED(rc))
10784 throw rc;
10785 } // end of offline
10786
10787 /* Lock lists are now up to date and include implicitly created media */
10788
10789 /* Go through remembered attachments and delete all implicitly created
10790 * diffs and fix up the attachment information */
10791 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10792 MediaData::AttachmentList implicitAtts;
10793 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10794 it != mMediaData->mAttachments.end();
10795 ++it)
10796 {
10797 ComObjPtr<MediumAttachment> pAtt = *it;
10798 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10799 if (pMedium.isNull())
10800 continue;
10801
10802 // Implicit attachments go on the list for deletion and back references are removed.
10803 if (pAtt->i_isImplicit())
10804 {
10805 /* Deassociate and mark for deletion */
10806 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10807 rc = pMedium->i_removeBackReference(mData->mUuid);
10808 if (FAILED(rc))
10809 throw rc;
10810 implicitAtts.push_back(pAtt);
10811 continue;
10812 }
10813
10814 /* Was this medium attached before? */
10815 if (!i_findAttachment(oldAtts, pMedium))
10816 {
10817 /* no: de-associate */
10818 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10819 rc = pMedium->i_removeBackReference(mData->mUuid);
10820 if (FAILED(rc))
10821 throw rc;
10822 continue;
10823 }
10824 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10825 }
10826
10827 /* If there are implicit attachments to delete, throw away the lock
10828 * map contents (which will unlock all media) since the medium
10829 * attachments will be rolled back. Below we need to completely
10830 * recreate the lock map anyway since it is infinitely complex to
10831 * do this incrementally (would need reconstructing each attachment
10832 * change, which would be extremely hairy). */
10833 if (implicitAtts.size() != 0)
10834 {
10835 ErrorInfoKeeper eik;
10836
10837 HRESULT rc1 = lockedMediaMap->Clear();
10838 AssertComRC(rc1);
10839 }
10840
10841 /* rollback hard disk changes */
10842 mMediaData.rollback();
10843
10844 MultiResult mrc(S_OK);
10845
10846 // Delete unused implicit diffs.
10847 if (implicitAtts.size() != 0)
10848 {
10849 alock.release();
10850
10851 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10852 {
10853 // Remove medium associated with this attachment.
10854 ComObjPtr<MediumAttachment> pAtt = *it;
10855 Assert(pAtt);
10856 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10857 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10858 Assert(pMedium);
10859
10860 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10861 // continue on delete failure, just collect error messages
10862 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10863 pMedium->i_getLocationFull().c_str() ));
10864 mrc = rc;
10865 }
10866
10867 alock.acquire();
10868
10869 /* if there is a VM recreate media lock map as mentioned above,
10870 * otherwise it is a waste of time and we leave things unlocked */
10871 if (aOnline)
10872 {
10873 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10874 /* must never be NULL, but better safe than sorry */
10875 if (!pMachine.isNull())
10876 {
10877 alock.release();
10878 rc = mData->mSession.mMachine->i_lockMedia();
10879 alock.acquire();
10880 if (FAILED(rc))
10881 throw rc;
10882 }
10883 }
10884 }
10885 }
10886 catch (HRESULT aRC) {rc = aRC;}
10887
10888 if (mData->mMachineState == MachineState_SettingUp)
10889 i_setMachineState(oldState);
10890
10891 /* unlock all hard disks we locked when there is no VM */
10892 if (!aOnline)
10893 {
10894 ErrorInfoKeeper eik;
10895
10896 HRESULT rc1 = lockedMediaMap->Clear();
10897 AssertComRC(rc1);
10898 }
10899
10900 return rc;
10901}
10902
10903
10904/**
10905 * Looks through the given list of media attachments for one with the given parameters
10906 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10907 * can be searched as well if needed.
10908 *
10909 * @param list
10910 * @param aControllerName
10911 * @param aControllerPort
10912 * @param aDevice
10913 * @return
10914 */
10915MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10916 IN_BSTR aControllerName,
10917 LONG aControllerPort,
10918 LONG aDevice)
10919{
10920 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10921 {
10922 MediumAttachment *pAttach = *it;
10923 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10924 return pAttach;
10925 }
10926
10927 return NULL;
10928}
10929
10930/**
10931 * Looks through the given list of media attachments for one with the given parameters
10932 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10933 * can be searched as well if needed.
10934 *
10935 * @param list
10936 * @param aControllerName
10937 * @param aControllerPort
10938 * @param aDevice
10939 * @return
10940 */
10941MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10942 ComObjPtr<Medium> pMedium)
10943{
10944 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10945 {
10946 MediumAttachment *pAttach = *it;
10947 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10948 if (pMediumThis == pMedium)
10949 return pAttach;
10950 }
10951
10952 return NULL;
10953}
10954
10955/**
10956 * Looks through the given list of media attachments for one with the given parameters
10957 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10958 * can be searched as well if needed.
10959 *
10960 * @param list
10961 * @param aControllerName
10962 * @param aControllerPort
10963 * @param aDevice
10964 * @return
10965 */
10966MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10967 Guid &id)
10968{
10969 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10970 {
10971 MediumAttachment *pAttach = *it;
10972 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10973 if (pMediumThis->i_getId() == id)
10974 return pAttach;
10975 }
10976
10977 return NULL;
10978}
10979
10980/**
10981 * Main implementation for Machine::DetachDevice. This also gets called
10982 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10983 *
10984 * @param pAttach Medium attachment to detach.
10985 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10986 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10987 * SnapshotMachine, and this must be its snapshot.
10988 * @return
10989 */
10990HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10991 AutoWriteLock &writeLock,
10992 Snapshot *pSnapshot)
10993{
10994 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10995 DeviceType_T mediumType = pAttach->i_getType();
10996
10997 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10998
10999 if (pAttach->i_isImplicit())
11000 {
11001 /* attempt to implicitly delete the implicitly created diff */
11002
11003 /// @todo move the implicit flag from MediumAttachment to Medium
11004 /// and forbid any hard disk operation when it is implicit. Or maybe
11005 /// a special media state for it to make it even more simple.
11006
11007 Assert(mMediaData.isBackedUp());
11008
11009 /* will release the lock before the potentially lengthy operation, so
11010 * protect with the special state */
11011 MachineState_T oldState = mData->mMachineState;
11012 i_setMachineState(MachineState_SettingUp);
11013
11014 writeLock.release();
11015
11016 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11017 true /*aWait*/);
11018
11019 writeLock.acquire();
11020
11021 i_setMachineState(oldState);
11022
11023 if (FAILED(rc)) return rc;
11024 }
11025
11026 i_setModified(IsModified_Storage);
11027 mMediaData.backup();
11028 mMediaData->mAttachments.remove(pAttach);
11029
11030 if (!oldmedium.isNull())
11031 {
11032 // if this is from a snapshot, do not defer detachment to commitMedia()
11033 if (pSnapshot)
11034 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11035 // else if non-hard disk media, do not defer detachment to commitMedia() either
11036 else if (mediumType != DeviceType_HardDisk)
11037 oldmedium->i_removeBackReference(mData->mUuid);
11038 }
11039
11040 return S_OK;
11041}
11042
11043/**
11044 * Goes thru all media of the given list and
11045 *
11046 * 1) calls i_detachDevice() on each of them for this machine and
11047 * 2) adds all Medium objects found in the process to the given list,
11048 * depending on cleanupMode.
11049 *
11050 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11051 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11052 * media to the list.
11053 *
11054 * This gets called from Machine::Unregister, both for the actual Machine and
11055 * the SnapshotMachine objects that might be found in the snapshots.
11056 *
11057 * Requires caller and locking. The machine lock must be passed in because it
11058 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11059 *
11060 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11061 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11062 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11063 * Full, then all media get added;
11064 * otherwise no media get added.
11065 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11066 * @return
11067 */
11068HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11069 Snapshot *pSnapshot,
11070 CleanupMode_T cleanupMode,
11071 MediaList &llMedia)
11072{
11073 Assert(isWriteLockOnCurrentThread());
11074
11075 HRESULT rc;
11076
11077 // make a temporary list because i_detachDevice invalidates iterators into
11078 // mMediaData->mAttachments
11079 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11080
11081 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11082 {
11083 ComObjPtr<MediumAttachment> &pAttach = *it;
11084 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11085
11086 if (!pMedium.isNull())
11087 {
11088 AutoCaller mac(pMedium);
11089 if (FAILED(mac.rc())) return mac.rc();
11090 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11091 DeviceType_T devType = pMedium->i_getDeviceType();
11092 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11093 && devType == DeviceType_HardDisk)
11094 || (cleanupMode == CleanupMode_Full)
11095 )
11096 {
11097 llMedia.push_back(pMedium);
11098 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11099 /* Not allowed to keep this lock as below we need the parent
11100 * medium lock, and the lock order is parent to child. */
11101 lock.release();
11102 /*
11103 * Search for medias which are not attached to any machine, but
11104 * in the chain to an attached disk. Mediums are only consided
11105 * if they are:
11106 * - have only one child
11107 * - no references to any machines
11108 * - are of normal medium type
11109 */
11110 while (!pParent.isNull())
11111 {
11112 AutoCaller mac1(pParent);
11113 if (FAILED(mac1.rc())) return mac1.rc();
11114 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11115 if (pParent->i_getChildren().size() == 1)
11116 {
11117 if ( pParent->i_getMachineBackRefCount() == 0
11118 && pParent->i_getType() == MediumType_Normal
11119 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11120 llMedia.push_back(pParent);
11121 }
11122 else
11123 break;
11124 pParent = pParent->i_getParent();
11125 }
11126 }
11127 }
11128
11129 // real machine: then we need to use the proper method
11130 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11131
11132 if (FAILED(rc))
11133 return rc;
11134 }
11135
11136 return S_OK;
11137}
11138
11139/**
11140 * Perform deferred hard disk detachments.
11141 *
11142 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11143 * backed up).
11144 *
11145 * If @a aOnline is @c true then this method will also unlock the old hard disks
11146 * for which the new implicit diffs were created and will lock these new diffs for
11147 * writing.
11148 *
11149 * @param aOnline Whether the VM was online prior to this operation.
11150 *
11151 * @note Locks this object for writing!
11152 */
11153void Machine::i_commitMedia(bool aOnline /*= false*/)
11154{
11155 AutoCaller autoCaller(this);
11156 AssertComRCReturnVoid(autoCaller.rc());
11157
11158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11159
11160 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11161
11162 HRESULT rc = S_OK;
11163
11164 /* no attach/detach operations -- nothing to do */
11165 if (!mMediaData.isBackedUp())
11166 return;
11167
11168 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11169 bool fMediaNeedsLocking = false;
11170
11171 /* enumerate new attachments */
11172 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11173 it != mMediaData->mAttachments.end();
11174 ++it)
11175 {
11176 MediumAttachment *pAttach = *it;
11177
11178 pAttach->i_commit();
11179
11180 Medium* pMedium = pAttach->i_getMedium();
11181 bool fImplicit = pAttach->i_isImplicit();
11182
11183 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11184 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11185 fImplicit));
11186
11187 /** @todo convert all this Machine-based voodoo to MediumAttachment
11188 * based commit logic. */
11189 if (fImplicit)
11190 {
11191 /* convert implicit attachment to normal */
11192 pAttach->i_setImplicit(false);
11193
11194 if ( aOnline
11195 && pMedium
11196 && pAttach->i_getType() == DeviceType_HardDisk
11197 )
11198 {
11199 /* update the appropriate lock list */
11200 MediumLockList *pMediumLockList;
11201 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11202 AssertComRC(rc);
11203 if (pMediumLockList)
11204 {
11205 /* unlock if there's a need to change the locking */
11206 if (!fMediaNeedsLocking)
11207 {
11208 rc = mData->mSession.mLockedMedia.Unlock();
11209 AssertComRC(rc);
11210 fMediaNeedsLocking = true;
11211 }
11212 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11213 AssertComRC(rc);
11214 rc = pMediumLockList->Append(pMedium, true);
11215 AssertComRC(rc);
11216 }
11217 }
11218
11219 continue;
11220 }
11221
11222 if (pMedium)
11223 {
11224 /* was this medium attached before? */
11225 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11226 {
11227 MediumAttachment *pOldAttach = *oldIt;
11228 if (pOldAttach->i_getMedium() == pMedium)
11229 {
11230 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11231
11232 /* yes: remove from old to avoid de-association */
11233 oldAtts.erase(oldIt);
11234 break;
11235 }
11236 }
11237 }
11238 }
11239
11240 /* enumerate remaining old attachments and de-associate from the
11241 * current machine state */
11242 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11243 {
11244 MediumAttachment *pAttach = *it;
11245 Medium* pMedium = pAttach->i_getMedium();
11246
11247 /* Detach only hard disks, since DVD/floppy media is detached
11248 * instantly in MountMedium. */
11249 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11250 {
11251 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11252
11253 /* now de-associate from the current machine state */
11254 rc = pMedium->i_removeBackReference(mData->mUuid);
11255 AssertComRC(rc);
11256
11257 if (aOnline)
11258 {
11259 /* unlock since medium is not used anymore */
11260 MediumLockList *pMediumLockList;
11261 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11262 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11263 {
11264 /* this happens for online snapshots, there the attachment
11265 * is changing, but only to a diff image created under
11266 * the old one, so there is no separate lock list */
11267 Assert(!pMediumLockList);
11268 }
11269 else
11270 {
11271 AssertComRC(rc);
11272 if (pMediumLockList)
11273 {
11274 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11275 AssertComRC(rc);
11276 }
11277 }
11278 }
11279 }
11280 }
11281
11282 /* take media locks again so that the locking state is consistent */
11283 if (fMediaNeedsLocking)
11284 {
11285 Assert(aOnline);
11286 rc = mData->mSession.mLockedMedia.Lock();
11287 AssertComRC(rc);
11288 }
11289
11290 /* commit the hard disk changes */
11291 mMediaData.commit();
11292
11293 if (i_isSessionMachine())
11294 {
11295 /*
11296 * Update the parent machine to point to the new owner.
11297 * This is necessary because the stored parent will point to the
11298 * session machine otherwise and cause crashes or errors later
11299 * when the session machine gets invalid.
11300 */
11301 /** @todo Change the MediumAttachment class to behave like any other
11302 * class in this regard by creating peer MediumAttachment
11303 * objects for session machines and share the data with the peer
11304 * machine.
11305 */
11306 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11307 it != mMediaData->mAttachments.end();
11308 ++it)
11309 (*it)->i_updateParentMachine(mPeer);
11310
11311 /* attach new data to the primary machine and reshare it */
11312 mPeer->mMediaData.attach(mMediaData);
11313 }
11314
11315 return;
11316}
11317
11318/**
11319 * Perform deferred deletion of implicitly created diffs.
11320 *
11321 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11322 * backed up).
11323 *
11324 * @note Locks this object for writing!
11325 */
11326void Machine::i_rollbackMedia()
11327{
11328 AutoCaller autoCaller(this);
11329 AssertComRCReturnVoid(autoCaller.rc());
11330
11331 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11332 LogFlowThisFunc(("Entering rollbackMedia\n"));
11333
11334 HRESULT rc = S_OK;
11335
11336 /* no attach/detach operations -- nothing to do */
11337 if (!mMediaData.isBackedUp())
11338 return;
11339
11340 /* enumerate new attachments */
11341 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11342 it != mMediaData->mAttachments.end();
11343 ++it)
11344 {
11345 MediumAttachment *pAttach = *it;
11346 /* Fix up the backrefs for DVD/floppy media. */
11347 if (pAttach->i_getType() != DeviceType_HardDisk)
11348 {
11349 Medium* pMedium = pAttach->i_getMedium();
11350 if (pMedium)
11351 {
11352 rc = pMedium->i_removeBackReference(mData->mUuid);
11353 AssertComRC(rc);
11354 }
11355 }
11356
11357 (*it)->i_rollback();
11358
11359 pAttach = *it;
11360 /* Fix up the backrefs for DVD/floppy media. */
11361 if (pAttach->i_getType() != DeviceType_HardDisk)
11362 {
11363 Medium* pMedium = pAttach->i_getMedium();
11364 if (pMedium)
11365 {
11366 rc = pMedium->i_addBackReference(mData->mUuid);
11367 AssertComRC(rc);
11368 }
11369 }
11370 }
11371
11372 /** @todo convert all this Machine-based voodoo to MediumAttachment
11373 * based rollback logic. */
11374 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11375
11376 return;
11377}
11378
11379/**
11380 * Returns true if the settings file is located in the directory named exactly
11381 * as the machine; this means, among other things, that the machine directory
11382 * should be auto-renamed.
11383 *
11384 * @param aSettingsDir if not NULL, the full machine settings file directory
11385 * name will be assigned there.
11386 *
11387 * @note Doesn't lock anything.
11388 * @note Not thread safe (must be called from this object's lock).
11389 */
11390bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11391{
11392 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11393 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11394 if (aSettingsDir)
11395 *aSettingsDir = strMachineDirName;
11396 strMachineDirName.stripPath(); // vmname
11397 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11398 strConfigFileOnly.stripPath() // vmname.vbox
11399 .stripSuffix(); // vmname
11400 /** @todo hack, make somehow use of ComposeMachineFilename */
11401 if (mUserData->s.fDirectoryIncludesUUID)
11402 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11403
11404 AssertReturn(!strMachineDirName.isEmpty(), false);
11405 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11406
11407 return strMachineDirName == strConfigFileOnly;
11408}
11409
11410/**
11411 * Discards all changes to machine settings.
11412 *
11413 * @param aNotify Whether to notify the direct session about changes or not.
11414 *
11415 * @note Locks objects for writing!
11416 */
11417void Machine::i_rollback(bool aNotify)
11418{
11419 AutoCaller autoCaller(this);
11420 AssertComRCReturn(autoCaller.rc(), (void)0);
11421
11422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11423
11424 if (!mStorageControllers.isNull())
11425 {
11426 if (mStorageControllers.isBackedUp())
11427 {
11428 /* unitialize all new devices (absent in the backed up list). */
11429 StorageControllerList::const_iterator it = mStorageControllers->begin();
11430 StorageControllerList *backedList = mStorageControllers.backedUpData();
11431 while (it != mStorageControllers->end())
11432 {
11433 if ( std::find(backedList->begin(), backedList->end(), *it)
11434 == backedList->end()
11435 )
11436 {
11437 (*it)->uninit();
11438 }
11439 ++it;
11440 }
11441
11442 /* restore the list */
11443 mStorageControllers.rollback();
11444 }
11445
11446 /* rollback any changes to devices after restoring the list */
11447 if (mData->flModifications & IsModified_Storage)
11448 {
11449 StorageControllerList::const_iterator it = mStorageControllers->begin();
11450 while (it != mStorageControllers->end())
11451 {
11452 (*it)->i_rollback();
11453 ++it;
11454 }
11455 }
11456 }
11457
11458 if (!mUSBControllers.isNull())
11459 {
11460 if (mUSBControllers.isBackedUp())
11461 {
11462 /* unitialize all new devices (absent in the backed up list). */
11463 USBControllerList::const_iterator it = mUSBControllers->begin();
11464 USBControllerList *backedList = mUSBControllers.backedUpData();
11465 while (it != mUSBControllers->end())
11466 {
11467 if ( std::find(backedList->begin(), backedList->end(), *it)
11468 == backedList->end()
11469 )
11470 {
11471 (*it)->uninit();
11472 }
11473 ++it;
11474 }
11475
11476 /* restore the list */
11477 mUSBControllers.rollback();
11478 }
11479
11480 /* rollback any changes to devices after restoring the list */
11481 if (mData->flModifications & IsModified_USB)
11482 {
11483 USBControllerList::const_iterator it = mUSBControllers->begin();
11484 while (it != mUSBControllers->end())
11485 {
11486 (*it)->i_rollback();
11487 ++it;
11488 }
11489 }
11490 }
11491
11492 mUserData.rollback();
11493
11494 mHWData.rollback();
11495
11496 if (mData->flModifications & IsModified_Storage)
11497 i_rollbackMedia();
11498
11499 if (mBIOSSettings)
11500 mBIOSSettings->i_rollback();
11501
11502 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11503 mVRDEServer->i_rollback();
11504
11505 if (mAudioAdapter)
11506 mAudioAdapter->i_rollback();
11507
11508 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11509 mUSBDeviceFilters->i_rollback();
11510
11511 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11512 mBandwidthControl->i_rollback();
11513
11514 if (!mHWData.isNull())
11515 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11516 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11517 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11518 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11519
11520 if (mData->flModifications & IsModified_NetworkAdapters)
11521 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11522 if ( mNetworkAdapters[slot]
11523 && mNetworkAdapters[slot]->i_isModified())
11524 {
11525 mNetworkAdapters[slot]->i_rollback();
11526 networkAdapters[slot] = mNetworkAdapters[slot];
11527 }
11528
11529 if (mData->flModifications & IsModified_SerialPorts)
11530 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11531 if ( mSerialPorts[slot]
11532 && mSerialPorts[slot]->i_isModified())
11533 {
11534 mSerialPorts[slot]->i_rollback();
11535 serialPorts[slot] = mSerialPorts[slot];
11536 }
11537
11538 if (mData->flModifications & IsModified_ParallelPorts)
11539 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11540 if ( mParallelPorts[slot]
11541 && mParallelPorts[slot]->i_isModified())
11542 {
11543 mParallelPorts[slot]->i_rollback();
11544 parallelPorts[slot] = mParallelPorts[slot];
11545 }
11546
11547 if (aNotify)
11548 {
11549 /* inform the direct session about changes */
11550
11551 ComObjPtr<Machine> that = this;
11552 uint32_t flModifications = mData->flModifications;
11553 alock.release();
11554
11555 if (flModifications & IsModified_SharedFolders)
11556 that->i_onSharedFolderChange();
11557
11558 if (flModifications & IsModified_VRDEServer)
11559 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11560 if (flModifications & IsModified_USB)
11561 that->i_onUSBControllerChange();
11562
11563 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11564 if (networkAdapters[slot])
11565 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11566 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11567 if (serialPorts[slot])
11568 that->i_onSerialPortChange(serialPorts[slot]);
11569 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11570 if (parallelPorts[slot])
11571 that->i_onParallelPortChange(parallelPorts[slot]);
11572
11573 if (flModifications & IsModified_Storage)
11574 that->i_onStorageControllerChange();
11575
11576#if 0
11577 if (flModifications & IsModified_BandwidthControl)
11578 that->onBandwidthControlChange();
11579#endif
11580 }
11581}
11582
11583/**
11584 * Commits all the changes to machine settings.
11585 *
11586 * Note that this operation is supposed to never fail.
11587 *
11588 * @note Locks this object and children for writing.
11589 */
11590void Machine::i_commit()
11591{
11592 AutoCaller autoCaller(this);
11593 AssertComRCReturnVoid(autoCaller.rc());
11594
11595 AutoCaller peerCaller(mPeer);
11596 AssertComRCReturnVoid(peerCaller.rc());
11597
11598 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11599
11600 /*
11601 * use safe commit to ensure Snapshot machines (that share mUserData)
11602 * will still refer to a valid memory location
11603 */
11604 mUserData.commitCopy();
11605
11606 mHWData.commit();
11607
11608 if (mMediaData.isBackedUp())
11609 i_commitMedia(Global::IsOnline(mData->mMachineState));
11610
11611 mBIOSSettings->i_commit();
11612 mVRDEServer->i_commit();
11613 mAudioAdapter->i_commit();
11614 mUSBDeviceFilters->i_commit();
11615 mBandwidthControl->i_commit();
11616
11617 /* Since mNetworkAdapters is a list which might have been changed (resized)
11618 * without using the Backupable<> template we need to handle the copying
11619 * of the list entries manually, including the creation of peers for the
11620 * new objects. */
11621 bool commitNetworkAdapters = false;
11622 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11623 if (mPeer)
11624 {
11625 /* commit everything, even the ones which will go away */
11626 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11627 mNetworkAdapters[slot]->i_commit();
11628 /* copy over the new entries, creating a peer and uninit the original */
11629 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11630 for (size_t slot = 0; slot < newSize; slot++)
11631 {
11632 /* look if this adapter has a peer device */
11633 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11634 if (!peer)
11635 {
11636 /* no peer means the adapter is a newly created one;
11637 * create a peer owning data this data share it with */
11638 peer.createObject();
11639 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11640 }
11641 mPeer->mNetworkAdapters[slot] = peer;
11642 }
11643 /* uninit any no longer needed network adapters */
11644 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11645 mNetworkAdapters[slot]->uninit();
11646 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11647 {
11648 if (mPeer->mNetworkAdapters[slot])
11649 mPeer->mNetworkAdapters[slot]->uninit();
11650 }
11651 /* Keep the original network adapter count until this point, so that
11652 * discarding a chipset type change will not lose settings. */
11653 mNetworkAdapters.resize(newSize);
11654 mPeer->mNetworkAdapters.resize(newSize);
11655 }
11656 else
11657 {
11658 /* we have no peer (our parent is the newly created machine);
11659 * just commit changes to the network adapters */
11660 commitNetworkAdapters = true;
11661 }
11662 if (commitNetworkAdapters)
11663 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11664 mNetworkAdapters[slot]->i_commit();
11665
11666 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11667 mSerialPorts[slot]->i_commit();
11668 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11669 mParallelPorts[slot]->i_commit();
11670
11671 bool commitStorageControllers = false;
11672
11673 if (mStorageControllers.isBackedUp())
11674 {
11675 mStorageControllers.commit();
11676
11677 if (mPeer)
11678 {
11679 /* Commit all changes to new controllers (this will reshare data with
11680 * peers for those who have peers) */
11681 StorageControllerList *newList = new StorageControllerList();
11682 StorageControllerList::const_iterator it = mStorageControllers->begin();
11683 while (it != mStorageControllers->end())
11684 {
11685 (*it)->i_commit();
11686
11687 /* look if this controller has a peer device */
11688 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11689 if (!peer)
11690 {
11691 /* no peer means the device is a newly created one;
11692 * create a peer owning data this device share it with */
11693 peer.createObject();
11694 peer->init(mPeer, *it, true /* aReshare */);
11695 }
11696 else
11697 {
11698 /* remove peer from the old list */
11699 mPeer->mStorageControllers->remove(peer);
11700 }
11701 /* and add it to the new list */
11702 newList->push_back(peer);
11703
11704 ++it;
11705 }
11706
11707 /* uninit old peer's controllers that are left */
11708 it = mPeer->mStorageControllers->begin();
11709 while (it != mPeer->mStorageControllers->end())
11710 {
11711 (*it)->uninit();
11712 ++it;
11713 }
11714
11715 /* attach new list of controllers to our peer */
11716 mPeer->mStorageControllers.attach(newList);
11717 }
11718 else
11719 {
11720 /* we have no peer (our parent is the newly created machine);
11721 * just commit changes to devices */
11722 commitStorageControllers = true;
11723 }
11724 }
11725 else
11726 {
11727 /* the list of controllers itself is not changed,
11728 * just commit changes to controllers themselves */
11729 commitStorageControllers = true;
11730 }
11731
11732 if (commitStorageControllers)
11733 {
11734 StorageControllerList::const_iterator it = mStorageControllers->begin();
11735 while (it != mStorageControllers->end())
11736 {
11737 (*it)->i_commit();
11738 ++it;
11739 }
11740 }
11741
11742 bool commitUSBControllers = false;
11743
11744 if (mUSBControllers.isBackedUp())
11745 {
11746 mUSBControllers.commit();
11747
11748 if (mPeer)
11749 {
11750 /* Commit all changes to new controllers (this will reshare data with
11751 * peers for those who have peers) */
11752 USBControllerList *newList = new USBControllerList();
11753 USBControllerList::const_iterator it = mUSBControllers->begin();
11754 while (it != mUSBControllers->end())
11755 {
11756 (*it)->i_commit();
11757
11758 /* look if this controller has a peer device */
11759 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11760 if (!peer)
11761 {
11762 /* no peer means the device is a newly created one;
11763 * create a peer owning data this device share it with */
11764 peer.createObject();
11765 peer->init(mPeer, *it, true /* aReshare */);
11766 }
11767 else
11768 {
11769 /* remove peer from the old list */
11770 mPeer->mUSBControllers->remove(peer);
11771 }
11772 /* and add it to the new list */
11773 newList->push_back(peer);
11774
11775 ++it;
11776 }
11777
11778 /* uninit old peer's controllers that are left */
11779 it = mPeer->mUSBControllers->begin();
11780 while (it != mPeer->mUSBControllers->end())
11781 {
11782 (*it)->uninit();
11783 ++it;
11784 }
11785
11786 /* attach new list of controllers to our peer */
11787 mPeer->mUSBControllers.attach(newList);
11788 }
11789 else
11790 {
11791 /* we have no peer (our parent is the newly created machine);
11792 * just commit changes to devices */
11793 commitUSBControllers = true;
11794 }
11795 }
11796 else
11797 {
11798 /* the list of controllers itself is not changed,
11799 * just commit changes to controllers themselves */
11800 commitUSBControllers = true;
11801 }
11802
11803 if (commitUSBControllers)
11804 {
11805 USBControllerList::const_iterator it = mUSBControllers->begin();
11806 while (it != mUSBControllers->end())
11807 {
11808 (*it)->i_commit();
11809 ++it;
11810 }
11811 }
11812
11813 if (i_isSessionMachine())
11814 {
11815 /* attach new data to the primary machine and reshare it */
11816 mPeer->mUserData.attach(mUserData);
11817 mPeer->mHWData.attach(mHWData);
11818 /* mMediaData is reshared by fixupMedia */
11819 // mPeer->mMediaData.attach(mMediaData);
11820 Assert(mPeer->mMediaData.data() == mMediaData.data());
11821 }
11822}
11823
11824/**
11825 * Copies all the hardware data from the given machine.
11826 *
11827 * Currently, only called when the VM is being restored from a snapshot. In
11828 * particular, this implies that the VM is not running during this method's
11829 * call.
11830 *
11831 * @note This method must be called from under this object's lock.
11832 *
11833 * @note This method doesn't call #commit(), so all data remains backed up and
11834 * unsaved.
11835 */
11836void Machine::i_copyFrom(Machine *aThat)
11837{
11838 AssertReturnVoid(!i_isSnapshotMachine());
11839 AssertReturnVoid(aThat->i_isSnapshotMachine());
11840
11841 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11842
11843 mHWData.assignCopy(aThat->mHWData);
11844
11845 // create copies of all shared folders (mHWData after attaching a copy
11846 // contains just references to original objects)
11847 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11848 it != mHWData->mSharedFolders.end();
11849 ++it)
11850 {
11851 ComObjPtr<SharedFolder> folder;
11852 folder.createObject();
11853 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11854 AssertComRC(rc);
11855 *it = folder;
11856 }
11857
11858 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11859 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11860 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11861 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11862 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11863
11864 /* create private copies of all controllers */
11865 mStorageControllers.backup();
11866 mStorageControllers->clear();
11867 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11868 it != aThat->mStorageControllers->end();
11869 ++it)
11870 {
11871 ComObjPtr<StorageController> ctrl;
11872 ctrl.createObject();
11873 ctrl->initCopy(this, *it);
11874 mStorageControllers->push_back(ctrl);
11875 }
11876
11877 /* create private copies of all USB controllers */
11878 mUSBControllers.backup();
11879 mUSBControllers->clear();
11880 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11881 it != aThat->mUSBControllers->end();
11882 ++it)
11883 {
11884 ComObjPtr<USBController> ctrl;
11885 ctrl.createObject();
11886 ctrl->initCopy(this, *it);
11887 mUSBControllers->push_back(ctrl);
11888 }
11889
11890 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11891 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11892 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11893 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11894 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11895 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11896 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11897}
11898
11899/**
11900 * Returns whether the given storage controller is hotplug capable.
11901 *
11902 * @returns true if the controller supports hotplugging
11903 * false otherwise.
11904 * @param enmCtrlType The controller type to check for.
11905 */
11906bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11907{
11908 ComPtr<ISystemProperties> systemProperties;
11909 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11910 if (FAILED(rc))
11911 return false;
11912
11913 BOOL aHotplugCapable = FALSE;
11914 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11915
11916 return RT_BOOL(aHotplugCapable);
11917}
11918
11919#ifdef VBOX_WITH_RESOURCE_USAGE_API
11920
11921void Machine::i_getDiskList(MediaList &list)
11922{
11923 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11924 it != mMediaData->mAttachments.end();
11925 ++it)
11926 {
11927 MediumAttachment* pAttach = *it;
11928 /* just in case */
11929 AssertStmt(pAttach, continue);
11930
11931 AutoCaller localAutoCallerA(pAttach);
11932 if (FAILED(localAutoCallerA.rc())) continue;
11933
11934 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11935
11936 if (pAttach->i_getType() == DeviceType_HardDisk)
11937 list.push_back(pAttach->i_getMedium());
11938 }
11939}
11940
11941void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11942{
11943 AssertReturnVoid(isWriteLockOnCurrentThread());
11944 AssertPtrReturnVoid(aCollector);
11945
11946 pm::CollectorHAL *hal = aCollector->getHAL();
11947 /* Create sub metrics */
11948 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11949 "Percentage of processor time spent in user mode by the VM process.");
11950 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11951 "Percentage of processor time spent in kernel mode by the VM process.");
11952 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11953 "Size of resident portion of VM process in memory.");
11954 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11955 "Actual size of all VM disks combined.");
11956 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11957 "Network receive rate.");
11958 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11959 "Network transmit rate.");
11960 /* Create and register base metrics */
11961 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11962 cpuLoadUser, cpuLoadKernel);
11963 aCollector->registerBaseMetric(cpuLoad);
11964 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11965 ramUsageUsed);
11966 aCollector->registerBaseMetric(ramUsage);
11967 MediaList disks;
11968 i_getDiskList(disks);
11969 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11970 diskUsageUsed);
11971 aCollector->registerBaseMetric(diskUsage);
11972
11973 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11974 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11975 new pm::AggregateAvg()));
11976 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11977 new pm::AggregateMin()));
11978 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11979 new pm::AggregateMax()));
11980 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11981 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11982 new pm::AggregateAvg()));
11983 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11984 new pm::AggregateMin()));
11985 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11986 new pm::AggregateMax()));
11987
11988 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11989 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11990 new pm::AggregateAvg()));
11991 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11992 new pm::AggregateMin()));
11993 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11994 new pm::AggregateMax()));
11995
11996 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11997 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11998 new pm::AggregateAvg()));
11999 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12000 new pm::AggregateMin()));
12001 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12002 new pm::AggregateMax()));
12003
12004
12005 /* Guest metrics collector */
12006 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12007 aCollector->registerGuest(mCollectorGuest);
12008 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12009 this, __PRETTY_FUNCTION__, mCollectorGuest));
12010
12011 /* Create sub metrics */
12012 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12013 "Percentage of processor time spent in user mode as seen by the guest.");
12014 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12015 "Percentage of processor time spent in kernel mode as seen by the guest.");
12016 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12017 "Percentage of processor time spent idling as seen by the guest.");
12018
12019 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12020 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12021 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12022 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12023 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12024 pm::SubMetric *guestMemCache = new pm::SubMetric(
12025 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12026
12027 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12028 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12029
12030 /* Create and register base metrics */
12031 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12032 machineNetRx, machineNetTx);
12033 aCollector->registerBaseMetric(machineNetRate);
12034
12035 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12036 guestLoadUser, guestLoadKernel, guestLoadIdle);
12037 aCollector->registerBaseMetric(guestCpuLoad);
12038
12039 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12040 guestMemTotal, guestMemFree,
12041 guestMemBalloon, guestMemShared,
12042 guestMemCache, guestPagedTotal);
12043 aCollector->registerBaseMetric(guestCpuMem);
12044
12045 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12046 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12047 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12048 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12049
12050 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12051 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12052 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12053 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12054
12055 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12056 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12057 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12058 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12059
12060 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12061 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12062 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12063 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12064
12065 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12066 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12067 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12068 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12069
12070 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12071 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12072 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12073 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12074
12075 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12076 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12077 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12078 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12079
12080 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12081 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12082 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12083 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12084
12085 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12086 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12087 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12088 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12089
12090 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12091 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12092 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12093 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12094
12095 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12096 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12097 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12098 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12099}
12100
12101void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12102{
12103 AssertReturnVoid(isWriteLockOnCurrentThread());
12104
12105 if (aCollector)
12106 {
12107 aCollector->unregisterMetricsFor(aMachine);
12108 aCollector->unregisterBaseMetricsFor(aMachine);
12109 }
12110}
12111
12112#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12113
12114
12115////////////////////////////////////////////////////////////////////////////////
12116
12117DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12118
12119HRESULT SessionMachine::FinalConstruct()
12120{
12121 LogFlowThisFunc(("\n"));
12122
12123 mClientToken = NULL;
12124
12125 return BaseFinalConstruct();
12126}
12127
12128void SessionMachine::FinalRelease()
12129{
12130 LogFlowThisFunc(("\n"));
12131
12132 Assert(!mClientToken);
12133 /* paranoia, should not hang around any more */
12134 if (mClientToken)
12135 {
12136 delete mClientToken;
12137 mClientToken = NULL;
12138 }
12139
12140 uninit(Uninit::Unexpected);
12141
12142 BaseFinalRelease();
12143}
12144
12145/**
12146 * @note Must be called only by Machine::LockMachine() from its own write lock.
12147 */
12148HRESULT SessionMachine::init(Machine *aMachine)
12149{
12150 LogFlowThisFuncEnter();
12151 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12152
12153 AssertReturn(aMachine, E_INVALIDARG);
12154
12155 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12156
12157 /* Enclose the state transition NotReady->InInit->Ready */
12158 AutoInitSpan autoInitSpan(this);
12159 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12160
12161 HRESULT rc = S_OK;
12162
12163 /* create the machine client token */
12164 try
12165 {
12166 mClientToken = new ClientToken(aMachine, this);
12167 if (!mClientToken->isReady())
12168 {
12169 delete mClientToken;
12170 mClientToken = NULL;
12171 rc = E_FAIL;
12172 }
12173 }
12174 catch (std::bad_alloc &)
12175 {
12176 rc = E_OUTOFMEMORY;
12177 }
12178 if (FAILED(rc))
12179 return rc;
12180
12181 /* memorize the peer Machine */
12182 unconst(mPeer) = aMachine;
12183 /* share the parent pointer */
12184 unconst(mParent) = aMachine->mParent;
12185
12186 /* take the pointers to data to share */
12187 mData.share(aMachine->mData);
12188 mSSData.share(aMachine->mSSData);
12189
12190 mUserData.share(aMachine->mUserData);
12191 mHWData.share(aMachine->mHWData);
12192 mMediaData.share(aMachine->mMediaData);
12193
12194 mStorageControllers.allocate();
12195 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12196 it != aMachine->mStorageControllers->end();
12197 ++it)
12198 {
12199 ComObjPtr<StorageController> ctl;
12200 ctl.createObject();
12201 ctl->init(this, *it);
12202 mStorageControllers->push_back(ctl);
12203 }
12204
12205 mUSBControllers.allocate();
12206 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12207 it != aMachine->mUSBControllers->end();
12208 ++it)
12209 {
12210 ComObjPtr<USBController> ctl;
12211 ctl.createObject();
12212 ctl->init(this, *it);
12213 mUSBControllers->push_back(ctl);
12214 }
12215
12216 unconst(mBIOSSettings).createObject();
12217 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12218 /* create another VRDEServer object that will be mutable */
12219 unconst(mVRDEServer).createObject();
12220 mVRDEServer->init(this, aMachine->mVRDEServer);
12221 /* create another audio adapter object that will be mutable */
12222 unconst(mAudioAdapter).createObject();
12223 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12224 /* create a list of serial ports that will be mutable */
12225 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12226 {
12227 unconst(mSerialPorts[slot]).createObject();
12228 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12229 }
12230 /* create a list of parallel ports that will be mutable */
12231 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12232 {
12233 unconst(mParallelPorts[slot]).createObject();
12234 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12235 }
12236
12237 /* create another USB device filters object that will be mutable */
12238 unconst(mUSBDeviceFilters).createObject();
12239 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12240
12241 /* create a list of network adapters that will be mutable */
12242 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12243 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12244 {
12245 unconst(mNetworkAdapters[slot]).createObject();
12246 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12247 }
12248
12249 /* create another bandwidth control object that will be mutable */
12250 unconst(mBandwidthControl).createObject();
12251 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12252
12253 /* default is to delete saved state on Saved -> PoweredOff transition */
12254 mRemoveSavedState = true;
12255
12256 /* Confirm a successful initialization when it's the case */
12257 autoInitSpan.setSucceeded();
12258
12259 miNATNetworksStarted = 0;
12260
12261 LogFlowThisFuncLeave();
12262 return rc;
12263}
12264
12265/**
12266 * Uninitializes this session object. If the reason is other than
12267 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12268 * or the client watcher code.
12269 *
12270 * @param aReason uninitialization reason
12271 *
12272 * @note Locks mParent + this object for writing.
12273 */
12274void SessionMachine::uninit(Uninit::Reason aReason)
12275{
12276 LogFlowThisFuncEnter();
12277 LogFlowThisFunc(("reason=%d\n", aReason));
12278
12279 /*
12280 * Strongly reference ourselves to prevent this object deletion after
12281 * mData->mSession.mMachine.setNull() below (which can release the last
12282 * reference and call the destructor). Important: this must be done before
12283 * accessing any members (and before AutoUninitSpan that does it as well).
12284 * This self reference will be released as the very last step on return.
12285 */
12286 ComObjPtr<SessionMachine> selfRef = this;
12287
12288 /* Enclose the state transition Ready->InUninit->NotReady */
12289 AutoUninitSpan autoUninitSpan(this);
12290 if (autoUninitSpan.uninitDone())
12291 {
12292 LogFlowThisFunc(("Already uninitialized\n"));
12293 LogFlowThisFuncLeave();
12294 return;
12295 }
12296
12297 if (autoUninitSpan.initFailed())
12298 {
12299 /* We've been called by init() because it's failed. It's not really
12300 * necessary (nor it's safe) to perform the regular uninit sequence
12301 * below, the following is enough.
12302 */
12303 LogFlowThisFunc(("Initialization failed.\n"));
12304 /* destroy the machine client token */
12305 if (mClientToken)
12306 {
12307 delete mClientToken;
12308 mClientToken = NULL;
12309 }
12310 uninitDataAndChildObjects();
12311 mData.free();
12312 unconst(mParent) = NULL;
12313 unconst(mPeer) = NULL;
12314 LogFlowThisFuncLeave();
12315 return;
12316 }
12317
12318 MachineState_T lastState;
12319 {
12320 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12321 lastState = mData->mMachineState;
12322 }
12323 NOREF(lastState);
12324
12325#ifdef VBOX_WITH_USB
12326 // release all captured USB devices, but do this before requesting the locks below
12327 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12328 {
12329 /* Console::captureUSBDevices() is called in the VM process only after
12330 * setting the machine state to Starting or Restoring.
12331 * Console::detachAllUSBDevices() will be called upon successful
12332 * termination. So, we need to release USB devices only if there was
12333 * an abnormal termination of a running VM.
12334 *
12335 * This is identical to SessionMachine::DetachAllUSBDevices except
12336 * for the aAbnormal argument. */
12337 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12338 AssertComRC(rc);
12339 NOREF(rc);
12340
12341 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12342 if (service)
12343 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12344 }
12345#endif /* VBOX_WITH_USB */
12346
12347 // we need to lock this object in uninit() because the lock is shared
12348 // with mPeer (as well as data we modify below). mParent lock is needed
12349 // by several calls to it, and USB needs host lock.
12350 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12351
12352#ifdef VBOX_WITH_RESOURCE_USAGE_API
12353 /*
12354 * It is safe to call Machine::i_unregisterMetrics() here because
12355 * PerformanceCollector::samplerCallback no longer accesses guest methods
12356 * holding the lock.
12357 */
12358 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12359 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12360 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12361 this, __PRETTY_FUNCTION__, mCollectorGuest));
12362 if (mCollectorGuest)
12363 {
12364 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12365 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12366 mCollectorGuest = NULL;
12367 }
12368#endif
12369
12370 if (aReason == Uninit::Abnormal)
12371 {
12372 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12373 Global::IsOnlineOrTransient(lastState)));
12374
12375 /* reset the state to Aborted */
12376 if (mData->mMachineState != MachineState_Aborted)
12377 i_setMachineState(MachineState_Aborted);
12378 }
12379
12380 // any machine settings modified?
12381 if (mData->flModifications)
12382 {
12383 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12384 i_rollback(false /* aNotify */);
12385 }
12386
12387 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12388 || !mConsoleTaskData.mSnapshot);
12389 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12390 {
12391 LogWarningThisFunc(("canceling failed save state request!\n"));
12392 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12393 }
12394 else if (!mConsoleTaskData.mSnapshot.isNull())
12395 {
12396 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12397
12398 /* delete all differencing hard disks created (this will also attach
12399 * their parents back by rolling back mMediaData) */
12400 i_rollbackMedia();
12401
12402 // delete the saved state file (it might have been already created)
12403 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12404 // think it's still in use
12405 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12406 mConsoleTaskData.mSnapshot->uninit();
12407 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12408 }
12409
12410 mData->mSession.mPID = NIL_RTPROCESS;
12411
12412 if (aReason == Uninit::Unexpected)
12413 {
12414 /* Uninitialization didn't come from #checkForDeath(), so tell the
12415 * client watcher thread to update the set of machines that have open
12416 * sessions. */
12417 mParent->i_updateClientWatcher();
12418 }
12419
12420 /* uninitialize all remote controls */
12421 if (mData->mSession.mRemoteControls.size())
12422 {
12423 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12424 mData->mSession.mRemoteControls.size()));
12425
12426 Data::Session::RemoteControlList::iterator it =
12427 mData->mSession.mRemoteControls.begin();
12428 while (it != mData->mSession.mRemoteControls.end())
12429 {
12430 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12431 HRESULT rc = (*it)->Uninitialize();
12432 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12433 if (FAILED(rc))
12434 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12435 ++it;
12436 }
12437 mData->mSession.mRemoteControls.clear();
12438 }
12439
12440 /* Remove all references to the NAT network service. The service will stop
12441 * if all references (also from other VMs) are removed. */
12442 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12443 {
12444 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12445 {
12446 NetworkAttachmentType_T type;
12447 HRESULT hrc;
12448
12449 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12450 if ( SUCCEEDED(hrc)
12451 && type == NetworkAttachmentType_NATNetwork)
12452 {
12453 Bstr name;
12454 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12455 if (SUCCEEDED(hrc))
12456 {
12457 multilock.release();
12458 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12459 mUserData->s.strName.c_str(), name.raw()));
12460 mParent->i_natNetworkRefDec(name.raw());
12461 multilock.acquire();
12462 }
12463 }
12464 }
12465 }
12466
12467 /*
12468 * An expected uninitialization can come only from #checkForDeath().
12469 * Otherwise it means that something's gone really wrong (for example,
12470 * the Session implementation has released the VirtualBox reference
12471 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12472 * etc). However, it's also possible, that the client releases the IPC
12473 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12474 * but the VirtualBox release event comes first to the server process.
12475 * This case is practically possible, so we should not assert on an
12476 * unexpected uninit, just log a warning.
12477 */
12478
12479 if ((aReason == Uninit::Unexpected))
12480 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12481
12482 if (aReason != Uninit::Normal)
12483 {
12484 mData->mSession.mDirectControl.setNull();
12485 }
12486 else
12487 {
12488 /* this must be null here (see #OnSessionEnd()) */
12489 Assert(mData->mSession.mDirectControl.isNull());
12490 Assert(mData->mSession.mState == SessionState_Unlocking);
12491 Assert(!mData->mSession.mProgress.isNull());
12492 }
12493 if (mData->mSession.mProgress)
12494 {
12495 if (aReason == Uninit::Normal)
12496 mData->mSession.mProgress->i_notifyComplete(S_OK);
12497 else
12498 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12499 COM_IIDOF(ISession),
12500 getComponentName(),
12501 tr("The VM session was aborted"));
12502 mData->mSession.mProgress.setNull();
12503 }
12504
12505 /* remove the association between the peer machine and this session machine */
12506 Assert( (SessionMachine*)mData->mSession.mMachine == this
12507 || aReason == Uninit::Unexpected);
12508
12509 /* reset the rest of session data */
12510 mData->mSession.mMachine.setNull();
12511 mData->mSession.mState = SessionState_Unlocked;
12512 mData->mSession.mType.setNull();
12513
12514 /* destroy the machine client token before leaving the exclusive lock */
12515 if (mClientToken)
12516 {
12517 delete mClientToken;
12518 mClientToken = NULL;
12519 }
12520
12521 /* fire an event */
12522 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12523
12524 uninitDataAndChildObjects();
12525
12526 /* free the essential data structure last */
12527 mData.free();
12528
12529 /* release the exclusive lock before setting the below two to NULL */
12530 multilock.release();
12531
12532 unconst(mParent) = NULL;
12533 unconst(mPeer) = NULL;
12534
12535 LogFlowThisFuncLeave();
12536}
12537
12538// util::Lockable interface
12539////////////////////////////////////////////////////////////////////////////////
12540
12541/**
12542 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12543 * with the primary Machine instance (mPeer).
12544 */
12545RWLockHandle *SessionMachine::lockHandle() const
12546{
12547 AssertReturn(mPeer != NULL, NULL);
12548 return mPeer->lockHandle();
12549}
12550
12551// IInternalMachineControl methods
12552////////////////////////////////////////////////////////////////////////////////
12553
12554/**
12555 * Passes collected guest statistics to performance collector object
12556 */
12557HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12558 ULONG aCpuKernel, ULONG aCpuIdle,
12559 ULONG aMemTotal, ULONG aMemFree,
12560 ULONG aMemBalloon, ULONG aMemShared,
12561 ULONG aMemCache, ULONG aPageTotal,
12562 ULONG aAllocVMM, ULONG aFreeVMM,
12563 ULONG aBalloonedVMM, ULONG aSharedVMM,
12564 ULONG aVmNetRx, ULONG aVmNetTx)
12565{
12566#ifdef VBOX_WITH_RESOURCE_USAGE_API
12567 if (mCollectorGuest)
12568 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12569 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12570 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12571 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12572
12573 return S_OK;
12574#else
12575 NOREF(aValidStats);
12576 NOREF(aCpuUser);
12577 NOREF(aCpuKernel);
12578 NOREF(aCpuIdle);
12579 NOREF(aMemTotal);
12580 NOREF(aMemFree);
12581 NOREF(aMemBalloon);
12582 NOREF(aMemShared);
12583 NOREF(aMemCache);
12584 NOREF(aPageTotal);
12585 NOREF(aAllocVMM);
12586 NOREF(aFreeVMM);
12587 NOREF(aBalloonedVMM);
12588 NOREF(aSharedVMM);
12589 NOREF(aVmNetRx);
12590 NOREF(aVmNetTx);
12591 return E_NOTIMPL;
12592#endif
12593}
12594
12595/**
12596 * @note Locks this object for writing.
12597 */
12598HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12599{
12600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12601
12602 mRemoveSavedState = RT_BOOL(aRemove);
12603
12604 return S_OK;
12605}
12606
12607/**
12608 * @note Locks the same as #i_setMachineState() does.
12609 */
12610HRESULT SessionMachine::updateState(MachineState_T aState)
12611{
12612 return i_setMachineState(aState);
12613}
12614
12615/**
12616 * @note Locks this object for writing.
12617 */
12618HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12619{
12620 IProgress* pProgress(aProgress);
12621
12622 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12623
12624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12625
12626 if (mData->mSession.mState != SessionState_Locked)
12627 return VBOX_E_INVALID_OBJECT_STATE;
12628
12629 if (!mData->mSession.mProgress.isNull())
12630 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12631
12632 /* If we didn't reference the NAT network service yet, add a reference to
12633 * force a start */
12634 if (miNATNetworksStarted < 1)
12635 {
12636 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12637 {
12638 NetworkAttachmentType_T type;
12639 HRESULT hrc;
12640 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12641 if ( SUCCEEDED(hrc)
12642 && type == NetworkAttachmentType_NATNetwork)
12643 {
12644 Bstr name;
12645 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12646 if (SUCCEEDED(hrc))
12647 {
12648 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12649 mUserData->s.strName.c_str(), name.raw()));
12650 mPeer->lockHandle()->unlockWrite();
12651 mParent->i_natNetworkRefInc(name.raw());
12652#ifdef RT_LOCK_STRICT
12653 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12654#else
12655 mPeer->lockHandle()->lockWrite();
12656#endif
12657 }
12658 }
12659 }
12660 miNATNetworksStarted++;
12661 }
12662
12663 LogFlowThisFunc(("returns S_OK.\n"));
12664 return S_OK;
12665}
12666
12667/**
12668 * @note Locks this object for writing.
12669 */
12670HRESULT SessionMachine::endPowerUp(LONG aResult)
12671{
12672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12673
12674 if (mData->mSession.mState != SessionState_Locked)
12675 return VBOX_E_INVALID_OBJECT_STATE;
12676
12677 /* Finalize the LaunchVMProcess progress object. */
12678 if (mData->mSession.mProgress)
12679 {
12680 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12681 mData->mSession.mProgress.setNull();
12682 }
12683
12684 if (SUCCEEDED((HRESULT)aResult))
12685 {
12686#ifdef VBOX_WITH_RESOURCE_USAGE_API
12687 /* The VM has been powered up successfully, so it makes sense
12688 * now to offer the performance metrics for a running machine
12689 * object. Doing it earlier wouldn't be safe. */
12690 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12691 mData->mSession.mPID);
12692#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12693 }
12694
12695 return S_OK;
12696}
12697
12698/**
12699 * @note Locks this object for writing.
12700 */
12701HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12702{
12703 LogFlowThisFuncEnter();
12704
12705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12706
12707 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12708 E_FAIL);
12709
12710 /* create a progress object to track operation completion */
12711 ComObjPtr<Progress> pProgress;
12712 pProgress.createObject();
12713 pProgress->init(i_getVirtualBox(),
12714 static_cast<IMachine *>(this) /* aInitiator */,
12715 Bstr(tr("Stopping the virtual machine")).raw(),
12716 FALSE /* aCancelable */);
12717
12718 /* fill in the console task data */
12719 mConsoleTaskData.mLastState = mData->mMachineState;
12720 mConsoleTaskData.mProgress = pProgress;
12721
12722 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12723 i_setMachineState(MachineState_Stopping);
12724
12725 pProgress.queryInterfaceTo(aProgress.asOutParam());
12726
12727 return S_OK;
12728}
12729
12730/**
12731 * @note Locks this object for writing.
12732 */
12733HRESULT SessionMachine::endPoweringDown(LONG aResult,
12734 const com::Utf8Str &aErrMsg)
12735{
12736 LogFlowThisFuncEnter();
12737
12738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12739
12740 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12741 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12742 && mConsoleTaskData.mLastState != MachineState_Null,
12743 E_FAIL);
12744
12745 /*
12746 * On failure, set the state to the state we had when BeginPoweringDown()
12747 * was called (this is expected by Console::PowerDown() and the associated
12748 * task). On success the VM process already changed the state to
12749 * MachineState_PoweredOff, so no need to do anything.
12750 */
12751 if (FAILED(aResult))
12752 i_setMachineState(mConsoleTaskData.mLastState);
12753
12754 /* notify the progress object about operation completion */
12755 Assert(mConsoleTaskData.mProgress);
12756 if (SUCCEEDED(aResult))
12757 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12758 else
12759 {
12760 if (aErrMsg.length())
12761 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12762 COM_IIDOF(ISession),
12763 getComponentName(),
12764 aErrMsg.c_str());
12765 else
12766 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12767 }
12768
12769 /* clear out the temporary saved state data */
12770 mConsoleTaskData.mLastState = MachineState_Null;
12771 mConsoleTaskData.mProgress.setNull();
12772
12773 LogFlowThisFuncLeave();
12774 return S_OK;
12775}
12776
12777
12778/**
12779 * Goes through the USB filters of the given machine to see if the given
12780 * device matches any filter or not.
12781 *
12782 * @note Locks the same as USBController::hasMatchingFilter() does.
12783 */
12784HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12785 BOOL *aMatched,
12786 ULONG *aMaskedInterfaces)
12787{
12788 LogFlowThisFunc(("\n"));
12789
12790#ifdef VBOX_WITH_USB
12791 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12792#else
12793 NOREF(aDevice);
12794 NOREF(aMaskedInterfaces);
12795 *aMatched = FALSE;
12796#endif
12797
12798 return S_OK;
12799}
12800
12801/**
12802 * @note Locks the same as Host::captureUSBDevice() does.
12803 */
12804HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
12805{
12806 LogFlowThisFunc(("\n"));
12807
12808#ifdef VBOX_WITH_USB
12809 /* if captureDeviceForVM() fails, it must have set extended error info */
12810 clearError();
12811 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12812 if (FAILED(rc)) return rc;
12813
12814 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12815 AssertReturn(service, E_FAIL);
12816 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
12817#else
12818 NOREF(aId);
12819 return E_NOTIMPL;
12820#endif
12821}
12822
12823/**
12824 * @note Locks the same as Host::detachUSBDevice() does.
12825 */
12826HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12827 BOOL aDone)
12828{
12829 LogFlowThisFunc(("\n"));
12830
12831#ifdef VBOX_WITH_USB
12832 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12833 AssertReturn(service, E_FAIL);
12834 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12835#else
12836 NOREF(aId);
12837 NOREF(aDone);
12838 return E_NOTIMPL;
12839#endif
12840}
12841
12842/**
12843 * Inserts all machine filters to the USB proxy service and then calls
12844 * Host::autoCaptureUSBDevices().
12845 *
12846 * Called by Console from the VM process upon VM startup.
12847 *
12848 * @note Locks what called methods lock.
12849 */
12850HRESULT SessionMachine::autoCaptureUSBDevices()
12851{
12852 LogFlowThisFunc(("\n"));
12853
12854#ifdef VBOX_WITH_USB
12855 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12856 AssertComRC(rc);
12857 NOREF(rc);
12858
12859 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12860 AssertReturn(service, E_FAIL);
12861 return service->autoCaptureDevicesForVM(this);
12862#else
12863 return S_OK;
12864#endif
12865}
12866
12867/**
12868 * Removes all machine filters from the USB proxy service and then calls
12869 * Host::detachAllUSBDevices().
12870 *
12871 * Called by Console from the VM process upon normal VM termination or by
12872 * SessionMachine::uninit() upon abnormal VM termination (from under the
12873 * Machine/SessionMachine lock).
12874 *
12875 * @note Locks what called methods lock.
12876 */
12877HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12878{
12879 LogFlowThisFunc(("\n"));
12880
12881#ifdef VBOX_WITH_USB
12882 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12883 AssertComRC(rc);
12884 NOREF(rc);
12885
12886 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12887 AssertReturn(service, E_FAIL);
12888 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12889#else
12890 NOREF(aDone);
12891 return S_OK;
12892#endif
12893}
12894
12895/**
12896 * @note Locks this object for writing.
12897 */
12898HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12899 ComPtr<IProgress> &aProgress)
12900{
12901 LogFlowThisFuncEnter();
12902
12903 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12904 /*
12905 * We don't assert below because it might happen that a non-direct session
12906 * informs us it is closed right after we've been uninitialized -- it's ok.
12907 */
12908
12909 /* get IInternalSessionControl interface */
12910 ComPtr<IInternalSessionControl> control(aSession);
12911
12912 ComAssertRet(!control.isNull(), E_INVALIDARG);
12913
12914 /* Creating a Progress object requires the VirtualBox lock, and
12915 * thus locking it here is required by the lock order rules. */
12916 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12917
12918 if (control == mData->mSession.mDirectControl)
12919 {
12920 /* The direct session is being normally closed by the client process
12921 * ----------------------------------------------------------------- */
12922
12923 /* go to the closing state (essential for all open*Session() calls and
12924 * for #checkForDeath()) */
12925 Assert(mData->mSession.mState == SessionState_Locked);
12926 mData->mSession.mState = SessionState_Unlocking;
12927
12928 /* set direct control to NULL to release the remote instance */
12929 mData->mSession.mDirectControl.setNull();
12930 LogFlowThisFunc(("Direct control is set to NULL\n"));
12931
12932 if (mData->mSession.mProgress)
12933 {
12934 /* finalize the progress, someone might wait if a frontend
12935 * closes the session before powering on the VM. */
12936 mData->mSession.mProgress->notifyComplete(E_FAIL,
12937 COM_IIDOF(ISession),
12938 getComponentName(),
12939 tr("The VM session was closed before any attempt to power it on"));
12940 mData->mSession.mProgress.setNull();
12941 }
12942
12943 /* Create the progress object the client will use to wait until
12944 * #checkForDeath() is called to uninitialize this session object after
12945 * it releases the IPC semaphore.
12946 * Note! Because we're "reusing" mProgress here, this must be a proxy
12947 * object just like for LaunchVMProcess. */
12948 Assert(mData->mSession.mProgress.isNull());
12949 ComObjPtr<ProgressProxy> progress;
12950 progress.createObject();
12951 ComPtr<IUnknown> pPeer(mPeer);
12952 progress->init(mParent, pPeer,
12953 Bstr(tr("Closing session")).raw(),
12954 FALSE /* aCancelable */);
12955 progress.queryInterfaceTo(aProgress.asOutParam());
12956 mData->mSession.mProgress = progress;
12957 }
12958 else
12959 {
12960 /* the remote session is being normally closed */
12961 Data::Session::RemoteControlList::iterator it =
12962 mData->mSession.mRemoteControls.begin();
12963 while (it != mData->mSession.mRemoteControls.end())
12964 {
12965 if (control == *it)
12966 break;
12967 ++it;
12968 }
12969 BOOL found = it != mData->mSession.mRemoteControls.end();
12970 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12971 E_INVALIDARG);
12972 // This MUST be erase(it), not remove(*it) as the latter triggers a
12973 // very nasty use after free due to the place where the value "lives".
12974 mData->mSession.mRemoteControls.erase(it);
12975 }
12976
12977 /* signal the client watcher thread, because the client is going away */
12978 mParent->i_updateClientWatcher();
12979
12980 LogFlowThisFuncLeave();
12981 return S_OK;
12982}
12983
12984/**
12985 * @note Locks this object for writing.
12986 */
12987HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
12988 com::Utf8Str &aStateFilePath)
12989{
12990 LogFlowThisFuncEnter();
12991
12992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12993
12994 AssertReturn( mData->mMachineState == MachineState_Paused
12995 && mConsoleTaskData.mLastState == MachineState_Null
12996 && mConsoleTaskData.strStateFilePath.isEmpty(),
12997 E_FAIL);
12998
12999 /* create a progress object to track operation completion */
13000 ComObjPtr<Progress> pProgress;
13001 pProgress.createObject();
13002 pProgress->init(i_getVirtualBox(),
13003 static_cast<IMachine *>(this) /* aInitiator */,
13004 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13005 FALSE /* aCancelable */);
13006
13007 /* stateFilePath is null when the machine is not running */
13008 if (mData->mMachineState == MachineState_Paused)
13009 i_composeSavedStateFilename(aStateFilePath);
13010
13011 /* fill in the console task data */
13012 mConsoleTaskData.mLastState = mData->mMachineState;
13013 mConsoleTaskData.strStateFilePath = aStateFilePath;
13014 mConsoleTaskData.mProgress = pProgress;
13015
13016 /* set the state to Saving (this is expected by Console::SaveState()) */
13017 i_setMachineState(MachineState_Saving);
13018
13019 pProgress.queryInterfaceTo(aProgress.asOutParam());
13020
13021 return S_OK;
13022}
13023
13024/**
13025 * @note Locks mParent + this object for writing.
13026 */
13027HRESULT SessionMachine::endSavingState(LONG aResult,
13028 const com::Utf8Str &aErrMsg)
13029{
13030 LogFlowThisFunc(("\n"));
13031
13032 /* endSavingState() need mParent lock */
13033 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13034
13035 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13036 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13037 && mConsoleTaskData.mLastState != MachineState_Null
13038 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13039 E_FAIL);
13040
13041 /*
13042 * On failure, set the state to the state we had when BeginSavingState()
13043 * was called (this is expected by Console::SaveState() and the associated
13044 * task). On success the VM process already changed the state to
13045 * MachineState_Saved, so no need to do anything.
13046 */
13047 if (FAILED(aResult))
13048 i_setMachineState(mConsoleTaskData.mLastState);
13049
13050 return i_endSavingState(aResult, aErrMsg);
13051}
13052
13053/**
13054 * @note Locks this object for writing.
13055 */
13056HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13057{
13058 LogFlowThisFunc(("\n"));
13059
13060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13061
13062 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13063 || mData->mMachineState == MachineState_Teleported
13064 || mData->mMachineState == MachineState_Aborted
13065 , E_FAIL); /** @todo setError. */
13066
13067 com::Utf8Str stateFilePathFull;
13068 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13069 if (RT_FAILURE(vrc))
13070 return setError(VBOX_E_FILE_ERROR,
13071 tr("Invalid saved state file path '%s' (%Rrc)"),
13072 aSavedStateFile.c_str(),
13073 vrc);
13074
13075 mSSData->strStateFilePath = stateFilePathFull;
13076
13077 /* The below i_setMachineState() will detect the state transition and will
13078 * update the settings file */
13079
13080 return i_setMachineState(MachineState_Saved);
13081}
13082
13083HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13084 std::vector<com::Utf8Str> &aValues,
13085 std::vector<LONG64> &aTimestamps,
13086 std::vector<com::Utf8Str> &aFlags)
13087{
13088 LogFlowThisFunc(("\n"));
13089
13090#ifdef VBOX_WITH_GUEST_PROPS
13091 using namespace guestProp;
13092
13093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13094
13095 size_t cEntries = mHWData->mGuestProperties.size();
13096 aNames.resize(cEntries);
13097 aValues.resize(cEntries);
13098 aTimestamps.resize(cEntries);
13099 aFlags.resize(cEntries);
13100
13101 size_t i = 0;
13102 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13103 it != mHWData->mGuestProperties.end();
13104 ++it, ++i)
13105 {
13106 char szFlags[MAX_FLAGS_LEN + 1];
13107 aNames[i] = it->first;
13108 aValues[i] = it->second.strValue;
13109 aTimestamps[i] = it->second.mTimestamp;
13110
13111 /* If it is NULL, keep it NULL. */
13112 if (it->second.mFlags)
13113 {
13114 writeFlags(it->second.mFlags, szFlags);
13115 aFlags[i] = szFlags;
13116 }
13117 else
13118 aFlags[i] = "";
13119 }
13120 return S_OK;
13121#else
13122 ReturnComNotImplemented();
13123#endif
13124}
13125
13126HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13127 const com::Utf8Str &aValue,
13128 LONG64 aTimestamp,
13129 const com::Utf8Str &aFlags)
13130{
13131 LogFlowThisFunc(("\n"));
13132
13133#ifdef VBOX_WITH_GUEST_PROPS
13134 using namespace guestProp;
13135
13136 try
13137 {
13138 /*
13139 * Convert input up front.
13140 */
13141 uint32_t fFlags = NILFLAG;
13142 if (aFlags.length())
13143 {
13144 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13145 AssertRCReturn(vrc, E_INVALIDARG);
13146 }
13147
13148 /*
13149 * Now grab the object lock, validate the state and do the update.
13150 */
13151
13152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13153
13154 switch (mData->mMachineState)
13155 {
13156 case MachineState_Paused:
13157 case MachineState_Running:
13158 case MachineState_Teleporting:
13159 case MachineState_TeleportingPausedVM:
13160 case MachineState_LiveSnapshotting:
13161 case MachineState_DeletingSnapshotOnline:
13162 case MachineState_DeletingSnapshotPaused:
13163 case MachineState_Saving:
13164 case MachineState_Stopping:
13165 break;
13166
13167 default:
13168 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13169 VBOX_E_INVALID_VM_STATE);
13170 }
13171
13172 i_setModified(IsModified_MachineData);
13173 mHWData.backup();
13174
13175 bool fDelete = !aValue.length();
13176 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13177 if (it != mHWData->mGuestProperties.end())
13178 {
13179 if (!fDelete)
13180 {
13181 it->second.strValue = aValue;
13182 it->second.mTimestamp = aTimestamp;
13183 it->second.mFlags = fFlags;
13184 }
13185 else
13186 mHWData->mGuestProperties.erase(it);
13187
13188 mData->mGuestPropertiesModified = TRUE;
13189 }
13190 else if (!fDelete)
13191 {
13192 HWData::GuestProperty prop;
13193 prop.strValue = aValue;
13194 prop.mTimestamp = aTimestamp;
13195 prop.mFlags = fFlags;
13196
13197 mHWData->mGuestProperties[aName] = prop;
13198 mData->mGuestPropertiesModified = TRUE;
13199 }
13200
13201 /*
13202 * Send a callback notification if appropriate
13203 */
13204 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13205 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13206 RTSTR_MAX,
13207 aName.c_str(),
13208 RTSTR_MAX, NULL)
13209 )
13210 {
13211 alock.release();
13212
13213 mParent->i_onGuestPropertyChange(mData->mUuid,
13214 Bstr(aName).raw(),
13215 Bstr(aValue).raw(),
13216 Bstr(aFlags).raw());
13217 }
13218 }
13219 catch (...)
13220 {
13221 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13222 }
13223 return S_OK;
13224#else
13225 ReturnComNotImplemented();
13226#endif
13227}
13228
13229
13230HRESULT SessionMachine::lockMedia()
13231{
13232 AutoMultiWriteLock2 alock(this->lockHandle(),
13233 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13234
13235 AssertReturn( mData->mMachineState == MachineState_Starting
13236 || mData->mMachineState == MachineState_Restoring
13237 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13238
13239 clearError();
13240 alock.release();
13241 return i_lockMedia();
13242}
13243
13244HRESULT SessionMachine::unlockMedia()
13245{
13246 HRESULT hrc = i_unlockMedia();
13247 return hrc;
13248}
13249
13250HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13251 ComPtr<IMediumAttachment> &aNewAttachment)
13252{
13253 // request the host lock first, since might be calling Host methods for getting host drives;
13254 // next, protect the media tree all the while we're in here, as well as our member variables
13255 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13256 this->lockHandle(),
13257 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13258
13259 IMediumAttachment *iAttach = aAttachment;
13260 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13261
13262 Bstr ctrlName;
13263 LONG lPort;
13264 LONG lDevice;
13265 bool fTempEject;
13266 {
13267 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13268
13269 /* Need to query the details first, as the IMediumAttachment reference
13270 * might be to the original settings, which we are going to change. */
13271 ctrlName = pAttach->i_getControllerName();
13272 lPort = pAttach->i_getPort();
13273 lDevice = pAttach->i_getDevice();
13274 fTempEject = pAttach->i_getTempEject();
13275 }
13276
13277 if (!fTempEject)
13278 {
13279 /* Remember previously mounted medium. The medium before taking the
13280 * backup is not necessarily the same thing. */
13281 ComObjPtr<Medium> oldmedium;
13282 oldmedium = pAttach->i_getMedium();
13283
13284 i_setModified(IsModified_Storage);
13285 mMediaData.backup();
13286
13287 // The backup operation makes the pAttach reference point to the
13288 // old settings. Re-get the correct reference.
13289 pAttach = i_findAttachment(mMediaData->mAttachments,
13290 ctrlName.raw(),
13291 lPort,
13292 lDevice);
13293
13294 {
13295 AutoCaller autoAttachCaller(this);
13296 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13297
13298 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13299 if (!oldmedium.isNull())
13300 oldmedium->i_removeBackReference(mData->mUuid);
13301
13302 pAttach->i_updateMedium(NULL);
13303 pAttach->i_updateEjected();
13304 }
13305
13306 i_setModified(IsModified_Storage);
13307 }
13308 else
13309 {
13310 {
13311 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13312 pAttach->i_updateEjected();
13313 }
13314 }
13315
13316 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13317
13318 return S_OK;
13319}
13320
13321// public methods only for internal purposes
13322/////////////////////////////////////////////////////////////////////////////
13323
13324#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13325/**
13326 * Called from the client watcher thread to check for expected or unexpected
13327 * death of the client process that has a direct session to this machine.
13328 *
13329 * On Win32 and on OS/2, this method is called only when we've got the
13330 * mutex (i.e. the client has either died or terminated normally) so it always
13331 * returns @c true (the client is terminated, the session machine is
13332 * uninitialized).
13333 *
13334 * On other platforms, the method returns @c true if the client process has
13335 * terminated normally or abnormally and the session machine was uninitialized,
13336 * and @c false if the client process is still alive.
13337 *
13338 * @note Locks this object for writing.
13339 */
13340bool SessionMachine::i_checkForDeath()
13341{
13342 Uninit::Reason reason;
13343 bool terminated = false;
13344
13345 /* Enclose autoCaller with a block because calling uninit() from under it
13346 * will deadlock. */
13347 {
13348 AutoCaller autoCaller(this);
13349 if (!autoCaller.isOk())
13350 {
13351 /* return true if not ready, to cause the client watcher to exclude
13352 * the corresponding session from watching */
13353 LogFlowThisFunc(("Already uninitialized!\n"));
13354 return true;
13355 }
13356
13357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13358
13359 /* Determine the reason of death: if the session state is Closing here,
13360 * everything is fine. Otherwise it means that the client did not call
13361 * OnSessionEnd() before it released the IPC semaphore. This may happen
13362 * either because the client process has abnormally terminated, or
13363 * because it simply forgot to call ISession::Close() before exiting. We
13364 * threat the latter also as an abnormal termination (see
13365 * Session::uninit() for details). */
13366 reason = mData->mSession.mState == SessionState_Unlocking ?
13367 Uninit::Normal :
13368 Uninit::Abnormal;
13369
13370 if (mClientToken)
13371 terminated = mClientToken->release();
13372 } /* AutoCaller block */
13373
13374 if (terminated)
13375 uninit(reason);
13376
13377 return terminated;
13378}
13379
13380void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13381{
13382 LogFlowThisFunc(("\n"));
13383
13384 strTokenId.setNull();
13385
13386 AutoCaller autoCaller(this);
13387 AssertComRCReturnVoid(autoCaller.rc());
13388
13389 Assert(mClientToken);
13390 if (mClientToken)
13391 mClientToken->getId(strTokenId);
13392}
13393#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13394IToken *SessionMachine::i_getToken()
13395{
13396 LogFlowThisFunc(("\n"));
13397
13398 AutoCaller autoCaller(this);
13399 AssertComRCReturn(autoCaller.rc(), NULL);
13400
13401 Assert(mClientToken);
13402 if (mClientToken)
13403 return mClientToken->getToken();
13404 else
13405 return NULL;
13406}
13407#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13408
13409Machine::ClientToken *SessionMachine::i_getClientToken()
13410{
13411 LogFlowThisFunc(("\n"));
13412
13413 AutoCaller autoCaller(this);
13414 AssertComRCReturn(autoCaller.rc(), NULL);
13415
13416 return mClientToken;
13417}
13418
13419
13420/**
13421 * @note Locks this object for reading.
13422 */
13423HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13424{
13425 LogFlowThisFunc(("\n"));
13426
13427 AutoCaller autoCaller(this);
13428 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13429
13430 ComPtr<IInternalSessionControl> directControl;
13431 {
13432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13433 directControl = mData->mSession.mDirectControl;
13434 }
13435
13436 /* ignore notifications sent after #OnSessionEnd() is called */
13437 if (!directControl)
13438 return S_OK;
13439
13440 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13441}
13442
13443/**
13444 * @note Locks this object for reading.
13445 */
13446HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13447 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13448 IN_BSTR aGuestIp, LONG aGuestPort)
13449{
13450 LogFlowThisFunc(("\n"));
13451
13452 AutoCaller autoCaller(this);
13453 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13454
13455 ComPtr<IInternalSessionControl> directControl;
13456 {
13457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13458 directControl = mData->mSession.mDirectControl;
13459 }
13460
13461 /* ignore notifications sent after #OnSessionEnd() is called */
13462 if (!directControl)
13463 return S_OK;
13464 /*
13465 * instead acting like callback we ask IVirtualBox deliver corresponding event
13466 */
13467
13468 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13469 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13470 return S_OK;
13471}
13472
13473/**
13474 * @note Locks this object for reading.
13475 */
13476HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13477{
13478 LogFlowThisFunc(("\n"));
13479
13480 AutoCaller autoCaller(this);
13481 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13482
13483 ComPtr<IInternalSessionControl> directControl;
13484 {
13485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13486 directControl = mData->mSession.mDirectControl;
13487 }
13488
13489 /* ignore notifications sent after #OnSessionEnd() is called */
13490 if (!directControl)
13491 return S_OK;
13492
13493 return directControl->OnSerialPortChange(serialPort);
13494}
13495
13496/**
13497 * @note Locks this object for reading.
13498 */
13499HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13500{
13501 LogFlowThisFunc(("\n"));
13502
13503 AutoCaller autoCaller(this);
13504 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13505
13506 ComPtr<IInternalSessionControl> directControl;
13507 {
13508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13509 directControl = mData->mSession.mDirectControl;
13510 }
13511
13512 /* ignore notifications sent after #OnSessionEnd() is called */
13513 if (!directControl)
13514 return S_OK;
13515
13516 return directControl->OnParallelPortChange(parallelPort);
13517}
13518
13519/**
13520 * @note Locks this object for reading.
13521 */
13522HRESULT SessionMachine::i_onStorageControllerChange()
13523{
13524 LogFlowThisFunc(("\n"));
13525
13526 AutoCaller autoCaller(this);
13527 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13528
13529 ComPtr<IInternalSessionControl> directControl;
13530 {
13531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13532 directControl = mData->mSession.mDirectControl;
13533 }
13534
13535 /* ignore notifications sent after #OnSessionEnd() is called */
13536 if (!directControl)
13537 return S_OK;
13538
13539 return directControl->OnStorageControllerChange();
13540}
13541
13542/**
13543 * @note Locks this object for reading.
13544 */
13545HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13546{
13547 LogFlowThisFunc(("\n"));
13548
13549 AutoCaller autoCaller(this);
13550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13551
13552 ComPtr<IInternalSessionControl> directControl;
13553 {
13554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13555 directControl = mData->mSession.mDirectControl;
13556 }
13557
13558 /* ignore notifications sent after #OnSessionEnd() is called */
13559 if (!directControl)
13560 return S_OK;
13561
13562 return directControl->OnMediumChange(aAttachment, aForce);
13563}
13564
13565/**
13566 * @note Locks this object for reading.
13567 */
13568HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13569{
13570 LogFlowThisFunc(("\n"));
13571
13572 AutoCaller autoCaller(this);
13573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13574
13575 ComPtr<IInternalSessionControl> directControl;
13576 {
13577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13578 directControl = mData->mSession.mDirectControl;
13579 }
13580
13581 /* ignore notifications sent after #OnSessionEnd() is called */
13582 if (!directControl)
13583 return S_OK;
13584
13585 return directControl->OnCPUChange(aCPU, aRemove);
13586}
13587
13588HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13589{
13590 LogFlowThisFunc(("\n"));
13591
13592 AutoCaller autoCaller(this);
13593 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13594
13595 ComPtr<IInternalSessionControl> directControl;
13596 {
13597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13598 directControl = mData->mSession.mDirectControl;
13599 }
13600
13601 /* ignore notifications sent after #OnSessionEnd() is called */
13602 if (!directControl)
13603 return S_OK;
13604
13605 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13606}
13607
13608/**
13609 * @note Locks this object for reading.
13610 */
13611HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13612{
13613 LogFlowThisFunc(("\n"));
13614
13615 AutoCaller autoCaller(this);
13616 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13617
13618 ComPtr<IInternalSessionControl> directControl;
13619 {
13620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13621 directControl = mData->mSession.mDirectControl;
13622 }
13623
13624 /* ignore notifications sent after #OnSessionEnd() is called */
13625 if (!directControl)
13626 return S_OK;
13627
13628 return directControl->OnVRDEServerChange(aRestart);
13629}
13630
13631/**
13632 * @note Locks this object for reading.
13633 */
13634HRESULT SessionMachine::i_onVideoCaptureChange()
13635{
13636 LogFlowThisFunc(("\n"));
13637
13638 AutoCaller autoCaller(this);
13639 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13640
13641 ComPtr<IInternalSessionControl> directControl;
13642 {
13643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13644 directControl = mData->mSession.mDirectControl;
13645 }
13646
13647 /* ignore notifications sent after #OnSessionEnd() is called */
13648 if (!directControl)
13649 return S_OK;
13650
13651 return directControl->OnVideoCaptureChange();
13652}
13653
13654/**
13655 * @note Locks this object for reading.
13656 */
13657HRESULT SessionMachine::i_onUSBControllerChange()
13658{
13659 LogFlowThisFunc(("\n"));
13660
13661 AutoCaller autoCaller(this);
13662 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13663
13664 ComPtr<IInternalSessionControl> directControl;
13665 {
13666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13667 directControl = mData->mSession.mDirectControl;
13668 }
13669
13670 /* ignore notifications sent after #OnSessionEnd() is called */
13671 if (!directControl)
13672 return S_OK;
13673
13674 return directControl->OnUSBControllerChange();
13675}
13676
13677/**
13678 * @note Locks this object for reading.
13679 */
13680HRESULT SessionMachine::i_onSharedFolderChange()
13681{
13682 LogFlowThisFunc(("\n"));
13683
13684 AutoCaller autoCaller(this);
13685 AssertComRCReturnRC(autoCaller.rc());
13686
13687 ComPtr<IInternalSessionControl> directControl;
13688 {
13689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13690 directControl = mData->mSession.mDirectControl;
13691 }
13692
13693 /* ignore notifications sent after #OnSessionEnd() is called */
13694 if (!directControl)
13695 return S_OK;
13696
13697 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13698}
13699
13700/**
13701 * @note Locks this object for reading.
13702 */
13703HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13704{
13705 LogFlowThisFunc(("\n"));
13706
13707 AutoCaller autoCaller(this);
13708 AssertComRCReturnRC(autoCaller.rc());
13709
13710 ComPtr<IInternalSessionControl> directControl;
13711 {
13712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13713 directControl = mData->mSession.mDirectControl;
13714 }
13715
13716 /* ignore notifications sent after #OnSessionEnd() is called */
13717 if (!directControl)
13718 return S_OK;
13719
13720 return directControl->OnClipboardModeChange(aClipboardMode);
13721}
13722
13723/**
13724 * @note Locks this object for reading.
13725 */
13726HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13727{
13728 LogFlowThisFunc(("\n"));
13729
13730 AutoCaller autoCaller(this);
13731 AssertComRCReturnRC(autoCaller.rc());
13732
13733 ComPtr<IInternalSessionControl> directControl;
13734 {
13735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13736 directControl = mData->mSession.mDirectControl;
13737 }
13738
13739 /* ignore notifications sent after #OnSessionEnd() is called */
13740 if (!directControl)
13741 return S_OK;
13742
13743 return directControl->OnDnDModeChange(aDnDMode);
13744}
13745
13746/**
13747 * @note Locks this object for reading.
13748 */
13749HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13750{
13751 LogFlowThisFunc(("\n"));
13752
13753 AutoCaller autoCaller(this);
13754 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13755
13756 ComPtr<IInternalSessionControl> directControl;
13757 {
13758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13759 directControl = mData->mSession.mDirectControl;
13760 }
13761
13762 /* ignore notifications sent after #OnSessionEnd() is called */
13763 if (!directControl)
13764 return S_OK;
13765
13766 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13767}
13768
13769/**
13770 * @note Locks this object for reading.
13771 */
13772HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13773{
13774 LogFlowThisFunc(("\n"));
13775
13776 AutoCaller autoCaller(this);
13777 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13778
13779 ComPtr<IInternalSessionControl> directControl;
13780 {
13781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13782 directControl = mData->mSession.mDirectControl;
13783 }
13784
13785 /* ignore notifications sent after #OnSessionEnd() is called */
13786 if (!directControl)
13787 return S_OK;
13788
13789 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13790}
13791
13792/**
13793 * Returns @c true if this machine's USB controller reports it has a matching
13794 * filter for the given USB device and @c false otherwise.
13795 *
13796 * @note locks this object for reading.
13797 */
13798bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13799{
13800 AutoCaller autoCaller(this);
13801 /* silently return if not ready -- this method may be called after the
13802 * direct machine session has been called */
13803 if (!autoCaller.isOk())
13804 return false;
13805
13806#ifdef VBOX_WITH_USB
13807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13808
13809 switch (mData->mMachineState)
13810 {
13811 case MachineState_Starting:
13812 case MachineState_Restoring:
13813 case MachineState_TeleportingIn:
13814 case MachineState_Paused:
13815 case MachineState_Running:
13816 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13817 * elsewhere... */
13818 alock.release();
13819 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13820 default: break;
13821 }
13822#else
13823 NOREF(aDevice);
13824 NOREF(aMaskedIfs);
13825#endif
13826 return false;
13827}
13828
13829/**
13830 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13831 */
13832HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13833 IVirtualBoxErrorInfo *aError,
13834 ULONG aMaskedIfs,
13835 const com::Utf8Str &aCaptureFilename)
13836{
13837 LogFlowThisFunc(("\n"));
13838
13839 AutoCaller autoCaller(this);
13840
13841 /* This notification may happen after the machine object has been
13842 * uninitialized (the session was closed), so don't assert. */
13843 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13844
13845 ComPtr<IInternalSessionControl> directControl;
13846 {
13847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13848 directControl = mData->mSession.mDirectControl;
13849 }
13850
13851 /* fail on notifications sent after #OnSessionEnd() is called, it is
13852 * expected by the caller */
13853 if (!directControl)
13854 return E_FAIL;
13855
13856 /* No locks should be held at this point. */
13857 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13858 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13859
13860 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
13861}
13862
13863/**
13864 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13865 */
13866HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13867 IVirtualBoxErrorInfo *aError)
13868{
13869 LogFlowThisFunc(("\n"));
13870
13871 AutoCaller autoCaller(this);
13872
13873 /* This notification may happen after the machine object has been
13874 * uninitialized (the session was closed), so don't assert. */
13875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13876
13877 ComPtr<IInternalSessionControl> directControl;
13878 {
13879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13880 directControl = mData->mSession.mDirectControl;
13881 }
13882
13883 /* fail on notifications sent after #OnSessionEnd() is called, it is
13884 * expected by the caller */
13885 if (!directControl)
13886 return E_FAIL;
13887
13888 /* No locks should be held at this point. */
13889 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13890 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13891
13892 return directControl->OnUSBDeviceDetach(aId, aError);
13893}
13894
13895// protected methods
13896/////////////////////////////////////////////////////////////////////////////
13897
13898/**
13899 * Helper method to finalize saving the state.
13900 *
13901 * @note Must be called from under this object's lock.
13902 *
13903 * @param aRc S_OK if the snapshot has been taken successfully
13904 * @param aErrMsg human readable error message for failure
13905 *
13906 * @note Locks mParent + this objects for writing.
13907 */
13908HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13909{
13910 LogFlowThisFuncEnter();
13911
13912 AutoCaller autoCaller(this);
13913 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13914
13915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13916
13917 HRESULT rc = S_OK;
13918
13919 if (SUCCEEDED(aRc))
13920 {
13921 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13922
13923 /* save all VM settings */
13924 rc = i_saveSettings(NULL);
13925 // no need to check whether VirtualBox.xml needs saving also since
13926 // we can't have a name change pending at this point
13927 }
13928 else
13929 {
13930 // delete the saved state file (it might have been already created);
13931 // we need not check whether this is shared with a snapshot here because
13932 // we certainly created this saved state file here anew
13933 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13934 }
13935
13936 /* notify the progress object about operation completion */
13937 Assert(mConsoleTaskData.mProgress);
13938 if (SUCCEEDED(aRc))
13939 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13940 else
13941 {
13942 if (aErrMsg.length())
13943 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13944 COM_IIDOF(ISession),
13945 getComponentName(),
13946 aErrMsg.c_str());
13947 else
13948 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13949 }
13950
13951 /* clear out the temporary saved state data */
13952 mConsoleTaskData.mLastState = MachineState_Null;
13953 mConsoleTaskData.strStateFilePath.setNull();
13954 mConsoleTaskData.mProgress.setNull();
13955
13956 LogFlowThisFuncLeave();
13957 return rc;
13958}
13959
13960/**
13961 * Deletes the given file if it is no longer in use by either the current machine state
13962 * (if the machine is "saved") or any of the machine's snapshots.
13963 *
13964 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13965 * but is different for each SnapshotMachine. When calling this, the order of calling this
13966 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13967 * is therefore critical. I know, it's all rather messy.
13968 *
13969 * @param strStateFile
13970 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13971 * the test for whether the saved state file is in use.
13972 */
13973void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13974 Snapshot *pSnapshotToIgnore)
13975{
13976 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13977 if ( (strStateFile.isNotEmpty())
13978 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13979 )
13980 // ... and it must also not be shared with other snapshots
13981 if ( !mData->mFirstSnapshot
13982 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13983 // this checks the SnapshotMachine's state file paths
13984 )
13985 RTFileDelete(strStateFile.c_str());
13986}
13987
13988/**
13989 * Locks the attached media.
13990 *
13991 * All attached hard disks are locked for writing and DVD/floppy are locked for
13992 * reading. Parents of attached hard disks (if any) are locked for reading.
13993 *
13994 * This method also performs accessibility check of all media it locks: if some
13995 * media is inaccessible, the method will return a failure and a bunch of
13996 * extended error info objects per each inaccessible medium.
13997 *
13998 * Note that this method is atomic: if it returns a success, all media are
13999 * locked as described above; on failure no media is locked at all (all
14000 * succeeded individual locks will be undone).
14001 *
14002 * The caller is responsible for doing the necessary state sanity checks.
14003 *
14004 * The locks made by this method must be undone by calling #unlockMedia() when
14005 * no more needed.
14006 */
14007HRESULT SessionMachine::i_lockMedia()
14008{
14009 AutoCaller autoCaller(this);
14010 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14011
14012 AutoMultiWriteLock2 alock(this->lockHandle(),
14013 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14014
14015 /* bail out if trying to lock things with already set up locking */
14016 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14017
14018 MultiResult mrc(S_OK);
14019
14020 /* Collect locking information for all medium objects attached to the VM. */
14021 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14022 it != mMediaData->mAttachments.end();
14023 ++it)
14024 {
14025 MediumAttachment* pAtt = *it;
14026 DeviceType_T devType = pAtt->i_getType();
14027 Medium *pMedium = pAtt->i_getMedium();
14028
14029 MediumLockList *pMediumLockList(new MediumLockList());
14030 // There can be attachments without a medium (floppy/dvd), and thus
14031 // it's impossible to create a medium lock list. It still makes sense
14032 // to have the empty medium lock list in the map in case a medium is
14033 // attached later.
14034 if (pMedium != NULL)
14035 {
14036 MediumType_T mediumType = pMedium->i_getType();
14037 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14038 || mediumType == MediumType_Shareable;
14039 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14040
14041 alock.release();
14042 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14043 !fIsReadOnlyLock /* fMediumLockWrite */,
14044 false /* fMediumLockWriteAll */,
14045 NULL,
14046 *pMediumLockList);
14047 alock.acquire();
14048 if (FAILED(mrc))
14049 {
14050 delete pMediumLockList;
14051 mData->mSession.mLockedMedia.Clear();
14052 break;
14053 }
14054 }
14055
14056 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14057 if (FAILED(rc))
14058 {
14059 mData->mSession.mLockedMedia.Clear();
14060 mrc = setError(rc,
14061 tr("Collecting locking information for all attached media failed"));
14062 break;
14063 }
14064 }
14065
14066 if (SUCCEEDED(mrc))
14067 {
14068 /* Now lock all media. If this fails, nothing is locked. */
14069 alock.release();
14070 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14071 alock.acquire();
14072 if (FAILED(rc))
14073 {
14074 mrc = setError(rc,
14075 tr("Locking of attached media failed"));
14076 }
14077 }
14078
14079 return mrc;
14080}
14081
14082/**
14083 * Undoes the locks made by by #lockMedia().
14084 */
14085HRESULT SessionMachine::i_unlockMedia()
14086{
14087 AutoCaller autoCaller(this);
14088 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14089
14090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14091
14092 /* we may be holding important error info on the current thread;
14093 * preserve it */
14094 ErrorInfoKeeper eik;
14095
14096 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14097 AssertComRC(rc);
14098 return rc;
14099}
14100
14101/**
14102 * Helper to change the machine state (reimplementation).
14103 *
14104 * @note Locks this object for writing.
14105 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14106 * it can cause crashes in random places due to unexpectedly committing
14107 * the current settings. The caller is responsible for that. The call
14108 * to saveStateSettings is fine, because this method does not commit.
14109 */
14110HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14111{
14112 LogFlowThisFuncEnter();
14113 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14114
14115 AutoCaller autoCaller(this);
14116 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14117
14118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14119
14120 MachineState_T oldMachineState = mData->mMachineState;
14121
14122 AssertMsgReturn(oldMachineState != aMachineState,
14123 ("oldMachineState=%s, aMachineState=%s\n",
14124 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14125 E_FAIL);
14126
14127 HRESULT rc = S_OK;
14128
14129 int stsFlags = 0;
14130 bool deleteSavedState = false;
14131
14132 /* detect some state transitions */
14133
14134 if ( ( oldMachineState == MachineState_Saved
14135 && aMachineState == MachineState_Restoring)
14136 || ( ( oldMachineState == MachineState_PoweredOff
14137 || oldMachineState == MachineState_Teleported
14138 || oldMachineState == MachineState_Aborted
14139 )
14140 && ( aMachineState == MachineState_TeleportingIn
14141 || aMachineState == MachineState_Starting
14142 )
14143 )
14144 )
14145 {
14146 /* The EMT thread is about to start */
14147
14148 /* Nothing to do here for now... */
14149
14150 /// @todo NEWMEDIA don't let mDVDDrive and other children
14151 /// change anything when in the Starting/Restoring state
14152 }
14153 else if ( ( oldMachineState == MachineState_Running
14154 || oldMachineState == MachineState_Paused
14155 || oldMachineState == MachineState_Teleporting
14156 || oldMachineState == MachineState_LiveSnapshotting
14157 || oldMachineState == MachineState_Stuck
14158 || oldMachineState == MachineState_Starting
14159 || oldMachineState == MachineState_Stopping
14160 || oldMachineState == MachineState_Saving
14161 || oldMachineState == MachineState_Restoring
14162 || oldMachineState == MachineState_TeleportingPausedVM
14163 || oldMachineState == MachineState_TeleportingIn
14164 )
14165 && ( aMachineState == MachineState_PoweredOff
14166 || aMachineState == MachineState_Saved
14167 || aMachineState == MachineState_Teleported
14168 || aMachineState == MachineState_Aborted
14169 )
14170 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14171 * snapshot */
14172 && ( mConsoleTaskData.mSnapshot.isNull()
14173 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14174 )
14175 )
14176 {
14177 /* The EMT thread has just stopped, unlock attached media. Note that as
14178 * opposed to locking that is done from Console, we do unlocking here
14179 * because the VM process may have aborted before having a chance to
14180 * properly unlock all media it locked. */
14181
14182 unlockMedia();
14183 }
14184
14185 if (oldMachineState == MachineState_Restoring)
14186 {
14187 if (aMachineState != MachineState_Saved)
14188 {
14189 /*
14190 * delete the saved state file once the machine has finished
14191 * restoring from it (note that Console sets the state from
14192 * Restoring to Saved if the VM couldn't restore successfully,
14193 * to give the user an ability to fix an error and retry --
14194 * we keep the saved state file in this case)
14195 */
14196 deleteSavedState = true;
14197 }
14198 }
14199 else if ( oldMachineState == MachineState_Saved
14200 && ( aMachineState == MachineState_PoweredOff
14201 || aMachineState == MachineState_Aborted
14202 || aMachineState == MachineState_Teleported
14203 )
14204 )
14205 {
14206 /*
14207 * delete the saved state after Console::ForgetSavedState() is called
14208 * or if the VM process (owning a direct VM session) crashed while the
14209 * VM was Saved
14210 */
14211
14212 /// @todo (dmik)
14213 // Not sure that deleting the saved state file just because of the
14214 // client death before it attempted to restore the VM is a good
14215 // thing. But when it crashes we need to go to the Aborted state
14216 // which cannot have the saved state file associated... The only
14217 // way to fix this is to make the Aborted condition not a VM state
14218 // but a bool flag: i.e., when a crash occurs, set it to true and
14219 // change the state to PoweredOff or Saved depending on the
14220 // saved state presence.
14221
14222 deleteSavedState = true;
14223 mData->mCurrentStateModified = TRUE;
14224 stsFlags |= SaveSTS_CurStateModified;
14225 }
14226
14227 if ( aMachineState == MachineState_Starting
14228 || aMachineState == MachineState_Restoring
14229 || aMachineState == MachineState_TeleportingIn
14230 )
14231 {
14232 /* set the current state modified flag to indicate that the current
14233 * state is no more identical to the state in the
14234 * current snapshot */
14235 if (!mData->mCurrentSnapshot.isNull())
14236 {
14237 mData->mCurrentStateModified = TRUE;
14238 stsFlags |= SaveSTS_CurStateModified;
14239 }
14240 }
14241
14242 if (deleteSavedState)
14243 {
14244 if (mRemoveSavedState)
14245 {
14246 Assert(!mSSData->strStateFilePath.isEmpty());
14247
14248 // it is safe to delete the saved state file if ...
14249 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14250 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14251 // ... none of the snapshots share the saved state file
14252 )
14253 RTFileDelete(mSSData->strStateFilePath.c_str());
14254 }
14255
14256 mSSData->strStateFilePath.setNull();
14257 stsFlags |= SaveSTS_StateFilePath;
14258 }
14259
14260 /* redirect to the underlying peer machine */
14261 mPeer->i_setMachineState(aMachineState);
14262
14263 if ( aMachineState == MachineState_PoweredOff
14264 || aMachineState == MachineState_Teleported
14265 || aMachineState == MachineState_Aborted
14266 || aMachineState == MachineState_Saved)
14267 {
14268 /* the machine has stopped execution
14269 * (or the saved state file was adopted) */
14270 stsFlags |= SaveSTS_StateTimeStamp;
14271 }
14272
14273 if ( ( oldMachineState == MachineState_PoweredOff
14274 || oldMachineState == MachineState_Aborted
14275 || oldMachineState == MachineState_Teleported
14276 )
14277 && aMachineState == MachineState_Saved)
14278 {
14279 /* the saved state file was adopted */
14280 Assert(!mSSData->strStateFilePath.isEmpty());
14281 stsFlags |= SaveSTS_StateFilePath;
14282 }
14283
14284#ifdef VBOX_WITH_GUEST_PROPS
14285 if ( aMachineState == MachineState_PoweredOff
14286 || aMachineState == MachineState_Aborted
14287 || aMachineState == MachineState_Teleported)
14288 {
14289 /* Make sure any transient guest properties get removed from the
14290 * property store on shutdown. */
14291 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14292
14293 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14294 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14295 while (it != llGuestProperties.end())
14296 {
14297 const settings::GuestProperty &prop = *it;
14298 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14299 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14300 {
14301 it = llGuestProperties.erase(it);
14302 fNeedsSaving = true;
14303 }
14304 else
14305 {
14306 ++it;
14307 }
14308 }
14309
14310 if (fNeedsSaving)
14311 {
14312 mData->mCurrentStateModified = TRUE;
14313 stsFlags |= SaveSTS_CurStateModified;
14314 }
14315 }
14316#endif /* VBOX_WITH_GUEST_PROPS */
14317
14318 rc = i_saveStateSettings(stsFlags);
14319
14320 if ( ( oldMachineState != MachineState_PoweredOff
14321 && oldMachineState != MachineState_Aborted
14322 && oldMachineState != MachineState_Teleported
14323 )
14324 && ( aMachineState == MachineState_PoweredOff
14325 || aMachineState == MachineState_Aborted
14326 || aMachineState == MachineState_Teleported
14327 )
14328 )
14329 {
14330 /* we've been shut down for any reason */
14331 /* no special action so far */
14332 }
14333
14334 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14335 LogFlowThisFuncLeave();
14336 return rc;
14337}
14338
14339/**
14340 * Sends the current machine state value to the VM process.
14341 *
14342 * @note Locks this object for reading, then calls a client process.
14343 */
14344HRESULT SessionMachine::i_updateMachineStateOnClient()
14345{
14346 AutoCaller autoCaller(this);
14347 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14348
14349 ComPtr<IInternalSessionControl> directControl;
14350 {
14351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14352 AssertReturn(!!mData, E_FAIL);
14353 directControl = mData->mSession.mDirectControl;
14354
14355 /* directControl may be already set to NULL here in #OnSessionEnd()
14356 * called too early by the direct session process while there is still
14357 * some operation (like deleting the snapshot) in progress. The client
14358 * process in this case is waiting inside Session::close() for the
14359 * "end session" process object to complete, while #uninit() called by
14360 * #checkForDeath() on the Watcher thread is waiting for the pending
14361 * operation to complete. For now, we accept this inconsistent behavior
14362 * and simply do nothing here. */
14363
14364 if (mData->mSession.mState == SessionState_Unlocking)
14365 return S_OK;
14366
14367 AssertReturn(!directControl.isNull(), E_FAIL);
14368 }
14369
14370 return directControl->UpdateMachineState(mData->mMachineState);
14371}
14372
14373HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14374{
14375 NOREF(aRemove);
14376 ReturnComNotImplemented();
14377}
14378
14379HRESULT Machine::updateState(MachineState_T aState)
14380{
14381 NOREF(aState);
14382 ReturnComNotImplemented();
14383}
14384
14385HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14386{
14387 NOREF(aProgress);
14388 ReturnComNotImplemented();
14389}
14390
14391HRESULT Machine::endPowerUp(LONG aResult)
14392{
14393 NOREF(aResult);
14394 ReturnComNotImplemented();
14395}
14396
14397HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14398{
14399 NOREF(aProgress);
14400 ReturnComNotImplemented();
14401}
14402
14403HRESULT Machine::endPoweringDown(LONG aResult,
14404 const com::Utf8Str &aErrMsg)
14405{
14406 NOREF(aResult);
14407 NOREF(aErrMsg);
14408 ReturnComNotImplemented();
14409}
14410
14411HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14412 BOOL *aMatched,
14413 ULONG *aMaskedInterfaces)
14414{
14415 NOREF(aDevice);
14416 NOREF(aMatched);
14417 NOREF(aMaskedInterfaces);
14418 ReturnComNotImplemented();
14419
14420}
14421
14422HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14423{
14424 NOREF(aId); NOREF(aCaptureFilename);
14425 ReturnComNotImplemented();
14426}
14427
14428HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14429 BOOL aDone)
14430{
14431 NOREF(aId);
14432 NOREF(aDone);
14433 ReturnComNotImplemented();
14434}
14435
14436HRESULT Machine::autoCaptureUSBDevices()
14437{
14438 ReturnComNotImplemented();
14439}
14440
14441HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14442{
14443 NOREF(aDone);
14444 ReturnComNotImplemented();
14445}
14446
14447HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14448 ComPtr<IProgress> &aProgress)
14449{
14450 NOREF(aSession);
14451 NOREF(aProgress);
14452 ReturnComNotImplemented();
14453}
14454
14455HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14456 com::Utf8Str &aStateFilePath)
14457{
14458 NOREF(aProgress);
14459 NOREF(aStateFilePath);
14460 ReturnComNotImplemented();
14461}
14462
14463HRESULT Machine::endSavingState(LONG aResult,
14464 const com::Utf8Str &aErrMsg)
14465{
14466 NOREF(aResult);
14467 NOREF(aErrMsg);
14468 ReturnComNotImplemented();
14469}
14470
14471HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14472{
14473 NOREF(aSavedStateFile);
14474 ReturnComNotImplemented();
14475}
14476
14477HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14478 const com::Utf8Str &aName,
14479 const com::Utf8Str &aDescription,
14480 const ComPtr<IProgress> &aConsoleProgress,
14481 BOOL aFTakingSnapshotOnline,
14482 com::Utf8Str &aStateFilePath)
14483{
14484 NOREF(aInitiator);
14485 NOREF(aName);
14486 NOREF(aDescription);
14487 NOREF(aConsoleProgress);
14488 NOREF(aFTakingSnapshotOnline);
14489 NOREF(aStateFilePath);
14490 ReturnComNotImplemented();
14491}
14492
14493HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14494{
14495 NOREF(aSuccess);
14496 ReturnComNotImplemented();
14497}
14498
14499HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14500 const com::Guid &aStartId,
14501 const com::Guid &aEndId,
14502 BOOL aDeleteAllChildren,
14503 MachineState_T *aMachineState,
14504 ComPtr<IProgress> &aProgress)
14505{
14506 NOREF(aInitiator);
14507 NOREF(aStartId);
14508 NOREF(aEndId);
14509 NOREF(aDeleteAllChildren);
14510 NOREF(aMachineState);
14511 NOREF(aProgress);
14512 ReturnComNotImplemented();
14513}
14514
14515HRESULT Machine::finishOnlineMergeMedium()
14516{
14517 ReturnComNotImplemented();
14518}
14519
14520HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14521 const ComPtr<ISnapshot> &aSnapshot,
14522 MachineState_T *aMachineState,
14523 ComPtr<IProgress> &aProgress)
14524{
14525 NOREF(aInitiator);
14526 NOREF(aSnapshot);
14527 NOREF(aMachineState);
14528 NOREF(aProgress);
14529 ReturnComNotImplemented();
14530}
14531
14532HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14533 std::vector<com::Utf8Str> &aValues,
14534 std::vector<LONG64> &aTimestamps,
14535 std::vector<com::Utf8Str> &aFlags)
14536{
14537 NOREF(aNames);
14538 NOREF(aValues);
14539 NOREF(aTimestamps);
14540 NOREF(aFlags);
14541 ReturnComNotImplemented();
14542}
14543
14544HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14545 const com::Utf8Str &aValue,
14546 LONG64 aTimestamp,
14547 const com::Utf8Str &aFlags)
14548{
14549 NOREF(aName);
14550 NOREF(aValue);
14551 NOREF(aTimestamp);
14552 NOREF(aFlags);
14553 ReturnComNotImplemented();
14554}
14555
14556HRESULT Machine::lockMedia()
14557{
14558 ReturnComNotImplemented();
14559}
14560
14561HRESULT Machine::unlockMedia()
14562{
14563 ReturnComNotImplemented();
14564}
14565
14566HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14567 ComPtr<IMediumAttachment> &aNewAttachment)
14568{
14569 NOREF(aAttachment);
14570 NOREF(aNewAttachment);
14571 ReturnComNotImplemented();
14572}
14573
14574HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14575 ULONG aCpuUser,
14576 ULONG aCpuKernel,
14577 ULONG aCpuIdle,
14578 ULONG aMemTotal,
14579 ULONG aMemFree,
14580 ULONG aMemBalloon,
14581 ULONG aMemShared,
14582 ULONG aMemCache,
14583 ULONG aPagedTotal,
14584 ULONG aMemAllocTotal,
14585 ULONG aMemFreeTotal,
14586 ULONG aMemBalloonTotal,
14587 ULONG aMemSharedTotal,
14588 ULONG aVmNetRx,
14589 ULONG aVmNetTx)
14590{
14591 NOREF(aValidStats);
14592 NOREF(aCpuUser);
14593 NOREF(aCpuKernel);
14594 NOREF(aCpuIdle);
14595 NOREF(aMemTotal);
14596 NOREF(aMemFree);
14597 NOREF(aMemBalloon);
14598 NOREF(aMemShared);
14599 NOREF(aMemCache);
14600 NOREF(aPagedTotal);
14601 NOREF(aMemAllocTotal);
14602 NOREF(aMemFreeTotal);
14603 NOREF(aMemBalloonTotal);
14604 NOREF(aMemSharedTotal);
14605 NOREF(aVmNetRx);
14606 NOREF(aVmNetTx);
14607 ReturnComNotImplemented();
14608}
Note: See TracBrowser for help on using the repository browser.

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