VirtualBox

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

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

Main/Machine: fix overlooked case sensitive frontend name check

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 506.0 KB
Line 
1/* $Id: MachineImpl.cpp 55168 2015-04-09 17:36:51Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#ifdef VBOX_WITH_DTRACE_R3_MAIN
90# include "dtrace/VBoxAPI.h"
91#endif
92
93#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
94# define HOSTSUFF_EXE ".exe"
95#else /* !RT_OS_WINDOWS */
96# define HOSTSUFF_EXE ""
97#endif /* !RT_OS_WINDOWS */
98
99// defines / prototypes
100/////////////////////////////////////////////////////////////////////////////
101
102/////////////////////////////////////////////////////////////////////////////
103// Machine::Data structure
104/////////////////////////////////////////////////////////////////////////////
105
106Machine::Data::Data()
107{
108 mRegistered = FALSE;
109 pMachineConfigFile = NULL;
110 /* Contains hints on what has changed when the user is using the VM (config
111 * changes, running the VM, ...). This is used to decide if a config needs
112 * to be written to disk. */
113 flModifications = 0;
114 /* VM modification usually also trigger setting the current state to
115 * "Modified". Although this is not always the case. An e.g. is the VM
116 * initialization phase or when snapshot related data is changed. The
117 * actually behavior is controlled by the following flag. */
118 m_fAllowStateModification = false;
119 mAccessible = FALSE;
120 /* mUuid is initialized in Machine::init() */
121
122 mMachineState = MachineState_PoweredOff;
123 RTTimeNow(&mLastStateChange);
124
125 mMachineStateDeps = 0;
126 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
127 mMachineStateChangePending = 0;
128
129 mCurrentStateModified = TRUE;
130 mGuestPropertiesModified = FALSE;
131
132 mSession.mPID = NIL_RTPROCESS;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mTripleFaultReset = false;
197 mHPETEnabled = false;
198
199 /* default boot order: floppy - DVD - HDD */
200 mBootOrder[0] = DeviceType_Floppy;
201 mBootOrder[1] = DeviceType_DVD;
202 mBootOrder[2] = DeviceType_HardDisk;
203 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
204 mBootOrder[i] = DeviceType_Null;
205
206 mClipboardMode = ClipboardMode_Disabled;
207 mDnDMode = DnDMode_Disabled;
208 mGuestPropertyNotificationPatterns = "";
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222
223 /* Maximum CPU execution cap by default. */
224 mCpuExecutionCap = 100;
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HDData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::MediaData::MediaData()
236{
237}
238
239Machine::MediaData::~MediaData()
240{
241}
242
243/////////////////////////////////////////////////////////////////////////////
244// Machine class
245/////////////////////////////////////////////////////////////////////////////
246
247// constructor / destructor
248/////////////////////////////////////////////////////////////////////////////
249
250Machine::Machine() :
251#ifdef VBOX_WITH_RESOURCE_USAGE_API
252 mCollectorGuest(NULL),
253#endif
254 mPeer(NULL),
255 mParent(NULL),
256 mSerialPorts(),
257 mParallelPorts(),
258 uRegistryNeedsSaving(0)
259{}
260
261Machine::~Machine()
262{}
263
264HRESULT Machine::FinalConstruct()
265{
266 LogFlowThisFunc(("\n"));
267 return BaseFinalConstruct();
268}
269
270void Machine::FinalRelease()
271{
272 LogFlowThisFunc(("\n"));
273 uninit();
274 BaseFinalRelease();
275}
276
277/**
278 * Initializes a new machine instance; this init() variant creates a new, empty machine.
279 * This gets called from VirtualBox::CreateMachine().
280 *
281 * @param aParent Associated parent object
282 * @param strConfigFile Local file system path to the VM settings file (can
283 * be relative to the VirtualBox config directory).
284 * @param strName name for the machine
285 * @param llGroups list of groups for the machine
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Apply BIOS defaults */
349 mBIOSSettings->i_applyDefaults(aOsType);
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Let the OS type select 64-bit ness. */
360 mHWData->mLongMode = aOsType->i_is64Bit()
361 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 }
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 i_allowStateModification();
367
368 /* commit all changes made during the initialization */
369 i_commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param aConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = i_registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 i_allowStateModification();
469
470 i_commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->i_unregisterMachineMedia(i_getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent
607 * @return
608 */
609HRESULT Machine::initImpl(VirtualBox *aParent,
610 const Utf8Str &strConfigFile)
611{
612 LogFlowThisFuncEnter();
613
614 AssertReturn(aParent, E_INVALIDARG);
615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
616
617 HRESULT rc = S_OK;
618
619 /* share the parent weakly */
620 unconst(mParent) = aParent;
621
622 /* allocate the essential machine data structure (the rest will be
623 * allocated later by initDataAndChildObjects() */
624 mData.allocate();
625
626 /* memorize the config file name (as provided) */
627 mData->m_strConfigFile = strConfigFile;
628
629 /* get the full file name */
630 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
631 if (RT_FAILURE(vrc1))
632 return setError(VBOX_E_FILE_ERROR,
633 tr("Invalid machine settings file name '%s' (%Rrc)"),
634 strConfigFile.c_str(),
635 vrc1);
636
637 LogFlowThisFuncLeave();
638
639 return rc;
640}
641
642/**
643 * Tries to create a machine settings file in the path stored in the machine
644 * instance data. Used when a new machine is created to fail gracefully if
645 * the settings file could not be written (e.g. because machine dir is read-only).
646 * @return
647 */
648HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
649{
650 HRESULT rc = S_OK;
651
652 // when we create a new machine, we must be able to create the settings file
653 RTFILE f = NIL_RTFILE;
654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
655 if ( RT_SUCCESS(vrc)
656 || vrc == VERR_SHARING_VIOLATION
657 )
658 {
659 if (RT_SUCCESS(vrc))
660 RTFileClose(f);
661 if (!fForceOverwrite)
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Machine settings file '%s' already exists"),
664 mData->m_strConfigFileFull.c_str());
665 else
666 {
667 /* try to delete the config file, as otherwise the creation
668 * of a new settings file will fail. */
669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
670 if (RT_FAILURE(vrc2))
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Could not delete the existing settings file '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(), vrc2);
674 }
675 }
676 else if ( vrc != VERR_FILE_NOT_FOUND
677 && vrc != VERR_PATH_NOT_FOUND
678 )
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Invalid machine settings file name '%s' (%Rrc)"),
681 mData->m_strConfigFileFull.c_str(),
682 vrc);
683 return rc;
684}
685
686/**
687 * Initializes the registered machine by loading the settings file.
688 * This method is separated from #init() in order to make it possible to
689 * retry the operation after VirtualBox startup instead of refusing to
690 * startup the whole VirtualBox server in case if the settings file of some
691 * registered VM is invalid or inaccessible.
692 *
693 * @note Must be always called from this object's write lock
694 * (unless called from #init() that doesn't need any locking).
695 * @note Locks the mUSBController method for writing.
696 * @note Subclasses must not call this method.
697 */
698HRESULT Machine::i_registeredInit()
699{
700 AssertReturn(!i_isSessionMachine(), E_FAIL);
701 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
702 AssertReturn(mData->mUuid.isValid(), E_FAIL);
703 AssertReturn(!mData->mAccessible, E_FAIL);
704
705 HRESULT rc = initDataAndChildObjects();
706
707 if (SUCCEEDED(rc))
708 {
709 /* Temporarily reset the registered flag in order to let setters
710 * potentially called from loadSettings() succeed (isMutable() used in
711 * all setters will return FALSE for a Machine instance if mRegistered
712 * is TRUE). */
713 mData->mRegistered = FALSE;
714
715 try
716 {
717 // load and parse machine XML; this will throw on XML or logic errors
718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
719
720 if (mData->mUuid != mData->pMachineConfigFile->uuid)
721 throw setError(E_FAIL,
722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
723 mData->pMachineConfigFile->uuid.raw(),
724 mData->m_strConfigFileFull.c_str(),
725 mData->mUuid.toString().c_str(),
726 mParent->i_settingsFilePath().c_str());
727
728 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
729 NULL /* const Guid *puuidRegistry */);
730 if (FAILED(rc)) throw rc;
731 }
732 catch (HRESULT err)
733 {
734 /* we assume that error info is set by the thrower */
735 rc = err;
736 }
737 catch (...)
738 {
739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
740 }
741
742 /* Restore the registered flag (even on failure) */
743 mData->mRegistered = TRUE;
744 }
745
746 if (SUCCEEDED(rc))
747 {
748 /* Set mAccessible to TRUE only if we successfully locked and loaded
749 * the settings file */
750 mData->mAccessible = TRUE;
751
752 /* commit all changes made during loading the settings file */
753 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
754 /// @todo r=klaus for some reason the settings loading logic backs up
755 // the settings, and therefore a commit is needed. Should probably be changed.
756 }
757 else
758 {
759 /* If the machine is registered, then, instead of returning a
760 * failure, we mark it as inaccessible and set the result to
761 * success to give it a try later */
762
763 /* fetch the current error info */
764 mData->mAccessError = com::ErrorInfo();
765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
766 mData->mUuid.raw(),
767 mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 i_rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->i_unregisterMachineMedia(i_getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 i_saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!i_isSnapshotMachine());
820 Assert(!i_isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it does
835 * VirtualBox::addCaller() for the duration of the
836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 LogWarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(i_getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->i_unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
879 i_rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// Wrapped IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
894{
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 aParent = pVirtualBox;
898
899 return S_OK;
900}
901
902
903HRESULT Machine::getAccessible(BOOL *aAccessible)
904{
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->i_dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = i_registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->i_onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
959 {
960 /* return shortly */
961 aAccessError = NULL;
962 return S_OK;
963 }
964
965 HRESULT rc = S_OK;
966
967 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
968 rc = errorInfo.createObject();
969 if (SUCCEEDED(rc))
970 {
971 errorInfo->init(mData->mAccessError.getResultCode(),
972 mData->mAccessError.getInterfaceID().ref(),
973 Utf8Str(mData->mAccessError.getComponent()).c_str(),
974 Utf8Str(mData->mAccessError.getText()));
975 aAccessError = errorInfo;
976 }
977
978 return rc;
979}
980
981HRESULT Machine::getName(com::Utf8Str &aName)
982{
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 aName = mUserData->s.strName;
986
987 return S_OK;
988}
989
990HRESULT Machine::setName(const com::Utf8Str &aName)
991{
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995
996 if (test.isValid())
997 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
998
999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 HRESULT rc = i_checkStateDependency(MutableStateDep);
1002 if (FAILED(rc)) return rc;
1003
1004 i_setModified(IsModified_MachineData);
1005 mUserData.backup();
1006 mUserData->s.strName = aName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1012{
1013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 aDescription = mUserData->s.strDescription;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1021{
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 // this can be done in principle in any state as it doesn't affect the VM
1025 // significantly, but play safe by not messing around while complex
1026 // activities are going on
1027 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
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 rc = i_checkStateDependency(MutableOrSavedStateDep);
1068 if (FAILED(rc)) return rc;
1069
1070 i_setModified(IsModified_MachineData);
1071 mUserData.backup();
1072 mUserData->s.llGroups = llGroups;
1073
1074 return S_OK;
1075}
1076
1077HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1078{
1079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1080
1081 aOSTypeId = mUserData->s.strOsType;
1082
1083 return S_OK;
1084}
1085
1086HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1087{
1088 /* look up the object by Id to check it is valid */
1089 ComPtr<IGuestOSType> guestOSType;
1090 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1091 if (FAILED(rc)) return rc;
1092
1093 /* when setting, always use the "etalon" value for consistency -- lookup
1094 * by ID is case-insensitive and the input value may have different case */
1095 Bstr osTypeId;
1096 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1097 if (FAILED(rc)) return rc;
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 rc = i_checkStateDependency(MutableStateDep);
1102 if (FAILED(rc)) return rc;
1103
1104 i_setModified(IsModified_MachineData);
1105 mUserData.backup();
1106 mUserData->s.strOsType = osTypeId;
1107
1108 return S_OK;
1109}
1110
1111HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1112{
1113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 *aFirmwareType = mHWData->mFirmwareType;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1121{
1122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 HRESULT rc = i_checkStateDependency(MutableStateDep);
1125 if (FAILED(rc)) return rc;
1126
1127 i_setModified(IsModified_MachineData);
1128 mHWData.backup();
1129 mHWData->mFirmwareType = aFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1135{
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1144{
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 HRESULT rc = i_checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 i_setModified(IsModified_MachineData);
1151 mHWData.backup();
1152 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aPointingHIDType = mHWData->mPointingHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mPointingHIDType = aPointingHIDType;
1176
1177 return S_OK;
1178}
1179
1180HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1181{
1182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 *aChipsetType = mHWData->mChipsetType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1190{
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = i_checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 if (aChipsetType != mHWData->mChipsetType)
1197 {
1198 i_setModified(IsModified_MachineData);
1199 mHWData.backup();
1200 mHWData->mChipsetType = aChipsetType;
1201
1202 // Resize network adapter array, to be finalized on commit/rollback.
1203 // We must not throw away entries yet, otherwise settings are lost
1204 // without a way to roll back.
1205 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1206 size_t oldCount = mNetworkAdapters.size();
1207 if (newCount > oldCount)
1208 {
1209 mNetworkAdapters.resize(newCount);
1210 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1211 {
1212 unconst(mNetworkAdapters[slot]).createObject();
1213 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1214 }
1215 }
1216 }
1217
1218 return S_OK;
1219}
1220
1221HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1222{
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 *aParavirtProvider = mHWData->mParavirtProvider;
1226
1227 return S_OK;
1228}
1229
1230HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1231{
1232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 HRESULT rc = i_checkStateDependency(MutableStateDep);
1235 if (FAILED(rc)) return rc;
1236
1237 if (aParavirtProvider != mHWData->mParavirtProvider)
1238 {
1239 i_setModified(IsModified_MachineData);
1240 mHWData.backup();
1241 mHWData->mParavirtProvider = aParavirtProvider;
1242 }
1243
1244 return S_OK;
1245}
1246
1247HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1248{
1249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 *aParavirtProvider = mHWData->mParavirtProvider;
1252 switch (mHWData->mParavirtProvider)
1253 {
1254 case ParavirtProvider_None:
1255 case ParavirtProvider_HyperV:
1256 case ParavirtProvider_KVM:
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 if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1305 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1306 || mUserData->s.strOsType == "Linux"
1307 || mUserData->s.strOsType == "Linux_64"
1308 || mUserData->s.strOsType == "ArchLinux"
1309 || mUserData->s.strOsType == "ArchLinux_64"
1310 || mUserData->s.strOsType == "Debian"
1311 || mUserData->s.strOsType == "Debian_64"
1312 || mUserData->s.strOsType == "Fedora"
1313 || mUserData->s.strOsType == "Fedora_64"
1314 || mUserData->s.strOsType == "Gentoo"
1315 || mUserData->s.strOsType == "Gentoo_64"
1316 || mUserData->s.strOsType == "Mandriva"
1317 || mUserData->s.strOsType == "Mandriva_64"
1318 || mUserData->s.strOsType == "OpenSUSE"
1319 || mUserData->s.strOsType == "OpenSUSE_64"
1320 || mUserData->s.strOsType == "Oracle"
1321 || mUserData->s.strOsType == "Oracle_64"
1322 || mUserData->s.strOsType == "RedHat"
1323 || mUserData->s.strOsType == "RedHat_64"
1324 || mUserData->s.strOsType == "Turbolinux"
1325 || mUserData->s.strOsType == "Turbolinux_64"
1326 || mUserData->s.strOsType == "Ubuntu"
1327 || mUserData->s.strOsType == "Ubuntu_64"
1328 || mUserData->s.strOsType == "Xandros"
1329 || mUserData->s.strOsType == "Xandros_64")
1330 {
1331 *aParavirtProvider = ParavirtProvider_KVM;
1332 }
1333 else
1334 *aParavirtProvider = ParavirtProvider_None;
1335 break;
1336 }
1337 }
1338 break;
1339 }
1340 }
1341
1342 Assert( *aParavirtProvider == ParavirtProvider_None
1343 || *aParavirtProvider == ParavirtProvider_Minimal
1344 || *aParavirtProvider == ParavirtProvider_HyperV
1345 || *aParavirtProvider == ParavirtProvider_KVM);
1346 return S_OK;
1347}
1348
1349HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1350{
1351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 aHardwareVersion = mHWData->mHWVersion;
1354
1355 return S_OK;
1356}
1357
1358HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1359{
1360 /* check known version */
1361 Utf8Str hwVersion = aHardwareVersion;
1362 if ( hwVersion.compare("1") != 0
1363 && hwVersion.compare("2") != 0)
1364 return setError(E_INVALIDARG,
1365 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1366
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 HRESULT rc = i_checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 i_setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 mHWData->mHWVersion = aHardwareVersion;
1375
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 if (!mHWData->mHardwareUUID.isZero())
1384 aHardwareUUID = mHWData->mHardwareUUID;
1385 else
1386 aHardwareUUID = mData->mUuid;
1387
1388 return S_OK;
1389}
1390
1391HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1392{
1393 if (!aHardwareUUID.isValid())
1394 return E_INVALIDARG;
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 HRESULT rc = i_checkStateDependency(MutableStateDep);
1399 if (FAILED(rc)) return rc;
1400
1401 i_setModified(IsModified_MachineData);
1402 mHWData.backup();
1403 if (aHardwareUUID == mData->mUuid)
1404 mHWData->mHardwareUUID.clear();
1405 else
1406 mHWData->mHardwareUUID = aHardwareUUID;
1407
1408 return S_OK;
1409}
1410
1411HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1412{
1413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 *aMemorySize = mHWData->mMemorySize;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::setMemorySize(ULONG aMemorySize)
1421{
1422 /* check RAM limits */
1423 if ( aMemorySize < MM_RAM_MIN_IN_MB
1424 || aMemorySize > MM_RAM_MAX_IN_MB
1425 )
1426 return setError(E_INVALIDARG,
1427 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1428 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1429
1430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 HRESULT rc = i_checkStateDependency(MutableStateDep);
1433 if (FAILED(rc)) return rc;
1434
1435 i_setModified(IsModified_MachineData);
1436 mHWData.backup();
1437 mHWData->mMemorySize = aMemorySize;
1438
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 *aCPUCount = mHWData->mCPUCount;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setCPUCount(ULONG aCPUCount)
1452{
1453 /* check CPU limits */
1454 if ( aCPUCount < SchemaDefs::MinCPUCount
1455 || aCPUCount > SchemaDefs::MaxCPUCount
1456 )
1457 return setError(E_INVALIDARG,
1458 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1459 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1460
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1464 if (mHWData->mCPUHotPlugEnabled)
1465 {
1466 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1467 {
1468 if (mHWData->mCPUAttached[idx])
1469 return setError(E_INVALIDARG,
1470 tr("There is still a CPU attached to socket %lu."
1471 "Detach the CPU before removing the socket"),
1472 aCPUCount, idx+1);
1473 }
1474 }
1475
1476 HRESULT rc = i_checkStateDependency(MutableStateDep);
1477 if (FAILED(rc)) return rc;
1478
1479 i_setModified(IsModified_MachineData);
1480 mHWData.backup();
1481 mHWData->mCPUCount = aCPUCount;
1482
1483 return S_OK;
1484}
1485
1486HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1487{
1488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1491
1492 return S_OK;
1493}
1494
1495HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1496{
1497 HRESULT rc = S_OK;
1498
1499 /* check throttle limits */
1500 if ( aCPUExecutionCap < 1
1501 || aCPUExecutionCap > 100
1502 )
1503 return setError(E_INVALIDARG,
1504 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1505 aCPUExecutionCap, 1, 100);
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 alock.release();
1510 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1511 alock.acquire();
1512 if (FAILED(rc)) return rc;
1513
1514 i_setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1517
1518 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1519 if (Global::IsOnline(mData->mMachineState))
1520 i_saveSettings(NULL);
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1539
1540 rc = i_checkStateDependency(MutableStateDep);
1541 if (FAILED(rc)) return rc;
1542
1543 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1544 {
1545 if (aCPUHotPlugEnabled)
1546 {
1547 i_setModified(IsModified_MachineData);
1548 mHWData.backup();
1549
1550 /* Add the amount of CPUs currently attached */
1551 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1552 mHWData->mCPUAttached[i] = true;
1553 }
1554 else
1555 {
1556 /*
1557 * We can disable hotplug only if the amount of maximum CPUs is equal
1558 * to the amount of attached CPUs
1559 */
1560 unsigned cCpusAttached = 0;
1561 unsigned iHighestId = 0;
1562
1563 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1564 {
1565 if (mHWData->mCPUAttached[i])
1566 {
1567 cCpusAttached++;
1568 iHighestId = i;
1569 }
1570 }
1571
1572 if ( (cCpusAttached != mHWData->mCPUCount)
1573 || (iHighestId >= mHWData->mCPUCount))
1574 return setError(E_INVALIDARG,
1575 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1576
1577 i_setModified(IsModified_MachineData);
1578 mHWData.backup();
1579 }
1580 }
1581
1582 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1583
1584 return rc;
1585}
1586
1587HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1588{
1589#ifdef VBOX_WITH_USB_CARDREADER
1590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1591
1592 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1593
1594 return S_OK;
1595#else
1596 NOREF(aEmulatedUSBCardReaderEnabled);
1597 return E_NOTIMPL;
1598#endif
1599}
1600
1601HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1602{
1603#ifdef VBOX_WITH_USB_CARDREADER
1604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1605
1606 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1607 if (FAILED(rc)) return rc;
1608
1609 i_setModified(IsModified_MachineData);
1610 mHWData.backup();
1611 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1612
1613 return S_OK;
1614#else
1615 NOREF(aEmulatedUSBCardReaderEnabled);
1616 return E_NOTIMPL;
1617#endif
1618}
1619
1620HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1621{
1622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 *aHPETEnabled = mHWData->mHPETEnabled;
1625
1626 return S_OK;
1627}
1628
1629HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1630{
1631 HRESULT rc = S_OK;
1632
1633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1634
1635 rc = i_checkStateDependency(MutableStateDep);
1636 if (FAILED(rc)) return rc;
1637
1638 i_setModified(IsModified_MachineData);
1639 mHWData.backup();
1640
1641 mHWData->mHPETEnabled = aHPETEnabled;
1642
1643 return rc;
1644}
1645
1646HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1647{
1648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1649
1650 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1651 return S_OK;
1652}
1653
1654HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1655{
1656 HRESULT rc = S_OK;
1657
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659
1660 i_setModified(IsModified_MachineData);
1661 mHWData.backup();
1662 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1663
1664 alock.release();
1665 rc = i_onVideoCaptureChange();
1666 alock.acquire();
1667 if (FAILED(rc))
1668 {
1669 /*
1670 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1671 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1672 * determine if it should start or stop capturing. Therefore we need to manually
1673 * undo change.
1674 */
1675 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1676 return rc;
1677 }
1678
1679 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1680 if (Global::IsOnline(mData->mMachineState))
1681 i_saveSettings(NULL);
1682
1683 return rc;
1684}
1685
1686HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1687{
1688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1689 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1690 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1691 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1692 return S_OK;
1693}
1694
1695HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1696{
1697 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1698 bool fChanged = false;
1699
1700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1701
1702 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1703 {
1704 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1705 {
1706 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1707 fChanged = true;
1708 }
1709 }
1710 if (fChanged)
1711 {
1712 alock.release();
1713 HRESULT rc = i_onVideoCaptureChange();
1714 alock.acquire();
1715 if (FAILED(rc)) return rc;
1716 i_setModified(IsModified_MachineData);
1717
1718 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1719 if (Global::IsOnline(mData->mMachineState))
1720 i_saveSettings(NULL);
1721 }
1722
1723 return S_OK;
1724}
1725
1726HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1727{
1728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1729 if (mHWData->mVideoCaptureFile.isEmpty())
1730 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1731 else
1732 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1733 return S_OK;
1734}
1735
1736HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1737{
1738 Utf8Str strFile(aVideoCaptureFile);
1739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1740
1741 if ( Global::IsOnline(mData->mMachineState)
1742 && mHWData->mVideoCaptureEnabled)
1743 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1744
1745 if (!RTPathStartsWithRoot(strFile.c_str()))
1746 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1747
1748 if (!strFile.isEmpty())
1749 {
1750 Utf8Str defaultFile;
1751 i_getDefaultVideoCaptureFile(defaultFile);
1752 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1753 strFile.setNull();
1754 }
1755
1756 i_setModified(IsModified_MachineData);
1757 mHWData.backup();
1758 mHWData->mVideoCaptureFile = strFile;
1759
1760 return S_OK;
1761}
1762
1763HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1767 return S_OK;
1768}
1769
1770HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1771{
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 if ( Global::IsOnline(mData->mMachineState)
1775 && mHWData->mVideoCaptureEnabled)
1776 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1786{
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1789 return S_OK;
1790}
1791
1792HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1793{
1794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 if ( Global::IsOnline(mData->mMachineState)
1797 && mHWData->mVideoCaptureEnabled)
1798 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1799
1800 i_setModified(IsModified_MachineData);
1801 mHWData.backup();
1802 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1808{
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1811 return S_OK;
1812}
1813
1814HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1815{
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 i_setModified(IsModified_MachineData);
1823 mHWData.backup();
1824 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1833 return S_OK;
1834}
1835
1836HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1837{
1838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 if ( Global::IsOnline(mData->mMachineState)
1841 && mHWData->mVideoCaptureEnabled)
1842 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1855 return S_OK;
1856}
1857
1858HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1859{
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 i_setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1877 return S_OK;
1878}
1879
1880HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1881{
1882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 if ( Global::IsOnline(mData->mMachineState)
1885 && mHWData->mVideoCaptureEnabled)
1886 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1887
1888 i_setModified(IsModified_MachineData);
1889 mHWData.backup();
1890 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1896{
1897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1900 return S_OK;
1901}
1902
1903HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1904{
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 if ( Global::IsOnline(mData->mMachineState)
1908 && mHWData->mVideoCaptureEnabled)
1909 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1910
1911 i_setModified(IsModified_MachineData);
1912 mHWData.backup();
1913 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1914
1915 return S_OK;
1916}
1917
1918HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1919{
1920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1921
1922 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1923
1924 return S_OK;
1925}
1926
1927HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1928{
1929 switch (aGraphicsControllerType)
1930 {
1931 case GraphicsControllerType_Null:
1932 case GraphicsControllerType_VBoxVGA:
1933#ifdef VBOX_WITH_VMSVGA
1934 case GraphicsControllerType_VMSVGA:
1935#endif
1936 break;
1937 default:
1938 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1939 }
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->mGraphicsControllerType = aGraphicsControllerType;
1949
1950 return S_OK;
1951}
1952
1953HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1954{
1955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 *aVRAMSize = mHWData->mVRAMSize;
1958
1959 return S_OK;
1960}
1961
1962HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1963{
1964 /* check VRAM limits */
1965 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1966 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1967 return setError(E_INVALIDARG,
1968 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1969 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1970
1971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 HRESULT rc = i_checkStateDependency(MutableStateDep);
1974 if (FAILED(rc)) return rc;
1975
1976 i_setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mVRAMSize = aVRAMSize;
1979
1980 return S_OK;
1981}
1982
1983/** @todo this method should not be public */
1984HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1985{
1986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1989
1990 return S_OK;
1991}
1992
1993/**
1994 * Set the memory balloon size.
1995 *
1996 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1997 * we have to make sure that we never call IGuest from here.
1998 */
1999HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2000{
2001 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2002#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2003 /* check limits */
2004 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2005 return setError(E_INVALIDARG,
2006 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2007 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2008
2009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2014
2015 return S_OK;
2016#else
2017 NOREF(aMemoryBalloonSize);
2018 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2019#endif
2020}
2021
2022HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2023{
2024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2027 return S_OK;
2028}
2029
2030HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2031{
2032#ifdef VBOX_WITH_PAGE_SHARING
2033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2039 return S_OK;
2040#else
2041 NOREF(aPageFusionEnabled);
2042 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2043#endif
2044}
2045
2046HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2047{
2048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2051
2052 return S_OK;
2053}
2054
2055HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2056{
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 HRESULT rc = i_checkStateDependency(MutableStateDep);
2060 if (FAILED(rc)) return rc;
2061
2062 /** @todo check validity! */
2063
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2067
2068 return S_OK;
2069}
2070
2071
2072HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2073{
2074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2077
2078 return S_OK;
2079}
2080
2081HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2082{
2083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 HRESULT rc = i_checkStateDependency(MutableStateDep);
2086 if (FAILED(rc)) return rc;
2087
2088 /** @todo check validity! */
2089 i_setModified(IsModified_MachineData);
2090 mHWData.backup();
2091 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2092
2093 return S_OK;
2094}
2095
2096HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2097{
2098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2099
2100 *aMonitorCount = mHWData->mMonitorCount;
2101
2102 return S_OK;
2103}
2104
2105HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2106{
2107 /* make sure monitor count is a sensible number */
2108 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2109 return setError(E_INVALIDARG,
2110 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2111 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2112
2113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 HRESULT rc = i_checkStateDependency(MutableStateDep);
2116 if (FAILED(rc)) return rc;
2117
2118 i_setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mMonitorCount = aMonitorCount;
2121
2122 return S_OK;
2123}
2124
2125HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2126{
2127 /* mBIOSSettings is constant during life time, no need to lock */
2128 aBIOSSettings = mBIOSSettings;
2129
2130 return S_OK;
2131}
2132
2133HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2134{
2135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 switch (aProperty)
2138 {
2139 case CPUPropertyType_PAE:
2140 *aValue = mHWData->mPAEEnabled;
2141 break;
2142
2143 case CPUPropertyType_Synthetic:
2144 *aValue = mHWData->mSyntheticCpu;
2145 break;
2146
2147 case CPUPropertyType_LongMode:
2148 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2149 *aValue = TRUE;
2150 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2151 *aValue = FALSE;
2152#if HC_ARCH_BITS == 64
2153 else
2154 *aValue = TRUE;
2155#else
2156 else
2157 {
2158 *aValue = FALSE;
2159
2160 ComPtr<IGuestOSType> ptrGuestOSType;
2161 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2162 if (SUCCEEDED(hrc2))
2163 {
2164 BOOL fIs64Bit = FALSE;
2165 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2166 if (SUCCEEDED(hrc2) && fIs64Bit)
2167 {
2168 ComObjPtr<Host> ptrHost = mParent->i_host();
2169 alock.release();
2170
2171 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2172 if (FAILED(hrc2))
2173 *aValue = FALSE;
2174 }
2175 }
2176 }
2177#endif
2178 break;
2179
2180 case CPUPropertyType_TripleFaultReset:
2181 *aValue = mHWData->mTripleFaultReset;
2182 break;
2183
2184 default:
2185 return E_INVALIDARG;
2186 }
2187 return S_OK;
2188}
2189
2190HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2191{
2192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2193
2194 HRESULT rc = i_checkStateDependency(MutableStateDep);
2195 if (FAILED(rc)) return rc;
2196
2197 switch (aProperty)
2198 {
2199 case CPUPropertyType_PAE:
2200 i_setModified(IsModified_MachineData);
2201 mHWData.backup();
2202 mHWData->mPAEEnabled = !!aValue;
2203 break;
2204
2205 case CPUPropertyType_Synthetic:
2206 i_setModified(IsModified_MachineData);
2207 mHWData.backup();
2208 mHWData->mSyntheticCpu = !!aValue;
2209 break;
2210
2211 case CPUPropertyType_LongMode:
2212 i_setModified(IsModified_MachineData);
2213 mHWData.backup();
2214 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2215 break;
2216
2217 case CPUPropertyType_TripleFaultReset:
2218 i_setModified(IsModified_MachineData);
2219 mHWData.backup();
2220 mHWData->mTripleFaultReset = !!aValue;
2221 break;
2222
2223 default:
2224 return E_INVALIDARG;
2225 }
2226 return S_OK;
2227}
2228
2229HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2230{
2231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
2233 switch(aId)
2234 {
2235 case 0x0:
2236 case 0x1:
2237 case 0x2:
2238 case 0x3:
2239 case 0x4:
2240 case 0x5:
2241 case 0x6:
2242 case 0x7:
2243 case 0x8:
2244 case 0x9:
2245 case 0xA:
2246 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2247 return E_INVALIDARG;
2248
2249 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2250 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2251 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2252 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2253 break;
2254
2255 case 0x80000000:
2256 case 0x80000001:
2257 case 0x80000002:
2258 case 0x80000003:
2259 case 0x80000004:
2260 case 0x80000005:
2261 case 0x80000006:
2262 case 0x80000007:
2263 case 0x80000008:
2264 case 0x80000009:
2265 case 0x8000000A:
2266 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2267 return E_INVALIDARG;
2268
2269 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2270 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2271 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2272 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2273 break;
2274
2275 default:
2276 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2277 }
2278 return S_OK;
2279}
2280
2281
2282HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2283{
2284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2285
2286 HRESULT rc = i_checkStateDependency(MutableStateDep);
2287 if (FAILED(rc)) return rc;
2288
2289 switch(aId)
2290 {
2291 case 0x0:
2292 case 0x1:
2293 case 0x2:
2294 case 0x3:
2295 case 0x4:
2296 case 0x5:
2297 case 0x6:
2298 case 0x7:
2299 case 0x8:
2300 case 0x9:
2301 case 0xA:
2302 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2303 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2304 i_setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2307 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2308 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2309 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2310 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2311 break;
2312
2313 case 0x80000000:
2314 case 0x80000001:
2315 case 0x80000002:
2316 case 0x80000003:
2317 case 0x80000004:
2318 case 0x80000005:
2319 case 0x80000006:
2320 case 0x80000007:
2321 case 0x80000008:
2322 case 0x80000009:
2323 case 0x8000000A:
2324 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2325 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2329 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2330 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2331 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2332 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2333 break;
2334
2335 default:
2336 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2337 }
2338 return S_OK;
2339}
2340
2341HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2342{
2343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2344
2345 HRESULT rc = i_checkStateDependency(MutableStateDep);
2346 if (FAILED(rc)) return rc;
2347
2348 switch(aId)
2349 {
2350 case 0x0:
2351 case 0x1:
2352 case 0x2:
2353 case 0x3:
2354 case 0x4:
2355 case 0x5:
2356 case 0x6:
2357 case 0x7:
2358 case 0x8:
2359 case 0x9:
2360 case 0xA:
2361 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2362 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2363 i_setModified(IsModified_MachineData);
2364 mHWData.backup();
2365 /* Invalidate leaf. */
2366 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2367 break;
2368
2369 case 0x80000000:
2370 case 0x80000001:
2371 case 0x80000002:
2372 case 0x80000003:
2373 case 0x80000004:
2374 case 0x80000005:
2375 case 0x80000006:
2376 case 0x80000007:
2377 case 0x80000008:
2378 case 0x80000009:
2379 case 0x8000000A:
2380 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2381 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2382 i_setModified(IsModified_MachineData);
2383 mHWData.backup();
2384 /* Invalidate leaf. */
2385 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2386 break;
2387
2388 default:
2389 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2390 }
2391 return S_OK;
2392}
2393
2394HRESULT Machine::removeAllCPUIDLeaves()
2395{
2396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 HRESULT rc = i_checkStateDependency(MutableStateDep);
2399 if (FAILED(rc)) return rc;
2400
2401 i_setModified(IsModified_MachineData);
2402 mHWData.backup();
2403
2404 /* Invalidate all standard leafs. */
2405 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2406 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2407
2408 /* Invalidate all extended leafs. */
2409 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2410 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2411
2412 return S_OK;
2413}
2414HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2415{
2416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2417
2418 switch(aProperty)
2419 {
2420 case HWVirtExPropertyType_Enabled:
2421 *aValue = mHWData->mHWVirtExEnabled;
2422 break;
2423
2424 case HWVirtExPropertyType_VPID:
2425 *aValue = mHWData->mHWVirtExVPIDEnabled;
2426 break;
2427
2428 case HWVirtExPropertyType_NestedPaging:
2429 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2430 break;
2431
2432 case HWVirtExPropertyType_UnrestrictedExecution:
2433 *aValue = mHWData->mHWVirtExUXEnabled;
2434 break;
2435
2436 case HWVirtExPropertyType_LargePages:
2437 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2438#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2439 *aValue = FALSE;
2440#endif
2441 break;
2442
2443 case HWVirtExPropertyType_Force:
2444 *aValue = mHWData->mHWVirtExForceEnabled;
2445 break;
2446
2447 default:
2448 return E_INVALIDARG;
2449 }
2450 return S_OK;
2451}
2452
2453HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2454{
2455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2456
2457 HRESULT rc = i_checkStateDependency(MutableStateDep);
2458 if (FAILED(rc)) return rc;
2459
2460 switch(aProperty)
2461 {
2462 case HWVirtExPropertyType_Enabled:
2463 i_setModified(IsModified_MachineData);
2464 mHWData.backup();
2465 mHWData->mHWVirtExEnabled = !!aValue;
2466 break;
2467
2468 case HWVirtExPropertyType_VPID:
2469 i_setModified(IsModified_MachineData);
2470 mHWData.backup();
2471 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2472 break;
2473
2474 case HWVirtExPropertyType_NestedPaging:
2475 i_setModified(IsModified_MachineData);
2476 mHWData.backup();
2477 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2478 break;
2479
2480 case HWVirtExPropertyType_UnrestrictedExecution:
2481 i_setModified(IsModified_MachineData);
2482 mHWData.backup();
2483 mHWData->mHWVirtExUXEnabled = !!aValue;
2484 break;
2485
2486 case HWVirtExPropertyType_LargePages:
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2490 break;
2491
2492 case HWVirtExPropertyType_Force:
2493 i_setModified(IsModified_MachineData);
2494 mHWData.backup();
2495 mHWData->mHWVirtExForceEnabled = !!aValue;
2496 break;
2497
2498 default:
2499 return E_INVALIDARG;
2500 }
2501
2502 return S_OK;
2503}
2504
2505HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2506{
2507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2515{
2516 /* @todo (r=dmik):
2517 * 1. Allow to change the name of the snapshot folder containing snapshots
2518 * 2. Rename the folder on disk instead of just changing the property
2519 * value (to be smart and not to leave garbage). Note that it cannot be
2520 * done here because the change may be rolled back. Thus, the right
2521 * place is #saveSettings().
2522 */
2523
2524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2525
2526 HRESULT rc = i_checkStateDependency(MutableStateDep);
2527 if (FAILED(rc)) return rc;
2528
2529 if (!mData->mCurrentSnapshot.isNull())
2530 return setError(E_FAIL,
2531 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2532
2533 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2534
2535 if (strSnapshotFolder.isEmpty())
2536 strSnapshotFolder = "Snapshots";
2537 int vrc = i_calculateFullPath(strSnapshotFolder,
2538 strSnapshotFolder);
2539 if (RT_FAILURE(vrc))
2540 return setError(E_FAIL,
2541 tr("Invalid snapshot folder '%s' (%Rrc)"),
2542 strSnapshotFolder.c_str(), vrc);
2543
2544 i_setModified(IsModified_MachineData);
2545 mUserData.backup();
2546
2547 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2548
2549 return S_OK;
2550}
2551
2552HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2553{
2554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2555
2556 aMediumAttachments.resize(mMediaData->mAttachments.size());
2557 size_t i = 0;
2558 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2559 it != mMediaData->mAttachments.end(); ++it, ++i)
2560 aMediumAttachments[i] = *it;
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 Assert(!!mVRDEServer);
2570
2571 aVRDEServer = mVRDEServer;
2572
2573 return S_OK;
2574}
2575
2576HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2577{
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 aAudioAdapter = mAudioAdapter;
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2586{
2587#ifdef VBOX_WITH_VUSB
2588 clearError();
2589 MultiResult rc(S_OK);
2590
2591# ifdef VBOX_WITH_USB
2592 rc = mParent->i_host()->i_checkUSBProxyService();
2593 if (FAILED(rc)) return rc;
2594# endif
2595
2596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2597
2598 USBControllerList data = *mUSBControllers.data();
2599 aUSBControllers.resize(data.size());
2600 size_t i = 0;
2601 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2602 aUSBControllers[i] = *it;
2603
2604 return S_OK;
2605#else
2606 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2607 * extended error info to indicate that USB is simply not available
2608 * (w/o treating it as a failure), for example, as in OSE */
2609 NOREF(aUSBControllers);
2610 ReturnComNotImplemented();
2611#endif /* VBOX_WITH_VUSB */
2612}
2613
2614HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2615{
2616#ifdef VBOX_WITH_VUSB
2617 clearError();
2618 MultiResult rc(S_OK);
2619
2620# ifdef VBOX_WITH_USB
2621 rc = mParent->i_host()->i_checkUSBProxyService();
2622 if (FAILED(rc)) return rc;
2623# endif
2624
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 aUSBDeviceFilters = mUSBDeviceFilters;
2628 return rc;
2629#else
2630 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2631 * extended error info to indicate that USB is simply not available
2632 * (w/o treating it as a failure), for example, as in OSE */
2633 NOREF(aUSBDeviceFilters);
2634 ReturnComNotImplemented();
2635#endif /* VBOX_WITH_VUSB */
2636}
2637
2638HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 aSettingsFilePath = mData->m_strConfigFileFull;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2652 if (FAILED(rc)) return rc;
2653
2654 if (!mData->pMachineConfigFile->fileExists())
2655 // this is a new machine, and no config file exists yet:
2656 *aSettingsModified = TRUE;
2657 else
2658 *aSettingsModified = (mData->flModifications != 0);
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2664{
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 *aSessionState = mData->mSession.mState;
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2674{
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 aSessionType = mData->mSession.mType;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 *aSessionPID = mData->mSession.mPID;
2687
2688 return S_OK;
2689}
2690
2691HRESULT Machine::getState(MachineState_T *aState)
2692{
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 *aState = mData->mMachineState;
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2710{
2711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2712
2713 aStateFilePath = mSSData->strStateFilePath;
2714
2715 return S_OK;
2716}
2717
2718HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2719{
2720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 i_getLogFolder(aLogFolder);
2723
2724 return S_OK;
2725}
2726
2727HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2728{
2729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2730
2731 aCurrentSnapshot = mData->mCurrentSnapshot;
2732
2733 return S_OK;
2734}
2735
2736HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2737{
2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2741 ? 0
2742 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 /* Note: for machines with no snapshots, we always return FALSE
2752 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2753 * reasons :) */
2754
2755 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2756 ? FALSE
2757 : mData->mCurrentStateModified;
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 aSharedFolders.resize(mHWData->mSharedFolders.size());
2767 size_t i = 0;
2768 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2769 it != mHWData->mSharedFolders.end(); ++i, ++it)
2770 aSharedFolders[i] = *it;
2771
2772 return S_OK;
2773}
2774
2775HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 *aClipboardMode = mHWData->mClipboardMode;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2785{
2786 HRESULT rc = S_OK;
2787
2788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 alock.release();
2791 rc = i_onClipboardModeChange(aClipboardMode);
2792 alock.acquire();
2793 if (FAILED(rc)) return rc;
2794
2795 i_setModified(IsModified_MachineData);
2796 mHWData.backup();
2797 mHWData->mClipboardMode = aClipboardMode;
2798
2799 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2800 if (Global::IsOnline(mData->mMachineState))
2801 i_saveSettings(NULL);
2802
2803 return S_OK;
2804}
2805
2806HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2807{
2808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2809
2810 *aDnDMode = mHWData->mDnDMode;
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2816{
2817 HRESULT rc = S_OK;
2818
2819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 alock.release();
2822 rc = i_onDnDModeChange(aDnDMode);
2823
2824 alock.acquire();
2825 if (FAILED(rc)) return rc;
2826
2827 i_setModified(IsModified_MachineData);
2828 mHWData.backup();
2829 mHWData->mDnDMode = aDnDMode;
2830
2831 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2832 if (Global::IsOnline(mData->mMachineState))
2833 i_saveSettings(NULL);
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 try
2843 {
2844 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2845 }
2846 catch (...)
2847 {
2848 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2849 }
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2855{
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2859 if (FAILED(rc)) return rc;
2860
2861 i_setModified(IsModified_MachineData);
2862 mHWData.backup();
2863 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2864 return rc;
2865}
2866
2867HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870 StorageControllerList data = *mStorageControllers.data();
2871 size_t i = 0;
2872 aStorageControllers.resize(data.size());
2873 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2874 aStorageControllers[i] = *it;
2875 return S_OK;
2876}
2877
2878HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2879{
2880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2881
2882 *aEnabled = mUserData->s.fTeleporterEnabled;
2883
2884 return S_OK;
2885}
2886
2887HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2888{
2889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 /* Only allow it to be set to true when PoweredOff or Aborted.
2892 (Clearing it is always permitted.) */
2893 if ( aTeleporterEnabled
2894 && mData->mRegistered
2895 && ( !i_isSessionMachine()
2896 || ( mData->mMachineState != MachineState_PoweredOff
2897 && mData->mMachineState != MachineState_Teleported
2898 && mData->mMachineState != MachineState_Aborted
2899 )
2900 )
2901 )
2902 return setError(VBOX_E_INVALID_VM_STATE,
2903 tr("The machine is not powered off (state is %s)"),
2904 Global::stringifyMachineState(mData->mMachineState));
2905
2906 i_setModified(IsModified_MachineData);
2907 mUserData.backup();
2908 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2914{
2915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2923{
2924 if (aTeleporterPort >= _64K)
2925 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2926
2927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2930 if (FAILED(rc)) return rc;
2931
2932 i_setModified(IsModified_MachineData);
2933 mUserData.backup();
2934 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2935
2936 return S_OK;
2937}
2938
2939HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2940{
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2953 if (FAILED(rc)) return rc;
2954
2955 i_setModified(IsModified_MachineData);
2956 mUserData.backup();
2957 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2958
2959 return S_OK;
2960}
2961
2962HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2963{
2964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2965 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2966
2967 return S_OK;
2968}
2969
2970HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2971{
2972 /*
2973 * Hash the password first.
2974 */
2975 com::Utf8Str aT = aTeleporterPassword;
2976
2977 if (!aT.isEmpty())
2978 {
2979 if (VBoxIsPasswordHashed(&aT))
2980 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2981 VBoxHashPassword(&aT);
2982 }
2983
2984 /*
2985 * Do the update.
2986 */
2987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2988 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2989 if (SUCCEEDED(hrc))
2990 {
2991 i_setModified(IsModified_MachineData);
2992 mUserData.backup();
2993 mUserData->s.strTeleporterPassword = aT;
2994 }
2995
2996 return hrc;
2997}
2998
2999HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3000{
3001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3004 return S_OK;
3005}
3006
3007HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3008{
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 /* @todo deal with running state change. */
3012 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3013 if (FAILED(rc)) return rc;
3014
3015 i_setModified(IsModified_MachineData);
3016 mUserData.backup();
3017 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3018 return S_OK;
3019}
3020
3021HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3022{
3023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3026 return S_OK;
3027}
3028
3029HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3030{
3031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 /* @todo deal with running state change. */
3034 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3035 if (FAILED(rc)) return rc;
3036
3037 i_setModified(IsModified_MachineData);
3038 mUserData.backup();
3039 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3040 return S_OK;
3041}
3042
3043HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3044{
3045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3048 return S_OK;
3049}
3050
3051HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3052{
3053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3054
3055 /* @todo deal with running state change. */
3056 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3057 if (FAILED(rc)) return rc;
3058
3059 i_setModified(IsModified_MachineData);
3060 mUserData.backup();
3061 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3062 return S_OK;
3063}
3064
3065HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3066{
3067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3068
3069 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3070
3071 return S_OK;
3072}
3073
3074HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3075{
3076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3077
3078 /* @todo deal with running state change. */
3079 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3080 if (FAILED(rc)) return rc;
3081
3082 i_setModified(IsModified_MachineData);
3083 mUserData.backup();
3084 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3085
3086 return S_OK;
3087}
3088
3089HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3090{
3091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3094 return S_OK;
3095}
3096
3097HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3098{
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 /* @todo deal with running state change. */
3102 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3103 if (FAILED(rc)) return rc;
3104
3105 i_setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3108 return S_OK;
3109}
3110
3111HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3112{
3113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3114
3115 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3116
3117 return S_OK;
3118}
3119
3120HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3121{
3122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 /* Only allow it to be set to true when PoweredOff or Aborted.
3125 (Clearing it is always permitted.) */
3126 if ( aRTCUseUTC
3127 && mData->mRegistered
3128 && ( !i_isSessionMachine()
3129 || ( mData->mMachineState != MachineState_PoweredOff
3130 && mData->mMachineState != MachineState_Teleported
3131 && mData->mMachineState != MachineState_Aborted
3132 )
3133 )
3134 )
3135 return setError(VBOX_E_INVALID_VM_STATE,
3136 tr("The machine is not powered off (state is %s)"),
3137 Global::stringifyMachineState(mData->mMachineState));
3138
3139 i_setModified(IsModified_MachineData);
3140 mUserData.backup();
3141 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3142
3143 return S_OK;
3144}
3145
3146HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3147{
3148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3151
3152 return S_OK;
3153}
3154
3155HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3156{
3157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3158
3159 HRESULT rc = i_checkStateDependency(MutableStateDep);
3160 if (FAILED(rc)) return rc;
3161
3162 i_setModified(IsModified_MachineData);
3163 mHWData.backup();
3164 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3165
3166 return S_OK;
3167}
3168
3169HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3170{
3171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3172
3173 *aIOCacheSize = mHWData->mIOCacheSize;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3179{
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 HRESULT rc = i_checkStateDependency(MutableStateDep);
3183 if (FAILED(rc)) return rc;
3184
3185 i_setModified(IsModified_MachineData);
3186 mHWData.backup();
3187 mHWData->mIOCacheSize = aIOCacheSize;
3188
3189 return S_OK;
3190}
3191
3192
3193/**
3194 * @note Locks objects!
3195 */
3196HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3197 LockType_T aLockType)
3198
3199{
3200 /* check the session state */
3201 SessionState_T state;
3202 HRESULT rc = aSession->COMGETTER(State)(&state);
3203 if (FAILED(rc)) return rc;
3204
3205 if (state != SessionState_Unlocked)
3206 return setError(VBOX_E_INVALID_OBJECT_STATE,
3207 tr("The given session is busy"));
3208
3209 // get the client's IInternalSessionControl interface
3210 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3211 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3212 E_INVALIDARG);
3213
3214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3215
3216 if (!mData->mRegistered)
3217 return setError(E_UNEXPECTED,
3218 tr("The machine '%s' is not registered"),
3219 mUserData->s.strName.c_str());
3220
3221 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3222
3223 SessionState_T oldState = mData->mSession.mState;
3224 /* Hack: in case the session is closing and there is a progress object
3225 * which allows waiting for the session to be closed, take the opportunity
3226 * and do a limited wait (max. 1 second). This helps a lot when the system
3227 * is busy and thus session closing can take a little while. */
3228 if ( mData->mSession.mState == SessionState_Unlocking
3229 && mData->mSession.mProgress)
3230 {
3231 alock.release();
3232 mData->mSession.mProgress->WaitForCompletion(1000);
3233 alock.acquire();
3234 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3235 }
3236
3237 // try again now
3238 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3239 // (i.e. session machine exists)
3240 && (aLockType == LockType_Shared) // caller wants a shared link to the
3241 // existing session that holds the write lock:
3242 )
3243 {
3244 // OK, share the session... we are now dealing with three processes:
3245 // 1) VBoxSVC (where this code runs);
3246 // 2) process C: the caller's client process (who wants a shared session);
3247 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3248
3249 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3250 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3251 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3252 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3253 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3254
3255 /*
3256 * Release the lock before calling the client process. It's safe here
3257 * since the only thing to do after we get the lock again is to add
3258 * the remote control to the list (which doesn't directly influence
3259 * anything).
3260 */
3261 alock.release();
3262
3263 // get the console of the session holding the write lock (this is a remote call)
3264 ComPtr<IConsole> pConsoleW;
3265 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3266 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3267 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3268 if (FAILED(rc))
3269 // the failure may occur w/o any error info (from RPC), so provide one
3270 return setError(VBOX_E_VM_ERROR,
3271 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3272
3273 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3274
3275 // share the session machine and W's console with the caller's session
3276 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3277 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3278 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3279
3280 if (FAILED(rc))
3281 // the failure may occur w/o any error info (from RPC), so provide one
3282 return setError(VBOX_E_VM_ERROR,
3283 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3284 alock.acquire();
3285
3286 // need to revalidate the state after acquiring the lock again
3287 if (mData->mSession.mState != SessionState_Locked)
3288 {
3289 pSessionControl->Uninitialize();
3290 return setError(VBOX_E_INVALID_SESSION_STATE,
3291 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3292 mUserData->s.strName.c_str());
3293 }
3294
3295 // add the caller's session to the list
3296 mData->mSession.mRemoteControls.push_back(pSessionControl);
3297 }
3298 else if ( mData->mSession.mState == SessionState_Locked
3299 || mData->mSession.mState == SessionState_Unlocking
3300 )
3301 {
3302 // sharing not permitted, or machine still unlocking:
3303 return setError(VBOX_E_INVALID_OBJECT_STATE,
3304 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3305 mUserData->s.strName.c_str());
3306 }
3307 else
3308 {
3309 // machine is not locked: then write-lock the machine (create the session machine)
3310
3311 // must not be busy
3312 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3313
3314 // get the caller's session PID
3315 RTPROCESS pid = NIL_RTPROCESS;
3316 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3317 pSessionControl->GetPID((ULONG*)&pid);
3318 Assert(pid != NIL_RTPROCESS);
3319
3320 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3321
3322 if (fLaunchingVMProcess)
3323 {
3324 if (mData->mSession.mPID == NIL_RTPROCESS)
3325 {
3326 // two or more clients racing for a lock, the one which set the
3327 // session state to Spawning will win, the others will get an
3328 // error as we can't decide here if waiting a little would help
3329 // (only for shared locks this would avoid an error)
3330 return setError(VBOX_E_INVALID_OBJECT_STATE,
3331 tr("The machine '%s' already has a lock request pending"),
3332 mUserData->s.strName.c_str());
3333 }
3334
3335 // this machine is awaiting for a spawning session to be opened:
3336 // then the calling process must be the one that got started by
3337 // LaunchVMProcess()
3338
3339 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3340 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3341
3342#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3343 /* Hardened windows builds spawns three processes when a VM is
3344 launched, the 3rd one is the one that will end up here. */
3345 RTPROCESS ppid;
3346 int rc = RTProcQueryParent(pid, &ppid);
3347 if (RT_SUCCESS(rc))
3348 rc = RTProcQueryParent(ppid, &ppid);
3349 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3350 || rc == VERR_ACCESS_DENIED)
3351 {
3352 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3353 mData->mSession.mPID = pid;
3354 }
3355#endif
3356
3357 if (mData->mSession.mPID != pid)
3358 return setError(E_ACCESSDENIED,
3359 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3360 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3361 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3362 }
3363
3364 // create the mutable SessionMachine from the current machine
3365 ComObjPtr<SessionMachine> sessionMachine;
3366 sessionMachine.createObject();
3367 rc = sessionMachine->init(this);
3368 AssertComRC(rc);
3369
3370 /* NOTE: doing return from this function after this point but
3371 * before the end is forbidden since it may call SessionMachine::uninit()
3372 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3373 * lock while still holding the Machine lock in alock so that a deadlock
3374 * is possible due to the wrong lock order. */
3375
3376 if (SUCCEEDED(rc))
3377 {
3378 /*
3379 * Set the session state to Spawning to protect against subsequent
3380 * attempts to open a session and to unregister the machine after
3381 * we release the lock.
3382 */
3383 SessionState_T origState = mData->mSession.mState;
3384 mData->mSession.mState = SessionState_Spawning;
3385
3386#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3387 /* Get the client token ID to be passed to the client process */
3388 Utf8Str strTokenId;
3389 sessionMachine->i_getTokenId(strTokenId);
3390 Assert(!strTokenId.isEmpty());
3391#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3392 /* Get the client token to be passed to the client process */
3393 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3394 /* The token is now "owned" by pToken, fix refcount */
3395 if (!pToken.isNull())
3396 pToken->Release();
3397#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3398
3399 /*
3400 * Release the lock before calling the client process -- it will call
3401 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3402 * because the state is Spawning, so that LaunchVMProcess() and
3403 * LockMachine() calls will fail. This method, called before we
3404 * acquire the lock again, will fail because of the wrong PID.
3405 *
3406 * Note that mData->mSession.mRemoteControls accessed outside
3407 * the lock may not be modified when state is Spawning, so it's safe.
3408 */
3409 alock.release();
3410
3411 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3412#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3413 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3414#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3415 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3416 /* Now the token is owned by the client process. */
3417 pToken.setNull();
3418#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3419 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3420
3421 /* The failure may occur w/o any error info (from RPC), so provide one */
3422 if (FAILED(rc))
3423 setError(VBOX_E_VM_ERROR,
3424 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3425
3426 if ( SUCCEEDED(rc)
3427 && fLaunchingVMProcess
3428 )
3429 {
3430 /* complete the remote session initialization */
3431
3432 /* get the console from the direct session */
3433 ComPtr<IConsole> console;
3434 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3435 ComAssertComRC(rc);
3436
3437 if (SUCCEEDED(rc) && !console)
3438 {
3439 ComAssert(!!console);
3440 rc = E_FAIL;
3441 }
3442
3443 /* assign machine & console to the remote session */
3444 if (SUCCEEDED(rc))
3445 {
3446 /*
3447 * after LaunchVMProcess(), the first and the only
3448 * entry in remoteControls is that remote session
3449 */
3450 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3451 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3452 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3453
3454 /* The failure may occur w/o any error info (from RPC), so provide one */
3455 if (FAILED(rc))
3456 setError(VBOX_E_VM_ERROR,
3457 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3458 }
3459
3460 if (FAILED(rc))
3461 pSessionControl->Uninitialize();
3462 }
3463
3464 /* acquire the lock again */
3465 alock.acquire();
3466
3467 /* Restore the session state */
3468 mData->mSession.mState = origState;
3469 }
3470
3471 // finalize spawning anyway (this is why we don't return on errors above)
3472 if (fLaunchingVMProcess)
3473 {
3474 /* Note that the progress object is finalized later */
3475 /** @todo Consider checking mData->mSession.mProgress for cancellation
3476 * around here. */
3477
3478 /* We don't reset mSession.mPID here because it is necessary for
3479 * SessionMachine::uninit() to reap the child process later. */
3480
3481 if (FAILED(rc))
3482 {
3483 /* Close the remote session, remove the remote control from the list
3484 * and reset session state to Closed (@note keep the code in sync
3485 * with the relevant part in checkForSpawnFailure()). */
3486
3487 Assert(mData->mSession.mRemoteControls.size() == 1);
3488 if (mData->mSession.mRemoteControls.size() == 1)
3489 {
3490 ErrorInfoKeeper eik;
3491 mData->mSession.mRemoteControls.front()->Uninitialize();
3492 }
3493
3494 mData->mSession.mRemoteControls.clear();
3495 mData->mSession.mState = SessionState_Unlocked;
3496 }
3497 }
3498 else
3499 {
3500 /* memorize PID of the directly opened session */
3501 if (SUCCEEDED(rc))
3502 mData->mSession.mPID = pid;
3503 }
3504
3505 if (SUCCEEDED(rc))
3506 {
3507 /* memorize the direct session control and cache IUnknown for it */
3508 mData->mSession.mDirectControl = pSessionControl;
3509 mData->mSession.mState = SessionState_Locked;
3510 /* associate the SessionMachine with this Machine */
3511 mData->mSession.mMachine = sessionMachine;
3512
3513 /* request an IUnknown pointer early from the remote party for later
3514 * identity checks (it will be internally cached within mDirectControl
3515 * at least on XPCOM) */
3516 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3517 NOREF(unk);
3518 }
3519
3520 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3521 * would break the lock order */
3522 alock.release();
3523
3524 /* uninitialize the created session machine on failure */
3525 if (FAILED(rc))
3526 sessionMachine->uninit();
3527
3528 }
3529
3530 if (SUCCEEDED(rc))
3531 {
3532 /*
3533 * tell the client watcher thread to update the set of
3534 * machines that have open sessions
3535 */
3536 mParent->i_updateClientWatcher();
3537
3538 if (oldState != SessionState_Locked)
3539 /* fire an event */
3540 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3541 }
3542
3543 return rc;
3544}
3545
3546/**
3547 * @note Locks objects!
3548 */
3549HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3550 const com::Utf8Str &aType,
3551 const com::Utf8Str &aEnvironment,
3552 ComPtr<IProgress> &aProgress)
3553{
3554 Utf8Str strFrontend(aType);
3555 /* "emergencystop" doesn't need the session, so skip the checks/interface
3556 * retrieval. This code doesn't quite fit in here, but introducing a
3557 * special API method would be even more effort, and would require explicit
3558 * support by every API client. It's better to hide the feature a bit. */
3559 if (strFrontend != "emergencystop")
3560 CheckComArgNotNull(aSession);
3561
3562 HRESULT rc = S_OK;
3563 if (strFrontend.isEmpty())
3564 {
3565 Bstr bstrFrontend;
3566 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3567 if (FAILED(rc))
3568 return rc;
3569 strFrontend = bstrFrontend;
3570 if (strFrontend.isEmpty())
3571 {
3572 ComPtr<ISystemProperties> systemProperties;
3573 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3574 if (FAILED(rc))
3575 return rc;
3576 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3577 if (FAILED(rc))
3578 return rc;
3579 strFrontend = bstrFrontend;
3580 }
3581 /* paranoia - emergencystop is not a valid default */
3582 if (strFrontend == "emergencystop")
3583 strFrontend = Utf8Str::Empty;
3584 }
3585 /* default frontend: Qt GUI */
3586 if (strFrontend.isEmpty())
3587 strFrontend = "GUI/Qt";
3588
3589 if (strFrontend != "emergencystop")
3590 {
3591 /* check the session state */
3592 SessionState_T state;
3593 rc = aSession->COMGETTER(State)(&state);
3594 if (FAILED(rc))
3595 return rc;
3596
3597 if (state != SessionState_Unlocked)
3598 return setError(VBOX_E_INVALID_OBJECT_STATE,
3599 tr("The given session is busy"));
3600
3601 /* get the IInternalSessionControl interface */
3602 ComPtr<IInternalSessionControl> control(aSession);
3603 ComAssertMsgRet(!control.isNull(),
3604 ("No IInternalSessionControl interface"),
3605 E_INVALIDARG);
3606
3607 /* get the teleporter enable state for the progress object init. */
3608 BOOL fTeleporterEnabled;
3609 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3610 if (FAILED(rc))
3611 return rc;
3612
3613 /* create a progress object */
3614 ComObjPtr<ProgressProxy> progress;
3615 progress.createObject();
3616 rc = progress->init(mParent,
3617 static_cast<IMachine*>(this),
3618 Bstr(tr("Starting VM")).raw(),
3619 TRUE /* aCancelable */,
3620 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3621 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3622 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3623 2 /* uFirstOperationWeight */,
3624 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3625
3626 if (SUCCEEDED(rc))
3627 {
3628 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3629 if (SUCCEEDED(rc))
3630 {
3631 aProgress = progress;
3632
3633 /* signal the client watcher thread */
3634 mParent->i_updateClientWatcher();
3635
3636 /* fire an event */
3637 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3638 }
3639 }
3640 }
3641 else
3642 {
3643 /* no progress object - either instant success or failure */
3644 aProgress = NULL;
3645
3646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3647
3648 if (mData->mSession.mState != SessionState_Locked)
3649 return setError(VBOX_E_INVALID_OBJECT_STATE,
3650 tr("The machine '%s' is not locked by a session"),
3651 mUserData->s.strName.c_str());
3652
3653 /* must have a VM process associated - do not kill normal API clients
3654 * with an open session */
3655 if (!Global::IsOnline(mData->mMachineState))
3656 return setError(VBOX_E_INVALID_OBJECT_STATE,
3657 tr("The machine '%s' does not have a VM process"),
3658 mUserData->s.strName.c_str());
3659
3660 /* forcibly terminate the VM process */
3661 if (mData->mSession.mPID != NIL_RTPROCESS)
3662 RTProcTerminate(mData->mSession.mPID);
3663
3664 /* signal the client watcher thread, as most likely the client has
3665 * been terminated */
3666 mParent->i_updateClientWatcher();
3667 }
3668
3669 return rc;
3670}
3671
3672HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3673{
3674 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3675 return setError(E_INVALIDARG,
3676 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3677 aPosition, SchemaDefs::MaxBootPosition);
3678
3679 if (aDevice == DeviceType_USB)
3680 return setError(E_NOTIMPL,
3681 tr("Booting from USB device is currently not supported"));
3682
3683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3684
3685 HRESULT rc = i_checkStateDependency(MutableStateDep);
3686 if (FAILED(rc)) return rc;
3687
3688 i_setModified(IsModified_MachineData);
3689 mHWData.backup();
3690 mHWData->mBootOrder[aPosition - 1] = aDevice;
3691
3692 return S_OK;
3693}
3694
3695HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3696{
3697 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3698 return setError(E_INVALIDARG,
3699 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3700 aPosition, SchemaDefs::MaxBootPosition);
3701
3702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3703
3704 *aDevice = mHWData->mBootOrder[aPosition - 1];
3705
3706 return S_OK;
3707}
3708
3709HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3710 LONG aControllerPort,
3711 LONG aDevice,
3712 DeviceType_T aType,
3713 const ComPtr<IMedium> &aMedium)
3714{
3715 IMedium *aM = aMedium;
3716 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3717 aName.c_str(), aControllerPort, aDevice, aType, aM));
3718
3719 // request the host lock first, since might be calling Host methods for getting host drives;
3720 // next, protect the media tree all the while we're in here, as well as our member variables
3721 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3722 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3723
3724 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3725 if (FAILED(rc)) return rc;
3726
3727 /// @todo NEWMEDIA implicit machine registration
3728 if (!mData->mRegistered)
3729 return setError(VBOX_E_INVALID_OBJECT_STATE,
3730 tr("Cannot attach storage devices to an unregistered machine"));
3731
3732 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3733
3734 /* Check for an existing controller. */
3735 ComObjPtr<StorageController> ctl;
3736 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3737 if (FAILED(rc)) return rc;
3738
3739 StorageControllerType_T ctrlType;
3740 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3741 if (FAILED(rc))
3742 return setError(E_FAIL,
3743 tr("Could not get type of controller '%s'"),
3744 aName.c_str());
3745
3746 bool fSilent = false;
3747 Utf8Str strReconfig;
3748
3749 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3750 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3751 if ( mData->mMachineState == MachineState_Paused
3752 && strReconfig == "1")
3753 fSilent = true;
3754
3755 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3756 bool fHotplug = false;
3757 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3758 fHotplug = true;
3759
3760 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3761 return setError(VBOX_E_INVALID_VM_STATE,
3762 tr("Controller '%s' does not support hotplugging"),
3763 aName.c_str());
3764
3765 // check that the port and device are not out of range
3766 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3767 if (FAILED(rc)) return rc;
3768
3769 /* check if the device slot is already busy */
3770 MediumAttachment *pAttachTemp;
3771 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3772 Bstr(aName).raw(),
3773 aControllerPort,
3774 aDevice)))
3775 {
3776 Medium *pMedium = pAttachTemp->i_getMedium();
3777 if (pMedium)
3778 {
3779 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3780 return setError(VBOX_E_OBJECT_IN_USE,
3781 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3782 pMedium->i_getLocationFull().c_str(),
3783 aControllerPort,
3784 aDevice,
3785 aName.c_str());
3786 }
3787 else
3788 return setError(VBOX_E_OBJECT_IN_USE,
3789 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3790 aControllerPort, aDevice, aName.c_str());
3791 }
3792
3793 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3794 if (aMedium && medium.isNull())
3795 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3796
3797 AutoCaller mediumCaller(medium);
3798 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3799
3800 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3801
3802 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3803 && !medium.isNull()
3804 )
3805 return setError(VBOX_E_OBJECT_IN_USE,
3806 tr("Medium '%s' is already attached to this virtual machine"),
3807 medium->i_getLocationFull().c_str());
3808
3809 if (!medium.isNull())
3810 {
3811 MediumType_T mtype = medium->i_getType();
3812 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3813 // For DVDs it's not written to the config file, so needs no global config
3814 // version bump. For floppies it's a new attribute "type", which is ignored
3815 // by older VirtualBox version, so needs no global config version bump either.
3816 // For hard disks this type is not accepted.
3817 if (mtype == MediumType_MultiAttach)
3818 {
3819 // This type is new with VirtualBox 4.0 and therefore requires settings
3820 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3821 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3822 // two reasons: The medium type is a property of the media registry tree, which
3823 // can reside in the global config file (for pre-4.0 media); we would therefore
3824 // possibly need to bump the global config version. We don't want to do that though
3825 // because that might make downgrading to pre-4.0 impossible.
3826 // As a result, we can only use these two new types if the medium is NOT in the
3827 // global registry:
3828 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3829 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3830 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3831 )
3832 return setError(VBOX_E_INVALID_OBJECT_STATE,
3833 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3834 "to machines that were created with VirtualBox 4.0 or later"),
3835 medium->i_getLocationFull().c_str());
3836 }
3837 }
3838
3839 bool fIndirect = false;
3840 if (!medium.isNull())
3841 fIndirect = medium->i_isReadOnly();
3842 bool associate = true;
3843
3844 do
3845 {
3846 if ( aType == DeviceType_HardDisk
3847 && mMediaData.isBackedUp())
3848 {
3849 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3850
3851 /* check if the medium was attached to the VM before we started
3852 * changing attachments in which case the attachment just needs to
3853 * be restored */
3854 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3855 {
3856 AssertReturn(!fIndirect, E_FAIL);
3857
3858 /* see if it's the same bus/channel/device */
3859 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3860 {
3861 /* the simplest case: restore the whole attachment
3862 * and return, nothing else to do */
3863 mMediaData->mAttachments.push_back(pAttachTemp);
3864
3865 /* Reattach the medium to the VM. */
3866 if (fHotplug || fSilent)
3867 {
3868 mediumLock.release();
3869 treeLock.release();
3870 alock.release();
3871
3872 MediumLockList *pMediumLockList(new MediumLockList());
3873
3874 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3875 true /* fMediumLockWrite */,
3876 false /* fMediumLockWriteAll */,
3877 NULL,
3878 *pMediumLockList);
3879 alock.acquire();
3880 if (FAILED(rc))
3881 delete pMediumLockList;
3882 else
3883 {
3884 mData->mSession.mLockedMedia.Unlock();
3885 alock.release();
3886 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3887 mData->mSession.mLockedMedia.Lock();
3888 alock.acquire();
3889 }
3890 alock.release();
3891
3892 if (SUCCEEDED(rc))
3893 {
3894 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3895 /* Remove lock list in case of error. */
3896 if (FAILED(rc))
3897 {
3898 mData->mSession.mLockedMedia.Unlock();
3899 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3900 mData->mSession.mLockedMedia.Lock();
3901 }
3902 }
3903 }
3904
3905 return S_OK;
3906 }
3907
3908 /* bus/channel/device differ; we need a new attachment object,
3909 * but don't try to associate it again */
3910 associate = false;
3911 break;
3912 }
3913 }
3914
3915 /* go further only if the attachment is to be indirect */
3916 if (!fIndirect)
3917 break;
3918
3919 /* perform the so called smart attachment logic for indirect
3920 * attachments. Note that smart attachment is only applicable to base
3921 * hard disks. */
3922
3923 if (medium->i_getParent().isNull())
3924 {
3925 /* first, investigate the backup copy of the current hard disk
3926 * attachments to make it possible to re-attach existing diffs to
3927 * another device slot w/o losing their contents */
3928 if (mMediaData.isBackedUp())
3929 {
3930 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3931
3932 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3933 uint32_t foundLevel = 0;
3934
3935 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3936 {
3937 uint32_t level = 0;
3938 MediumAttachment *pAttach = *it;
3939 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3940 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3941 if (pMedium.isNull())
3942 continue;
3943
3944 if (pMedium->i_getBase(&level) == medium)
3945 {
3946 /* skip the hard disk if its currently attached (we
3947 * cannot attach the same hard disk twice) */
3948 if (i_findAttachment(mMediaData->mAttachments,
3949 pMedium))
3950 continue;
3951
3952 /* matched device, channel and bus (i.e. attached to the
3953 * same place) will win and immediately stop the search;
3954 * otherwise the attachment that has the youngest
3955 * descendant of medium will be used
3956 */
3957 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3958 {
3959 /* the simplest case: restore the whole attachment
3960 * and return, nothing else to do */
3961 mMediaData->mAttachments.push_back(*it);
3962
3963 /* Reattach the medium to the VM. */
3964 if (fHotplug || fSilent)
3965 {
3966 mediumLock.release();
3967 treeLock.release();
3968 alock.release();
3969
3970 MediumLockList *pMediumLockList(new MediumLockList());
3971
3972 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3973 true /* fMediumLockWrite */,
3974 false /* fMediumLockWriteAll */,
3975 NULL,
3976 *pMediumLockList);
3977 alock.acquire();
3978 if (FAILED(rc))
3979 delete pMediumLockList;
3980 else
3981 {
3982 mData->mSession.mLockedMedia.Unlock();
3983 alock.release();
3984 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3985 mData->mSession.mLockedMedia.Lock();
3986 alock.acquire();
3987 }
3988 alock.release();
3989
3990 if (SUCCEEDED(rc))
3991 {
3992 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3993 /* Remove lock list in case of error. */
3994 if (FAILED(rc))
3995 {
3996 mData->mSession.mLockedMedia.Unlock();
3997 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3998 mData->mSession.mLockedMedia.Lock();
3999 }
4000 }
4001 }
4002
4003 return S_OK;
4004 }
4005 else if ( foundIt == oldAtts.end()
4006 || level > foundLevel /* prefer younger */
4007 )
4008 {
4009 foundIt = it;
4010 foundLevel = level;
4011 }
4012 }
4013 }
4014
4015 if (foundIt != oldAtts.end())
4016 {
4017 /* use the previously attached hard disk */
4018 medium = (*foundIt)->i_getMedium();
4019 mediumCaller.attach(medium);
4020 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4021 mediumLock.attach(medium);
4022 /* not implicit, doesn't require association with this VM */
4023 fIndirect = false;
4024 associate = false;
4025 /* go right to the MediumAttachment creation */
4026 break;
4027 }
4028 }
4029
4030 /* must give up the medium lock and medium tree lock as below we
4031 * go over snapshots, which needs a lock with higher lock order. */
4032 mediumLock.release();
4033 treeLock.release();
4034
4035 /* then, search through snapshots for the best diff in the given
4036 * hard disk's chain to base the new diff on */
4037
4038 ComObjPtr<Medium> base;
4039 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4040 while (snap)
4041 {
4042 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4043
4044 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4045
4046 MediumAttachment *pAttachFound = NULL;
4047 uint32_t foundLevel = 0;
4048
4049 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4050 {
4051 MediumAttachment *pAttach = *it;
4052 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4053 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4054 if (pMedium.isNull())
4055 continue;
4056
4057 uint32_t level = 0;
4058 if (pMedium->i_getBase(&level) == medium)
4059 {
4060 /* matched device, channel and bus (i.e. attached to the
4061 * same place) will win and immediately stop the search;
4062 * otherwise the attachment that has the youngest
4063 * descendant of medium will be used
4064 */
4065 if ( pAttach->i_getDevice() == aDevice
4066 && pAttach->i_getPort() == aControllerPort
4067 && pAttach->i_getControllerName() == aName
4068 )
4069 {
4070 pAttachFound = pAttach;
4071 break;
4072 }
4073 else if ( !pAttachFound
4074 || level > foundLevel /* prefer younger */
4075 )
4076 {
4077 pAttachFound = pAttach;
4078 foundLevel = level;
4079 }
4080 }
4081 }
4082
4083 if (pAttachFound)
4084 {
4085 base = pAttachFound->i_getMedium();
4086 break;
4087 }
4088
4089 snap = snap->i_getParent();
4090 }
4091
4092 /* re-lock medium tree and the medium, as we need it below */
4093 treeLock.acquire();
4094 mediumLock.acquire();
4095
4096 /* found a suitable diff, use it as a base */
4097 if (!base.isNull())
4098 {
4099 medium = base;
4100 mediumCaller.attach(medium);
4101 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4102 mediumLock.attach(medium);
4103 }
4104 }
4105
4106 Utf8Str strFullSnapshotFolder;
4107 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4108
4109 ComObjPtr<Medium> diff;
4110 diff.createObject();
4111 // store this diff in the same registry as the parent
4112 Guid uuidRegistryParent;
4113 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4114 {
4115 // parent image has no registry: this can happen if we're attaching a new immutable
4116 // image that has not yet been attached (medium then points to the base and we're
4117 // creating the diff image for the immutable, and the parent is not yet registered);
4118 // put the parent in the machine registry then
4119 mediumLock.release();
4120 treeLock.release();
4121 alock.release();
4122 i_addMediumToRegistry(medium);
4123 alock.acquire();
4124 treeLock.acquire();
4125 mediumLock.acquire();
4126 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4127 }
4128 rc = diff->init(mParent,
4129 medium->i_getPreferredDiffFormat(),
4130 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4131 uuidRegistryParent,
4132 DeviceType_HardDisk);
4133 if (FAILED(rc)) return rc;
4134
4135 /* Apply the normal locking logic to the entire chain. */
4136 MediumLockList *pMediumLockList(new MediumLockList());
4137 mediumLock.release();
4138 treeLock.release();
4139 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4140 true /* fMediumLockWrite */,
4141 false /* fMediumLockWriteAll */,
4142 medium,
4143 *pMediumLockList);
4144 treeLock.acquire();
4145 mediumLock.acquire();
4146 if (SUCCEEDED(rc))
4147 {
4148 mediumLock.release();
4149 treeLock.release();
4150 rc = pMediumLockList->Lock();
4151 treeLock.acquire();
4152 mediumLock.acquire();
4153 if (FAILED(rc))
4154 setError(rc,
4155 tr("Could not lock medium when creating diff '%s'"),
4156 diff->i_getLocationFull().c_str());
4157 else
4158 {
4159 /* will release the lock before the potentially lengthy
4160 * operation, so protect with the special state */
4161 MachineState_T oldState = mData->mMachineState;
4162 i_setMachineState(MachineState_SettingUp);
4163
4164 mediumLock.release();
4165 treeLock.release();
4166 alock.release();
4167
4168 rc = medium->i_createDiffStorage(diff,
4169 MediumVariant_Standard,
4170 pMediumLockList,
4171 NULL /* aProgress */,
4172 true /* aWait */);
4173
4174 alock.acquire();
4175 treeLock.acquire();
4176 mediumLock.acquire();
4177
4178 i_setMachineState(oldState);
4179 }
4180 }
4181
4182 /* Unlock the media and free the associated memory. */
4183 delete pMediumLockList;
4184
4185 if (FAILED(rc)) return rc;
4186
4187 /* use the created diff for the actual attachment */
4188 medium = diff;
4189 mediumCaller.attach(medium);
4190 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4191 mediumLock.attach(medium);
4192 }
4193 while (0);
4194
4195 ComObjPtr<MediumAttachment> attachment;
4196 attachment.createObject();
4197 rc = attachment->init(this,
4198 medium,
4199 aName,
4200 aControllerPort,
4201 aDevice,
4202 aType,
4203 fIndirect,
4204 false /* fPassthrough */,
4205 false /* fTempEject */,
4206 false /* fNonRotational */,
4207 false /* fDiscard */,
4208 fHotplug /* fHotPluggable */,
4209 Utf8Str::Empty);
4210 if (FAILED(rc)) return rc;
4211
4212 if (associate && !medium.isNull())
4213 {
4214 // as the last step, associate the medium to the VM
4215 rc = medium->i_addBackReference(mData->mUuid);
4216 // here we can fail because of Deleting, or being in process of creating a Diff
4217 if (FAILED(rc)) return rc;
4218
4219 mediumLock.release();
4220 treeLock.release();
4221 alock.release();
4222 i_addMediumToRegistry(medium);
4223 alock.acquire();
4224 treeLock.acquire();
4225 mediumLock.acquire();
4226 }
4227
4228 /* success: finally remember the attachment */
4229 i_setModified(IsModified_Storage);
4230 mMediaData.backup();
4231 mMediaData->mAttachments.push_back(attachment);
4232
4233 mediumLock.release();
4234 treeLock.release();
4235 alock.release();
4236
4237 if (fHotplug || fSilent)
4238 {
4239 if (!medium.isNull())
4240 {
4241 MediumLockList *pMediumLockList(new MediumLockList());
4242
4243 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4244 true /* fMediumLockWrite */,
4245 false /* fMediumLockWriteAll */,
4246 NULL,
4247 *pMediumLockList);
4248 alock.acquire();
4249 if (FAILED(rc))
4250 delete pMediumLockList;
4251 else
4252 {
4253 mData->mSession.mLockedMedia.Unlock();
4254 alock.release();
4255 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4256 mData->mSession.mLockedMedia.Lock();
4257 alock.acquire();
4258 }
4259 alock.release();
4260 }
4261
4262 if (SUCCEEDED(rc))
4263 {
4264 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4265 /* Remove lock list in case of error. */
4266 if (FAILED(rc))
4267 {
4268 mData->mSession.mLockedMedia.Unlock();
4269 mData->mSession.mLockedMedia.Remove(attachment);
4270 mData->mSession.mLockedMedia.Lock();
4271 }
4272 }
4273 }
4274
4275 /* Save modified registries, but skip this machine as it's the caller's
4276 * job to save its settings like all other settings changes. */
4277 mParent->i_unmarkRegistryModified(i_getId());
4278 mParent->i_saveModifiedRegistries();
4279
4280 return rc;
4281}
4282
4283HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4284 LONG aDevice)
4285{
4286 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4287 aName.c_str(), aControllerPort, aDevice));
4288
4289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4290
4291 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4292 if (FAILED(rc)) return rc;
4293
4294 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4295
4296 /* Check for an existing controller. */
4297 ComObjPtr<StorageController> ctl;
4298 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4299 if (FAILED(rc)) return rc;
4300
4301 StorageControllerType_T ctrlType;
4302 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4303 if (FAILED(rc))
4304 return setError(E_FAIL,
4305 tr("Could not get type of controller '%s'"),
4306 aName.c_str());
4307
4308 bool fSilent = false;
4309 Utf8Str strReconfig;
4310
4311 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4312 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4313 if ( mData->mMachineState == MachineState_Paused
4314 && strReconfig == "1")
4315 fSilent = true;
4316
4317 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4318 bool fHotplug = false;
4319 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4320 fHotplug = true;
4321
4322 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4323 return setError(VBOX_E_INVALID_VM_STATE,
4324 tr("Controller '%s' does not support hotplugging"),
4325 aName.c_str());
4326
4327 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4328 Bstr(aName).raw(),
4329 aControllerPort,
4330 aDevice);
4331 if (!pAttach)
4332 return setError(VBOX_E_OBJECT_NOT_FOUND,
4333 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4334 aDevice, aControllerPort, aName.c_str());
4335
4336 if (fHotplug && !pAttach->i_getHotPluggable())
4337 return setError(VBOX_E_NOT_SUPPORTED,
4338 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4339 aDevice, aControllerPort, aName.c_str());
4340
4341 /*
4342 * The VM has to detach the device before we delete any implicit diffs.
4343 * If this fails we can roll back without loosing data.
4344 */
4345 if (fHotplug || fSilent)
4346 {
4347 alock.release();
4348 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4349 alock.acquire();
4350 }
4351 if (FAILED(rc)) return rc;
4352
4353 /* If we are here everything went well and we can delete the implicit now. */
4354 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4355
4356 alock.release();
4357
4358 /* Save modified registries, but skip this machine as it's the caller's
4359 * job to save its settings like all other settings changes. */
4360 mParent->i_unmarkRegistryModified(i_getId());
4361 mParent->i_saveModifiedRegistries();
4362
4363 return rc;
4364}
4365
4366HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4367 LONG aDevice, BOOL aPassthrough)
4368{
4369 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4370 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4371
4372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4373
4374 HRESULT rc = i_checkStateDependency(MutableStateDep);
4375 if (FAILED(rc)) return rc;
4376
4377 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4378
4379 if (Global::IsOnlineOrTransient(mData->mMachineState))
4380 return setError(VBOX_E_INVALID_VM_STATE,
4381 tr("Invalid machine state: %s"),
4382 Global::stringifyMachineState(mData->mMachineState));
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 passthrough 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_updatePassthrough(!!aPassthrough);
4404
4405 return S_OK;
4406}
4407
4408HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4409 LONG aDevice, BOOL aTemporaryEject)
4410{
4411
4412 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4413 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4414
4415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4416
4417 HRESULT rc = i_checkStateDependency(MutableStateDep);
4418 if (FAILED(rc)) return rc;
4419
4420 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4421 Bstr(aName).raw(),
4422 aControllerPort,
4423 aDevice);
4424 if (!pAttach)
4425 return setError(VBOX_E_OBJECT_NOT_FOUND,
4426 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4427 aDevice, aControllerPort, aName.c_str());
4428
4429
4430 i_setModified(IsModified_Storage);
4431 mMediaData.backup();
4432
4433 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4434
4435 if (pAttach->i_getType() != DeviceType_DVD)
4436 return setError(E_INVALIDARG,
4437 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4438 aDevice, aControllerPort, aName.c_str());
4439 pAttach->i_updateTempEject(!!aTemporaryEject);
4440
4441 return S_OK;
4442}
4443
4444HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4445 LONG aDevice, BOOL aNonRotational)
4446{
4447
4448 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4449 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4450
4451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4452
4453 HRESULT rc = i_checkStateDependency(MutableStateDep);
4454 if (FAILED(rc)) return rc;
4455
4456 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4457
4458 if (Global::IsOnlineOrTransient(mData->mMachineState))
4459 return setError(VBOX_E_INVALID_VM_STATE,
4460 tr("Invalid machine state: %s"),
4461 Global::stringifyMachineState(mData->mMachineState));
4462
4463 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4464 Bstr(aName).raw(),
4465 aControllerPort,
4466 aDevice);
4467 if (!pAttach)
4468 return setError(VBOX_E_OBJECT_NOT_FOUND,
4469 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4470 aDevice, aControllerPort, aName.c_str());
4471
4472
4473 i_setModified(IsModified_Storage);
4474 mMediaData.backup();
4475
4476 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4477
4478 if (pAttach->i_getType() != DeviceType_HardDisk)
4479 return setError(E_INVALIDARG,
4480 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"),
4481 aDevice, aControllerPort, aName.c_str());
4482 pAttach->i_updateNonRotational(!!aNonRotational);
4483
4484 return S_OK;
4485}
4486
4487HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4488 LONG aDevice, BOOL aDiscard)
4489{
4490
4491 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4492 aName.c_str(), aControllerPort, aDevice, aDiscard));
4493
4494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4495
4496 HRESULT rc = i_checkStateDependency(MutableStateDep);
4497 if (FAILED(rc)) return rc;
4498
4499 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4500
4501 if (Global::IsOnlineOrTransient(mData->mMachineState))
4502 return setError(VBOX_E_INVALID_VM_STATE,
4503 tr("Invalid machine state: %s"),
4504 Global::stringifyMachineState(mData->mMachineState));
4505
4506 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4507 Bstr(aName).raw(),
4508 aControllerPort,
4509 aDevice);
4510 if (!pAttach)
4511 return setError(VBOX_E_OBJECT_NOT_FOUND,
4512 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4513 aDevice, aControllerPort, aName.c_str());
4514
4515
4516 i_setModified(IsModified_Storage);
4517 mMediaData.backup();
4518
4519 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4520
4521 if (pAttach->i_getType() != DeviceType_HardDisk)
4522 return setError(E_INVALIDARG,
4523 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"),
4524 aDevice, aControllerPort, aName.c_str());
4525 pAttach->i_updateDiscard(!!aDiscard);
4526
4527 return S_OK;
4528}
4529
4530HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4531 LONG aDevice, BOOL aHotPluggable)
4532{
4533 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4534 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4535
4536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4537
4538 HRESULT rc = i_checkStateDependency(MutableStateDep);
4539 if (FAILED(rc)) return rc;
4540
4541 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4542
4543 if (Global::IsOnlineOrTransient(mData->mMachineState))
4544 return setError(VBOX_E_INVALID_VM_STATE,
4545 tr("Invalid machine state: %s"),
4546 Global::stringifyMachineState(mData->mMachineState));
4547
4548 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4549 Bstr(aName).raw(),
4550 aControllerPort,
4551 aDevice);
4552 if (!pAttach)
4553 return setError(VBOX_E_OBJECT_NOT_FOUND,
4554 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4555 aDevice, aControllerPort, aName.c_str());
4556
4557 /* Check for an existing controller. */
4558 ComObjPtr<StorageController> ctl;
4559 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4560 if (FAILED(rc)) return rc;
4561
4562 StorageControllerType_T ctrlType;
4563 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4564 if (FAILED(rc))
4565 return setError(E_FAIL,
4566 tr("Could not get type of controller '%s'"),
4567 aName.c_str());
4568
4569 if (!i_isControllerHotplugCapable(ctrlType))
4570 return setError(VBOX_E_NOT_SUPPORTED,
4571 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4572 aName.c_str());
4573
4574 i_setModified(IsModified_Storage);
4575 mMediaData.backup();
4576
4577 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4578
4579 if (pAttach->i_getType() == DeviceType_Floppy)
4580 return setError(E_INVALIDARG,
4581 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"),
4582 aDevice, aControllerPort, aName.c_str());
4583 pAttach->i_updateHotPluggable(!!aHotPluggable);
4584
4585 return S_OK;
4586}
4587
4588HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4589 LONG aDevice)
4590{
4591 int rc = S_OK;
4592 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4593 aName.c_str(), aControllerPort, aDevice));
4594
4595 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4596
4597 return rc;
4598}
4599
4600HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4601 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4602{
4603 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4604 aName.c_str(), aControllerPort, aDevice));
4605
4606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4607
4608 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4609 if (FAILED(rc)) return rc;
4610
4611 if (Global::IsOnlineOrTransient(mData->mMachineState))
4612 return setError(VBOX_E_INVALID_VM_STATE,
4613 tr("Invalid machine state: %s"),
4614 Global::stringifyMachineState(mData->mMachineState));
4615
4616 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4617 Bstr(aName).raw(),
4618 aControllerPort,
4619 aDevice);
4620 if (!pAttach)
4621 return setError(VBOX_E_OBJECT_NOT_FOUND,
4622 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4623 aDevice, aControllerPort, aName.c_str());
4624
4625
4626 i_setModified(IsModified_Storage);
4627 mMediaData.backup();
4628
4629 IBandwidthGroup *iB = aBandwidthGroup;
4630 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4631 if (aBandwidthGroup && group.isNull())
4632 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4633
4634 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4635
4636 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4637 if (strBandwidthGroupOld.isNotEmpty())
4638 {
4639 /* Get the bandwidth group object and release it - this must not fail. */
4640 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4641 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4642 Assert(SUCCEEDED(rc));
4643
4644 pBandwidthGroupOld->i_release();
4645 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4646 }
4647
4648 if (!group.isNull())
4649 {
4650 group->i_reference();
4651 pAttach->i_updateBandwidthGroup(group->i_getName());
4652 }
4653
4654 return S_OK;
4655}
4656
4657HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4658 LONG aControllerPort,
4659 LONG aDevice,
4660 DeviceType_T aType)
4661{
4662 HRESULT rc = S_OK;
4663
4664 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4665 aName.c_str(), aControllerPort, aDevice, aType));
4666
4667 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4668
4669 return rc;
4670}
4671
4672
4673HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4674 LONG aControllerPort,
4675 LONG aDevice,
4676 BOOL aForce)
4677{
4678 int rc = S_OK;
4679 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4680 aName.c_str(), aControllerPort, aForce));
4681
4682 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4683
4684 return rc;
4685}
4686
4687HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4688 LONG aControllerPort,
4689 LONG aDevice,
4690 const ComPtr<IMedium> &aMedium,
4691 BOOL aForce)
4692{
4693 int rc = S_OK;
4694 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4695 aName.c_str(), aControllerPort, aDevice, aForce));
4696
4697 // request the host lock first, since might be calling Host methods for getting host drives;
4698 // next, protect the media tree all the while we're in here, as well as our member variables
4699 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4700 this->lockHandle(),
4701 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4702
4703 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4704 Bstr(aName).raw(),
4705 aControllerPort,
4706 aDevice);
4707 if (pAttach.isNull())
4708 return setError(VBOX_E_OBJECT_NOT_FOUND,
4709 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4710 aDevice, aControllerPort, aName.c_str());
4711
4712 /* Remember previously mounted medium. The medium before taking the
4713 * backup is not necessarily the same thing. */
4714 ComObjPtr<Medium> oldmedium;
4715 oldmedium = pAttach->i_getMedium();
4716
4717 IMedium *iM = aMedium;
4718 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4719 if (aMedium && pMedium.isNull())
4720 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4721
4722 AutoCaller mediumCaller(pMedium);
4723 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4724
4725 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4726 if (pMedium)
4727 {
4728 DeviceType_T mediumType = pAttach->i_getType();
4729 switch (mediumType)
4730 {
4731 case DeviceType_DVD:
4732 case DeviceType_Floppy:
4733 break;
4734
4735 default:
4736 return setError(VBOX_E_INVALID_OBJECT_STATE,
4737 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4738 aControllerPort,
4739 aDevice,
4740 aName.c_str());
4741 }
4742 }
4743
4744 i_setModified(IsModified_Storage);
4745 mMediaData.backup();
4746
4747 {
4748 // The backup operation makes the pAttach reference point to the
4749 // old settings. Re-get the correct reference.
4750 pAttach = i_findAttachment(mMediaData->mAttachments,
4751 Bstr(aName).raw(),
4752 aControllerPort,
4753 aDevice);
4754 if (!oldmedium.isNull())
4755 oldmedium->i_removeBackReference(mData->mUuid);
4756 if (!pMedium.isNull())
4757 {
4758 pMedium->i_addBackReference(mData->mUuid);
4759
4760 mediumLock.release();
4761 multiLock.release();
4762 i_addMediumToRegistry(pMedium);
4763 multiLock.acquire();
4764 mediumLock.acquire();
4765 }
4766
4767 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4768 pAttach->i_updateMedium(pMedium);
4769 }
4770
4771 i_setModified(IsModified_Storage);
4772
4773 mediumLock.release();
4774 multiLock.release();
4775 rc = i_onMediumChange(pAttach, aForce);
4776 multiLock.acquire();
4777 mediumLock.acquire();
4778
4779 /* On error roll back this change only. */
4780 if (FAILED(rc))
4781 {
4782 if (!pMedium.isNull())
4783 pMedium->i_removeBackReference(mData->mUuid);
4784 pAttach = i_findAttachment(mMediaData->mAttachments,
4785 Bstr(aName).raw(),
4786 aControllerPort,
4787 aDevice);
4788 /* If the attachment is gone in the meantime, bail out. */
4789 if (pAttach.isNull())
4790 return rc;
4791 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4792 if (!oldmedium.isNull())
4793 oldmedium->i_addBackReference(mData->mUuid);
4794 pAttach->i_updateMedium(oldmedium);
4795 }
4796
4797 mediumLock.release();
4798 multiLock.release();
4799
4800 /* Save modified registries, but skip this machine as it's the caller's
4801 * job to save its settings like all other settings changes. */
4802 mParent->i_unmarkRegistryModified(i_getId());
4803 mParent->i_saveModifiedRegistries();
4804
4805 return rc;
4806}
4807HRESULT Machine::getMedium(const com::Utf8Str &aName,
4808 LONG aControllerPort,
4809 LONG aDevice,
4810 ComPtr<IMedium> &aMedium)
4811{
4812 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4813 aName.c_str(), aControllerPort, aDevice));
4814
4815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4816
4817 aMedium = NULL;
4818
4819 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4820 Bstr(aName).raw(),
4821 aControllerPort,
4822 aDevice);
4823 if (pAttach.isNull())
4824 return setError(VBOX_E_OBJECT_NOT_FOUND,
4825 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4826 aDevice, aControllerPort, aName.c_str());
4827
4828 aMedium = pAttach->i_getMedium();
4829
4830 return S_OK;
4831}
4832
4833HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4834{
4835
4836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4837
4838 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4839
4840 return S_OK;
4841}
4842
4843HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4844{
4845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4846
4847 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4848
4849 return S_OK;
4850}
4851
4852HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4853{
4854 /* Do not assert if slot is out of range, just return the advertised
4855 status. testdriver/vbox.py triggers this in logVmInfo. */
4856 if (aSlot >= mNetworkAdapters.size())
4857 return setError(E_INVALIDARG,
4858 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4859 aSlot, mNetworkAdapters.size());
4860
4861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4862
4863 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4864
4865 return S_OK;
4866}
4867
4868HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4869{
4870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4871
4872 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4873 size_t i = 0;
4874 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4875 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4876 ++it, ++i)
4877 aKeys[i] = it->first;
4878
4879 return S_OK;
4880}
4881
4882 /**
4883 * @note Locks this object for reading.
4884 */
4885HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4886 com::Utf8Str &aValue)
4887{
4888 /* start with nothing found */
4889 aValue = "";
4890
4891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4892
4893 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4894 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4895 // found:
4896 aValue = it->second; // source is a Utf8Str
4897
4898 /* return the result to caller (may be empty) */
4899 return S_OK;
4900}
4901
4902 /**
4903 * @note Locks mParent for writing + this object for writing.
4904 */
4905HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4906{
4907 Utf8Str strOldValue; // empty
4908
4909 // locking note: we only hold the read lock briefly to look up the old value,
4910 // then release it and call the onExtraCanChange callbacks. There is a small
4911 // chance of a race insofar as the callback might be called twice if two callers
4912 // change the same key at the same time, but that's a much better solution
4913 // than the deadlock we had here before. The actual changing of the extradata
4914 // is then performed under the write lock and race-free.
4915
4916 // look up the old value first; if nothing has changed then we need not do anything
4917 {
4918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4919
4920 // For snapshots don't even think about allowing changes, extradata
4921 // is global for a machine, so there is nothing snapshot specific.
4922 if (i_isSnapshotMachine())
4923 return setError(VBOX_E_INVALID_VM_STATE,
4924 tr("Cannot set extradata for a snapshot"));
4925
4926 // check if the right IMachine instance is used
4927 if (mData->mRegistered && !i_isSessionMachine())
4928 return setError(VBOX_E_INVALID_VM_STATE,
4929 tr("Cannot set extradata for an immutable machine"));
4930
4931 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4932 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4933 strOldValue = it->second;
4934 }
4935
4936 bool fChanged;
4937 if ((fChanged = (strOldValue != aValue)))
4938 {
4939 // ask for permission from all listeners outside the locks;
4940 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4941 // lock to copy the list of callbacks to invoke
4942 Bstr error;
4943 Bstr bstrValue(aValue);
4944
4945 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4946 {
4947 const char *sep = error.isEmpty() ? "" : ": ";
4948 CBSTR err = error.raw();
4949 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4950 sep, err));
4951 return setError(E_ACCESSDENIED,
4952 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4953 aKey.c_str(),
4954 aValue.c_str(),
4955 sep,
4956 err);
4957 }
4958
4959 // data is changing and change not vetoed: then write it out under the lock
4960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4961
4962 if (aValue.isEmpty())
4963 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4964 else
4965 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4966 // creates a new key if needed
4967
4968 bool fNeedsGlobalSaveSettings = false;
4969 // This saving of settings is tricky: there is no "old state" for the
4970 // extradata items at all (unlike all other settings), so the old/new
4971 // settings comparison would give a wrong result!
4972 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4973
4974 if (fNeedsGlobalSaveSettings)
4975 {
4976 // save the global settings; for that we should hold only the VirtualBox lock
4977 alock.release();
4978 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4979 mParent->i_saveSettings();
4980 }
4981 }
4982
4983 // fire notification outside the lock
4984 if (fChanged)
4985 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4986
4987 return S_OK;
4988}
4989
4990HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4991{
4992 aProgress = NULL;
4993 NOREF(aSettingsFilePath);
4994 ReturnComNotImplemented();
4995}
4996
4997HRESULT Machine::saveSettings()
4998{
4999 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5000
5001 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5002 if (FAILED(rc)) return rc;
5003
5004 /* the settings file path may never be null */
5005 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5006
5007 /* save all VM data excluding snapshots */
5008 bool fNeedsGlobalSaveSettings = false;
5009 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5010 mlock.release();
5011
5012 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5013 {
5014 // save the global settings; for that we should hold only the VirtualBox lock
5015 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5016 rc = mParent->i_saveSettings();
5017 }
5018
5019 return rc;
5020}
5021
5022
5023HRESULT Machine::discardSettings()
5024{
5025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5026
5027 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5028 if (FAILED(rc)) return rc;
5029
5030 /*
5031 * during this rollback, the session will be notified if data has
5032 * been actually changed
5033 */
5034 i_rollback(true /* aNotify */);
5035
5036 return S_OK;
5037}
5038
5039/** @note Locks objects! */
5040HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5041 std::vector<ComPtr<IMedium> > &aMedia)
5042{
5043 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5044 AutoLimitedCaller autoCaller(this);
5045 AssertComRCReturnRC(autoCaller.rc());
5046
5047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5048
5049 Guid id(i_getId());
5050
5051 if (mData->mSession.mState != SessionState_Unlocked)
5052 return setError(VBOX_E_INVALID_OBJECT_STATE,
5053 tr("Cannot unregister the machine '%s' while it is locked"),
5054 mUserData->s.strName.c_str());
5055
5056 // wait for state dependents to drop to zero
5057 i_ensureNoStateDependencies();
5058
5059 if (!mData->mAccessible)
5060 {
5061 // inaccessible maschines can only be unregistered; uninitialize ourselves
5062 // here because currently there may be no unregistered that are inaccessible
5063 // (this state combination is not supported). Note releasing the caller and
5064 // leaving the lock before calling uninit()
5065 alock.release();
5066 autoCaller.release();
5067
5068 uninit();
5069
5070 mParent->i_unregisterMachine(this, id);
5071 // calls VirtualBox::i_saveSettings()
5072
5073 return S_OK;
5074 }
5075
5076 HRESULT rc = S_OK;
5077
5078 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5079 // discard saved state
5080 if (mData->mMachineState == MachineState_Saved)
5081 {
5082 // add the saved state file to the list of files the caller should delete
5083 Assert(!mSSData->strStateFilePath.isEmpty());
5084 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5085
5086 mSSData->strStateFilePath.setNull();
5087
5088 // unconditionally set the machine state to powered off, we now
5089 // know no session has locked the machine
5090 mData->mMachineState = MachineState_PoweredOff;
5091 }
5092
5093 size_t cSnapshots = 0;
5094 if (mData->mFirstSnapshot)
5095 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5096 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5097 // fail now before we start detaching media
5098 return setError(VBOX_E_INVALID_OBJECT_STATE,
5099 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5100 mUserData->s.strName.c_str(), cSnapshots);
5101
5102 // This list collects the medium objects from all medium attachments
5103 // which we will detach from the machine and its snapshots, in a specific
5104 // order which allows for closing all media without getting "media in use"
5105 // errors, simply by going through the list from the front to the back:
5106 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5107 // and must be closed before the parent media from the snapshots, or closing the parents
5108 // will fail because they still have children);
5109 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5110 // the root ("first") snapshot of the machine.
5111 MediaList llMedia;
5112
5113 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5114 && mMediaData->mAttachments.size()
5115 )
5116 {
5117 // we have media attachments: detach them all and add the Medium objects to our list
5118 if (aCleanupMode != CleanupMode_UnregisterOnly)
5119 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5120 else
5121 return setError(VBOX_E_INVALID_OBJECT_STATE,
5122 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5123 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5124 }
5125
5126 if (cSnapshots)
5127 {
5128 // add the media from the medium attachments of the snapshots to llMedia
5129 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5130 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5131 // into the children first
5132
5133 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5134 MachineState_T oldState = mData->mMachineState;
5135 mData->mMachineState = MachineState_DeletingSnapshot;
5136
5137 // make a copy of the first snapshot so the refcount does not drop to 0
5138 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5139 // because of the AutoCaller voodoo)
5140 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5141
5142 // GO!
5143 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5144
5145 mData->mMachineState = oldState;
5146 }
5147
5148 if (FAILED(rc))
5149 {
5150 i_rollbackMedia();
5151 return rc;
5152 }
5153
5154 // commit all the media changes made above
5155 i_commitMedia();
5156
5157 mData->mRegistered = false;
5158
5159 // machine lock no longer needed
5160 alock.release();
5161
5162 // return media to caller
5163 size_t i = 0;
5164 aMedia.resize(llMedia.size());
5165 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5166 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5167
5168 mParent->i_unregisterMachine(this, id);
5169 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5170
5171 return S_OK;
5172}
5173
5174struct Machine::DeleteTask
5175{
5176 ComObjPtr<Machine> pMachine;
5177 RTCList<ComPtr<IMedium> > llMediums;
5178 StringsList llFilesToDelete;
5179 ComObjPtr<Progress> pProgress;
5180};
5181
5182HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5183{
5184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5185
5186 HRESULT rc = i_checkStateDependency(MutableStateDep);
5187 if (FAILED(rc)) return rc;
5188
5189 if (mData->mRegistered)
5190 return setError(VBOX_E_INVALID_VM_STATE,
5191 tr("Cannot delete settings of a registered machine"));
5192
5193 DeleteTask *pTask = new DeleteTask;
5194 pTask->pMachine = this;
5195
5196 // collect files to delete
5197 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5198
5199 for (size_t i = 0; i < aMedia.size(); ++i)
5200 {
5201 IMedium *pIMedium(aMedia[i]);
5202 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5203 if (pMedium.isNull())
5204 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5205 SafeArray<BSTR> ids;
5206 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5207 if (FAILED(rc)) return rc;
5208 /* At this point the medium should not have any back references
5209 * anymore. If it has it is attached to another VM and *must* not
5210 * deleted. */
5211 if (ids.size() < 1)
5212 pTask->llMediums.append(pMedium);
5213 }
5214 if (mData->pMachineConfigFile->fileExists())
5215 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5216
5217 pTask->pProgress.createObject();
5218 pTask->pProgress->init(i_getVirtualBox(),
5219 static_cast<IMachine*>(this) /* aInitiator */,
5220 Bstr(tr("Deleting files")).raw(),
5221 true /* fCancellable */,
5222 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5223 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5224
5225 int vrc = RTThreadCreate(NULL,
5226 Machine::deleteThread,
5227 (void*)pTask,
5228 0,
5229 RTTHREADTYPE_MAIN_WORKER,
5230 0,
5231 "MachineDelete");
5232
5233 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5234
5235 if (RT_FAILURE(vrc))
5236 {
5237 delete pTask;
5238 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5239 }
5240
5241 LogFlowFuncLeave();
5242
5243 return S_OK;
5244}
5245
5246/**
5247 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5248 * calls Machine::deleteTaskWorker() on the actual machine object.
5249 * @param Thread
5250 * @param pvUser
5251 * @return
5252 */
5253/*static*/
5254DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5255{
5256 LogFlowFuncEnter();
5257
5258 DeleteTask *pTask = (DeleteTask*)pvUser;
5259 Assert(pTask);
5260 Assert(pTask->pMachine);
5261 Assert(pTask->pProgress);
5262
5263 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5264 pTask->pProgress->i_notifyComplete(rc);
5265
5266 delete pTask;
5267
5268 LogFlowFuncLeave();
5269
5270 NOREF(Thread);
5271
5272 return VINF_SUCCESS;
5273}
5274
5275/**
5276 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5277 * @param task
5278 * @return
5279 */
5280HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5281{
5282 AutoCaller autoCaller(this);
5283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5284
5285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5286
5287 HRESULT rc = S_OK;
5288
5289 try
5290 {
5291 ULONG uLogHistoryCount = 3;
5292 ComPtr<ISystemProperties> systemProperties;
5293 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5294 if (FAILED(rc)) throw rc;
5295
5296 if (!systemProperties.isNull())
5297 {
5298 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5299 if (FAILED(rc)) throw rc;
5300 }
5301
5302 MachineState_T oldState = mData->mMachineState;
5303 i_setMachineState(MachineState_SettingUp);
5304 alock.release();
5305 for (size_t i = 0; i < task.llMediums.size(); ++i)
5306 {
5307 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5308 {
5309 AutoCaller mac(pMedium);
5310 if (FAILED(mac.rc())) throw mac.rc();
5311 Utf8Str strLocation = pMedium->i_getLocationFull();
5312 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5313 if (FAILED(rc)) throw rc;
5314 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5315 }
5316 if (pMedium->i_isMediumFormatFile())
5317 {
5318 ComPtr<IProgress> pProgress2;
5319 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5320 if (FAILED(rc)) throw rc;
5321 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5322 if (FAILED(rc)) throw rc;
5323 /* Check the result of the asynchronous process. */
5324 LONG iRc;
5325 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5326 if (FAILED(rc)) throw rc;
5327 /* If the thread of the progress object has an error, then
5328 * retrieve the error info from there, or it'll be lost. */
5329 if (FAILED(iRc))
5330 throw setError(ProgressErrorInfo(pProgress2));
5331 }
5332
5333 /* Close the medium, deliberately without checking the return
5334 * code, and without leaving any trace in the error info, as
5335 * a failure here is a very minor issue, which shouldn't happen
5336 * as above we even managed to delete the medium. */
5337 {
5338 ErrorInfoKeeper eik;
5339 pMedium->Close();
5340 }
5341 }
5342 i_setMachineState(oldState);
5343 alock.acquire();
5344
5345 // delete the files pushed on the task list by Machine::Delete()
5346 // (this includes saved states of the machine and snapshots and
5347 // medium storage files from the IMedium list passed in, and the
5348 // machine XML file)
5349 StringsList::const_iterator it = task.llFilesToDelete.begin();
5350 while (it != task.llFilesToDelete.end())
5351 {
5352 const Utf8Str &strFile = *it;
5353 LogFunc(("Deleting file %s\n", strFile.c_str()));
5354 int vrc = RTFileDelete(strFile.c_str());
5355 if (RT_FAILURE(vrc))
5356 throw setError(VBOX_E_IPRT_ERROR,
5357 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5358
5359 ++it;
5360 if (it == task.llFilesToDelete.end())
5361 {
5362 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5363 if (FAILED(rc)) throw rc;
5364 break;
5365 }
5366
5367 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5368 if (FAILED(rc)) throw rc;
5369 }
5370
5371 /* delete the settings only when the file actually exists */
5372 if (mData->pMachineConfigFile->fileExists())
5373 {
5374 /* Delete any backup or uncommitted XML files. Ignore failures.
5375 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5376 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5377 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5378 RTFileDelete(otherXml.c_str());
5379 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5380 RTFileDelete(otherXml.c_str());
5381
5382 /* delete the Logs folder, nothing important should be left
5383 * there (we don't check for errors because the user might have
5384 * some private files there that we don't want to delete) */
5385 Utf8Str logFolder;
5386 getLogFolder(logFolder);
5387 Assert(logFolder.length());
5388 if (RTDirExists(logFolder.c_str()))
5389 {
5390 /* Delete all VBox.log[.N] files from the Logs folder
5391 * (this must be in sync with the rotation logic in
5392 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5393 * files that may have been created by the GUI. */
5394 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5395 logFolder.c_str(), RTPATH_DELIMITER);
5396 RTFileDelete(log.c_str());
5397 log = Utf8StrFmt("%s%cVBox.png",
5398 logFolder.c_str(), RTPATH_DELIMITER);
5399 RTFileDelete(log.c_str());
5400 for (int i = uLogHistoryCount; i > 0; i--)
5401 {
5402 log = Utf8StrFmt("%s%cVBox.log.%d",
5403 logFolder.c_str(), RTPATH_DELIMITER, i);
5404 RTFileDelete(log.c_str());
5405 log = Utf8StrFmt("%s%cVBox.png.%d",
5406 logFolder.c_str(), RTPATH_DELIMITER, i);
5407 RTFileDelete(log.c_str());
5408 }
5409#if defined(RT_OS_WINDOWS)
5410 log = Utf8StrFmt("%s%cVBoxStartup.log",
5411 logFolder.c_str(), RTPATH_DELIMITER);
5412 RTFileDelete(log.c_str());
5413#endif
5414
5415 RTDirRemove(logFolder.c_str());
5416 }
5417
5418 /* delete the Snapshots folder, nothing important should be left
5419 * there (we don't check for errors because the user might have
5420 * some private files there that we don't want to delete) */
5421 Utf8Str strFullSnapshotFolder;
5422 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5423 Assert(!strFullSnapshotFolder.isEmpty());
5424 if (RTDirExists(strFullSnapshotFolder.c_str()))
5425 RTDirRemove(strFullSnapshotFolder.c_str());
5426
5427 // delete the directory that contains the settings file, but only
5428 // if it matches the VM name
5429 Utf8Str settingsDir;
5430 if (i_isInOwnDir(&settingsDir))
5431 RTDirRemove(settingsDir.c_str());
5432 }
5433
5434 alock.release();
5435
5436 mParent->i_saveModifiedRegistries();
5437 }
5438 catch (HRESULT aRC) { rc = aRC; }
5439
5440 return rc;
5441}
5442
5443HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5444{
5445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5446
5447 ComObjPtr<Snapshot> pSnapshot;
5448 HRESULT rc;
5449
5450 if (aNameOrId.isEmpty())
5451 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5452 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5453 else
5454 {
5455 Guid uuid(aNameOrId);
5456 if (uuid.isValid())
5457 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5458 else
5459 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5460 }
5461 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5462
5463 return rc;
5464}
5465
5466HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5467{
5468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5469
5470 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5471 if (FAILED(rc)) return rc;
5472
5473 ComObjPtr<SharedFolder> sharedFolder;
5474 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5475 if (SUCCEEDED(rc))
5476 return setError(VBOX_E_OBJECT_IN_USE,
5477 tr("Shared folder named '%s' already exists"),
5478 aName.c_str());
5479
5480 sharedFolder.createObject();
5481 rc = sharedFolder->init(i_getMachine(),
5482 aName,
5483 aHostPath,
5484 !!aWritable,
5485 !!aAutomount,
5486 true /* fFailOnError */);
5487 if (FAILED(rc)) return rc;
5488
5489 i_setModified(IsModified_SharedFolders);
5490 mHWData.backup();
5491 mHWData->mSharedFolders.push_back(sharedFolder);
5492
5493 /* inform the direct session if any */
5494 alock.release();
5495 i_onSharedFolderChange();
5496
5497 return S_OK;
5498}
5499
5500HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5501{
5502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5503
5504 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5505 if (FAILED(rc)) return rc;
5506
5507 ComObjPtr<SharedFolder> sharedFolder;
5508 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5509 if (FAILED(rc)) return rc;
5510
5511 i_setModified(IsModified_SharedFolders);
5512 mHWData.backup();
5513 mHWData->mSharedFolders.remove(sharedFolder);
5514
5515 /* inform the direct session if any */
5516 alock.release();
5517 i_onSharedFolderChange();
5518
5519 return S_OK;
5520}
5521
5522HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5523{
5524 /* start with No */
5525 *aCanShow = FALSE;
5526
5527 ComPtr<IInternalSessionControl> directControl;
5528 {
5529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5530
5531 if (mData->mSession.mState != SessionState_Locked)
5532 return setError(VBOX_E_INVALID_VM_STATE,
5533 tr("Machine is not locked for session (session state: %s)"),
5534 Global::stringifySessionState(mData->mSession.mState));
5535
5536 directControl = mData->mSession.mDirectControl;
5537 }
5538
5539 /* ignore calls made after #OnSessionEnd() is called */
5540 if (!directControl)
5541 return S_OK;
5542
5543 LONG64 dummy;
5544 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5545}
5546
5547HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5548{
5549 ComPtr<IInternalSessionControl> directControl;
5550 {
5551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5552
5553 if (mData->mSession.mState != SessionState_Locked)
5554 return setError(E_FAIL,
5555 tr("Machine is not locked for session (session state: %s)"),
5556 Global::stringifySessionState(mData->mSession.mState));
5557
5558 directControl = mData->mSession.mDirectControl;
5559 }
5560
5561 /* ignore calls made after #OnSessionEnd() is called */
5562 if (!directControl)
5563 return S_OK;
5564
5565 BOOL dummy;
5566 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5567}
5568
5569#ifdef VBOX_WITH_GUEST_PROPS
5570/**
5571 * Look up a guest property in VBoxSVC's internal structures.
5572 */
5573HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5574 com::Utf8Str &aValue,
5575 LONG64 *aTimestamp,
5576 com::Utf8Str &aFlags) const
5577{
5578 using namespace guestProp;
5579
5580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5581 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5582
5583 if (it != mHWData->mGuestProperties.end())
5584 {
5585 char szFlags[MAX_FLAGS_LEN + 1];
5586 aValue = it->second.strValue;
5587 *aTimestamp = it->second.mTimestamp;
5588 writeFlags(it->second.mFlags, szFlags);
5589 aFlags = Utf8Str(szFlags);
5590 }
5591
5592 return S_OK;
5593}
5594
5595/**
5596 * Query the VM that a guest property belongs to for the property.
5597 * @returns E_ACCESSDENIED if the VM process is not available or not
5598 * currently handling queries and the lookup should then be done in
5599 * VBoxSVC.
5600 */
5601HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5602 com::Utf8Str &aValue,
5603 LONG64 *aTimestamp,
5604 com::Utf8Str &aFlags) const
5605{
5606 HRESULT rc = S_OK;
5607 BSTR bValue = NULL;
5608 BSTR bFlags = NULL;
5609
5610 ComPtr<IInternalSessionControl> directControl;
5611 directControl = mData->mSession.mDirectControl;
5612
5613 /* fail if we were called after #OnSessionEnd() is called. This is a
5614 * silly race condition. */
5615
5616 /** @todo This code is bothering API clients (like python script clients) with
5617 * the AccessGuestProperty call, creating unncessary IPC. Need to
5618 * have a way of figuring out which kind of direct session it is... */
5619 if (!directControl)
5620 rc = E_ACCESSDENIED;
5621 else
5622 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5623 0 /* accessMode */,
5624 &bValue, aTimestamp, &bFlags);
5625
5626 aValue = bValue;
5627 aFlags = bFlags;
5628
5629 return rc;
5630}
5631#endif // VBOX_WITH_GUEST_PROPS
5632
5633HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5634 com::Utf8Str &aValue,
5635 LONG64 *aTimestamp,
5636 com::Utf8Str &aFlags)
5637{
5638#ifndef VBOX_WITH_GUEST_PROPS
5639 ReturnComNotImplemented();
5640#else // VBOX_WITH_GUEST_PROPS
5641
5642 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5643
5644 if (rc == E_ACCESSDENIED)
5645 /* The VM is not running or the service is not (yet) accessible */
5646 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5647 return rc;
5648#endif // VBOX_WITH_GUEST_PROPS
5649}
5650
5651HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5652{
5653 LONG64 dummyTimestamp;
5654 com::Utf8Str dummyFlags;
5655 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5656 return rc;
5657
5658}
5659HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5660{
5661 com::Utf8Str dummyFlags;
5662 com::Utf8Str dummyValue;
5663 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5664 return rc;
5665}
5666
5667#ifdef VBOX_WITH_GUEST_PROPS
5668/**
5669 * Set a guest property in VBoxSVC's internal structures.
5670 */
5671HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5672 const com::Utf8Str &aFlags, bool fDelete)
5673{
5674 using namespace guestProp;
5675
5676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5677 HRESULT rc = S_OK;
5678
5679 rc = i_checkStateDependency(MutableOrSavedStateDep);
5680 if (FAILED(rc)) return rc;
5681
5682 try
5683 {
5684 uint32_t fFlags = NILFLAG;
5685 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5686 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5687
5688 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5689 if (it == mHWData->mGuestProperties.end())
5690 {
5691 if (!fDelete)
5692 {
5693 i_setModified(IsModified_MachineData);
5694 mHWData.backupEx();
5695
5696 RTTIMESPEC time;
5697 HWData::GuestProperty prop;
5698 prop.strValue = Bstr(aValue).raw();
5699 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5700 prop.mFlags = fFlags;
5701 mHWData->mGuestProperties[aName] = prop;
5702 }
5703 }
5704 else
5705 {
5706 if (it->second.mFlags & (RDONLYHOST))
5707 {
5708 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5709 }
5710 else
5711 {
5712 i_setModified(IsModified_MachineData);
5713 mHWData.backupEx();
5714
5715 /* The backupEx() operation invalidates our iterator,
5716 * so get a new one. */
5717 it = mHWData->mGuestProperties.find(aName);
5718 Assert(it != mHWData->mGuestProperties.end());
5719
5720 if (!fDelete)
5721 {
5722 RTTIMESPEC time;
5723 it->second.strValue = aValue;
5724 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5725 it->second.mFlags = fFlags;
5726 }
5727 else
5728 mHWData->mGuestProperties.erase(it);
5729 }
5730 }
5731
5732 if ( SUCCEEDED(rc)
5733 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5734 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5735 RTSTR_MAX,
5736 aName.c_str(),
5737 RTSTR_MAX,
5738 NULL)
5739 )
5740 )
5741 {
5742 alock.release();
5743
5744 mParent->i_onGuestPropertyChange(mData->mUuid,
5745 Bstr(aName).raw(),
5746 Bstr(aValue).raw(),
5747 Bstr(aFlags).raw());
5748 }
5749 }
5750 catch (std::bad_alloc &)
5751 {
5752 rc = E_OUTOFMEMORY;
5753 }
5754
5755 return rc;
5756}
5757
5758/**
5759 * Set a property on the VM that that property belongs to.
5760 * @returns E_ACCESSDENIED if the VM process is not available or not
5761 * currently handling queries and the setting should then be done in
5762 * VBoxSVC.
5763 */
5764HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5765 const com::Utf8Str &aFlags, bool fDelete)
5766{
5767 HRESULT rc;
5768
5769 try
5770 {
5771 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5772
5773 BSTR dummy = NULL; /* will not be changed (setter) */
5774 LONG64 dummy64;
5775 if (!directControl)
5776 rc = E_ACCESSDENIED;
5777 else
5778 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5779 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5780 fDelete? 2: 1 /* accessMode */,
5781 &dummy, &dummy64, &dummy);
5782 }
5783 catch (std::bad_alloc &)
5784 {
5785 rc = E_OUTOFMEMORY;
5786 }
5787
5788 return rc;
5789}
5790#endif // VBOX_WITH_GUEST_PROPS
5791
5792HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5793 const com::Utf8Str &aFlags)
5794{
5795#ifndef VBOX_WITH_GUEST_PROPS
5796 ReturnComNotImplemented();
5797#else // VBOX_WITH_GUEST_PROPS
5798 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5799 if (rc == E_ACCESSDENIED)
5800 /* The VM is not running or the service is not (yet) accessible */
5801 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5802 return rc;
5803#endif // VBOX_WITH_GUEST_PROPS
5804}
5805
5806HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5807{
5808 return setGuestProperty(aProperty, aValue, "");
5809}
5810
5811HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5812{
5813#ifndef VBOX_WITH_GUEST_PROPS
5814 ReturnComNotImplemented();
5815#else // VBOX_WITH_GUEST_PROPS
5816 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5817 if (rc == E_ACCESSDENIED)
5818 /* The VM is not running or the service is not (yet) accessible */
5819 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5820 return rc;
5821#endif // VBOX_WITH_GUEST_PROPS
5822}
5823
5824#ifdef VBOX_WITH_GUEST_PROPS
5825/**
5826 * Enumerate the guest properties in VBoxSVC's internal structures.
5827 */
5828HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5829 std::vector<com::Utf8Str> &aNames,
5830 std::vector<com::Utf8Str> &aValues,
5831 std::vector<LONG64> &aTimestamps,
5832 std::vector<com::Utf8Str> &aFlags)
5833{
5834 using namespace guestProp;
5835
5836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5837 Utf8Str strPatterns(aPatterns);
5838
5839 HWData::GuestPropertyMap propMap;
5840
5841 /*
5842 * Look for matching patterns and build up a list.
5843 */
5844 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5845 while (it != mHWData->mGuestProperties.end())
5846 {
5847 if ( strPatterns.isEmpty()
5848 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5849 RTSTR_MAX,
5850 it->first.c_str(),
5851 RTSTR_MAX,
5852 NULL)
5853 )
5854 propMap.insert(*it);
5855 it++;
5856 }
5857
5858 alock.release();
5859
5860 /*
5861 * And build up the arrays for returning the property information.
5862 */
5863 size_t cEntries = propMap.size();
5864
5865 aNames.resize(cEntries);
5866 aValues.resize(cEntries);
5867 aTimestamps.resize(cEntries);
5868 aFlags.resize(cEntries);
5869
5870 char szFlags[MAX_FLAGS_LEN + 1];
5871 size_t i= 0;
5872 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5873 {
5874 aNames[i] = it->first;
5875 aValues[i] = it->second.strValue;
5876 aTimestamps[i] = it->second.mTimestamp;
5877 writeFlags(it->second.mFlags, szFlags);
5878 aFlags[i] = Utf8Str(szFlags);
5879 }
5880
5881 return S_OK;
5882}
5883
5884/**
5885 * Enumerate the properties managed by a VM.
5886 * @returns E_ACCESSDENIED if the VM process is not available or not
5887 * currently handling queries and the setting should then be done in
5888 * VBoxSVC.
5889 */
5890HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5891 std::vector<com::Utf8Str> &aNames,
5892 std::vector<com::Utf8Str> &aValues,
5893 std::vector<LONG64> &aTimestamps,
5894 std::vector<com::Utf8Str> &aFlags)
5895{
5896 HRESULT rc;
5897 ComPtr<IInternalSessionControl> directControl;
5898 directControl = mData->mSession.mDirectControl;
5899
5900
5901 com::SafeArray<BSTR> bNames;
5902 com::SafeArray<BSTR> bValues;
5903 com::SafeArray<LONG64> bTimestamps;
5904 com::SafeArray<BSTR> bFlags;
5905
5906 if (!directControl)
5907 rc = E_ACCESSDENIED;
5908 else
5909 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5910 ComSafeArrayAsOutParam(bNames),
5911 ComSafeArrayAsOutParam(bValues),
5912 ComSafeArrayAsOutParam(bTimestamps),
5913 ComSafeArrayAsOutParam(bFlags));
5914 size_t i;
5915 aNames.resize(bNames.size());
5916 for (i = 0; i < bNames.size(); ++i)
5917 aNames[i] = Utf8Str(bNames[i]);
5918 aValues.resize(bValues.size());
5919 for (i = 0; i < bValues.size(); ++i)
5920 aValues[i] = Utf8Str(bValues[i]);
5921 aTimestamps.resize(bTimestamps.size());
5922 for (i = 0; i < bTimestamps.size(); ++i)
5923 aTimestamps[i] = bTimestamps[i];
5924 aFlags.resize(bFlags.size());
5925 for (i = 0; i < bFlags.size(); ++i)
5926 aFlags[i] = Utf8Str(bFlags[i]);
5927
5928 return rc;
5929}
5930#endif // VBOX_WITH_GUEST_PROPS
5931HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5932 std::vector<com::Utf8Str> &aNames,
5933 std::vector<com::Utf8Str> &aValues,
5934 std::vector<LONG64> &aTimestamps,
5935 std::vector<com::Utf8Str> &aFlags)
5936{
5937#ifndef VBOX_WITH_GUEST_PROPS
5938 ReturnComNotImplemented();
5939#else // VBOX_WITH_GUEST_PROPS
5940
5941 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5942
5943 if (rc == E_ACCESSDENIED)
5944 /* The VM is not running or the service is not (yet) accessible */
5945 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5946 return rc;
5947#endif // VBOX_WITH_GUEST_PROPS
5948}
5949
5950HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5951 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5952{
5953 MediaData::AttachmentList atts;
5954
5955 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5956 if (FAILED(rc)) return rc;
5957
5958 size_t i = 0;
5959 aMediumAttachments.resize(atts.size());
5960 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5961 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5962
5963 return S_OK;
5964}
5965
5966HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5967 LONG aControllerPort,
5968 LONG aDevice,
5969 ComPtr<IMediumAttachment> &aAttachment)
5970{
5971 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5972 aName.c_str(), aControllerPort, aDevice));
5973
5974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5975
5976 aAttachment = NULL;
5977
5978 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5979 Bstr(aName).raw(),
5980 aControllerPort,
5981 aDevice);
5982 if (pAttach.isNull())
5983 return setError(VBOX_E_OBJECT_NOT_FOUND,
5984 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5985 aDevice, aControllerPort, aName.c_str());
5986
5987 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5988
5989 return S_OK;
5990}
5991
5992
5993HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5994 StorageBus_T aConnectionType,
5995 ComPtr<IStorageController> &aController)
5996{
5997 if ( (aConnectionType <= StorageBus_Null)
5998 || (aConnectionType > StorageBus_USB))
5999 return setError(E_INVALIDARG,
6000 tr("Invalid connection type: %d"),
6001 aConnectionType);
6002
6003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6004
6005 HRESULT rc = i_checkStateDependency(MutableStateDep);
6006 if (FAILED(rc)) return rc;
6007
6008 /* try to find one with the name first. */
6009 ComObjPtr<StorageController> ctrl;
6010
6011 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6012 if (SUCCEEDED(rc))
6013 return setError(VBOX_E_OBJECT_IN_USE,
6014 tr("Storage controller named '%s' already exists"),
6015 aName.c_str());
6016
6017 ctrl.createObject();
6018
6019 /* get a new instance number for the storage controller */
6020 ULONG ulInstance = 0;
6021 bool fBootable = true;
6022 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6023 it != mStorageControllers->end();
6024 ++it)
6025 {
6026 if ((*it)->i_getStorageBus() == aConnectionType)
6027 {
6028 ULONG ulCurInst = (*it)->i_getInstance();
6029
6030 if (ulCurInst >= ulInstance)
6031 ulInstance = ulCurInst + 1;
6032
6033 /* Only one controller of each type can be marked as bootable. */
6034 if ((*it)->i_getBootable())
6035 fBootable = false;
6036 }
6037 }
6038
6039 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6040 if (FAILED(rc)) return rc;
6041
6042 i_setModified(IsModified_Storage);
6043 mStorageControllers.backup();
6044 mStorageControllers->push_back(ctrl);
6045
6046 ctrl.queryInterfaceTo(aController.asOutParam());
6047
6048 /* inform the direct session if any */
6049 alock.release();
6050 i_onStorageControllerChange();
6051
6052 return S_OK;
6053}
6054
6055HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6056 ComPtr<IStorageController> &aStorageController)
6057{
6058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6059
6060 ComObjPtr<StorageController> ctrl;
6061
6062 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6063 if (SUCCEEDED(rc))
6064 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6065
6066 return rc;
6067}
6068
6069HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6070 ComPtr<IStorageController> &aStorageController)
6071{
6072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6073
6074 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6075 it != mStorageControllers->end();
6076 ++it)
6077 {
6078 if ((*it)->i_getInstance() == aInstance)
6079 {
6080 (*it).queryInterfaceTo(aStorageController.asOutParam());
6081 return S_OK;
6082 }
6083 }
6084
6085 return setError(VBOX_E_OBJECT_NOT_FOUND,
6086 tr("Could not find a storage controller with instance number '%lu'"),
6087 aInstance);
6088}
6089
6090HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6091{
6092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6093
6094 HRESULT rc = i_checkStateDependency(MutableStateDep);
6095 if (FAILED(rc)) return rc;
6096
6097 ComObjPtr<StorageController> ctrl;
6098
6099 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6100 if (SUCCEEDED(rc))
6101 {
6102 /* Ensure that only one controller of each type is marked as bootable. */
6103 if (aBootable == TRUE)
6104 {
6105 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6106 it != mStorageControllers->end();
6107 ++it)
6108 {
6109 ComObjPtr<StorageController> aCtrl = (*it);
6110
6111 if ( (aCtrl->i_getName() != aName)
6112 && aCtrl->i_getBootable() == TRUE
6113 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6114 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6115 {
6116 aCtrl->i_setBootable(FALSE);
6117 break;
6118 }
6119 }
6120 }
6121
6122 if (SUCCEEDED(rc))
6123 {
6124 ctrl->i_setBootable(aBootable);
6125 i_setModified(IsModified_Storage);
6126 }
6127 }
6128
6129 if (SUCCEEDED(rc))
6130 {
6131 /* inform the direct session if any */
6132 alock.release();
6133 i_onStorageControllerChange();
6134 }
6135
6136 return rc;
6137}
6138
6139HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6140{
6141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6142
6143 HRESULT rc = i_checkStateDependency(MutableStateDep);
6144 if (FAILED(rc)) return rc;
6145
6146 ComObjPtr<StorageController> ctrl;
6147 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6148 if (FAILED(rc)) return rc;
6149
6150 {
6151 /* find all attached devices to the appropriate storage controller and detach them all */
6152 // make a temporary list because detachDevice invalidates iterators into
6153 // mMediaData->mAttachments
6154 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6155
6156 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6157 it != llAttachments2.end();
6158 ++it)
6159 {
6160 MediumAttachment *pAttachTemp = *it;
6161
6162 AutoCaller localAutoCaller(pAttachTemp);
6163 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6164
6165 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6166
6167 if (pAttachTemp->i_getControllerName() == aName)
6168 {
6169 rc = i_detachDevice(pAttachTemp, alock, NULL);
6170 if (FAILED(rc)) return rc;
6171 }
6172 }
6173 }
6174
6175 /* We can remove it now. */
6176 i_setModified(IsModified_Storage);
6177 mStorageControllers.backup();
6178
6179 ctrl->i_unshare();
6180
6181 mStorageControllers->remove(ctrl);
6182
6183 /* inform the direct session if any */
6184 alock.release();
6185 i_onStorageControllerChange();
6186
6187 return S_OK;
6188}
6189
6190HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6191 ComPtr<IUSBController> &aController)
6192{
6193 if ( (aType <= USBControllerType_Null)
6194 || (aType >= USBControllerType_Last))
6195 return setError(E_INVALIDARG,
6196 tr("Invalid USB controller type: %d"),
6197 aType);
6198
6199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6200
6201 HRESULT rc = i_checkStateDependency(MutableStateDep);
6202 if (FAILED(rc)) return rc;
6203
6204 /* try to find one with the same type first. */
6205 ComObjPtr<USBController> ctrl;
6206
6207 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6208 if (SUCCEEDED(rc))
6209 return setError(VBOX_E_OBJECT_IN_USE,
6210 tr("USB controller named '%s' already exists"),
6211 aName.c_str());
6212
6213 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6214 ULONG maxInstances;
6215 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6216 if (FAILED(rc))
6217 return rc;
6218
6219 ULONG cInstances = i_getUSBControllerCountByType(aType);
6220 if (cInstances >= maxInstances)
6221 return setError(E_INVALIDARG,
6222 tr("Too many USB controllers of this type"));
6223
6224 ctrl.createObject();
6225
6226 rc = ctrl->init(this, aName, aType);
6227 if (FAILED(rc)) return rc;
6228
6229 i_setModified(IsModified_USB);
6230 mUSBControllers.backup();
6231 mUSBControllers->push_back(ctrl);
6232
6233 ctrl.queryInterfaceTo(aController.asOutParam());
6234
6235 /* inform the direct session if any */
6236 alock.release();
6237 i_onUSBControllerChange();
6238
6239 return S_OK;
6240}
6241
6242HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6243{
6244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6245
6246 ComObjPtr<USBController> ctrl;
6247
6248 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6249 if (SUCCEEDED(rc))
6250 ctrl.queryInterfaceTo(aController.asOutParam());
6251
6252 return rc;
6253}
6254
6255HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6256 ULONG *aControllers)
6257{
6258 if ( (aType <= USBControllerType_Null)
6259 || (aType >= USBControllerType_Last))
6260 return setError(E_INVALIDARG,
6261 tr("Invalid USB controller type: %d"),
6262 aType);
6263
6264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6265
6266 ComObjPtr<USBController> ctrl;
6267
6268 *aControllers = i_getUSBControllerCountByType(aType);
6269
6270 return S_OK;
6271}
6272
6273HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6274{
6275
6276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6277
6278 HRESULT rc = i_checkStateDependency(MutableStateDep);
6279 if (FAILED(rc)) return rc;
6280
6281 ComObjPtr<USBController> ctrl;
6282 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6283 if (FAILED(rc)) return rc;
6284
6285 i_setModified(IsModified_USB);
6286 mUSBControllers.backup();
6287
6288 ctrl->i_unshare();
6289
6290 mUSBControllers->remove(ctrl);
6291
6292 /* inform the direct session if any */
6293 alock.release();
6294 i_onUSBControllerChange();
6295
6296 return S_OK;
6297}
6298
6299HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6300 ULONG *aOriginX,
6301 ULONG *aOriginY,
6302 ULONG *aWidth,
6303 ULONG *aHeight,
6304 BOOL *aEnabled)
6305{
6306 uint32_t u32OriginX= 0;
6307 uint32_t u32OriginY= 0;
6308 uint32_t u32Width = 0;
6309 uint32_t u32Height = 0;
6310 uint16_t u16Flags = 0;
6311
6312 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6313 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6314 if (RT_FAILURE(vrc))
6315 {
6316#ifdef RT_OS_WINDOWS
6317 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6318 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6319 * So just assign fEnable to TRUE again.
6320 * The right fix would be to change GUI API wrappers to make sure that parameters
6321 * are changed only if API succeeds.
6322 */
6323 *aEnabled = TRUE;
6324#endif
6325 return setError(VBOX_E_IPRT_ERROR,
6326 tr("Saved guest size is not available (%Rrc)"),
6327 vrc);
6328 }
6329
6330 *aOriginX = u32OriginX;
6331 *aOriginY = u32OriginY;
6332 *aWidth = u32Width;
6333 *aHeight = u32Height;
6334 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6335
6336 return S_OK;
6337}
6338
6339HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6340{
6341 if (aScreenId != 0)
6342 return E_NOTIMPL;
6343
6344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6345
6346 uint8_t *pu8Data = NULL;
6347 uint32_t cbData = 0;
6348 uint32_t u32Width = 0;
6349 uint32_t u32Height = 0;
6350
6351 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6352
6353 if (RT_FAILURE(vrc))
6354 return setError(VBOX_E_IPRT_ERROR,
6355 tr("Saved screenshot data is not available (%Rrc)"),
6356 vrc);
6357
6358 *aSize = cbData;
6359 *aWidth = u32Width;
6360 *aHeight = u32Height;
6361
6362 freeSavedDisplayScreenshot(pu8Data);
6363
6364 return S_OK;
6365}
6366
6367HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6368 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6369{
6370 if (aScreenId != 0)
6371 return E_NOTIMPL;
6372
6373 if ( aBitmapFormat != BitmapFormat_BGR0
6374 && aBitmapFormat != BitmapFormat_BGRA
6375 && aBitmapFormat != BitmapFormat_RGBA
6376 && aBitmapFormat != BitmapFormat_PNG)
6377 return setError(E_NOTIMPL,
6378 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6379
6380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6381
6382 uint8_t *pu8Data = NULL;
6383 uint32_t cbData = 0;
6384 uint32_t u32Width = 0;
6385 uint32_t u32Height = 0;
6386
6387 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6388
6389 if (RT_FAILURE(vrc))
6390 return setError(VBOX_E_IPRT_ERROR,
6391 tr("Saved thumbnail data is not available (%Rrc)"),
6392 vrc);
6393
6394 HRESULT hr = S_OK;
6395
6396 *aWidth = u32Width;
6397 *aHeight = u32Height;
6398
6399 if (cbData > 0)
6400 {
6401 /* Convert pixels to the format expected by the API caller. */
6402 if (aBitmapFormat == BitmapFormat_BGR0)
6403 {
6404 /* [0] B, [1] G, [2] R, [3] 0. */
6405 aData.resize(cbData);
6406 memcpy(&aData.front(), pu8Data, cbData);
6407 }
6408 else if (aBitmapFormat == BitmapFormat_BGRA)
6409 {
6410 /* [0] B, [1] G, [2] R, [3] A. */
6411 aData.resize(cbData);
6412 for (uint32_t i = 0; i < cbData; i += 4)
6413 {
6414 aData[i] = pu8Data[i];
6415 aData[i + 1] = pu8Data[i + 1];
6416 aData[i + 2] = pu8Data[i + 2];
6417 aData[i + 3] = 0xff;
6418 }
6419 }
6420 else if (aBitmapFormat == BitmapFormat_RGBA)
6421 {
6422 /* [0] R, [1] G, [2] B, [3] A. */
6423 aData.resize(cbData);
6424 for (uint32_t i = 0; i < cbData; i += 4)
6425 {
6426 aData[i] = pu8Data[i + 2];
6427 aData[i + 1] = pu8Data[i + 1];
6428 aData[i + 2] = pu8Data[i];
6429 aData[i + 3] = 0xff;
6430 }
6431 }
6432 else if (aBitmapFormat == BitmapFormat_PNG)
6433 {
6434 uint8_t *pu8PNG = NULL;
6435 uint32_t cbPNG = 0;
6436 uint32_t cxPNG = 0;
6437 uint32_t cyPNG = 0;
6438
6439 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6440
6441 if (RT_SUCCESS(vrc))
6442 {
6443 aData.resize(cbPNG);
6444 if (cbPNG)
6445 memcpy(&aData.front(), pu8PNG, cbPNG);
6446 }
6447 else
6448 hr = setError(VBOX_E_IPRT_ERROR,
6449 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6450 vrc);
6451
6452 RTMemFree(pu8PNG);
6453 }
6454 }
6455
6456 freeSavedDisplayScreenshot(pu8Data);
6457
6458 return hr;
6459}
6460
6461HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6462{
6463 if (aScreenId != 0)
6464 return E_NOTIMPL;
6465
6466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6467
6468 uint8_t *pu8Data = NULL;
6469 uint32_t cbData = 0;
6470 uint32_t u32Width = 0;
6471 uint32_t u32Height = 0;
6472
6473 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6474
6475 if (RT_FAILURE(vrc))
6476 return setError(VBOX_E_IPRT_ERROR,
6477 tr("Saved screenshot data is not available (%Rrc)"),
6478 vrc);
6479
6480 *aSize = cbData;
6481 *aWidth = u32Width;
6482 *aHeight = u32Height;
6483
6484 freeSavedDisplayScreenshot(pu8Data);
6485
6486 return S_OK;
6487}
6488
6489HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6490{
6491 if (aScreenId != 0)
6492 return E_NOTIMPL;
6493
6494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6495
6496 uint8_t *pu8Data = NULL;
6497 uint32_t cbData = 0;
6498 uint32_t u32Width = 0;
6499 uint32_t u32Height = 0;
6500
6501 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6502
6503 if (RT_FAILURE(vrc))
6504 return setError(VBOX_E_IPRT_ERROR,
6505 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6506 vrc);
6507
6508 *aWidth = u32Width;
6509 *aHeight = u32Height;
6510
6511 aData.resize(cbData);
6512 if (cbData)
6513 memcpy(&aData.front(), pu8Data, cbData);
6514
6515 freeSavedDisplayScreenshot(pu8Data);
6516
6517 return S_OK;
6518}
6519
6520HRESULT Machine::hotPlugCPU(ULONG aCpu)
6521{
6522 HRESULT rc = S_OK;
6523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6524
6525 if (!mHWData->mCPUHotPlugEnabled)
6526 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6527
6528 if (aCpu >= mHWData->mCPUCount)
6529 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6530
6531 if (mHWData->mCPUAttached[aCpu])
6532 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6533
6534 alock.release();
6535 rc = i_onCPUChange(aCpu, false);
6536 alock.acquire();
6537 if (FAILED(rc)) return rc;
6538
6539 i_setModified(IsModified_MachineData);
6540 mHWData.backup();
6541 mHWData->mCPUAttached[aCpu] = true;
6542
6543 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6544 if (Global::IsOnline(mData->mMachineState))
6545 i_saveSettings(NULL);
6546
6547 return S_OK;
6548}
6549
6550HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6551{
6552 HRESULT rc = S_OK;
6553
6554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 if (!mHWData->mCPUHotPlugEnabled)
6557 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6558
6559 if (aCpu >= SchemaDefs::MaxCPUCount)
6560 return setError(E_INVALIDARG,
6561 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6562 SchemaDefs::MaxCPUCount);
6563
6564 if (!mHWData->mCPUAttached[aCpu])
6565 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6566
6567 /* CPU 0 can't be detached */
6568 if (aCpu == 0)
6569 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6570
6571 alock.release();
6572 rc = i_onCPUChange(aCpu, true);
6573 alock.acquire();
6574 if (FAILED(rc)) return rc;
6575
6576 i_setModified(IsModified_MachineData);
6577 mHWData.backup();
6578 mHWData->mCPUAttached[aCpu] = false;
6579
6580 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6581 if (Global::IsOnline(mData->mMachineState))
6582 i_saveSettings(NULL);
6583
6584 return S_OK;
6585}
6586
6587HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6588{
6589 *aAttached = false;
6590
6591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6592
6593 /* If hotplug is enabled the CPU is always enabled. */
6594 if (!mHWData->mCPUHotPlugEnabled)
6595 {
6596 if (aCpu < mHWData->mCPUCount)
6597 *aAttached = true;
6598 }
6599 else
6600 {
6601 if (aCpu < SchemaDefs::MaxCPUCount)
6602 *aAttached = mHWData->mCPUAttached[aCpu];
6603 }
6604
6605 return S_OK;
6606}
6607
6608HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6609{
6610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6611
6612 Utf8Str log = i_queryLogFilename(aIdx);
6613 if (!RTFileExists(log.c_str()))
6614 log.setNull();
6615 aFilename = log;
6616
6617 return S_OK;
6618}
6619
6620HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6621{
6622 if (aSize < 0)
6623 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6624
6625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6626
6627 HRESULT rc = S_OK;
6628 Utf8Str log = i_queryLogFilename(aIdx);
6629
6630 /* do not unnecessarily hold the lock while doing something which does
6631 * not need the lock and potentially takes a long time. */
6632 alock.release();
6633
6634 /* Limit the chunk size to 32K for now, as that gives better performance
6635 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6636 * One byte expands to approx. 25 bytes of breathtaking XML. */
6637 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6638 aData.resize(cbData);
6639
6640 RTFILE LogFile;
6641 int vrc = RTFileOpen(&LogFile, log.c_str(),
6642 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6643 if (RT_SUCCESS(vrc))
6644 {
6645 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6646 if (RT_SUCCESS(vrc))
6647 aData.resize(cbData);
6648 else
6649 rc = setError(VBOX_E_IPRT_ERROR,
6650 tr("Could not read log file '%s' (%Rrc)"),
6651 log.c_str(), vrc);
6652 RTFileClose(LogFile);
6653 }
6654 else
6655 rc = setError(VBOX_E_IPRT_ERROR,
6656 tr("Could not open log file '%s' (%Rrc)"),
6657 log.c_str(), vrc);
6658
6659 if (FAILED(rc))
6660 aData.resize(0);
6661
6662 return rc;
6663}
6664
6665
6666/**
6667 * Currently this method doesn't attach device to the running VM,
6668 * just makes sure it's plugged on next VM start.
6669 */
6670HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6671{
6672 // lock scope
6673 {
6674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6675
6676 HRESULT rc = i_checkStateDependency(MutableStateDep);
6677 if (FAILED(rc)) return rc;
6678
6679 ChipsetType_T aChipset = ChipsetType_PIIX3;
6680 COMGETTER(ChipsetType)(&aChipset);
6681
6682 if (aChipset != ChipsetType_ICH9)
6683 {
6684 return setError(E_INVALIDARG,
6685 tr("Host PCI attachment only supported with ICH9 chipset"));
6686 }
6687
6688 // check if device with this host PCI address already attached
6689 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6690 it != mHWData->mPCIDeviceAssignments.end();
6691 ++it)
6692 {
6693 LONG iHostAddress = -1;
6694 ComPtr<PCIDeviceAttachment> pAttach;
6695 pAttach = *it;
6696 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6697 if (iHostAddress == aHostAddress)
6698 return setError(E_INVALIDARG,
6699 tr("Device with host PCI address already attached to this VM"));
6700 }
6701
6702 ComObjPtr<PCIDeviceAttachment> pda;
6703 char name[32];
6704
6705 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6706 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6707 Bstr bname(name);
6708 pda.createObject();
6709 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6710 i_setModified(IsModified_MachineData);
6711 mHWData.backup();
6712 mHWData->mPCIDeviceAssignments.push_back(pda);
6713 }
6714
6715 return S_OK;
6716}
6717
6718/**
6719 * Currently this method doesn't detach device from the running VM,
6720 * just makes sure it's not plugged on next VM start.
6721 */
6722HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6723{
6724 ComObjPtr<PCIDeviceAttachment> pAttach;
6725 bool fRemoved = false;
6726 HRESULT rc;
6727
6728 // lock scope
6729 {
6730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6731
6732 rc = i_checkStateDependency(MutableStateDep);
6733 if (FAILED(rc)) return rc;
6734
6735 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6736 it != mHWData->mPCIDeviceAssignments.end();
6737 ++it)
6738 {
6739 LONG iHostAddress = -1;
6740 pAttach = *it;
6741 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6742 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6743 {
6744 i_setModified(IsModified_MachineData);
6745 mHWData.backup();
6746 mHWData->mPCIDeviceAssignments.remove(pAttach);
6747 fRemoved = true;
6748 break;
6749 }
6750 }
6751 }
6752
6753
6754 /* Fire event outside of the lock */
6755 if (fRemoved)
6756 {
6757 Assert(!pAttach.isNull());
6758 ComPtr<IEventSource> es;
6759 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6760 Assert(SUCCEEDED(rc));
6761 Bstr mid;
6762 rc = this->COMGETTER(Id)(mid.asOutParam());
6763 Assert(SUCCEEDED(rc));
6764 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6765 }
6766
6767 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6768 tr("No host PCI device %08x attached"),
6769 aHostAddress
6770 );
6771}
6772
6773HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6774{
6775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6776
6777 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6778
6779 size_t i = 0;
6780 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6781 it != mHWData->mPCIDeviceAssignments.end();
6782 ++i, ++it)
6783 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6784
6785 return S_OK;
6786}
6787
6788HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6789{
6790 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6791
6792 return S_OK;
6793}
6794
6795HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6796{
6797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6798
6799 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6800
6801 return S_OK;
6802}
6803
6804HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6805{
6806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6807 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6808 if (SUCCEEDED(hrc))
6809 {
6810 hrc = mHWData.backupEx();
6811 if (SUCCEEDED(hrc))
6812 {
6813 i_setModified(IsModified_MachineData);
6814 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6815 }
6816 }
6817 return hrc;
6818}
6819
6820HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6821{
6822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6823 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6824 return S_OK;
6825}
6826
6827HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6828{
6829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6830 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6831 if (SUCCEEDED(hrc))
6832 {
6833 hrc = mHWData.backupEx();
6834 if (SUCCEEDED(hrc))
6835 {
6836 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6837 if (SUCCEEDED(hrc))
6838 i_setModified(IsModified_MachineData);
6839 }
6840 }
6841 return hrc;
6842}
6843
6844HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6845{
6846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6847
6848 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6849
6850 return S_OK;
6851}
6852
6853HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6854{
6855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6856 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6857 if (SUCCEEDED(hrc))
6858 {
6859 hrc = mHWData.backupEx();
6860 if (SUCCEEDED(hrc))
6861 {
6862 i_setModified(IsModified_MachineData);
6863 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6864 }
6865 }
6866 return hrc;
6867}
6868
6869HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6870{
6871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6872
6873 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6874
6875 return S_OK;
6876}
6877
6878HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6879{
6880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6881
6882 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6883 if ( SUCCEEDED(hrc)
6884 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6885 {
6886 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6887 int vrc;
6888
6889 if (aAutostartEnabled)
6890 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6891 else
6892 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6893
6894 if (RT_SUCCESS(vrc))
6895 {
6896 hrc = mHWData.backupEx();
6897 if (SUCCEEDED(hrc))
6898 {
6899 i_setModified(IsModified_MachineData);
6900 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6901 }
6902 }
6903 else if (vrc == VERR_NOT_SUPPORTED)
6904 hrc = setError(VBOX_E_NOT_SUPPORTED,
6905 tr("The VM autostart feature is not supported on this platform"));
6906 else if (vrc == VERR_PATH_NOT_FOUND)
6907 hrc = setError(E_FAIL,
6908 tr("The path to the autostart database is not set"));
6909 else
6910 hrc = setError(E_UNEXPECTED,
6911 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6912 aAutostartEnabled ? "Adding" : "Removing",
6913 mUserData->s.strName.c_str(), vrc);
6914 }
6915 return hrc;
6916}
6917
6918HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6919{
6920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6921
6922 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6923
6924 return S_OK;
6925}
6926
6927HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6928{
6929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6930 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6931 if (SUCCEEDED(hrc))
6932 {
6933 hrc = mHWData.backupEx();
6934 if (SUCCEEDED(hrc))
6935 {
6936 i_setModified(IsModified_MachineData);
6937 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6938 }
6939 }
6940 return hrc;
6941}
6942
6943HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6944{
6945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6946
6947 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6948
6949 return S_OK;
6950}
6951
6952HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6953{
6954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6955 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6956 if ( SUCCEEDED(hrc)
6957 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6958 {
6959 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6960 int vrc;
6961
6962 if (aAutostopType != AutostopType_Disabled)
6963 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6964 else
6965 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6966
6967 if (RT_SUCCESS(vrc))
6968 {
6969 hrc = mHWData.backupEx();
6970 if (SUCCEEDED(hrc))
6971 {
6972 i_setModified(IsModified_MachineData);
6973 mHWData->mAutostart.enmAutostopType = aAutostopType;
6974 }
6975 }
6976 else if (vrc == VERR_NOT_SUPPORTED)
6977 hrc = setError(VBOX_E_NOT_SUPPORTED,
6978 tr("The VM autostop feature is not supported on this platform"));
6979 else if (vrc == VERR_PATH_NOT_FOUND)
6980 hrc = setError(E_FAIL,
6981 tr("The path to the autostart database is not set"));
6982 else
6983 hrc = setError(E_UNEXPECTED,
6984 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6985 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6986 mUserData->s.strName.c_str(), vrc);
6987 }
6988 return hrc;
6989}
6990
6991HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6992{
6993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6994
6995 aDefaultFrontend = mHWData->mDefaultFrontend;
6996
6997 return S_OK;
6998}
6999
7000HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7001{
7002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7003 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7004 if (SUCCEEDED(hrc))
7005 {
7006 hrc = mHWData.backupEx();
7007 if (SUCCEEDED(hrc))
7008 {
7009 i_setModified(IsModified_MachineData);
7010 mHWData->mDefaultFrontend = aDefaultFrontend;
7011 }
7012 }
7013 return hrc;
7014}
7015
7016HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7017{
7018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7019 size_t cbIcon = mUserData->mIcon.size();
7020 aIcon.resize(cbIcon);
7021 if (cbIcon)
7022 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7023 return S_OK;
7024}
7025
7026HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7027{
7028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7029 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7030 if (SUCCEEDED(hrc))
7031 {
7032 i_setModified(IsModified_MachineData);
7033 mUserData.backup();
7034 size_t cbIcon = aIcon.size();
7035 mUserData->mIcon.resize(cbIcon);
7036 if (cbIcon)
7037 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7038 }
7039 return hrc;
7040}
7041
7042HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7043{
7044#ifdef VBOX_WITH_USB
7045 *aUSBProxyAvailable = true;
7046#else
7047 *aUSBProxyAvailable = false;
7048#endif
7049 return S_OK;
7050}
7051
7052HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7053 ComPtr<IProgress> &aProgress)
7054{
7055 ComObjPtr<Progress> pP;
7056 Progress *ppP = pP;
7057 IProgress *iP = static_cast<IProgress *>(ppP);
7058 IProgress **pProgress = &iP;
7059
7060 IMachine *pTarget = aTarget;
7061
7062 /* Convert the options. */
7063 RTCList<CloneOptions_T> optList;
7064 if (aOptions.size())
7065 for (size_t i = 0; i < aOptions.size(); ++i)
7066 optList.append(aOptions[i]);
7067
7068 if (optList.contains(CloneOptions_Link))
7069 {
7070 if (!i_isSnapshotMachine())
7071 return setError(E_INVALIDARG,
7072 tr("Linked clone can only be created from a snapshot"));
7073 if (aMode != CloneMode_MachineState)
7074 return setError(E_INVALIDARG,
7075 tr("Linked clone can only be created for a single machine state"));
7076 }
7077 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7078
7079 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7080
7081 HRESULT rc = pWorker->start(pProgress);
7082
7083 pP = static_cast<Progress *>(*pProgress);
7084 pP.queryInterfaceTo(aProgress.asOutParam());
7085
7086 return rc;
7087
7088}
7089
7090// public methods for internal purposes
7091/////////////////////////////////////////////////////////////////////////////
7092
7093/**
7094 * Adds the given IsModified_* flag to the dirty flags of the machine.
7095 * This must be called either during i_loadSettings or under the machine write lock.
7096 * @param fl
7097 */
7098void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7099{
7100 mData->flModifications |= fl;
7101 if (fAllowStateModification && i_isStateModificationAllowed())
7102 mData->mCurrentStateModified = true;
7103}
7104
7105/**
7106 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7107 * care of the write locking.
7108 *
7109 * @param fModifications The flag to add.
7110 */
7111void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7112{
7113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7114 i_setModified(fModification, fAllowStateModification);
7115}
7116
7117/**
7118 * Saves the registry entry of this machine to the given configuration node.
7119 *
7120 * @param aEntryNode Node to save the registry entry to.
7121 *
7122 * @note locks this object for reading.
7123 */
7124HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7125{
7126 AutoLimitedCaller autoCaller(this);
7127 AssertComRCReturnRC(autoCaller.rc());
7128
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 data.uuid = mData->mUuid;
7132 data.strSettingsFile = mData->m_strConfigFile;
7133
7134 return S_OK;
7135}
7136
7137/**
7138 * Calculates the absolute path of the given path taking the directory of the
7139 * machine settings file as the current directory.
7140 *
7141 * @param aPath Path to calculate the absolute path for.
7142 * @param aResult Where to put the result (used only on success, can be the
7143 * same Utf8Str instance as passed in @a aPath).
7144 * @return IPRT result.
7145 *
7146 * @note Locks this object for reading.
7147 */
7148int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7149{
7150 AutoCaller autoCaller(this);
7151 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7152
7153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7156
7157 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7158
7159 strSettingsDir.stripFilename();
7160 char folder[RTPATH_MAX];
7161 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7162 if (RT_SUCCESS(vrc))
7163 aResult = folder;
7164
7165 return vrc;
7166}
7167
7168/**
7169 * Copies strSource to strTarget, making it relative to the machine folder
7170 * if it is a subdirectory thereof, or simply copying it otherwise.
7171 *
7172 * @param strSource Path to evaluate and copy.
7173 * @param strTarget Buffer to receive target path.
7174 *
7175 * @note Locks this object for reading.
7176 */
7177void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7178 Utf8Str &strTarget)
7179{
7180 AutoCaller autoCaller(this);
7181 AssertComRCReturn(autoCaller.rc(), (void)0);
7182
7183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7184
7185 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7186 // use strTarget as a temporary buffer to hold the machine settings dir
7187 strTarget = mData->m_strConfigFileFull;
7188 strTarget.stripFilename();
7189 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7190 {
7191 // is relative: then append what's left
7192 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7193 // for empty paths (only possible for subdirs) use "." to avoid
7194 // triggering default settings for not present config attributes.
7195 if (strTarget.isEmpty())
7196 strTarget = ".";
7197 }
7198 else
7199 // is not relative: then overwrite
7200 strTarget = strSource;
7201}
7202
7203/**
7204 * Returns the full path to the machine's log folder in the
7205 * \a aLogFolder argument.
7206 */
7207void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7208{
7209 AutoCaller autoCaller(this);
7210 AssertComRCReturnVoid(autoCaller.rc());
7211
7212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7213
7214 char szTmp[RTPATH_MAX];
7215 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7216 if (RT_SUCCESS(vrc))
7217 {
7218 if (szTmp[0] && !mUserData.isNull())
7219 {
7220 char szTmp2[RTPATH_MAX];
7221 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7222 if (RT_SUCCESS(vrc))
7223 aLogFolder = BstrFmt("%s%c%s",
7224 szTmp2,
7225 RTPATH_DELIMITER,
7226 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7227 }
7228 else
7229 vrc = VERR_PATH_IS_RELATIVE;
7230 }
7231
7232 if (RT_FAILURE(vrc))
7233 {
7234 // fallback if VBOX_USER_LOGHOME is not set or invalid
7235 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7236 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7237 aLogFolder.append(RTPATH_DELIMITER);
7238 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7239 }
7240}
7241
7242/**
7243 * Returns the full path to the machine's log file for an given index.
7244 */
7245Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7246 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7247{
7248 Utf8Str logFolder;
7249 getLogFolder(logFolder);
7250 Assert(logFolder.length());
7251 Utf8Str log;
7252 if (idx == 0)
7253 log = Utf8StrFmt("%s%cVBox.log",
7254 logFolder.c_str(), RTPATH_DELIMITER);
7255 else
7256 log = Utf8StrFmt("%s%cVBox.log.%d",
7257 logFolder.c_str(), RTPATH_DELIMITER, idx);
7258 return log;
7259}
7260
7261/**
7262 * Returns the full path to the machine's (hardened) startup log file.
7263 */
7264Utf8Str Machine::i_getStartupLogFilename(void)
7265{
7266 Utf8Str strFilename;
7267 getLogFolder(strFilename);
7268 Assert(strFilename.length());
7269 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7270 return strFilename;
7271}
7272
7273
7274/**
7275 * Composes a unique saved state filename based on the current system time. The filename is
7276 * granular to the second so this will work so long as no more than one snapshot is taken on
7277 * a machine per second.
7278 *
7279 * Before version 4.1, we used this formula for saved state files:
7280 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7281 * which no longer works because saved state files can now be shared between the saved state of the
7282 * "saved" machine and an online snapshot, and the following would cause problems:
7283 * 1) save machine
7284 * 2) create online snapshot from that machine state --> reusing saved state file
7285 * 3) save machine again --> filename would be reused, breaking the online snapshot
7286 *
7287 * So instead we now use a timestamp.
7288 *
7289 * @param str
7290 */
7291
7292void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7293{
7294 AutoCaller autoCaller(this);
7295 AssertComRCReturnVoid(autoCaller.rc());
7296
7297 {
7298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7299 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7300 }
7301
7302 RTTIMESPEC ts;
7303 RTTimeNow(&ts);
7304 RTTIME time;
7305 RTTimeExplode(&time, &ts);
7306
7307 strStateFilePath += RTPATH_DELIMITER;
7308 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7309 time.i32Year, time.u8Month, time.u8MonthDay,
7310 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7311}
7312
7313/**
7314 * Returns the full path to the default video capture file.
7315 */
7316void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7317{
7318 AutoCaller autoCaller(this);
7319 AssertComRCReturnVoid(autoCaller.rc());
7320
7321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7322
7323 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7324 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7325 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7326}
7327
7328/**
7329 * Returns whether at least one USB controller is present for the VM.
7330 */
7331bool Machine::i_isUSBControllerPresent()
7332{
7333 AutoCaller autoCaller(this);
7334 AssertComRCReturn(autoCaller.rc(), false);
7335
7336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7337
7338 return (mUSBControllers->size() > 0);
7339}
7340
7341/**
7342 * @note Locks this object for writing, calls the client process
7343 * (inside the lock).
7344 */
7345HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7346 const Utf8Str &strFrontend,
7347 const Utf8Str &strEnvironment,
7348 ProgressProxy *aProgress)
7349{
7350 LogFlowThisFuncEnter();
7351
7352 AssertReturn(aControl, E_FAIL);
7353 AssertReturn(aProgress, E_FAIL);
7354 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7355
7356 AutoCaller autoCaller(this);
7357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7358
7359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7360
7361 if (!mData->mRegistered)
7362 return setError(E_UNEXPECTED,
7363 tr("The machine '%s' is not registered"),
7364 mUserData->s.strName.c_str());
7365
7366 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7367
7368 /* The process started when launching a VM with separate UI/VM processes is always
7369 * the UI process, i.e. needs special handling as it won't claim the session. */
7370 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7371
7372 if (fSeparate)
7373 {
7374 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mType.compare("headless", Utf8Str::CaseInsensitive))
7375 return setError(VBOX_E_INVALID_OBJECT_STATE,
7376 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7377 mUserData->s.strName.c_str());
7378 }
7379 else
7380 {
7381 if ( mData->mSession.mState == SessionState_Locked
7382 || mData->mSession.mState == SessionState_Spawning
7383 || mData->mSession.mState == SessionState_Unlocking)
7384 return setError(VBOX_E_INVALID_OBJECT_STATE,
7385 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7386 mUserData->s.strName.c_str());
7387
7388 /* may not be busy */
7389 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7390 }
7391
7392 /* get the path to the executable */
7393 char szPath[RTPATH_MAX];
7394 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7395 size_t cchBufLeft = strlen(szPath);
7396 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7397 szPath[cchBufLeft] = 0;
7398 char *pszNamePart = szPath + cchBufLeft;
7399 cchBufLeft = sizeof(szPath) - cchBufLeft;
7400
7401 int vrc = VINF_SUCCESS;
7402 RTPROCESS pid = NIL_RTPROCESS;
7403
7404 RTENV env = RTENV_DEFAULT;
7405
7406 if (!strEnvironment.isEmpty())
7407 {
7408 char *newEnvStr = NULL;
7409
7410 do
7411 {
7412 /* clone the current environment */
7413 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7414 AssertRCBreakStmt(vrc2, vrc = vrc2);
7415
7416 newEnvStr = RTStrDup(strEnvironment.c_str());
7417 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7418
7419 /* put new variables to the environment
7420 * (ignore empty variable names here since RTEnv API
7421 * intentionally doesn't do that) */
7422 char *var = newEnvStr;
7423 for (char *p = newEnvStr; *p; ++p)
7424 {
7425 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7426 {
7427 *p = '\0';
7428 if (*var)
7429 {
7430 char *val = strchr(var, '=');
7431 if (val)
7432 {
7433 *val++ = '\0';
7434 vrc2 = RTEnvSetEx(env, var, val);
7435 }
7436 else
7437 vrc2 = RTEnvUnsetEx(env, var);
7438 if (RT_FAILURE(vrc2))
7439 break;
7440 }
7441 var = p + 1;
7442 }
7443 }
7444 if (RT_SUCCESS(vrc2) && *var)
7445 vrc2 = RTEnvPutEx(env, var);
7446
7447 AssertRCBreakStmt(vrc2, vrc = vrc2);
7448 }
7449 while (0);
7450
7451 if (newEnvStr != NULL)
7452 RTStrFree(newEnvStr);
7453 }
7454
7455 /* Hardened startup logging */
7456#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7457 Utf8Str strSupStartLogArg("--sup-startup-log=");
7458 {
7459 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7460 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7461 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7462 {
7463 Utf8Str strStartupLogDir = strStartupLogFile;
7464 strStartupLogDir.stripFilename();
7465 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7466 file without stripping the file. */
7467 }
7468 strSupStartLogArg.append(strStartupLogFile);
7469 }
7470 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7471#else
7472 const char *pszSupStartupLogArg = NULL;
7473#endif
7474
7475
7476#ifdef VBOX_WITH_QTGUI
7477 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7478 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7479 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7480 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7481 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7482 {
7483# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7484 /* Modify the base path so that we don't need to use ".." below. */
7485 RTPathStripTrailingSlash(szPath);
7486 RTPathStripFilename(szPath);
7487 cchBufLeft = strlen(szPath);
7488 pszNamePart = szPath + cchBufLeft;
7489 cchBufLeft = sizeof(szPath) - cchBufLeft;
7490
7491# define OSX_APP_NAME "VirtualBoxVM"
7492# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7493
7494 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7495 if ( strAppOverride.contains(".")
7496 || strAppOverride.contains("/")
7497 || strAppOverride.contains("\\")
7498 || strAppOverride.contains(":"))
7499 strAppOverride.setNull();
7500 Utf8Str strAppPath;
7501 if (!strAppOverride.isEmpty())
7502 {
7503 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7504 Utf8Str strFullPath(szPath);
7505 strFullPath.append(strAppPath);
7506 /* there is a race, but people using this deserve the failure */
7507 if (!RTFileExists(strFullPath.c_str()))
7508 strAppOverride.setNull();
7509 }
7510 if (strAppOverride.isEmpty())
7511 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7512 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7513 strcpy(pszNamePart, strAppPath.c_str());
7514# else
7515 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7516 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7517 strcpy(pszNamePart, s_szVirtualBox_exe);
7518# endif
7519
7520 Utf8Str idStr = mData->mUuid.toString();
7521 const char *apszArgs[] =
7522 {
7523 szPath,
7524 "--comment", mUserData->s.strName.c_str(),
7525 "--startvm", idStr.c_str(),
7526 "--no-startvm-errormsgbox",
7527 NULL, /* For "--separate". */
7528 NULL, /* For "--sup-startup-log". */
7529 NULL
7530 };
7531 unsigned iArg = 6;
7532 if (fSeparate)
7533 apszArgs[iArg++] = "--separate";
7534 apszArgs[iArg++] = pszSupStartupLogArg;
7535
7536 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7537 }
7538#else /* !VBOX_WITH_QTGUI */
7539 if (0)
7540 ;
7541#endif /* VBOX_WITH_QTGUI */
7542
7543 else
7544
7545#ifdef VBOX_WITH_VBOXSDL
7546 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7547 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7548 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7549 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7550 {
7551 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7552 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7553 strcpy(pszNamePart, s_szVBoxSDL_exe);
7554
7555 Utf8Str idStr = mData->mUuid.toString();
7556 const char *apszArgs[] =
7557 {
7558 szPath,
7559 "--comment", mUserData->s.strName.c_str(),
7560 "--startvm", idStr.c_str(),
7561 NULL, /* For "--separate". */
7562 NULL, /* For "--sup-startup-log". */
7563 NULL
7564 };
7565 unsigned iArg = 5;
7566 if (fSeparate)
7567 apszArgs[iArg++] = "--separate";
7568 apszArgs[iArg++] = pszSupStartupLogArg;
7569
7570 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7571 }
7572#else /* !VBOX_WITH_VBOXSDL */
7573 if (0)
7574 ;
7575#endif /* !VBOX_WITH_VBOXSDL */
7576
7577 else
7578
7579#ifdef VBOX_WITH_HEADLESS
7580 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7581 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7582 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7583 )
7584 {
7585 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7586 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7587 * and a VM works even if the server has not been installed.
7588 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7589 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7590 * differently in 4.0 and 3.x.
7591 */
7592 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7593 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7594 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7595
7596 Utf8Str idStr = mData->mUuid.toString();
7597 const char *apszArgs[] =
7598 {
7599 szPath,
7600 "--comment", mUserData->s.strName.c_str(),
7601 "--startvm", idStr.c_str(),
7602 "--vrde", "config",
7603 NULL, /* For "--capture". */
7604 NULL, /* For "--sup-startup-log". */
7605 NULL
7606 };
7607 unsigned iArg = 7;
7608 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7609 apszArgs[iArg++] = "--capture";
7610 apszArgs[iArg++] = pszSupStartupLogArg;
7611
7612# ifdef RT_OS_WINDOWS
7613 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7614# else
7615 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7616# endif
7617 }
7618#else /* !VBOX_WITH_HEADLESS */
7619 if (0)
7620 ;
7621#endif /* !VBOX_WITH_HEADLESS */
7622 else
7623 {
7624 RTEnvDestroy(env);
7625 return setError(E_INVALIDARG,
7626 tr("Invalid frontend name: '%s'"),
7627 strFrontend.c_str());
7628 }
7629
7630 RTEnvDestroy(env);
7631
7632 if (RT_FAILURE(vrc))
7633 return setError(VBOX_E_IPRT_ERROR,
7634 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7635 mUserData->s.strName.c_str(), vrc);
7636
7637 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7638
7639 if (!fSeparate)
7640 {
7641 /*
7642 * Note that we don't release the lock here before calling the client,
7643 * because it doesn't need to call us back if called with a NULL argument.
7644 * Releasing the lock here is dangerous because we didn't prepare the
7645 * launch data yet, but the client we've just started may happen to be
7646 * too fast and call LockMachine() that will fail (because of PID, etc.),
7647 * so that the Machine will never get out of the Spawning session state.
7648 */
7649
7650 /* inform the session that it will be a remote one */
7651 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7652#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7653 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7654#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7655 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7656#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7657 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7658
7659 if (FAILED(rc))
7660 {
7661 /* restore the session state */
7662 mData->mSession.mState = SessionState_Unlocked;
7663 alock.release();
7664 mParent->i_addProcessToReap(pid);
7665 /* The failure may occur w/o any error info (from RPC), so provide one */
7666 return setError(VBOX_E_VM_ERROR,
7667 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7668 }
7669
7670 /* attach launch data to the machine */
7671 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7672 mData->mSession.mRemoteControls.push_back(aControl);
7673 mData->mSession.mProgress = aProgress;
7674 mData->mSession.mPID = pid;
7675 mData->mSession.mState = SessionState_Spawning;
7676 mData->mSession.mType = strFrontend;
7677 }
7678 else
7679 {
7680 /* For separate UI process we declare the launch as completed instantly, as the
7681 * actual headless VM start may or may not come. No point in remembering anything
7682 * yet, as what matters for us is when the headless VM gets started. */
7683 aProgress->i_notifyComplete(S_OK);
7684 }
7685
7686 alock.release();
7687 mParent->i_addProcessToReap(pid);
7688
7689 LogFlowThisFuncLeave();
7690 return S_OK;
7691}
7692
7693/**
7694 * Returns @c true if the given session machine instance has an open direct
7695 * session (and optionally also for direct sessions which are closing) and
7696 * returns the session control machine instance if so.
7697 *
7698 * Note that when the method returns @c false, the arguments remain unchanged.
7699 *
7700 * @param aMachine Session machine object.
7701 * @param aControl Direct session control object (optional).
7702 * @param aAllowClosing If true then additionally a session which is currently
7703 * being closed will also be allowed.
7704 *
7705 * @note locks this object for reading.
7706 */
7707bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7708 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7709 bool aAllowClosing /*= false*/)
7710{
7711 AutoLimitedCaller autoCaller(this);
7712 AssertComRCReturn(autoCaller.rc(), false);
7713
7714 /* just return false for inaccessible machines */
7715 if (getObjectState().getState() != ObjectState::Ready)
7716 return false;
7717
7718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7719
7720 if ( mData->mSession.mState == SessionState_Locked
7721 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7722 )
7723 {
7724 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7725
7726 aMachine = mData->mSession.mMachine;
7727
7728 if (aControl != NULL)
7729 *aControl = mData->mSession.mDirectControl;
7730
7731 return true;
7732 }
7733
7734 return false;
7735}
7736
7737/**
7738 * Returns @c true if the given machine has an spawning direct session.
7739 *
7740 * @note locks this object for reading.
7741 */
7742bool Machine::i_isSessionSpawning()
7743{
7744 AutoLimitedCaller autoCaller(this);
7745 AssertComRCReturn(autoCaller.rc(), false);
7746
7747 /* just return false for inaccessible machines */
7748 if (getObjectState().getState() != ObjectState::Ready)
7749 return false;
7750
7751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7752
7753 if (mData->mSession.mState == SessionState_Spawning)
7754 return true;
7755
7756 return false;
7757}
7758
7759/**
7760 * Called from the client watcher thread to check for unexpected client process
7761 * death during Session_Spawning state (e.g. before it successfully opened a
7762 * direct session).
7763 *
7764 * On Win32 and on OS/2, this method is called only when we've got the
7765 * direct client's process termination notification, so it always returns @c
7766 * true.
7767 *
7768 * On other platforms, this method returns @c true if the client process is
7769 * terminated and @c false if it's still alive.
7770 *
7771 * @note Locks this object for writing.
7772 */
7773bool Machine::i_checkForSpawnFailure()
7774{
7775 AutoCaller autoCaller(this);
7776 if (!autoCaller.isOk())
7777 {
7778 /* nothing to do */
7779 LogFlowThisFunc(("Already uninitialized!\n"));
7780 return true;
7781 }
7782
7783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7784
7785 if (mData->mSession.mState != SessionState_Spawning)
7786 {
7787 /* nothing to do */
7788 LogFlowThisFunc(("Not spawning any more!\n"));
7789 return true;
7790 }
7791
7792 HRESULT rc = S_OK;
7793
7794 /* PID not yet initialized, skip check. */
7795 if (mData->mSession.mPID == NIL_RTPROCESS)
7796 return false;
7797
7798 RTPROCSTATUS status;
7799 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7800
7801 if (vrc != VERR_PROCESS_RUNNING)
7802 {
7803 Utf8Str strExtraInfo;
7804
7805#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7806 /* If the startup logfile exists and is of non-zero length, tell the
7807 user to look there for more details to encourage them to attach it
7808 when reporting startup issues. */
7809 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7810 uint64_t cbStartupLogFile = 0;
7811 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7812 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7813 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7814#endif
7815
7816 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7817 rc = setError(E_FAIL,
7818 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7819 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7820 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7821 rc = setError(E_FAIL,
7822 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7823 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7824 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7825 rc = setError(E_FAIL,
7826 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7827 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7828 else
7829 rc = setError(E_FAIL,
7830 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7831 i_getName().c_str(), vrc, strExtraInfo.c_str());
7832 }
7833
7834 if (FAILED(rc))
7835 {
7836 /* Close the remote session, remove the remote control from the list
7837 * and reset session state to Closed (@note keep the code in sync with
7838 * the relevant part in LockMachine()). */
7839
7840 Assert(mData->mSession.mRemoteControls.size() == 1);
7841 if (mData->mSession.mRemoteControls.size() == 1)
7842 {
7843 ErrorInfoKeeper eik;
7844 mData->mSession.mRemoteControls.front()->Uninitialize();
7845 }
7846
7847 mData->mSession.mRemoteControls.clear();
7848 mData->mSession.mState = SessionState_Unlocked;
7849
7850 /* finalize the progress after setting the state */
7851 if (!mData->mSession.mProgress.isNull())
7852 {
7853 mData->mSession.mProgress->notifyComplete(rc);
7854 mData->mSession.mProgress.setNull();
7855 }
7856
7857 mData->mSession.mPID = NIL_RTPROCESS;
7858
7859 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7860 return true;
7861 }
7862
7863 return false;
7864}
7865
7866/**
7867 * Checks whether the machine can be registered. If so, commits and saves
7868 * all settings.
7869 *
7870 * @note Must be called from mParent's write lock. Locks this object and
7871 * children for writing.
7872 */
7873HRESULT Machine::i_prepareRegister()
7874{
7875 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7876
7877 AutoLimitedCaller autoCaller(this);
7878 AssertComRCReturnRC(autoCaller.rc());
7879
7880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7881
7882 /* wait for state dependents to drop to zero */
7883 i_ensureNoStateDependencies();
7884
7885 if (!mData->mAccessible)
7886 return setError(VBOX_E_INVALID_OBJECT_STATE,
7887 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7888 mUserData->s.strName.c_str(),
7889 mData->mUuid.toString().c_str());
7890
7891 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7892
7893 if (mData->mRegistered)
7894 return setError(VBOX_E_INVALID_OBJECT_STATE,
7895 tr("The machine '%s' with UUID {%s} is already registered"),
7896 mUserData->s.strName.c_str(),
7897 mData->mUuid.toString().c_str());
7898
7899 HRESULT rc = S_OK;
7900
7901 // Ensure the settings are saved. If we are going to be registered and
7902 // no config file exists yet, create it by calling i_saveSettings() too.
7903 if ( (mData->flModifications)
7904 || (!mData->pMachineConfigFile->fileExists())
7905 )
7906 {
7907 rc = i_saveSettings(NULL);
7908 // no need to check whether VirtualBox.xml needs saving too since
7909 // we can't have a machine XML file rename pending
7910 if (FAILED(rc)) return rc;
7911 }
7912
7913 /* more config checking goes here */
7914
7915 if (SUCCEEDED(rc))
7916 {
7917 /* we may have had implicit modifications we want to fix on success */
7918 i_commit();
7919
7920 mData->mRegistered = true;
7921 }
7922 else
7923 {
7924 /* we may have had implicit modifications we want to cancel on failure*/
7925 i_rollback(false /* aNotify */);
7926 }
7927
7928 return rc;
7929}
7930
7931/**
7932 * Increases the number of objects dependent on the machine state or on the
7933 * registered state. Guarantees that these two states will not change at least
7934 * until #releaseStateDependency() is called.
7935 *
7936 * Depending on the @a aDepType value, additional state checks may be made.
7937 * These checks will set extended error info on failure. See
7938 * #checkStateDependency() for more info.
7939 *
7940 * If this method returns a failure, the dependency is not added and the caller
7941 * is not allowed to rely on any particular machine state or registration state
7942 * value and may return the failed result code to the upper level.
7943 *
7944 * @param aDepType Dependency type to add.
7945 * @param aState Current machine state (NULL if not interested).
7946 * @param aRegistered Current registered state (NULL if not interested).
7947 *
7948 * @note Locks this object for writing.
7949 */
7950HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7951 MachineState_T *aState /* = NULL */,
7952 BOOL *aRegistered /* = NULL */)
7953{
7954 AutoCaller autoCaller(this);
7955 AssertComRCReturnRC(autoCaller.rc());
7956
7957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7958
7959 HRESULT rc = i_checkStateDependency(aDepType);
7960 if (FAILED(rc)) return rc;
7961
7962 {
7963 if (mData->mMachineStateChangePending != 0)
7964 {
7965 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7966 * drop to zero so don't add more. It may make sense to wait a bit
7967 * and retry before reporting an error (since the pending state
7968 * transition should be really quick) but let's just assert for
7969 * now to see if it ever happens on practice. */
7970
7971 AssertFailed();
7972
7973 return setError(E_ACCESSDENIED,
7974 tr("Machine state change is in progress. Please retry the operation later."));
7975 }
7976
7977 ++mData->mMachineStateDeps;
7978 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7979 }
7980
7981 if (aState)
7982 *aState = mData->mMachineState;
7983 if (aRegistered)
7984 *aRegistered = mData->mRegistered;
7985
7986 return S_OK;
7987}
7988
7989/**
7990 * Decreases the number of objects dependent on the machine state.
7991 * Must always complete the #addStateDependency() call after the state
7992 * dependency is no more necessary.
7993 */
7994void Machine::i_releaseStateDependency()
7995{
7996 AutoCaller autoCaller(this);
7997 AssertComRCReturnVoid(autoCaller.rc());
7998
7999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8000
8001 /* releaseStateDependency() w/o addStateDependency()? */
8002 AssertReturnVoid(mData->mMachineStateDeps != 0);
8003 -- mData->mMachineStateDeps;
8004
8005 if (mData->mMachineStateDeps == 0)
8006 {
8007 /* inform i_ensureNoStateDependencies() that there are no more deps */
8008 if (mData->mMachineStateChangePending != 0)
8009 {
8010 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8011 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8012 }
8013 }
8014}
8015
8016Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8017{
8018 /* start with nothing found */
8019 Utf8Str strResult("");
8020
8021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8022
8023 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8024 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8025 // found:
8026 strResult = it->second; // source is a Utf8Str
8027
8028 return strResult;
8029}
8030
8031// protected methods
8032/////////////////////////////////////////////////////////////////////////////
8033
8034/**
8035 * Performs machine state checks based on the @a aDepType value. If a check
8036 * fails, this method will set extended error info, otherwise it will return
8037 * S_OK. It is supposed, that on failure, the caller will immediately return
8038 * the return value of this method to the upper level.
8039 *
8040 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8041 *
8042 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8043 * current state of this machine object allows to change settings of the
8044 * machine (i.e. the machine is not registered, or registered but not running
8045 * and not saved). It is useful to call this method from Machine setters
8046 * before performing any change.
8047 *
8048 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8049 * as for MutableStateDep except that if the machine is saved, S_OK is also
8050 * returned. This is useful in setters which allow changing machine
8051 * properties when it is in the saved state.
8052 *
8053 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8054 * if the current state of this machine object allows to change runtime
8055 * changeable settings of the machine (i.e. the machine is not registered, or
8056 * registered but either running or not running and not saved). It is useful
8057 * to call this method from Machine setters before performing any changes to
8058 * runtime changeable settings.
8059 *
8060 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8061 * the same as for MutableOrRunningStateDep except that if the machine is
8062 * saved, S_OK is also returned. This is useful in setters which allow
8063 * changing runtime and saved state changeable machine properties.
8064 *
8065 * @param aDepType Dependency type to check.
8066 *
8067 * @note Non Machine based classes should use #addStateDependency() and
8068 * #releaseStateDependency() methods or the smart AutoStateDependency
8069 * template.
8070 *
8071 * @note This method must be called from under this object's read or write
8072 * lock.
8073 */
8074HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8075{
8076 switch (aDepType)
8077 {
8078 case AnyStateDep:
8079 {
8080 break;
8081 }
8082 case MutableStateDep:
8083 {
8084 if ( mData->mRegistered
8085 && ( !i_isSessionMachine()
8086 || ( mData->mMachineState != MachineState_Aborted
8087 && mData->mMachineState != MachineState_Teleported
8088 && mData->mMachineState != MachineState_PoweredOff
8089 )
8090 )
8091 )
8092 return setError(VBOX_E_INVALID_VM_STATE,
8093 tr("The machine is not mutable (state is %s)"),
8094 Global::stringifyMachineState(mData->mMachineState));
8095 break;
8096 }
8097 case MutableOrSavedStateDep:
8098 {
8099 if ( mData->mRegistered
8100 && ( !i_isSessionMachine()
8101 || ( mData->mMachineState != MachineState_Aborted
8102 && mData->mMachineState != MachineState_Teleported
8103 && mData->mMachineState != MachineState_Saved
8104 && mData->mMachineState != MachineState_PoweredOff
8105 )
8106 )
8107 )
8108 return setError(VBOX_E_INVALID_VM_STATE,
8109 tr("The machine is not mutable (state is %s)"),
8110 Global::stringifyMachineState(mData->mMachineState));
8111 break;
8112 }
8113 case MutableOrRunningStateDep:
8114 {
8115 if ( mData->mRegistered
8116 && ( !i_isSessionMachine()
8117 || ( mData->mMachineState != MachineState_Aborted
8118 && mData->mMachineState != MachineState_Teleported
8119 && mData->mMachineState != MachineState_PoweredOff
8120 && !Global::IsOnline(mData->mMachineState)
8121 )
8122 )
8123 )
8124 return setError(VBOX_E_INVALID_VM_STATE,
8125 tr("The machine is not mutable (state is %s)"),
8126 Global::stringifyMachineState(mData->mMachineState));
8127 break;
8128 }
8129 case MutableOrSavedOrRunningStateDep:
8130 {
8131 if ( mData->mRegistered
8132 && ( !i_isSessionMachine()
8133 || ( mData->mMachineState != MachineState_Aborted
8134 && mData->mMachineState != MachineState_Teleported
8135 && mData->mMachineState != MachineState_Saved
8136 && mData->mMachineState != MachineState_PoweredOff
8137 && !Global::IsOnline(mData->mMachineState)
8138 )
8139 )
8140 )
8141 return setError(VBOX_E_INVALID_VM_STATE,
8142 tr("The machine is not mutable (state is %s)"),
8143 Global::stringifyMachineState(mData->mMachineState));
8144 break;
8145 }
8146 }
8147
8148 return S_OK;
8149}
8150
8151/**
8152 * Helper to initialize all associated child objects and allocate data
8153 * structures.
8154 *
8155 * This method must be called as a part of the object's initialization procedure
8156 * (usually done in the #init() method).
8157 *
8158 * @note Must be called only from #init() or from #registeredInit().
8159 */
8160HRESULT Machine::initDataAndChildObjects()
8161{
8162 AutoCaller autoCaller(this);
8163 AssertComRCReturnRC(autoCaller.rc());
8164 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8165 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8166
8167 AssertReturn(!mData->mAccessible, E_FAIL);
8168
8169 /* allocate data structures */
8170 mSSData.allocate();
8171 mUserData.allocate();
8172 mHWData.allocate();
8173 mMediaData.allocate();
8174 mStorageControllers.allocate();
8175 mUSBControllers.allocate();
8176
8177 /* initialize mOSTypeId */
8178 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8179
8180 /* create associated BIOS settings object */
8181 unconst(mBIOSSettings).createObject();
8182 mBIOSSettings->init(this);
8183
8184 /* create an associated VRDE object (default is disabled) */
8185 unconst(mVRDEServer).createObject();
8186 mVRDEServer->init(this);
8187
8188 /* create associated serial port objects */
8189 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8190 {
8191 unconst(mSerialPorts[slot]).createObject();
8192 mSerialPorts[slot]->init(this, slot);
8193 }
8194
8195 /* create associated parallel port objects */
8196 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8197 {
8198 unconst(mParallelPorts[slot]).createObject();
8199 mParallelPorts[slot]->init(this, slot);
8200 }
8201
8202 /* create the audio adapter object (always present, default is disabled) */
8203 unconst(mAudioAdapter).createObject();
8204 mAudioAdapter->init(this);
8205
8206 /* create the USB device filters object (always present) */
8207 unconst(mUSBDeviceFilters).createObject();
8208 mUSBDeviceFilters->init(this);
8209
8210 /* create associated network adapter objects */
8211 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8212 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8213 {
8214 unconst(mNetworkAdapters[slot]).createObject();
8215 mNetworkAdapters[slot]->init(this, slot);
8216 }
8217
8218 /* create the bandwidth control */
8219 unconst(mBandwidthControl).createObject();
8220 mBandwidthControl->init(this);
8221
8222 return S_OK;
8223}
8224
8225/**
8226 * Helper to uninitialize all associated child objects and to free all data
8227 * structures.
8228 *
8229 * This method must be called as a part of the object's uninitialization
8230 * procedure (usually done in the #uninit() method).
8231 *
8232 * @note Must be called only from #uninit() or from #registeredInit().
8233 */
8234void Machine::uninitDataAndChildObjects()
8235{
8236 AutoCaller autoCaller(this);
8237 AssertComRCReturnVoid(autoCaller.rc());
8238 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8239 || getObjectState().getState() == ObjectState::Limited);
8240
8241 /* tell all our other child objects we've been uninitialized */
8242 if (mBandwidthControl)
8243 {
8244 mBandwidthControl->uninit();
8245 unconst(mBandwidthControl).setNull();
8246 }
8247
8248 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8249 {
8250 if (mNetworkAdapters[slot])
8251 {
8252 mNetworkAdapters[slot]->uninit();
8253 unconst(mNetworkAdapters[slot]).setNull();
8254 }
8255 }
8256
8257 if (mUSBDeviceFilters)
8258 {
8259 mUSBDeviceFilters->uninit();
8260 unconst(mUSBDeviceFilters).setNull();
8261 }
8262
8263 if (mAudioAdapter)
8264 {
8265 mAudioAdapter->uninit();
8266 unconst(mAudioAdapter).setNull();
8267 }
8268
8269 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8270 {
8271 if (mParallelPorts[slot])
8272 {
8273 mParallelPorts[slot]->uninit();
8274 unconst(mParallelPorts[slot]).setNull();
8275 }
8276 }
8277
8278 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8279 {
8280 if (mSerialPorts[slot])
8281 {
8282 mSerialPorts[slot]->uninit();
8283 unconst(mSerialPorts[slot]).setNull();
8284 }
8285 }
8286
8287 if (mVRDEServer)
8288 {
8289 mVRDEServer->uninit();
8290 unconst(mVRDEServer).setNull();
8291 }
8292
8293 if (mBIOSSettings)
8294 {
8295 mBIOSSettings->uninit();
8296 unconst(mBIOSSettings).setNull();
8297 }
8298
8299 /* Deassociate media (only when a real Machine or a SnapshotMachine
8300 * instance is uninitialized; SessionMachine instances refer to real
8301 * Machine media). This is necessary for a clean re-initialization of
8302 * the VM after successfully re-checking the accessibility state. Note
8303 * that in case of normal Machine or SnapshotMachine uninitialization (as
8304 * a result of unregistering or deleting the snapshot), outdated media
8305 * attachments will already be uninitialized and deleted, so this
8306 * code will not affect them. */
8307 if ( !!mMediaData
8308 && (!i_isSessionMachine())
8309 )
8310 {
8311 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8312 it != mMediaData->mAttachments.end();
8313 ++it)
8314 {
8315 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8316 if (pMedium.isNull())
8317 continue;
8318 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8319 AssertComRC(rc);
8320 }
8321 }
8322
8323 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8324 {
8325 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8326 if (mData->mFirstSnapshot)
8327 {
8328 // snapshots tree is protected by machine write lock; strictly
8329 // this isn't necessary here since we're deleting the entire
8330 // machine, but otherwise we assert in Snapshot::uninit()
8331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8332 mData->mFirstSnapshot->uninit();
8333 mData->mFirstSnapshot.setNull();
8334 }
8335
8336 mData->mCurrentSnapshot.setNull();
8337 }
8338
8339 /* free data structures (the essential mData structure is not freed here
8340 * since it may be still in use) */
8341 mMediaData.free();
8342 mStorageControllers.free();
8343 mUSBControllers.free();
8344 mHWData.free();
8345 mUserData.free();
8346 mSSData.free();
8347}
8348
8349/**
8350 * Returns a pointer to the Machine object for this machine that acts like a
8351 * parent for complex machine data objects such as shared folders, etc.
8352 *
8353 * For primary Machine objects and for SnapshotMachine objects, returns this
8354 * object's pointer itself. For SessionMachine objects, returns the peer
8355 * (primary) machine pointer.
8356 */
8357Machine* Machine::i_getMachine()
8358{
8359 if (i_isSessionMachine())
8360 return (Machine*)mPeer;
8361 return this;
8362}
8363
8364/**
8365 * Makes sure that there are no machine state dependents. If necessary, waits
8366 * for the number of dependents to drop to zero.
8367 *
8368 * Make sure this method is called from under this object's write lock to
8369 * guarantee that no new dependents may be added when this method returns
8370 * control to the caller.
8371 *
8372 * @note Locks this object for writing. The lock will be released while waiting
8373 * (if necessary).
8374 *
8375 * @warning To be used only in methods that change the machine state!
8376 */
8377void Machine::i_ensureNoStateDependencies()
8378{
8379 AssertReturnVoid(isWriteLockOnCurrentThread());
8380
8381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8382
8383 /* Wait for all state dependents if necessary */
8384 if (mData->mMachineStateDeps != 0)
8385 {
8386 /* lazy semaphore creation */
8387 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8388 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8389
8390 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8391 mData->mMachineStateDeps));
8392
8393 ++mData->mMachineStateChangePending;
8394
8395 /* reset the semaphore before waiting, the last dependent will signal
8396 * it */
8397 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8398
8399 alock.release();
8400
8401 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8402
8403 alock.acquire();
8404
8405 -- mData->mMachineStateChangePending;
8406 }
8407}
8408
8409/**
8410 * Changes the machine state and informs callbacks.
8411 *
8412 * This method is not intended to fail so it either returns S_OK or asserts (and
8413 * returns a failure).
8414 *
8415 * @note Locks this object for writing.
8416 */
8417HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8418{
8419 LogFlowThisFuncEnter();
8420 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8421
8422 AutoCaller autoCaller(this);
8423 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8424
8425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8426
8427 /* wait for state dependents to drop to zero */
8428 i_ensureNoStateDependencies();
8429
8430 MachineState_T const enmOldState = mData->mMachineState;
8431 if (enmOldState != aMachineState)
8432 {
8433 mData->mMachineState = aMachineState;
8434 RTTimeNow(&mData->mLastStateChange);
8435
8436#ifdef VBOX_WITH_DTRACE_R3_MAIN
8437 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8438#endif
8439 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8440 }
8441
8442 LogFlowThisFuncLeave();
8443 return S_OK;
8444}
8445
8446/**
8447 * Searches for a shared folder with the given logical name
8448 * in the collection of shared folders.
8449 *
8450 * @param aName logical name of the shared folder
8451 * @param aSharedFolder where to return the found object
8452 * @param aSetError whether to set the error info if the folder is
8453 * not found
8454 * @return
8455 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8456 *
8457 * @note
8458 * must be called from under the object's lock!
8459 */
8460HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8461 ComObjPtr<SharedFolder> &aSharedFolder,
8462 bool aSetError /* = false */)
8463{
8464 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8465 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8466 it != mHWData->mSharedFolders.end();
8467 ++it)
8468 {
8469 SharedFolder *pSF = *it;
8470 AutoCaller autoCaller(pSF);
8471 if (pSF->i_getName() == aName)
8472 {
8473 aSharedFolder = pSF;
8474 rc = S_OK;
8475 break;
8476 }
8477 }
8478
8479 if (aSetError && FAILED(rc))
8480 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8481
8482 return rc;
8483}
8484
8485/**
8486 * Initializes all machine instance data from the given settings structures
8487 * from XML. The exception is the machine UUID which needs special handling
8488 * depending on the caller's use case, so the caller needs to set that herself.
8489 *
8490 * This gets called in several contexts during machine initialization:
8491 *
8492 * -- When machine XML exists on disk already and needs to be loaded into memory,
8493 * for example, from registeredInit() to load all registered machines on
8494 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8495 * attached to the machine should be part of some media registry already.
8496 *
8497 * -- During OVF import, when a machine config has been constructed from an
8498 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8499 * ensure that the media listed as attachments in the config (which have
8500 * been imported from the OVF) receive the correct registry ID.
8501 *
8502 * -- During VM cloning.
8503 *
8504 * @param config Machine settings from XML.
8505 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8506 * for each attached medium in the config.
8507 * @return
8508 */
8509HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8510 const Guid *puuidRegistry)
8511{
8512 // copy name, description, OS type, teleporter, UTC etc.
8513 mUserData->s = config.machineUserData;
8514
8515 // Decode the Icon overide data from config userdata and set onto Machine.
8516 #define DECODE_STR_MAX _1M
8517 const char* pszStr = config.machineUserData.ovIcon.c_str();
8518 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8519 if (cbOut > DECODE_STR_MAX)
8520 return setError(E_FAIL,
8521 tr("Icon Data too long.'%d' > '%d'"),
8522 cbOut,
8523 DECODE_STR_MAX);
8524 mUserData->mIcon.resize(cbOut);
8525 int vrc = VINF_SUCCESS;
8526 if (cbOut)
8527 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8528 if (RT_FAILURE(vrc))
8529 {
8530 mUserData->mIcon.resize(0);
8531 return setError(E_FAIL,
8532 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8533 pszStr,
8534 vrc);
8535 }
8536
8537 // look up the object by Id to check it is valid
8538 ComPtr<IGuestOSType> guestOSType;
8539 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8540 guestOSType.asOutParam());
8541 if (FAILED(rc)) return rc;
8542
8543 // stateFile (optional)
8544 if (config.strStateFile.isEmpty())
8545 mSSData->strStateFilePath.setNull();
8546 else
8547 {
8548 Utf8Str stateFilePathFull(config.strStateFile);
8549 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8550 if (RT_FAILURE(vrc))
8551 return setError(E_FAIL,
8552 tr("Invalid saved state file path '%s' (%Rrc)"),
8553 config.strStateFile.c_str(),
8554 vrc);
8555 mSSData->strStateFilePath = stateFilePathFull;
8556 }
8557
8558 // snapshot folder needs special processing so set it again
8559 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8560 if (FAILED(rc)) return rc;
8561
8562 /* Copy the extra data items (Not in any case config is already the same as
8563 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8564 * make sure the extra data map is copied). */
8565 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8566
8567 /* currentStateModified (optional, default is true) */
8568 mData->mCurrentStateModified = config.fCurrentStateModified;
8569
8570 mData->mLastStateChange = config.timeLastStateChange;
8571
8572 /*
8573 * note: all mUserData members must be assigned prior this point because
8574 * we need to commit changes in order to let mUserData be shared by all
8575 * snapshot machine instances.
8576 */
8577 mUserData.commitCopy();
8578
8579 // machine registry, if present (must be loaded before snapshots)
8580 if (config.canHaveOwnMediaRegistry())
8581 {
8582 // determine machine folder
8583 Utf8Str strMachineFolder = i_getSettingsFileFull();
8584 strMachineFolder.stripFilename();
8585 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8586 config.mediaRegistry,
8587 strMachineFolder);
8588 if (FAILED(rc)) return rc;
8589 }
8590
8591 /* Snapshot node (optional) */
8592 size_t cRootSnapshots;
8593 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8594 {
8595 // there must be only one root snapshot
8596 Assert(cRootSnapshots == 1);
8597
8598 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8599
8600 rc = i_loadSnapshot(snap,
8601 config.uuidCurrentSnapshot,
8602 NULL); // no parent == first snapshot
8603 if (FAILED(rc)) return rc;
8604 }
8605
8606 // hardware data
8607 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8608 if (FAILED(rc)) return rc;
8609
8610 // load storage controllers
8611 rc = i_loadStorageControllers(config.storageMachine,
8612 puuidRegistry,
8613 NULL /* puuidSnapshot */);
8614 if (FAILED(rc)) return rc;
8615
8616 /*
8617 * NOTE: the assignment below must be the last thing to do,
8618 * otherwise it will be not possible to change the settings
8619 * somewhere in the code above because all setters will be
8620 * blocked by i_checkStateDependency(MutableStateDep).
8621 */
8622
8623 /* set the machine state to Aborted or Saved when appropriate */
8624 if (config.fAborted)
8625 {
8626 mSSData->strStateFilePath.setNull();
8627
8628 /* no need to use i_setMachineState() during init() */
8629 mData->mMachineState = MachineState_Aborted;
8630 }
8631 else if (!mSSData->strStateFilePath.isEmpty())
8632 {
8633 /* no need to use i_setMachineState() during init() */
8634 mData->mMachineState = MachineState_Saved;
8635 }
8636
8637 // after loading settings, we are no longer different from the XML on disk
8638 mData->flModifications = 0;
8639
8640 return S_OK;
8641}
8642
8643/**
8644 * Recursively loads all snapshots starting from the given.
8645 *
8646 * @param aNode <Snapshot> node.
8647 * @param aCurSnapshotId Current snapshot ID from the settings file.
8648 * @param aParentSnapshot Parent snapshot.
8649 */
8650HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8651 const Guid &aCurSnapshotId,
8652 Snapshot *aParentSnapshot)
8653{
8654 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8655 AssertReturn(!i_isSessionMachine(), E_FAIL);
8656
8657 HRESULT rc = S_OK;
8658
8659 Utf8Str strStateFile;
8660 if (!data.strStateFile.isEmpty())
8661 {
8662 /* optional */
8663 strStateFile = data.strStateFile;
8664 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8665 if (RT_FAILURE(vrc))
8666 return setError(E_FAIL,
8667 tr("Invalid saved state file path '%s' (%Rrc)"),
8668 strStateFile.c_str(),
8669 vrc);
8670 }
8671
8672 /* create a snapshot machine object */
8673 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8674 pSnapshotMachine.createObject();
8675 rc = pSnapshotMachine->initFromSettings(this,
8676 data.hardware,
8677 &data.debugging,
8678 &data.autostart,
8679 data.storage,
8680 data.uuid.ref(),
8681 strStateFile);
8682 if (FAILED(rc)) return rc;
8683
8684 /* create a snapshot object */
8685 ComObjPtr<Snapshot> pSnapshot;
8686 pSnapshot.createObject();
8687 /* initialize the snapshot */
8688 rc = pSnapshot->init(mParent, // VirtualBox object
8689 data.uuid,
8690 data.strName,
8691 data.strDescription,
8692 data.timestamp,
8693 pSnapshotMachine,
8694 aParentSnapshot);
8695 if (FAILED(rc)) return rc;
8696
8697 /* memorize the first snapshot if necessary */
8698 if (!mData->mFirstSnapshot)
8699 mData->mFirstSnapshot = pSnapshot;
8700
8701 /* memorize the current snapshot when appropriate */
8702 if ( !mData->mCurrentSnapshot
8703 && pSnapshot->i_getId() == aCurSnapshotId
8704 )
8705 mData->mCurrentSnapshot = pSnapshot;
8706
8707 // now create the children
8708 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8709 it != data.llChildSnapshots.end();
8710 ++it)
8711 {
8712 const settings::Snapshot &childData = *it;
8713 // recurse
8714 rc = i_loadSnapshot(childData,
8715 aCurSnapshotId,
8716 pSnapshot); // parent = the one we created above
8717 if (FAILED(rc)) return rc;
8718 }
8719
8720 return rc;
8721}
8722
8723/**
8724 * Loads settings into mHWData.
8725 *
8726 * @param data Reference to the hardware settings.
8727 * @param pDbg Pointer to the debugging settings.
8728 * @param pAutostart Pointer to the autostart settings.
8729 */
8730HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8731 const settings::Autostart *pAutostart)
8732{
8733 AssertReturn(!i_isSessionMachine(), E_FAIL);
8734
8735 HRESULT rc = S_OK;
8736
8737 try
8738 {
8739 /* The hardware version attribute (optional). */
8740 mHWData->mHWVersion = data.strVersion;
8741 mHWData->mHardwareUUID = data.uuid;
8742
8743 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8744 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8745 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8746 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8747 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8748 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8749 mHWData->mPAEEnabled = data.fPAE;
8750 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8751 mHWData->mLongMode = data.enmLongMode;
8752 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8753 mHWData->mCPUCount = data.cCPUs;
8754 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8755 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8756
8757 // cpu
8758 if (mHWData->mCPUHotPlugEnabled)
8759 {
8760 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8761 it != data.llCpus.end();
8762 ++it)
8763 {
8764 const settings::Cpu &cpu = *it;
8765
8766 mHWData->mCPUAttached[cpu.ulId] = true;
8767 }
8768 }
8769
8770 // cpuid leafs
8771 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8772 it != data.llCpuIdLeafs.end();
8773 ++it)
8774 {
8775 const settings::CpuIdLeaf &leaf = *it;
8776
8777 switch (leaf.ulId)
8778 {
8779 case 0x0:
8780 case 0x1:
8781 case 0x2:
8782 case 0x3:
8783 case 0x4:
8784 case 0x5:
8785 case 0x6:
8786 case 0x7:
8787 case 0x8:
8788 case 0x9:
8789 case 0xA:
8790 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8791 break;
8792
8793 case 0x80000000:
8794 case 0x80000001:
8795 case 0x80000002:
8796 case 0x80000003:
8797 case 0x80000004:
8798 case 0x80000005:
8799 case 0x80000006:
8800 case 0x80000007:
8801 case 0x80000008:
8802 case 0x80000009:
8803 case 0x8000000A:
8804 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8805 break;
8806
8807 default:
8808 /* just ignore */
8809 break;
8810 }
8811 }
8812
8813 mHWData->mMemorySize = data.ulMemorySizeMB;
8814 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8815
8816 // boot order
8817 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8818 {
8819 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8820 if (it == data.mapBootOrder.end())
8821 mHWData->mBootOrder[i] = DeviceType_Null;
8822 else
8823 mHWData->mBootOrder[i] = it->second;
8824 }
8825
8826 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8827 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8828 mHWData->mMonitorCount = data.cMonitors;
8829 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8830 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8831 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8832 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8833 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8834 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8835 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8836 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8837 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8838 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8839 if (!data.strVideoCaptureFile.isEmpty())
8840 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8841 else
8842 mHWData->mVideoCaptureFile.setNull();
8843 mHWData->mFirmwareType = data.firmwareType;
8844 mHWData->mPointingHIDType = data.pointingHIDType;
8845 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8846 mHWData->mChipsetType = data.chipsetType;
8847 mHWData->mParavirtProvider = data.paravirtProvider;
8848 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8849 mHWData->mHPETEnabled = data.fHPETEnabled;
8850
8851 /* VRDEServer */
8852 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8853 if (FAILED(rc)) return rc;
8854
8855 /* BIOS */
8856 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8857 if (FAILED(rc)) return rc;
8858
8859 // Bandwidth control (must come before network adapters)
8860 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8861 if (FAILED(rc)) return rc;
8862
8863 /* Shared folders */
8864 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8865 it != data.usbSettings.llUSBControllers.end();
8866 ++it)
8867 {
8868 const settings::USBController &settingsCtrl = *it;
8869 ComObjPtr<USBController> newCtrl;
8870
8871 newCtrl.createObject();
8872 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8873 mUSBControllers->push_back(newCtrl);
8874 }
8875
8876 /* USB device filters */
8877 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8878 if (FAILED(rc)) return rc;
8879
8880 // network adapters
8881 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8882 size_t oldCount = mNetworkAdapters.size();
8883 if (newCount > oldCount)
8884 {
8885 mNetworkAdapters.resize(newCount);
8886 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8887 {
8888 unconst(mNetworkAdapters[slot]).createObject();
8889 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8890 }
8891 }
8892 else if (newCount < oldCount)
8893 mNetworkAdapters.resize(newCount);
8894 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8895 it != data.llNetworkAdapters.end();
8896 ++it)
8897 {
8898 const settings::NetworkAdapter &nic = *it;
8899
8900 /* slot unicity is guaranteed by XML Schema */
8901 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8902 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8903 if (FAILED(rc)) return rc;
8904 }
8905
8906 // serial ports
8907 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8908 it != data.llSerialPorts.end();
8909 ++it)
8910 {
8911 const settings::SerialPort &s = *it;
8912
8913 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8914 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8915 if (FAILED(rc)) return rc;
8916 }
8917
8918 // parallel ports (optional)
8919 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8920 it != data.llParallelPorts.end();
8921 ++it)
8922 {
8923 const settings::ParallelPort &p = *it;
8924
8925 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8926 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8927 if (FAILED(rc)) return rc;
8928 }
8929
8930 /* AudioAdapter */
8931 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8932 if (FAILED(rc)) return rc;
8933
8934 /* Shared folders */
8935 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8936 it != data.llSharedFolders.end();
8937 ++it)
8938 {
8939 const settings::SharedFolder &sf = *it;
8940
8941 ComObjPtr<SharedFolder> sharedFolder;
8942 /* Check for double entries. Not allowed! */
8943 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8944 if (SUCCEEDED(rc))
8945 return setError(VBOX_E_OBJECT_IN_USE,
8946 tr("Shared folder named '%s' already exists"),
8947 sf.strName.c_str());
8948
8949 /* Create the new shared folder. Don't break on error. This will be
8950 * reported when the machine starts. */
8951 sharedFolder.createObject();
8952 rc = sharedFolder->init(i_getMachine(),
8953 sf.strName,
8954 sf.strHostPath,
8955 RT_BOOL(sf.fWritable),
8956 RT_BOOL(sf.fAutoMount),
8957 false /* fFailOnError */);
8958 if (FAILED(rc)) return rc;
8959 mHWData->mSharedFolders.push_back(sharedFolder);
8960 }
8961
8962 // Clipboard
8963 mHWData->mClipboardMode = data.clipboardMode;
8964
8965 // drag'n'drop
8966 mHWData->mDnDMode = data.dndMode;
8967
8968 // guest settings
8969 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8970
8971 // IO settings
8972 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8973 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8974
8975 // Host PCI devices
8976 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8977 it != data.pciAttachments.end();
8978 ++it)
8979 {
8980 const settings::HostPCIDeviceAttachment &hpda = *it;
8981 ComObjPtr<PCIDeviceAttachment> pda;
8982
8983 pda.createObject();
8984 pda->i_loadSettings(this, hpda);
8985 mHWData->mPCIDeviceAssignments.push_back(pda);
8986 }
8987
8988 /*
8989 * (The following isn't really real hardware, but it lives in HWData
8990 * for reasons of convenience.)
8991 */
8992
8993#ifdef VBOX_WITH_GUEST_PROPS
8994 /* Guest properties (optional) */
8995 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8996 it != data.llGuestProperties.end();
8997 ++it)
8998 {
8999 const settings::GuestProperty &prop = *it;
9000 uint32_t fFlags = guestProp::NILFLAG;
9001 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9002 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9003 mHWData->mGuestProperties[prop.strName] = property;
9004 }
9005
9006 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9007#endif /* VBOX_WITH_GUEST_PROPS defined */
9008
9009 rc = i_loadDebugging(pDbg);
9010 if (FAILED(rc))
9011 return rc;
9012
9013 mHWData->mAutostart = *pAutostart;
9014
9015 /* default frontend */
9016 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9017 }
9018 catch(std::bad_alloc &)
9019 {
9020 return E_OUTOFMEMORY;
9021 }
9022
9023 AssertComRC(rc);
9024 return rc;
9025}
9026
9027/**
9028 * Called from Machine::loadHardware() to load the debugging settings of the
9029 * machine.
9030 *
9031 * @param pDbg Pointer to the settings.
9032 */
9033HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9034{
9035 mHWData->mDebugging = *pDbg;
9036 /* no more processing currently required, this will probably change. */
9037 return S_OK;
9038}
9039
9040/**
9041 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9042 *
9043 * @param data
9044 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9045 * @param puuidSnapshot
9046 * @return
9047 */
9048HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9049 const Guid *puuidRegistry,
9050 const Guid *puuidSnapshot)
9051{
9052 AssertReturn(!i_isSessionMachine(), E_FAIL);
9053
9054 HRESULT rc = S_OK;
9055
9056 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9057 it != data.llStorageControllers.end();
9058 ++it)
9059 {
9060 const settings::StorageController &ctlData = *it;
9061
9062 ComObjPtr<StorageController> pCtl;
9063 /* Try to find one with the name first. */
9064 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9065 if (SUCCEEDED(rc))
9066 return setError(VBOX_E_OBJECT_IN_USE,
9067 tr("Storage controller named '%s' already exists"),
9068 ctlData.strName.c_str());
9069
9070 pCtl.createObject();
9071 rc = pCtl->init(this,
9072 ctlData.strName,
9073 ctlData.storageBus,
9074 ctlData.ulInstance,
9075 ctlData.fBootable);
9076 if (FAILED(rc)) return rc;
9077
9078 mStorageControllers->push_back(pCtl);
9079
9080 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9081 if (FAILED(rc)) return rc;
9082
9083 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9084 if (FAILED(rc)) return rc;
9085
9086 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9087 if (FAILED(rc)) return rc;
9088
9089 /* Set IDE emulation settings (only for AHCI controller). */
9090 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9091 {
9092 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9093 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9094 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9095 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9096 )
9097 return rc;
9098 }
9099
9100 /* Load the attached devices now. */
9101 rc = i_loadStorageDevices(pCtl,
9102 ctlData,
9103 puuidRegistry,
9104 puuidSnapshot);
9105 if (FAILED(rc)) return rc;
9106 }
9107
9108 return S_OK;
9109}
9110
9111/**
9112 * Called from i_loadStorageControllers for a controller's devices.
9113 *
9114 * @param aStorageController
9115 * @param data
9116 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9117 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9118 * @return
9119 */
9120HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9121 const settings::StorageController &data,
9122 const Guid *puuidRegistry,
9123 const Guid *puuidSnapshot)
9124{
9125 HRESULT rc = S_OK;
9126
9127 /* paranoia: detect duplicate attachments */
9128 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9129 it != data.llAttachedDevices.end();
9130 ++it)
9131 {
9132 const settings::AttachedDevice &ad = *it;
9133
9134 for (settings::AttachedDevicesList::const_iterator it2 = it;
9135 it2 != data.llAttachedDevices.end();
9136 ++it2)
9137 {
9138 if (it == it2)
9139 continue;
9140
9141 const settings::AttachedDevice &ad2 = *it2;
9142
9143 if ( ad.lPort == ad2.lPort
9144 && ad.lDevice == ad2.lDevice)
9145 {
9146 return setError(E_FAIL,
9147 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9148 aStorageController->i_getName().c_str(),
9149 ad.lPort,
9150 ad.lDevice,
9151 mUserData->s.strName.c_str());
9152 }
9153 }
9154 }
9155
9156 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9157 it != data.llAttachedDevices.end();
9158 ++it)
9159 {
9160 const settings::AttachedDevice &dev = *it;
9161 ComObjPtr<Medium> medium;
9162
9163 switch (dev.deviceType)
9164 {
9165 case DeviceType_Floppy:
9166 case DeviceType_DVD:
9167 if (dev.strHostDriveSrc.isNotEmpty())
9168 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9169 false /* fRefresh */, medium);
9170 else
9171 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9172 dev.uuid,
9173 false /* fRefresh */,
9174 false /* aSetError */,
9175 medium);
9176 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9177 // This is not an error. The host drive or UUID might have vanished, so just go
9178 // ahead without this removeable medium attachment
9179 rc = S_OK;
9180 break;
9181
9182 case DeviceType_HardDisk:
9183 {
9184 /* find a hard disk by UUID */
9185 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9186 if (FAILED(rc))
9187 {
9188 if (i_isSnapshotMachine())
9189 {
9190 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9191 // so the user knows that the bad disk is in a snapshot somewhere
9192 com::ErrorInfo info;
9193 return setError(E_FAIL,
9194 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9195 puuidSnapshot->raw(),
9196 info.getText().raw());
9197 }
9198 else
9199 return rc;
9200 }
9201
9202 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9203
9204 if (medium->i_getType() == MediumType_Immutable)
9205 {
9206 if (i_isSnapshotMachine())
9207 return setError(E_FAIL,
9208 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9209 "of the virtual machine '%s' ('%s')"),
9210 medium->i_getLocationFull().c_str(),
9211 dev.uuid.raw(),
9212 puuidSnapshot->raw(),
9213 mUserData->s.strName.c_str(),
9214 mData->m_strConfigFileFull.c_str());
9215
9216 return setError(E_FAIL,
9217 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9218 medium->i_getLocationFull().c_str(),
9219 dev.uuid.raw(),
9220 mUserData->s.strName.c_str(),
9221 mData->m_strConfigFileFull.c_str());
9222 }
9223
9224 if (medium->i_getType() == MediumType_MultiAttach)
9225 {
9226 if (i_isSnapshotMachine())
9227 return setError(E_FAIL,
9228 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9229 "of the virtual machine '%s' ('%s')"),
9230 medium->i_getLocationFull().c_str(),
9231 dev.uuid.raw(),
9232 puuidSnapshot->raw(),
9233 mUserData->s.strName.c_str(),
9234 mData->m_strConfigFileFull.c_str());
9235
9236 return setError(E_FAIL,
9237 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9238 medium->i_getLocationFull().c_str(),
9239 dev.uuid.raw(),
9240 mUserData->s.strName.c_str(),
9241 mData->m_strConfigFileFull.c_str());
9242 }
9243
9244 if ( !i_isSnapshotMachine()
9245 && medium->i_getChildren().size() != 0
9246 )
9247 return setError(E_FAIL,
9248 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9249 "because it has %d differencing child hard disks"),
9250 medium->i_getLocationFull().c_str(),
9251 dev.uuid.raw(),
9252 mUserData->s.strName.c_str(),
9253 mData->m_strConfigFileFull.c_str(),
9254 medium->i_getChildren().size());
9255
9256 if (i_findAttachment(mMediaData->mAttachments,
9257 medium))
9258 return setError(E_FAIL,
9259 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9260 medium->i_getLocationFull().c_str(),
9261 dev.uuid.raw(),
9262 mUserData->s.strName.c_str(),
9263 mData->m_strConfigFileFull.c_str());
9264
9265 break;
9266 }
9267
9268 default:
9269 return setError(E_FAIL,
9270 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9271 medium->i_getLocationFull().c_str(),
9272 mUserData->s.strName.c_str(),
9273 mData->m_strConfigFileFull.c_str());
9274 }
9275
9276 if (FAILED(rc))
9277 break;
9278
9279 /* Bandwidth groups are loaded at this point. */
9280 ComObjPtr<BandwidthGroup> pBwGroup;
9281
9282 if (!dev.strBwGroup.isEmpty())
9283 {
9284 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9285 if (FAILED(rc))
9286 return setError(E_FAIL,
9287 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9288 medium->i_getLocationFull().c_str(),
9289 dev.strBwGroup.c_str(),
9290 mUserData->s.strName.c_str(),
9291 mData->m_strConfigFileFull.c_str());
9292 pBwGroup->i_reference();
9293 }
9294
9295 const Bstr controllerName = aStorageController->i_getName();
9296 ComObjPtr<MediumAttachment> pAttachment;
9297 pAttachment.createObject();
9298 rc = pAttachment->init(this,
9299 medium,
9300 controllerName,
9301 dev.lPort,
9302 dev.lDevice,
9303 dev.deviceType,
9304 false,
9305 dev.fPassThrough,
9306 dev.fTempEject,
9307 dev.fNonRotational,
9308 dev.fDiscard,
9309 dev.fHotPluggable,
9310 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9311 if (FAILED(rc)) break;
9312
9313 /* associate the medium with this machine and snapshot */
9314 if (!medium.isNull())
9315 {
9316 AutoCaller medCaller(medium);
9317 if (FAILED(medCaller.rc())) return medCaller.rc();
9318 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9319
9320 if (i_isSnapshotMachine())
9321 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9322 else
9323 rc = medium->i_addBackReference(mData->mUuid);
9324 /* If the medium->addBackReference fails it sets an appropriate
9325 * error message, so no need to do any guesswork here. */
9326
9327 if (puuidRegistry)
9328 // caller wants registry ID to be set on all attached media (OVF import case)
9329 medium->i_addRegistry(*puuidRegistry);
9330 }
9331
9332 if (FAILED(rc))
9333 break;
9334
9335 /* back up mMediaData to let registeredInit() properly rollback on failure
9336 * (= limited accessibility) */
9337 i_setModified(IsModified_Storage);
9338 mMediaData.backup();
9339 mMediaData->mAttachments.push_back(pAttachment);
9340 }
9341
9342 return rc;
9343}
9344
9345/**
9346 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9347 *
9348 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9349 * @param aSnapshot where to return the found snapshot
9350 * @param aSetError true to set extended error info on failure
9351 */
9352HRESULT Machine::i_findSnapshotById(const Guid &aId,
9353 ComObjPtr<Snapshot> &aSnapshot,
9354 bool aSetError /* = false */)
9355{
9356 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9357
9358 if (!mData->mFirstSnapshot)
9359 {
9360 if (aSetError)
9361 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9362 return E_FAIL;
9363 }
9364
9365 if (aId.isZero())
9366 aSnapshot = mData->mFirstSnapshot;
9367 else
9368 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9369
9370 if (!aSnapshot)
9371 {
9372 if (aSetError)
9373 return setError(E_FAIL,
9374 tr("Could not find a snapshot with UUID {%s}"),
9375 aId.toString().c_str());
9376 return E_FAIL;
9377 }
9378
9379 return S_OK;
9380}
9381
9382/**
9383 * Returns the snapshot with the given name or fails of no such snapshot.
9384 *
9385 * @param aName snapshot name to find
9386 * @param aSnapshot where to return the found snapshot
9387 * @param aSetError true to set extended error info on failure
9388 */
9389HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9390 ComObjPtr<Snapshot> &aSnapshot,
9391 bool aSetError /* = false */)
9392{
9393 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9394
9395 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9396
9397 if (!mData->mFirstSnapshot)
9398 {
9399 if (aSetError)
9400 return setError(VBOX_E_OBJECT_NOT_FOUND,
9401 tr("This machine does not have any snapshots"));
9402 return VBOX_E_OBJECT_NOT_FOUND;
9403 }
9404
9405 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9406
9407 if (!aSnapshot)
9408 {
9409 if (aSetError)
9410 return setError(VBOX_E_OBJECT_NOT_FOUND,
9411 tr("Could not find a snapshot named '%s'"), strName.c_str());
9412 return VBOX_E_OBJECT_NOT_FOUND;
9413 }
9414
9415 return S_OK;
9416}
9417
9418/**
9419 * Returns a storage controller object with the given name.
9420 *
9421 * @param aName storage controller name to find
9422 * @param aStorageController where to return the found storage controller
9423 * @param aSetError true to set extended error info on failure
9424 */
9425HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9426 ComObjPtr<StorageController> &aStorageController,
9427 bool aSetError /* = false */)
9428{
9429 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9430
9431 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9432 it != mStorageControllers->end();
9433 ++it)
9434 {
9435 if ((*it)->i_getName() == aName)
9436 {
9437 aStorageController = (*it);
9438 return S_OK;
9439 }
9440 }
9441
9442 if (aSetError)
9443 return setError(VBOX_E_OBJECT_NOT_FOUND,
9444 tr("Could not find a storage controller named '%s'"),
9445 aName.c_str());
9446 return VBOX_E_OBJECT_NOT_FOUND;
9447}
9448
9449/**
9450 * Returns a USB controller object with the given name.
9451 *
9452 * @param aName USB controller name to find
9453 * @param aUSBController where to return the found USB controller
9454 * @param aSetError true to set extended error info on failure
9455 */
9456HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9457 ComObjPtr<USBController> &aUSBController,
9458 bool aSetError /* = false */)
9459{
9460 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9461
9462 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9463 it != mUSBControllers->end();
9464 ++it)
9465 {
9466 if ((*it)->i_getName() == aName)
9467 {
9468 aUSBController = (*it);
9469 return S_OK;
9470 }
9471 }
9472
9473 if (aSetError)
9474 return setError(VBOX_E_OBJECT_NOT_FOUND,
9475 tr("Could not find a storage controller named '%s'"),
9476 aName.c_str());
9477 return VBOX_E_OBJECT_NOT_FOUND;
9478}
9479
9480/**
9481 * Returns the number of USB controller instance of the given type.
9482 *
9483 * @param enmType USB controller type.
9484 */
9485ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9486{
9487 ULONG cCtrls = 0;
9488
9489 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9490 it != mUSBControllers->end();
9491 ++it)
9492 {
9493 if ((*it)->i_getControllerType() == enmType)
9494 cCtrls++;
9495 }
9496
9497 return cCtrls;
9498}
9499
9500HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9501 MediaData::AttachmentList &atts)
9502{
9503 AutoCaller autoCaller(this);
9504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9505
9506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9507
9508 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9509 it != mMediaData->mAttachments.end();
9510 ++it)
9511 {
9512 const ComObjPtr<MediumAttachment> &pAtt = *it;
9513 // should never happen, but deal with NULL pointers in the list.
9514 AssertStmt(!pAtt.isNull(), continue);
9515
9516 // getControllerName() needs caller+read lock
9517 AutoCaller autoAttCaller(pAtt);
9518 if (FAILED(autoAttCaller.rc()))
9519 {
9520 atts.clear();
9521 return autoAttCaller.rc();
9522 }
9523 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9524
9525 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9526 atts.push_back(pAtt);
9527 }
9528
9529 return S_OK;
9530}
9531
9532
9533/**
9534 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9535 * file if the machine name was changed and about creating a new settings file
9536 * if this is a new machine.
9537 *
9538 * @note Must be never called directly but only from #saveSettings().
9539 */
9540HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9541{
9542 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9543
9544 HRESULT rc = S_OK;
9545
9546 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9547
9548 /// @todo need to handle primary group change, too
9549
9550 /* attempt to rename the settings file if machine name is changed */
9551 if ( mUserData->s.fNameSync
9552 && mUserData.isBackedUp()
9553 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9554 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9555 )
9556 {
9557 bool dirRenamed = false;
9558 bool fileRenamed = false;
9559
9560 Utf8Str configFile, newConfigFile;
9561 Utf8Str configFilePrev, newConfigFilePrev;
9562 Utf8Str configDir, newConfigDir;
9563
9564 do
9565 {
9566 int vrc = VINF_SUCCESS;
9567
9568 Utf8Str name = mUserData.backedUpData()->s.strName;
9569 Utf8Str newName = mUserData->s.strName;
9570 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9571 if (group == "/")
9572 group.setNull();
9573 Utf8Str newGroup = mUserData->s.llGroups.front();
9574 if (newGroup == "/")
9575 newGroup.setNull();
9576
9577 configFile = mData->m_strConfigFileFull;
9578
9579 /* first, rename the directory if it matches the group and machine name */
9580 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9581 group.c_str(), RTPATH_DELIMITER, name.c_str());
9582 /** @todo hack, make somehow use of ComposeMachineFilename */
9583 if (mUserData->s.fDirectoryIncludesUUID)
9584 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9585 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9586 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9587 /** @todo hack, make somehow use of ComposeMachineFilename */
9588 if (mUserData->s.fDirectoryIncludesUUID)
9589 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9590 configDir = configFile;
9591 configDir.stripFilename();
9592 newConfigDir = configDir;
9593 if ( configDir.length() >= groupPlusName.length()
9594 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9595 groupPlusName.c_str()))
9596 {
9597 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9598 Utf8Str newConfigBaseDir(newConfigDir);
9599 newConfigDir.append(newGroupPlusName);
9600 /* consistency: use \ if appropriate on the platform */
9601 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9602 /* new dir and old dir cannot be equal here because of 'if'
9603 * above and because name != newName */
9604 Assert(configDir != newConfigDir);
9605 if (!fSettingsFileIsNew)
9606 {
9607 /* perform real rename only if the machine is not new */
9608 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9609 if ( vrc == VERR_FILE_NOT_FOUND
9610 || vrc == VERR_PATH_NOT_FOUND)
9611 {
9612 /* create the parent directory, then retry renaming */
9613 Utf8Str parent(newConfigDir);
9614 parent.stripFilename();
9615 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9616 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9617 }
9618 if (RT_FAILURE(vrc))
9619 {
9620 rc = setError(E_FAIL,
9621 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9622 configDir.c_str(),
9623 newConfigDir.c_str(),
9624 vrc);
9625 break;
9626 }
9627 /* delete subdirectories which are no longer needed */
9628 Utf8Str dir(configDir);
9629 dir.stripFilename();
9630 while (dir != newConfigBaseDir && dir != ".")
9631 {
9632 vrc = RTDirRemove(dir.c_str());
9633 if (RT_FAILURE(vrc))
9634 break;
9635 dir.stripFilename();
9636 }
9637 dirRenamed = true;
9638 }
9639 }
9640
9641 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9642 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9643
9644 /* then try to rename the settings file itself */
9645 if (newConfigFile != configFile)
9646 {
9647 /* get the path to old settings file in renamed directory */
9648 configFile = Utf8StrFmt("%s%c%s",
9649 newConfigDir.c_str(),
9650 RTPATH_DELIMITER,
9651 RTPathFilename(configFile.c_str()));
9652 if (!fSettingsFileIsNew)
9653 {
9654 /* perform real rename only if the machine is not new */
9655 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9656 if (RT_FAILURE(vrc))
9657 {
9658 rc = setError(E_FAIL,
9659 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9660 configFile.c_str(),
9661 newConfigFile.c_str(),
9662 vrc);
9663 break;
9664 }
9665 fileRenamed = true;
9666 configFilePrev = configFile;
9667 configFilePrev += "-prev";
9668 newConfigFilePrev = newConfigFile;
9669 newConfigFilePrev += "-prev";
9670 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9671 }
9672 }
9673
9674 // update m_strConfigFileFull amd mConfigFile
9675 mData->m_strConfigFileFull = newConfigFile;
9676 // compute the relative path too
9677 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9678
9679 // store the old and new so that VirtualBox::i_saveSettings() can update
9680 // the media registry
9681 if ( mData->mRegistered
9682 && (configDir != newConfigDir || configFile != newConfigFile))
9683 {
9684 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9685
9686 if (pfNeedsGlobalSaveSettings)
9687 *pfNeedsGlobalSaveSettings = true;
9688 }
9689
9690 // in the saved state file path, replace the old directory with the new directory
9691 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9692 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9693
9694 // and do the same thing for the saved state file paths of all the online snapshots
9695 if (mData->mFirstSnapshot)
9696 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9697 newConfigDir.c_str());
9698 }
9699 while (0);
9700
9701 if (FAILED(rc))
9702 {
9703 /* silently try to rename everything back */
9704 if (fileRenamed)
9705 {
9706 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9707 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9708 }
9709 if (dirRenamed)
9710 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9711 }
9712
9713 if (FAILED(rc)) return rc;
9714 }
9715
9716 if (fSettingsFileIsNew)
9717 {
9718 /* create a virgin config file */
9719 int vrc = VINF_SUCCESS;
9720
9721 /* ensure the settings directory exists */
9722 Utf8Str path(mData->m_strConfigFileFull);
9723 path.stripFilename();
9724 if (!RTDirExists(path.c_str()))
9725 {
9726 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9727 if (RT_FAILURE(vrc))
9728 {
9729 return setError(E_FAIL,
9730 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9731 path.c_str(),
9732 vrc);
9733 }
9734 }
9735
9736 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9737 path = Utf8Str(mData->m_strConfigFileFull);
9738 RTFILE f = NIL_RTFILE;
9739 vrc = RTFileOpen(&f, path.c_str(),
9740 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9741 if (RT_FAILURE(vrc))
9742 return setError(E_FAIL,
9743 tr("Could not create the settings file '%s' (%Rrc)"),
9744 path.c_str(),
9745 vrc);
9746 RTFileClose(f);
9747 }
9748
9749 return rc;
9750}
9751
9752/**
9753 * Saves and commits machine data, user data and hardware data.
9754 *
9755 * Note that on failure, the data remains uncommitted.
9756 *
9757 * @a aFlags may combine the following flags:
9758 *
9759 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9760 * Used when saving settings after an operation that makes them 100%
9761 * correspond to the settings from the current snapshot.
9762 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9763 * #isReallyModified() returns false. This is necessary for cases when we
9764 * change machine data directly, not through the backup()/commit() mechanism.
9765 * - SaveS_Force: settings will be saved without doing a deep compare of the
9766 * settings structures. This is used when this is called because snapshots
9767 * have changed to avoid the overhead of the deep compare.
9768 *
9769 * @note Must be called from under this object's write lock. Locks children for
9770 * writing.
9771 *
9772 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9773 * initialized to false and that will be set to true by this function if
9774 * the caller must invoke VirtualBox::i_saveSettings() because the global
9775 * settings have changed. This will happen if a machine rename has been
9776 * saved and the global machine and media registries will therefore need
9777 * updating.
9778 */
9779HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9780 int aFlags /*= 0*/)
9781{
9782 LogFlowThisFuncEnter();
9783
9784 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9785
9786 /* make sure child objects are unable to modify the settings while we are
9787 * saving them */
9788 i_ensureNoStateDependencies();
9789
9790 AssertReturn(!i_isSnapshotMachine(),
9791 E_FAIL);
9792
9793 HRESULT rc = S_OK;
9794 bool fNeedsWrite = false;
9795
9796 /* First, prepare to save settings. It will care about renaming the
9797 * settings directory and file if the machine name was changed and about
9798 * creating a new settings file if this is a new machine. */
9799 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9800 if (FAILED(rc)) return rc;
9801
9802 // keep a pointer to the current settings structures
9803 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9804 settings::MachineConfigFile *pNewConfig = NULL;
9805
9806 try
9807 {
9808 // make a fresh one to have everyone write stuff into
9809 pNewConfig = new settings::MachineConfigFile(NULL);
9810 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9811
9812 // now go and copy all the settings data from COM to the settings structures
9813 // (this calles i_saveSettings() on all the COM objects in the machine)
9814 i_copyMachineDataToSettings(*pNewConfig);
9815
9816 if (aFlags & SaveS_ResetCurStateModified)
9817 {
9818 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9819 mData->mCurrentStateModified = FALSE;
9820 fNeedsWrite = true; // always, no need to compare
9821 }
9822 else if (aFlags & SaveS_Force)
9823 {
9824 fNeedsWrite = true; // always, no need to compare
9825 }
9826 else
9827 {
9828 if (!mData->mCurrentStateModified)
9829 {
9830 // do a deep compare of the settings that we just saved with the settings
9831 // previously stored in the config file; this invokes MachineConfigFile::operator==
9832 // which does a deep compare of all the settings, which is expensive but less expensive
9833 // than writing out XML in vain
9834 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9835
9836 // could still be modified if any settings changed
9837 mData->mCurrentStateModified = fAnySettingsChanged;
9838
9839 fNeedsWrite = fAnySettingsChanged;
9840 }
9841 else
9842 fNeedsWrite = true;
9843 }
9844
9845 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9846
9847 if (fNeedsWrite)
9848 // now spit it all out!
9849 pNewConfig->write(mData->m_strConfigFileFull);
9850
9851 mData->pMachineConfigFile = pNewConfig;
9852 delete pOldConfig;
9853 i_commit();
9854
9855 // after saving settings, we are no longer different from the XML on disk
9856 mData->flModifications = 0;
9857 }
9858 catch (HRESULT err)
9859 {
9860 // we assume that error info is set by the thrower
9861 rc = err;
9862
9863 // restore old config
9864 delete pNewConfig;
9865 mData->pMachineConfigFile = pOldConfig;
9866 }
9867 catch (...)
9868 {
9869 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9870 }
9871
9872 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9873 {
9874 /* Fire the data change event, even on failure (since we've already
9875 * committed all data). This is done only for SessionMachines because
9876 * mutable Machine instances are always not registered (i.e. private
9877 * to the client process that creates them) and thus don't need to
9878 * inform callbacks. */
9879 if (i_isSessionMachine())
9880 mParent->i_onMachineDataChange(mData->mUuid);
9881 }
9882
9883 LogFlowThisFunc(("rc=%08X\n", rc));
9884 LogFlowThisFuncLeave();
9885 return rc;
9886}
9887
9888/**
9889 * Implementation for saving the machine settings into the given
9890 * settings::MachineConfigFile instance. This copies machine extradata
9891 * from the previous machine config file in the instance data, if any.
9892 *
9893 * This gets called from two locations:
9894 *
9895 * -- Machine::i_saveSettings(), during the regular XML writing;
9896 *
9897 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9898 * exported to OVF and we write the VirtualBox proprietary XML
9899 * into a <vbox:Machine> tag.
9900 *
9901 * This routine fills all the fields in there, including snapshots, *except*
9902 * for the following:
9903 *
9904 * -- fCurrentStateModified. There is some special logic associated with that.
9905 *
9906 * The caller can then call MachineConfigFile::write() or do something else
9907 * with it.
9908 *
9909 * Caller must hold the machine lock!
9910 *
9911 * This throws XML errors and HRESULT, so the caller must have a catch block!
9912 */
9913void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9914{
9915 // deep copy extradata
9916 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9917
9918 config.uuid = mData->mUuid;
9919
9920 // copy name, description, OS type, teleport, UTC etc.
9921 config.machineUserData = mUserData->s;
9922
9923 // Encode the Icon Override data from Machine and store on config userdata.
9924 std::vector<BYTE> iconByte;
9925 getIcon(iconByte);
9926 ssize_t cbData = iconByte.size();
9927 if (cbData > 0)
9928 {
9929 ssize_t cchOut = RTBase64EncodedLength(cbData);
9930 Utf8Str strIconData;
9931 strIconData.reserve(cchOut+1);
9932 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9933 strIconData.mutableRaw(), strIconData.capacity(),
9934 NULL);
9935 if (RT_FAILURE(vrc))
9936 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9937 strIconData.jolt();
9938 config.machineUserData.ovIcon = strIconData;
9939 }
9940 else
9941 config.machineUserData.ovIcon.setNull();
9942
9943 if ( mData->mMachineState == MachineState_Saved
9944 || mData->mMachineState == MachineState_Restoring
9945 // when deleting a snapshot we may or may not have a saved state in the current state,
9946 // so let's not assert here please
9947 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9948 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9949 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9950 && (!mSSData->strStateFilePath.isEmpty())
9951 )
9952 )
9953 {
9954 Assert(!mSSData->strStateFilePath.isEmpty());
9955 /* try to make the file name relative to the settings file dir */
9956 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9957 }
9958 else
9959 {
9960 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9961 config.strStateFile.setNull();
9962 }
9963
9964 if (mData->mCurrentSnapshot)
9965 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9966 else
9967 config.uuidCurrentSnapshot.clear();
9968
9969 config.timeLastStateChange = mData->mLastStateChange;
9970 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9971 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9972
9973 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9974 if (FAILED(rc)) throw rc;
9975
9976 rc = i_saveStorageControllers(config.storageMachine);
9977 if (FAILED(rc)) throw rc;
9978
9979 // save machine's media registry if this is VirtualBox 4.0 or later
9980 if (config.canHaveOwnMediaRegistry())
9981 {
9982 // determine machine folder
9983 Utf8Str strMachineFolder = i_getSettingsFileFull();
9984 strMachineFolder.stripFilename();
9985 mParent->i_saveMediaRegistry(config.mediaRegistry,
9986 i_getId(), // only media with registry ID == machine UUID
9987 strMachineFolder);
9988 // this throws HRESULT
9989 }
9990
9991 // save snapshots
9992 rc = i_saveAllSnapshots(config);
9993 if (FAILED(rc)) throw rc;
9994}
9995
9996/**
9997 * Saves all snapshots of the machine into the given machine config file. Called
9998 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9999 * @param config
10000 * @return
10001 */
10002HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10003{
10004 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10005
10006 HRESULT rc = S_OK;
10007
10008 try
10009 {
10010 config.llFirstSnapshot.clear();
10011
10012 if (mData->mFirstSnapshot)
10013 {
10014 // the settings use a list for "the first snapshot"
10015 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10016
10017 // get reference to the snapshot on the list and work on that
10018 // element straight in the list to avoid excessive copying later
10019 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10020 if (FAILED(rc)) throw rc;
10021 }
10022
10023// if (mType == IsSessionMachine)
10024// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10025
10026 }
10027 catch (HRESULT err)
10028 {
10029 /* we assume that error info is set by the thrower */
10030 rc = err;
10031 }
10032 catch (...)
10033 {
10034 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10035 }
10036
10037 return rc;
10038}
10039
10040/**
10041 * Saves the VM hardware configuration. It is assumed that the
10042 * given node is empty.
10043 *
10044 * @param data Reference to the settings object for the hardware config.
10045 * @param pDbg Pointer to the settings object for the debugging config
10046 * which happens to live in mHWData.
10047 * @param pAutostart Pointer to the settings object for the autostart config
10048 * which happens to live in mHWData.
10049 */
10050HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10051 settings::Autostart *pAutostart)
10052{
10053 HRESULT rc = S_OK;
10054
10055 try
10056 {
10057 /* The hardware version attribute (optional).
10058 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10059 if ( mHWData->mHWVersion == "1"
10060 && mSSData->strStateFilePath.isEmpty()
10061 )
10062 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10063 other point needs to be found where this can be done. */
10064
10065 data.strVersion = mHWData->mHWVersion;
10066 data.uuid = mHWData->mHardwareUUID;
10067
10068 // CPU
10069 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10070 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10071 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10072 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10073 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10074 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10075 data.fPAE = !!mHWData->mPAEEnabled;
10076 data.enmLongMode = mHWData->mLongMode;
10077 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10078 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10079
10080 /* Standard and Extended CPUID leafs. */
10081 data.llCpuIdLeafs.clear();
10082 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10083 {
10084 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10085 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10086 }
10087 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10088 {
10089 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10090 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10091 }
10092
10093 data.cCPUs = mHWData->mCPUCount;
10094 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10095 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10096
10097 data.llCpus.clear();
10098 if (data.fCpuHotPlug)
10099 {
10100 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10101 {
10102 if (mHWData->mCPUAttached[idx])
10103 {
10104 settings::Cpu cpu;
10105 cpu.ulId = idx;
10106 data.llCpus.push_back(cpu);
10107 }
10108 }
10109 }
10110
10111 // memory
10112 data.ulMemorySizeMB = mHWData->mMemorySize;
10113 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10114
10115 // firmware
10116 data.firmwareType = mHWData->mFirmwareType;
10117
10118 // HID
10119 data.pointingHIDType = mHWData->mPointingHIDType;
10120 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10121
10122 // chipset
10123 data.chipsetType = mHWData->mChipsetType;
10124
10125 // paravirt
10126 data.paravirtProvider = mHWData->mParavirtProvider;
10127
10128
10129 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10130
10131 // HPET
10132 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10133
10134 // boot order
10135 data.mapBootOrder.clear();
10136 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10137 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10138
10139 // display
10140 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10141 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10142 data.cMonitors = mHWData->mMonitorCount;
10143 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10144 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10145 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10146 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10147 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10148 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10149 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10150 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10151 {
10152 if (mHWData->maVideoCaptureScreens[i])
10153 ASMBitSet(&data.u64VideoCaptureScreens, i);
10154 else
10155 ASMBitClear(&data.u64VideoCaptureScreens, i);
10156 }
10157 /* store relative video capture file if possible */
10158 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10159
10160 /* VRDEServer settings (optional) */
10161 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10162 if (FAILED(rc)) throw rc;
10163
10164 /* BIOS (required) */
10165 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10166 if (FAILED(rc)) throw rc;
10167
10168 /* USB Controller (required) */
10169 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10170 {
10171 ComObjPtr<USBController> ctrl = *it;
10172 settings::USBController settingsCtrl;
10173
10174 settingsCtrl.strName = ctrl->i_getName();
10175 settingsCtrl.enmType = ctrl->i_getControllerType();
10176
10177 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10178 }
10179
10180 /* USB device filters (required) */
10181 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10182 if (FAILED(rc)) throw rc;
10183
10184 /* Network adapters (required) */
10185 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10186 data.llNetworkAdapters.clear();
10187 /* Write out only the nominal number of network adapters for this
10188 * chipset type. Since Machine::commit() hasn't been called there
10189 * may be extra NIC settings in the vector. */
10190 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10191 {
10192 settings::NetworkAdapter nic;
10193 nic.ulSlot = (uint32_t)slot;
10194 /* paranoia check... must not be NULL, but must not crash either. */
10195 if (mNetworkAdapters[slot])
10196 {
10197 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10198 if (FAILED(rc)) throw rc;
10199
10200 data.llNetworkAdapters.push_back(nic);
10201 }
10202 }
10203
10204 /* Serial ports */
10205 data.llSerialPorts.clear();
10206 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10207 {
10208 settings::SerialPort s;
10209 s.ulSlot = slot;
10210 rc = mSerialPorts[slot]->i_saveSettings(s);
10211 if (FAILED(rc)) return rc;
10212
10213 data.llSerialPorts.push_back(s);
10214 }
10215
10216 /* Parallel ports */
10217 data.llParallelPorts.clear();
10218 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10219 {
10220 settings::ParallelPort p;
10221 p.ulSlot = slot;
10222 rc = mParallelPorts[slot]->i_saveSettings(p);
10223 if (FAILED(rc)) return rc;
10224
10225 data.llParallelPorts.push_back(p);
10226 }
10227
10228 /* Audio adapter */
10229 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10230 if (FAILED(rc)) return rc;
10231
10232 /* Shared folders */
10233 data.llSharedFolders.clear();
10234 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10235 it != mHWData->mSharedFolders.end();
10236 ++it)
10237 {
10238 SharedFolder *pSF = *it;
10239 AutoCaller sfCaller(pSF);
10240 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10241 settings::SharedFolder sf;
10242 sf.strName = pSF->i_getName();
10243 sf.strHostPath = pSF->i_getHostPath();
10244 sf.fWritable = !!pSF->i_isWritable();
10245 sf.fAutoMount = !!pSF->i_isAutoMounted();
10246
10247 data.llSharedFolders.push_back(sf);
10248 }
10249
10250 // clipboard
10251 data.clipboardMode = mHWData->mClipboardMode;
10252
10253 // drag'n'drop
10254 data.dndMode = mHWData->mDnDMode;
10255
10256 /* Guest */
10257 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10258
10259 // IO settings
10260 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10261 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10262
10263 /* BandwidthControl (required) */
10264 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10265 if (FAILED(rc)) throw rc;
10266
10267 /* Host PCI devices */
10268 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10269 it != mHWData->mPCIDeviceAssignments.end();
10270 ++it)
10271 {
10272 ComObjPtr<PCIDeviceAttachment> pda = *it;
10273 settings::HostPCIDeviceAttachment hpda;
10274
10275 rc = pda->i_saveSettings(hpda);
10276 if (FAILED(rc)) throw rc;
10277
10278 data.pciAttachments.push_back(hpda);
10279 }
10280
10281
10282 // guest properties
10283 data.llGuestProperties.clear();
10284#ifdef VBOX_WITH_GUEST_PROPS
10285 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10286 it != mHWData->mGuestProperties.end();
10287 ++it)
10288 {
10289 HWData::GuestProperty property = it->second;
10290
10291 /* Remove transient guest properties at shutdown unless we
10292 * are saving state */
10293 if ( ( mData->mMachineState == MachineState_PoweredOff
10294 || mData->mMachineState == MachineState_Aborted
10295 || mData->mMachineState == MachineState_Teleported)
10296 && ( property.mFlags & guestProp::TRANSIENT
10297 || property.mFlags & guestProp::TRANSRESET))
10298 continue;
10299 settings::GuestProperty prop;
10300 prop.strName = it->first;
10301 prop.strValue = property.strValue;
10302 prop.timestamp = property.mTimestamp;
10303 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10304 guestProp::writeFlags(property.mFlags, szFlags);
10305 prop.strFlags = szFlags;
10306
10307 data.llGuestProperties.push_back(prop);
10308 }
10309
10310 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10311 /* I presume this doesn't require a backup(). */
10312 mData->mGuestPropertiesModified = FALSE;
10313#endif /* VBOX_WITH_GUEST_PROPS defined */
10314
10315 *pDbg = mHWData->mDebugging;
10316 *pAutostart = mHWData->mAutostart;
10317
10318 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10319 }
10320 catch(std::bad_alloc &)
10321 {
10322 return E_OUTOFMEMORY;
10323 }
10324
10325 AssertComRC(rc);
10326 return rc;
10327}
10328
10329/**
10330 * Saves the storage controller configuration.
10331 *
10332 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10333 */
10334HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10335{
10336 data.llStorageControllers.clear();
10337
10338 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10339 it != mStorageControllers->end();
10340 ++it)
10341 {
10342 HRESULT rc;
10343 ComObjPtr<StorageController> pCtl = *it;
10344
10345 settings::StorageController ctl;
10346 ctl.strName = pCtl->i_getName();
10347 ctl.controllerType = pCtl->i_getControllerType();
10348 ctl.storageBus = pCtl->i_getStorageBus();
10349 ctl.ulInstance = pCtl->i_getInstance();
10350 ctl.fBootable = pCtl->i_getBootable();
10351
10352 /* Save the port count. */
10353 ULONG portCount;
10354 rc = pCtl->COMGETTER(PortCount)(&portCount);
10355 ComAssertComRCRet(rc, rc);
10356 ctl.ulPortCount = portCount;
10357
10358 /* Save fUseHostIOCache */
10359 BOOL fUseHostIOCache;
10360 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10361 ComAssertComRCRet(rc, rc);
10362 ctl.fUseHostIOCache = !!fUseHostIOCache;
10363
10364 /* Save IDE emulation settings. */
10365 if (ctl.controllerType == StorageControllerType_IntelAhci)
10366 {
10367 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10368 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10369 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10370 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10371 )
10372 ComAssertComRCRet(rc, rc);
10373 }
10374
10375 /* save the devices now. */
10376 rc = i_saveStorageDevices(pCtl, ctl);
10377 ComAssertComRCRet(rc, rc);
10378
10379 data.llStorageControllers.push_back(ctl);
10380 }
10381
10382 return S_OK;
10383}
10384
10385/**
10386 * Saves the hard disk configuration.
10387 */
10388HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10389 settings::StorageController &data)
10390{
10391 MediaData::AttachmentList atts;
10392
10393 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10394 if (FAILED(rc)) return rc;
10395
10396 data.llAttachedDevices.clear();
10397 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10398 it != atts.end();
10399 ++it)
10400 {
10401 settings::AttachedDevice dev;
10402 IMediumAttachment *iA = *it;
10403 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10404 Medium *pMedium = pAttach->i_getMedium();
10405
10406 dev.deviceType = pAttach->i_getType();
10407 dev.lPort = pAttach->i_getPort();
10408 dev.lDevice = pAttach->i_getDevice();
10409 dev.fPassThrough = pAttach->i_getPassthrough();
10410 dev.fHotPluggable = pAttach->i_getHotPluggable();
10411 if (pMedium)
10412 {
10413 if (pMedium->i_isHostDrive())
10414 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10415 else
10416 dev.uuid = pMedium->i_getId();
10417 dev.fTempEject = pAttach->i_getTempEject();
10418 dev.fNonRotational = pAttach->i_getNonRotational();
10419 dev.fDiscard = pAttach->i_getDiscard();
10420 }
10421
10422 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10423
10424 data.llAttachedDevices.push_back(dev);
10425 }
10426
10427 return S_OK;
10428}
10429
10430/**
10431 * Saves machine state settings as defined by aFlags
10432 * (SaveSTS_* values).
10433 *
10434 * @param aFlags Combination of SaveSTS_* flags.
10435 *
10436 * @note Locks objects for writing.
10437 */
10438HRESULT Machine::i_saveStateSettings(int aFlags)
10439{
10440 if (aFlags == 0)
10441 return S_OK;
10442
10443 AutoCaller autoCaller(this);
10444 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10445
10446 /* This object's write lock is also necessary to serialize file access
10447 * (prevent concurrent reads and writes) */
10448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10449
10450 HRESULT rc = S_OK;
10451
10452 Assert(mData->pMachineConfigFile);
10453
10454 try
10455 {
10456 if (aFlags & SaveSTS_CurStateModified)
10457 mData->pMachineConfigFile->fCurrentStateModified = true;
10458
10459 if (aFlags & SaveSTS_StateFilePath)
10460 {
10461 if (!mSSData->strStateFilePath.isEmpty())
10462 /* try to make the file name relative to the settings file dir */
10463 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10464 else
10465 mData->pMachineConfigFile->strStateFile.setNull();
10466 }
10467
10468 if (aFlags & SaveSTS_StateTimeStamp)
10469 {
10470 Assert( mData->mMachineState != MachineState_Aborted
10471 || mSSData->strStateFilePath.isEmpty());
10472
10473 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10474
10475 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10476//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10477 }
10478
10479 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10480 }
10481 catch (...)
10482 {
10483 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10484 }
10485
10486 return rc;
10487}
10488
10489/**
10490 * Ensures that the given medium is added to a media registry. If this machine
10491 * was created with 4.0 or later, then the machine registry is used. Otherwise
10492 * the global VirtualBox media registry is used.
10493 *
10494 * Caller must NOT hold machine lock, media tree or any medium locks!
10495 *
10496 * @param pMedium
10497 */
10498void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10499{
10500 /* Paranoia checks: do not hold machine or media tree locks. */
10501 AssertReturnVoid(!isWriteLockOnCurrentThread());
10502 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10503
10504 ComObjPtr<Medium> pBase;
10505 {
10506 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10507 pBase = pMedium->i_getBase();
10508 }
10509
10510 /* Paranoia checks: do not hold medium locks. */
10511 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10512 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10513
10514 // decide which medium registry to use now that the medium is attached:
10515 Guid uuid;
10516 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10517 // machine XML is VirtualBox 4.0 or higher:
10518 uuid = i_getId(); // machine UUID
10519 else
10520 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10521
10522 if (pMedium->i_addRegistry(uuid))
10523 mParent->i_markRegistryModified(uuid);
10524
10525 /* For more complex hard disk structures it can happen that the base
10526 * medium isn't yet associated with any medium registry. Do that now. */
10527 if (pMedium != pBase)
10528 {
10529 /* Tree lock needed by Medium::addRegistry when recursing. */
10530 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10531 if (pBase->i_addRegistryRecursive(uuid))
10532 {
10533 treeLock.release();
10534 mParent->i_markRegistryModified(uuid);
10535 }
10536 }
10537}
10538
10539/**
10540 * Creates differencing hard disks for all normal hard disks attached to this
10541 * machine and a new set of attachments to refer to created disks.
10542 *
10543 * Used when taking a snapshot or when deleting the current state. Gets called
10544 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10545 *
10546 * This method assumes that mMediaData contains the original hard disk attachments
10547 * it needs to create diffs for. On success, these attachments will be replaced
10548 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10549 * called to delete created diffs which will also rollback mMediaData and restore
10550 * whatever was backed up before calling this method.
10551 *
10552 * Attachments with non-normal hard disks are left as is.
10553 *
10554 * If @a aOnline is @c false then the original hard disks that require implicit
10555 * diffs will be locked for reading. Otherwise it is assumed that they are
10556 * already locked for writing (when the VM was started). Note that in the latter
10557 * case it is responsibility of the caller to lock the newly created diffs for
10558 * writing if this method succeeds.
10559 *
10560 * @param aProgress Progress object to run (must contain at least as
10561 * many operations left as the number of hard disks
10562 * attached).
10563 * @param aOnline Whether the VM was online prior to this operation.
10564 *
10565 * @note The progress object is not marked as completed, neither on success nor
10566 * on failure. This is a responsibility of the caller.
10567 *
10568 * @note Locks this object and the media tree for writing.
10569 */
10570HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10571 ULONG aWeight,
10572 bool aOnline)
10573{
10574 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10575
10576 AutoCaller autoCaller(this);
10577 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10578
10579 AutoMultiWriteLock2 alock(this->lockHandle(),
10580 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10581
10582 /* must be in a protective state because we release the lock below */
10583 AssertReturn( mData->mMachineState == MachineState_Saving
10584 || mData->mMachineState == MachineState_LiveSnapshotting
10585 || mData->mMachineState == MachineState_RestoringSnapshot
10586 || mData->mMachineState == MachineState_DeletingSnapshot
10587 , E_FAIL);
10588
10589 HRESULT rc = S_OK;
10590
10591 // use appropriate locked media map (online or offline)
10592 MediumLockListMap lockedMediaOffline;
10593 MediumLockListMap *lockedMediaMap;
10594 if (aOnline)
10595 lockedMediaMap = &mData->mSession.mLockedMedia;
10596 else
10597 lockedMediaMap = &lockedMediaOffline;
10598
10599 try
10600 {
10601 if (!aOnline)
10602 {
10603 /* lock all attached hard disks early to detect "in use"
10604 * situations before creating actual diffs */
10605 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10606 it != mMediaData->mAttachments.end();
10607 ++it)
10608 {
10609 MediumAttachment* pAtt = *it;
10610 if (pAtt->i_getType() == DeviceType_HardDisk)
10611 {
10612 Medium* pMedium = pAtt->i_getMedium();
10613 Assert(pMedium);
10614
10615 MediumLockList *pMediumLockList(new MediumLockList());
10616 alock.release();
10617 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10618 false /* fMediumLockWrite */,
10619 false /* fMediumLockWriteAll */,
10620 NULL,
10621 *pMediumLockList);
10622 alock.acquire();
10623 if (FAILED(rc))
10624 {
10625 delete pMediumLockList;
10626 throw rc;
10627 }
10628 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10629 if (FAILED(rc))
10630 {
10631 throw setError(rc,
10632 tr("Collecting locking information for all attached media failed"));
10633 }
10634 }
10635 }
10636
10637 /* Now lock all media. If this fails, nothing is locked. */
10638 alock.release();
10639 rc = lockedMediaMap->Lock();
10640 alock.acquire();
10641 if (FAILED(rc))
10642 {
10643 throw setError(rc,
10644 tr("Locking of attached media failed"));
10645 }
10646 }
10647
10648 /* remember the current list (note that we don't use backup() since
10649 * mMediaData may be already backed up) */
10650 MediaData::AttachmentList atts = mMediaData->mAttachments;
10651
10652 /* start from scratch */
10653 mMediaData->mAttachments.clear();
10654
10655 /* go through remembered attachments and create diffs for normal hard
10656 * disks and attach them */
10657 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10658 it != atts.end();
10659 ++it)
10660 {
10661 MediumAttachment* pAtt = *it;
10662
10663 DeviceType_T devType = pAtt->i_getType();
10664 Medium* pMedium = pAtt->i_getMedium();
10665
10666 if ( devType != DeviceType_HardDisk
10667 || pMedium == NULL
10668 || pMedium->i_getType() != MediumType_Normal)
10669 {
10670 /* copy the attachment as is */
10671
10672 /** @todo the progress object created in Console::TakeSnaphot
10673 * only expects operations for hard disks. Later other
10674 * device types need to show up in the progress as well. */
10675 if (devType == DeviceType_HardDisk)
10676 {
10677 if (pMedium == NULL)
10678 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10679 aWeight); // weight
10680 else
10681 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10682 pMedium->i_getBase()->i_getName().c_str()).raw(),
10683 aWeight); // weight
10684 }
10685
10686 mMediaData->mAttachments.push_back(pAtt);
10687 continue;
10688 }
10689
10690 /* need a diff */
10691 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10692 pMedium->i_getBase()->i_getName().c_str()).raw(),
10693 aWeight); // weight
10694
10695 Utf8Str strFullSnapshotFolder;
10696 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10697
10698 ComObjPtr<Medium> diff;
10699 diff.createObject();
10700 // store the diff in the same registry as the parent
10701 // (this cannot fail here because we can't create implicit diffs for
10702 // unregistered images)
10703 Guid uuidRegistryParent;
10704 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10705 Assert(fInRegistry); NOREF(fInRegistry);
10706 rc = diff->init(mParent,
10707 pMedium->i_getPreferredDiffFormat(),
10708 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10709 uuidRegistryParent,
10710 DeviceType_HardDisk);
10711 if (FAILED(rc)) throw rc;
10712
10713 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10714 * the push_back? Looks like we're going to release medium with the
10715 * wrong kind of lock (general issue with if we fail anywhere at all)
10716 * and an orphaned VDI in the snapshots folder. */
10717
10718 /* update the appropriate lock list */
10719 MediumLockList *pMediumLockList;
10720 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10721 AssertComRCThrowRC(rc);
10722 if (aOnline)
10723 {
10724 alock.release();
10725 /* The currently attached medium will be read-only, change
10726 * the lock type to read. */
10727 rc = pMediumLockList->Update(pMedium, false);
10728 alock.acquire();
10729 AssertComRCThrowRC(rc);
10730 }
10731
10732 /* release the locks before the potentially lengthy operation */
10733 alock.release();
10734 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10735 pMediumLockList,
10736 NULL /* aProgress */,
10737 true /* aWait */);
10738 alock.acquire();
10739 if (FAILED(rc)) throw rc;
10740
10741 /* actual lock list update is done in Medium::commitMedia */
10742
10743 rc = diff->i_addBackReference(mData->mUuid);
10744 AssertComRCThrowRC(rc);
10745
10746 /* add a new attachment */
10747 ComObjPtr<MediumAttachment> attachment;
10748 attachment.createObject();
10749 rc = attachment->init(this,
10750 diff,
10751 pAtt->i_getControllerName(),
10752 pAtt->i_getPort(),
10753 pAtt->i_getDevice(),
10754 DeviceType_HardDisk,
10755 true /* aImplicit */,
10756 false /* aPassthrough */,
10757 false /* aTempEject */,
10758 pAtt->i_getNonRotational(),
10759 pAtt->i_getDiscard(),
10760 pAtt->i_getHotPluggable(),
10761 pAtt->i_getBandwidthGroup());
10762 if (FAILED(rc)) throw rc;
10763
10764 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10765 AssertComRCThrowRC(rc);
10766 mMediaData->mAttachments.push_back(attachment);
10767 }
10768 }
10769 catch (HRESULT aRC) { rc = aRC; }
10770
10771 /* unlock all hard disks we locked when there is no VM */
10772 if (!aOnline)
10773 {
10774 ErrorInfoKeeper eik;
10775
10776 HRESULT rc1 = lockedMediaMap->Clear();
10777 AssertComRC(rc1);
10778 }
10779
10780 return rc;
10781}
10782
10783/**
10784 * Deletes implicit differencing hard disks created either by
10785 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10786 *
10787 * Note that to delete hard disks created by #AttachDevice() this method is
10788 * called from #fixupMedia() when the changes are rolled back.
10789 *
10790 * @note Locks this object and the media tree for writing.
10791 */
10792HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10793{
10794 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10795
10796 AutoCaller autoCaller(this);
10797 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10798
10799 AutoMultiWriteLock2 alock(this->lockHandle(),
10800 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10801
10802 /* We absolutely must have backed up state. */
10803 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10804
10805 /* Check if there are any implicitly created diff images. */
10806 bool fImplicitDiffs = false;
10807 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10808 it != mMediaData->mAttachments.end();
10809 ++it)
10810 {
10811 const ComObjPtr<MediumAttachment> &pAtt = *it;
10812 if (pAtt->i_isImplicit())
10813 {
10814 fImplicitDiffs = true;
10815 break;
10816 }
10817 }
10818 /* If there is nothing to do, leave early. This saves lots of image locking
10819 * effort. It also avoids a MachineStateChanged event without real reason.
10820 * This is important e.g. when loading a VM config, because there should be
10821 * no events. Otherwise API clients can become thoroughly confused for
10822 * inaccessible VMs (the code for loading VM configs uses this method for
10823 * cleanup if the config makes no sense), as they take such events as an
10824 * indication that the VM is alive, and they would force the VM config to
10825 * be reread, leading to an endless loop. */
10826 if (!fImplicitDiffs)
10827 return S_OK;
10828
10829 HRESULT rc = S_OK;
10830 MachineState_T oldState = mData->mMachineState;
10831
10832 /* will release the lock before the potentially lengthy operation,
10833 * so protect with the special state (unless already protected) */
10834 if ( oldState != MachineState_Saving
10835 && oldState != MachineState_LiveSnapshotting
10836 && oldState != MachineState_RestoringSnapshot
10837 && oldState != MachineState_DeletingSnapshot
10838 && oldState != MachineState_DeletingSnapshotOnline
10839 && oldState != MachineState_DeletingSnapshotPaused
10840 )
10841 i_setMachineState(MachineState_SettingUp);
10842
10843 // use appropriate locked media map (online or offline)
10844 MediumLockListMap lockedMediaOffline;
10845 MediumLockListMap *lockedMediaMap;
10846 if (aOnline)
10847 lockedMediaMap = &mData->mSession.mLockedMedia;
10848 else
10849 lockedMediaMap = &lockedMediaOffline;
10850
10851 try
10852 {
10853 if (!aOnline)
10854 {
10855 /* lock all attached hard disks early to detect "in use"
10856 * situations before deleting actual diffs */
10857 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10858 it != mMediaData->mAttachments.end();
10859 ++it)
10860 {
10861 MediumAttachment* pAtt = *it;
10862 if (pAtt->i_getType() == DeviceType_HardDisk)
10863 {
10864 Medium* pMedium = pAtt->i_getMedium();
10865 Assert(pMedium);
10866
10867 MediumLockList *pMediumLockList(new MediumLockList());
10868 alock.release();
10869 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10870 false /* fMediumLockWrite */,
10871 false /* fMediumLockWriteAll */,
10872 NULL,
10873 *pMediumLockList);
10874 alock.acquire();
10875
10876 if (FAILED(rc))
10877 {
10878 delete pMediumLockList;
10879 throw rc;
10880 }
10881
10882 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10883 if (FAILED(rc))
10884 throw rc;
10885 }
10886 }
10887
10888 if (FAILED(rc))
10889 throw rc;
10890 } // end of offline
10891
10892 /* Lock lists are now up to date and include implicitly created media */
10893
10894 /* Go through remembered attachments and delete all implicitly created
10895 * diffs and fix up the attachment information */
10896 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10897 MediaData::AttachmentList implicitAtts;
10898 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10899 it != mMediaData->mAttachments.end();
10900 ++it)
10901 {
10902 ComObjPtr<MediumAttachment> pAtt = *it;
10903 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10904 if (pMedium.isNull())
10905 continue;
10906
10907 // Implicit attachments go on the list for deletion and back references are removed.
10908 if (pAtt->i_isImplicit())
10909 {
10910 /* Deassociate and mark for deletion */
10911 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10912 rc = pMedium->i_removeBackReference(mData->mUuid);
10913 if (FAILED(rc))
10914 throw rc;
10915 implicitAtts.push_back(pAtt);
10916 continue;
10917 }
10918
10919 /* Was this medium attached before? */
10920 if (!i_findAttachment(oldAtts, pMedium))
10921 {
10922 /* no: de-associate */
10923 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10924 rc = pMedium->i_removeBackReference(mData->mUuid);
10925 if (FAILED(rc))
10926 throw rc;
10927 continue;
10928 }
10929 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10930 }
10931
10932 /* If there are implicit attachments to delete, throw away the lock
10933 * map contents (which will unlock all media) since the medium
10934 * attachments will be rolled back. Below we need to completely
10935 * recreate the lock map anyway since it is infinitely complex to
10936 * do this incrementally (would need reconstructing each attachment
10937 * change, which would be extremely hairy). */
10938 if (implicitAtts.size() != 0)
10939 {
10940 ErrorInfoKeeper eik;
10941
10942 HRESULT rc1 = lockedMediaMap->Clear();
10943 AssertComRC(rc1);
10944 }
10945
10946 /* rollback hard disk changes */
10947 mMediaData.rollback();
10948
10949 MultiResult mrc(S_OK);
10950
10951 // Delete unused implicit diffs.
10952 if (implicitAtts.size() != 0)
10953 {
10954 alock.release();
10955
10956 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10957 {
10958 // Remove medium associated with this attachment.
10959 ComObjPtr<MediumAttachment> pAtt = *it;
10960 Assert(pAtt);
10961 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10962 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10963 Assert(pMedium);
10964
10965 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10966 // continue on delete failure, just collect error messages
10967 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10968 pMedium->i_getLocationFull().c_str() ));
10969 mrc = rc;
10970 }
10971
10972 alock.acquire();
10973
10974 /* if there is a VM recreate media lock map as mentioned above,
10975 * otherwise it is a waste of time and we leave things unlocked */
10976 if (aOnline)
10977 {
10978 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10979 /* must never be NULL, but better safe than sorry */
10980 if (!pMachine.isNull())
10981 {
10982 alock.release();
10983 rc = mData->mSession.mMachine->i_lockMedia();
10984 alock.acquire();
10985 if (FAILED(rc))
10986 throw rc;
10987 }
10988 }
10989 }
10990 }
10991 catch (HRESULT aRC) {rc = aRC;}
10992
10993 if (mData->mMachineState == MachineState_SettingUp)
10994 i_setMachineState(oldState);
10995
10996 /* unlock all hard disks we locked when there is no VM */
10997 if (!aOnline)
10998 {
10999 ErrorInfoKeeper eik;
11000
11001 HRESULT rc1 = lockedMediaMap->Clear();
11002 AssertComRC(rc1);
11003 }
11004
11005 return rc;
11006}
11007
11008
11009/**
11010 * Looks through the given list of media attachments for one with the given parameters
11011 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11012 * can be searched as well if needed.
11013 *
11014 * @param list
11015 * @param aControllerName
11016 * @param aControllerPort
11017 * @param aDevice
11018 * @return
11019 */
11020MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11021 IN_BSTR aControllerName,
11022 LONG aControllerPort,
11023 LONG aDevice)
11024{
11025 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11026 {
11027 MediumAttachment *pAttach = *it;
11028 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11029 return pAttach;
11030 }
11031
11032 return NULL;
11033}
11034
11035/**
11036 * Looks through the given list of media attachments for one with the given parameters
11037 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11038 * can be searched as well if needed.
11039 *
11040 * @param list
11041 * @param aControllerName
11042 * @param aControllerPort
11043 * @param aDevice
11044 * @return
11045 */
11046MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11047 ComObjPtr<Medium> pMedium)
11048{
11049 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11050 {
11051 MediumAttachment *pAttach = *it;
11052 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11053 if (pMediumThis == pMedium)
11054 return pAttach;
11055 }
11056
11057 return NULL;
11058}
11059
11060/**
11061 * Looks through the given list of media attachments for one with the given parameters
11062 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11063 * can be searched as well if needed.
11064 *
11065 * @param list
11066 * @param aControllerName
11067 * @param aControllerPort
11068 * @param aDevice
11069 * @return
11070 */
11071MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11072 Guid &id)
11073{
11074 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11075 {
11076 MediumAttachment *pAttach = *it;
11077 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11078 if (pMediumThis->i_getId() == id)
11079 return pAttach;
11080 }
11081
11082 return NULL;
11083}
11084
11085/**
11086 * Main implementation for Machine::DetachDevice. This also gets called
11087 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11088 *
11089 * @param pAttach Medium attachment to detach.
11090 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11091 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11092 * SnapshotMachine, and this must be its snapshot.
11093 * @return
11094 */
11095HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11096 AutoWriteLock &writeLock,
11097 Snapshot *pSnapshot)
11098{
11099 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11100 DeviceType_T mediumType = pAttach->i_getType();
11101
11102 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11103
11104 if (pAttach->i_isImplicit())
11105 {
11106 /* attempt to implicitly delete the implicitly created diff */
11107
11108 /// @todo move the implicit flag from MediumAttachment to Medium
11109 /// and forbid any hard disk operation when it is implicit. Or maybe
11110 /// a special media state for it to make it even more simple.
11111
11112 Assert(mMediaData.isBackedUp());
11113
11114 /* will release the lock before the potentially lengthy operation, so
11115 * protect with the special state */
11116 MachineState_T oldState = mData->mMachineState;
11117 i_setMachineState(MachineState_SettingUp);
11118
11119 writeLock.release();
11120
11121 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11122 true /*aWait*/);
11123
11124 writeLock.acquire();
11125
11126 i_setMachineState(oldState);
11127
11128 if (FAILED(rc)) return rc;
11129 }
11130
11131 i_setModified(IsModified_Storage);
11132 mMediaData.backup();
11133 mMediaData->mAttachments.remove(pAttach);
11134
11135 if (!oldmedium.isNull())
11136 {
11137 // if this is from a snapshot, do not defer detachment to commitMedia()
11138 if (pSnapshot)
11139 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11140 // else if non-hard disk media, do not defer detachment to commitMedia() either
11141 else if (mediumType != DeviceType_HardDisk)
11142 oldmedium->i_removeBackReference(mData->mUuid);
11143 }
11144
11145 return S_OK;
11146}
11147
11148/**
11149 * Goes thru all media of the given list and
11150 *
11151 * 1) calls i_detachDevice() on each of them for this machine and
11152 * 2) adds all Medium objects found in the process to the given list,
11153 * depending on cleanupMode.
11154 *
11155 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11156 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11157 * media to the list.
11158 *
11159 * This gets called from Machine::Unregister, both for the actual Machine and
11160 * the SnapshotMachine objects that might be found in the snapshots.
11161 *
11162 * Requires caller and locking. The machine lock must be passed in because it
11163 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11164 *
11165 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11166 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11167 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11168 * Full, then all media get added;
11169 * otherwise no media get added.
11170 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11171 * @return
11172 */
11173HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11174 Snapshot *pSnapshot,
11175 CleanupMode_T cleanupMode,
11176 MediaList &llMedia)
11177{
11178 Assert(isWriteLockOnCurrentThread());
11179
11180 HRESULT rc;
11181
11182 // make a temporary list because i_detachDevice invalidates iterators into
11183 // mMediaData->mAttachments
11184 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11185
11186 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11187 {
11188 ComObjPtr<MediumAttachment> &pAttach = *it;
11189 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11190
11191 if (!pMedium.isNull())
11192 {
11193 AutoCaller mac(pMedium);
11194 if (FAILED(mac.rc())) return mac.rc();
11195 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11196 DeviceType_T devType = pMedium->i_getDeviceType();
11197 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11198 && devType == DeviceType_HardDisk)
11199 || (cleanupMode == CleanupMode_Full)
11200 )
11201 {
11202 llMedia.push_back(pMedium);
11203 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11204 /* Not allowed to keep this lock as below we need the parent
11205 * medium lock, and the lock order is parent to child. */
11206 lock.release();
11207 /*
11208 * Search for medias which are not attached to any machine, but
11209 * in the chain to an attached disk. Mediums are only consided
11210 * if they are:
11211 * - have only one child
11212 * - no references to any machines
11213 * - are of normal medium type
11214 */
11215 while (!pParent.isNull())
11216 {
11217 AutoCaller mac1(pParent);
11218 if (FAILED(mac1.rc())) return mac1.rc();
11219 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11220 if (pParent->i_getChildren().size() == 1)
11221 {
11222 if ( pParent->i_getMachineBackRefCount() == 0
11223 && pParent->i_getType() == MediumType_Normal
11224 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11225 llMedia.push_back(pParent);
11226 }
11227 else
11228 break;
11229 pParent = pParent->i_getParent();
11230 }
11231 }
11232 }
11233
11234 // real machine: then we need to use the proper method
11235 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11236
11237 if (FAILED(rc))
11238 return rc;
11239 }
11240
11241 return S_OK;
11242}
11243
11244/**
11245 * Perform deferred hard disk detachments.
11246 *
11247 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11248 * backed up).
11249 *
11250 * If @a aOnline is @c true then this method will also unlock the old hard disks
11251 * for which the new implicit diffs were created and will lock these new diffs for
11252 * writing.
11253 *
11254 * @param aOnline Whether the VM was online prior to this operation.
11255 *
11256 * @note Locks this object for writing!
11257 */
11258void Machine::i_commitMedia(bool aOnline /*= false*/)
11259{
11260 AutoCaller autoCaller(this);
11261 AssertComRCReturnVoid(autoCaller.rc());
11262
11263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11264
11265 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11266
11267 HRESULT rc = S_OK;
11268
11269 /* no attach/detach operations -- nothing to do */
11270 if (!mMediaData.isBackedUp())
11271 return;
11272
11273 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11274 bool fMediaNeedsLocking = false;
11275
11276 /* enumerate new attachments */
11277 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11278 it != mMediaData->mAttachments.end();
11279 ++it)
11280 {
11281 MediumAttachment *pAttach = *it;
11282
11283 pAttach->i_commit();
11284
11285 Medium* pMedium = pAttach->i_getMedium();
11286 bool fImplicit = pAttach->i_isImplicit();
11287
11288 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11289 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11290 fImplicit));
11291
11292 /** @todo convert all this Machine-based voodoo to MediumAttachment
11293 * based commit logic. */
11294 if (fImplicit)
11295 {
11296 /* convert implicit attachment to normal */
11297 pAttach->i_setImplicit(false);
11298
11299 if ( aOnline
11300 && pMedium
11301 && pAttach->i_getType() == DeviceType_HardDisk
11302 )
11303 {
11304 /* update the appropriate lock list */
11305 MediumLockList *pMediumLockList;
11306 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11307 AssertComRC(rc);
11308 if (pMediumLockList)
11309 {
11310 /* unlock if there's a need to change the locking */
11311 if (!fMediaNeedsLocking)
11312 {
11313 rc = mData->mSession.mLockedMedia.Unlock();
11314 AssertComRC(rc);
11315 fMediaNeedsLocking = true;
11316 }
11317 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11318 AssertComRC(rc);
11319 rc = pMediumLockList->Append(pMedium, true);
11320 AssertComRC(rc);
11321 }
11322 }
11323
11324 continue;
11325 }
11326
11327 if (pMedium)
11328 {
11329 /* was this medium attached before? */
11330 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11331 {
11332 MediumAttachment *pOldAttach = *oldIt;
11333 if (pOldAttach->i_getMedium() == pMedium)
11334 {
11335 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11336
11337 /* yes: remove from old to avoid de-association */
11338 oldAtts.erase(oldIt);
11339 break;
11340 }
11341 }
11342 }
11343 }
11344
11345 /* enumerate remaining old attachments and de-associate from the
11346 * current machine state */
11347 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11348 {
11349 MediumAttachment *pAttach = *it;
11350 Medium* pMedium = pAttach->i_getMedium();
11351
11352 /* Detach only hard disks, since DVD/floppy media is detached
11353 * instantly in MountMedium. */
11354 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11355 {
11356 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11357
11358 /* now de-associate from the current machine state */
11359 rc = pMedium->i_removeBackReference(mData->mUuid);
11360 AssertComRC(rc);
11361
11362 if (aOnline)
11363 {
11364 /* unlock since medium is not used anymore */
11365 MediumLockList *pMediumLockList;
11366 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11367 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11368 {
11369 /* this happens for online snapshots, there the attachment
11370 * is changing, but only to a diff image created under
11371 * the old one, so there is no separate lock list */
11372 Assert(!pMediumLockList);
11373 }
11374 else
11375 {
11376 AssertComRC(rc);
11377 if (pMediumLockList)
11378 {
11379 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11380 AssertComRC(rc);
11381 }
11382 }
11383 }
11384 }
11385 }
11386
11387 /* take media locks again so that the locking state is consistent */
11388 if (fMediaNeedsLocking)
11389 {
11390 Assert(aOnline);
11391 rc = mData->mSession.mLockedMedia.Lock();
11392 AssertComRC(rc);
11393 }
11394
11395 /* commit the hard disk changes */
11396 mMediaData.commit();
11397
11398 if (i_isSessionMachine())
11399 {
11400 /*
11401 * Update the parent machine to point to the new owner.
11402 * This is necessary because the stored parent will point to the
11403 * session machine otherwise and cause crashes or errors later
11404 * when the session machine gets invalid.
11405 */
11406 /** @todo Change the MediumAttachment class to behave like any other
11407 * class in this regard by creating peer MediumAttachment
11408 * objects for session machines and share the data with the peer
11409 * machine.
11410 */
11411 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11412 it != mMediaData->mAttachments.end();
11413 ++it)
11414 (*it)->i_updateParentMachine(mPeer);
11415
11416 /* attach new data to the primary machine and reshare it */
11417 mPeer->mMediaData.attach(mMediaData);
11418 }
11419
11420 return;
11421}
11422
11423/**
11424 * Perform deferred deletion of implicitly created diffs.
11425 *
11426 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11427 * backed up).
11428 *
11429 * @note Locks this object for writing!
11430 */
11431void Machine::i_rollbackMedia()
11432{
11433 AutoCaller autoCaller(this);
11434 AssertComRCReturnVoid(autoCaller.rc());
11435
11436 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11437 LogFlowThisFunc(("Entering rollbackMedia\n"));
11438
11439 HRESULT rc = S_OK;
11440
11441 /* no attach/detach operations -- nothing to do */
11442 if (!mMediaData.isBackedUp())
11443 return;
11444
11445 /* enumerate new attachments */
11446 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11447 it != mMediaData->mAttachments.end();
11448 ++it)
11449 {
11450 MediumAttachment *pAttach = *it;
11451 /* Fix up the backrefs for DVD/floppy media. */
11452 if (pAttach->i_getType() != DeviceType_HardDisk)
11453 {
11454 Medium* pMedium = pAttach->i_getMedium();
11455 if (pMedium)
11456 {
11457 rc = pMedium->i_removeBackReference(mData->mUuid);
11458 AssertComRC(rc);
11459 }
11460 }
11461
11462 (*it)->i_rollback();
11463
11464 pAttach = *it;
11465 /* Fix up the backrefs for DVD/floppy media. */
11466 if (pAttach->i_getType() != DeviceType_HardDisk)
11467 {
11468 Medium* pMedium = pAttach->i_getMedium();
11469 if (pMedium)
11470 {
11471 rc = pMedium->i_addBackReference(mData->mUuid);
11472 AssertComRC(rc);
11473 }
11474 }
11475 }
11476
11477 /** @todo convert all this Machine-based voodoo to MediumAttachment
11478 * based rollback logic. */
11479 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11480
11481 return;
11482}
11483
11484/**
11485 * Returns true if the settings file is located in the directory named exactly
11486 * as the machine; this means, among other things, that the machine directory
11487 * should be auto-renamed.
11488 *
11489 * @param aSettingsDir if not NULL, the full machine settings file directory
11490 * name will be assigned there.
11491 *
11492 * @note Doesn't lock anything.
11493 * @note Not thread safe (must be called from this object's lock).
11494 */
11495bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11496{
11497 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11498 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11499 if (aSettingsDir)
11500 *aSettingsDir = strMachineDirName;
11501 strMachineDirName.stripPath(); // vmname
11502 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11503 strConfigFileOnly.stripPath() // vmname.vbox
11504 .stripSuffix(); // vmname
11505 /** @todo hack, make somehow use of ComposeMachineFilename */
11506 if (mUserData->s.fDirectoryIncludesUUID)
11507 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11508
11509 AssertReturn(!strMachineDirName.isEmpty(), false);
11510 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11511
11512 return strMachineDirName == strConfigFileOnly;
11513}
11514
11515/**
11516 * Discards all changes to machine settings.
11517 *
11518 * @param aNotify Whether to notify the direct session about changes or not.
11519 *
11520 * @note Locks objects for writing!
11521 */
11522void Machine::i_rollback(bool aNotify)
11523{
11524 AutoCaller autoCaller(this);
11525 AssertComRCReturn(autoCaller.rc(), (void)0);
11526
11527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11528
11529 if (!mStorageControllers.isNull())
11530 {
11531 if (mStorageControllers.isBackedUp())
11532 {
11533 /* unitialize all new devices (absent in the backed up list). */
11534 StorageControllerList::const_iterator it = mStorageControllers->begin();
11535 StorageControllerList *backedList = mStorageControllers.backedUpData();
11536 while (it != mStorageControllers->end())
11537 {
11538 if ( std::find(backedList->begin(), backedList->end(), *it)
11539 == backedList->end()
11540 )
11541 {
11542 (*it)->uninit();
11543 }
11544 ++it;
11545 }
11546
11547 /* restore the list */
11548 mStorageControllers.rollback();
11549 }
11550
11551 /* rollback any changes to devices after restoring the list */
11552 if (mData->flModifications & IsModified_Storage)
11553 {
11554 StorageControllerList::const_iterator it = mStorageControllers->begin();
11555 while (it != mStorageControllers->end())
11556 {
11557 (*it)->i_rollback();
11558 ++it;
11559 }
11560 }
11561 }
11562
11563 if (!mUSBControllers.isNull())
11564 {
11565 if (mUSBControllers.isBackedUp())
11566 {
11567 /* unitialize all new devices (absent in the backed up list). */
11568 USBControllerList::const_iterator it = mUSBControllers->begin();
11569 USBControllerList *backedList = mUSBControllers.backedUpData();
11570 while (it != mUSBControllers->end())
11571 {
11572 if ( std::find(backedList->begin(), backedList->end(), *it)
11573 == backedList->end()
11574 )
11575 {
11576 (*it)->uninit();
11577 }
11578 ++it;
11579 }
11580
11581 /* restore the list */
11582 mUSBControllers.rollback();
11583 }
11584
11585 /* rollback any changes to devices after restoring the list */
11586 if (mData->flModifications & IsModified_USB)
11587 {
11588 USBControllerList::const_iterator it = mUSBControllers->begin();
11589 while (it != mUSBControllers->end())
11590 {
11591 (*it)->i_rollback();
11592 ++it;
11593 }
11594 }
11595 }
11596
11597 mUserData.rollback();
11598
11599 mHWData.rollback();
11600
11601 if (mData->flModifications & IsModified_Storage)
11602 i_rollbackMedia();
11603
11604 if (mBIOSSettings)
11605 mBIOSSettings->i_rollback();
11606
11607 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11608 mVRDEServer->i_rollback();
11609
11610 if (mAudioAdapter)
11611 mAudioAdapter->i_rollback();
11612
11613 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11614 mUSBDeviceFilters->i_rollback();
11615
11616 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11617 mBandwidthControl->i_rollback();
11618
11619 if (!mHWData.isNull())
11620 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11621 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11622 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11623 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11624
11625 if (mData->flModifications & IsModified_NetworkAdapters)
11626 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11627 if ( mNetworkAdapters[slot]
11628 && mNetworkAdapters[slot]->i_isModified())
11629 {
11630 mNetworkAdapters[slot]->i_rollback();
11631 networkAdapters[slot] = mNetworkAdapters[slot];
11632 }
11633
11634 if (mData->flModifications & IsModified_SerialPorts)
11635 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11636 if ( mSerialPorts[slot]
11637 && mSerialPorts[slot]->i_isModified())
11638 {
11639 mSerialPorts[slot]->i_rollback();
11640 serialPorts[slot] = mSerialPorts[slot];
11641 }
11642
11643 if (mData->flModifications & IsModified_ParallelPorts)
11644 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11645 if ( mParallelPorts[slot]
11646 && mParallelPorts[slot]->i_isModified())
11647 {
11648 mParallelPorts[slot]->i_rollback();
11649 parallelPorts[slot] = mParallelPorts[slot];
11650 }
11651
11652 if (aNotify)
11653 {
11654 /* inform the direct session about changes */
11655
11656 ComObjPtr<Machine> that = this;
11657 uint32_t flModifications = mData->flModifications;
11658 alock.release();
11659
11660 if (flModifications & IsModified_SharedFolders)
11661 that->i_onSharedFolderChange();
11662
11663 if (flModifications & IsModified_VRDEServer)
11664 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11665 if (flModifications & IsModified_USB)
11666 that->i_onUSBControllerChange();
11667
11668 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11669 if (networkAdapters[slot])
11670 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11671 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11672 if (serialPorts[slot])
11673 that->i_onSerialPortChange(serialPorts[slot]);
11674 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11675 if (parallelPorts[slot])
11676 that->i_onParallelPortChange(parallelPorts[slot]);
11677
11678 if (flModifications & IsModified_Storage)
11679 that->i_onStorageControllerChange();
11680
11681#if 0
11682 if (flModifications & IsModified_BandwidthControl)
11683 that->onBandwidthControlChange();
11684#endif
11685 }
11686}
11687
11688/**
11689 * Commits all the changes to machine settings.
11690 *
11691 * Note that this operation is supposed to never fail.
11692 *
11693 * @note Locks this object and children for writing.
11694 */
11695void Machine::i_commit()
11696{
11697 AutoCaller autoCaller(this);
11698 AssertComRCReturnVoid(autoCaller.rc());
11699
11700 AutoCaller peerCaller(mPeer);
11701 AssertComRCReturnVoid(peerCaller.rc());
11702
11703 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11704
11705 /*
11706 * use safe commit to ensure Snapshot machines (that share mUserData)
11707 * will still refer to a valid memory location
11708 */
11709 mUserData.commitCopy();
11710
11711 mHWData.commit();
11712
11713 if (mMediaData.isBackedUp())
11714 i_commitMedia(Global::IsOnline(mData->mMachineState));
11715
11716 mBIOSSettings->i_commit();
11717 mVRDEServer->i_commit();
11718 mAudioAdapter->i_commit();
11719 mUSBDeviceFilters->i_commit();
11720 mBandwidthControl->i_commit();
11721
11722 /* Since mNetworkAdapters is a list which might have been changed (resized)
11723 * without using the Backupable<> template we need to handle the copying
11724 * of the list entries manually, including the creation of peers for the
11725 * new objects. */
11726 bool commitNetworkAdapters = false;
11727 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11728 if (mPeer)
11729 {
11730 /* commit everything, even the ones which will go away */
11731 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11732 mNetworkAdapters[slot]->i_commit();
11733 /* copy over the new entries, creating a peer and uninit the original */
11734 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11735 for (size_t slot = 0; slot < newSize; slot++)
11736 {
11737 /* look if this adapter has a peer device */
11738 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11739 if (!peer)
11740 {
11741 /* no peer means the adapter is a newly created one;
11742 * create a peer owning data this data share it with */
11743 peer.createObject();
11744 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11745 }
11746 mPeer->mNetworkAdapters[slot] = peer;
11747 }
11748 /* uninit any no longer needed network adapters */
11749 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11750 mNetworkAdapters[slot]->uninit();
11751 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11752 {
11753 if (mPeer->mNetworkAdapters[slot])
11754 mPeer->mNetworkAdapters[slot]->uninit();
11755 }
11756 /* Keep the original network adapter count until this point, so that
11757 * discarding a chipset type change will not lose settings. */
11758 mNetworkAdapters.resize(newSize);
11759 mPeer->mNetworkAdapters.resize(newSize);
11760 }
11761 else
11762 {
11763 /* we have no peer (our parent is the newly created machine);
11764 * just commit changes to the network adapters */
11765 commitNetworkAdapters = true;
11766 }
11767 if (commitNetworkAdapters)
11768 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11769 mNetworkAdapters[slot]->i_commit();
11770
11771 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11772 mSerialPorts[slot]->i_commit();
11773 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11774 mParallelPorts[slot]->i_commit();
11775
11776 bool commitStorageControllers = false;
11777
11778 if (mStorageControllers.isBackedUp())
11779 {
11780 mStorageControllers.commit();
11781
11782 if (mPeer)
11783 {
11784 /* Commit all changes to new controllers (this will reshare data with
11785 * peers for those who have peers) */
11786 StorageControllerList *newList = new StorageControllerList();
11787 StorageControllerList::const_iterator it = mStorageControllers->begin();
11788 while (it != mStorageControllers->end())
11789 {
11790 (*it)->i_commit();
11791
11792 /* look if this controller has a peer device */
11793 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11794 if (!peer)
11795 {
11796 /* no peer means the device is a newly created one;
11797 * create a peer owning data this device share it with */
11798 peer.createObject();
11799 peer->init(mPeer, *it, true /* aReshare */);
11800 }
11801 else
11802 {
11803 /* remove peer from the old list */
11804 mPeer->mStorageControllers->remove(peer);
11805 }
11806 /* and add it to the new list */
11807 newList->push_back(peer);
11808
11809 ++it;
11810 }
11811
11812 /* uninit old peer's controllers that are left */
11813 it = mPeer->mStorageControllers->begin();
11814 while (it != mPeer->mStorageControllers->end())
11815 {
11816 (*it)->uninit();
11817 ++it;
11818 }
11819
11820 /* attach new list of controllers to our peer */
11821 mPeer->mStorageControllers.attach(newList);
11822 }
11823 else
11824 {
11825 /* we have no peer (our parent is the newly created machine);
11826 * just commit changes to devices */
11827 commitStorageControllers = true;
11828 }
11829 }
11830 else
11831 {
11832 /* the list of controllers itself is not changed,
11833 * just commit changes to controllers themselves */
11834 commitStorageControllers = true;
11835 }
11836
11837 if (commitStorageControllers)
11838 {
11839 StorageControllerList::const_iterator it = mStorageControllers->begin();
11840 while (it != mStorageControllers->end())
11841 {
11842 (*it)->i_commit();
11843 ++it;
11844 }
11845 }
11846
11847 bool commitUSBControllers = false;
11848
11849 if (mUSBControllers.isBackedUp())
11850 {
11851 mUSBControllers.commit();
11852
11853 if (mPeer)
11854 {
11855 /* Commit all changes to new controllers (this will reshare data with
11856 * peers for those who have peers) */
11857 USBControllerList *newList = new USBControllerList();
11858 USBControllerList::const_iterator it = mUSBControllers->begin();
11859 while (it != mUSBControllers->end())
11860 {
11861 (*it)->i_commit();
11862
11863 /* look if this controller has a peer device */
11864 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11865 if (!peer)
11866 {
11867 /* no peer means the device is a newly created one;
11868 * create a peer owning data this device share it with */
11869 peer.createObject();
11870 peer->init(mPeer, *it, true /* aReshare */);
11871 }
11872 else
11873 {
11874 /* remove peer from the old list */
11875 mPeer->mUSBControllers->remove(peer);
11876 }
11877 /* and add it to the new list */
11878 newList->push_back(peer);
11879
11880 ++it;
11881 }
11882
11883 /* uninit old peer's controllers that are left */
11884 it = mPeer->mUSBControllers->begin();
11885 while (it != mPeer->mUSBControllers->end())
11886 {
11887 (*it)->uninit();
11888 ++it;
11889 }
11890
11891 /* attach new list of controllers to our peer */
11892 mPeer->mUSBControllers.attach(newList);
11893 }
11894 else
11895 {
11896 /* we have no peer (our parent is the newly created machine);
11897 * just commit changes to devices */
11898 commitUSBControllers = true;
11899 }
11900 }
11901 else
11902 {
11903 /* the list of controllers itself is not changed,
11904 * just commit changes to controllers themselves */
11905 commitUSBControllers = true;
11906 }
11907
11908 if (commitUSBControllers)
11909 {
11910 USBControllerList::const_iterator it = mUSBControllers->begin();
11911 while (it != mUSBControllers->end())
11912 {
11913 (*it)->i_commit();
11914 ++it;
11915 }
11916 }
11917
11918 if (i_isSessionMachine())
11919 {
11920 /* attach new data to the primary machine and reshare it */
11921 mPeer->mUserData.attach(mUserData);
11922 mPeer->mHWData.attach(mHWData);
11923 /* mMediaData is reshared by fixupMedia */
11924 // mPeer->mMediaData.attach(mMediaData);
11925 Assert(mPeer->mMediaData.data() == mMediaData.data());
11926 }
11927}
11928
11929/**
11930 * Copies all the hardware data from the given machine.
11931 *
11932 * Currently, only called when the VM is being restored from a snapshot. In
11933 * particular, this implies that the VM is not running during this method's
11934 * call.
11935 *
11936 * @note This method must be called from under this object's lock.
11937 *
11938 * @note This method doesn't call #commit(), so all data remains backed up and
11939 * unsaved.
11940 */
11941void Machine::i_copyFrom(Machine *aThat)
11942{
11943 AssertReturnVoid(!i_isSnapshotMachine());
11944 AssertReturnVoid(aThat->i_isSnapshotMachine());
11945
11946 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11947
11948 mHWData.assignCopy(aThat->mHWData);
11949
11950 // create copies of all shared folders (mHWData after attaching a copy
11951 // contains just references to original objects)
11952 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11953 it != mHWData->mSharedFolders.end();
11954 ++it)
11955 {
11956 ComObjPtr<SharedFolder> folder;
11957 folder.createObject();
11958 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11959 AssertComRC(rc);
11960 *it = folder;
11961 }
11962
11963 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11964 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11965 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11966 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11967 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11968
11969 /* create private copies of all controllers */
11970 mStorageControllers.backup();
11971 mStorageControllers->clear();
11972 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11973 it != aThat->mStorageControllers->end();
11974 ++it)
11975 {
11976 ComObjPtr<StorageController> ctrl;
11977 ctrl.createObject();
11978 ctrl->initCopy(this, *it);
11979 mStorageControllers->push_back(ctrl);
11980 }
11981
11982 /* create private copies of all USB controllers */
11983 mUSBControllers.backup();
11984 mUSBControllers->clear();
11985 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11986 it != aThat->mUSBControllers->end();
11987 ++it)
11988 {
11989 ComObjPtr<USBController> ctrl;
11990 ctrl.createObject();
11991 ctrl->initCopy(this, *it);
11992 mUSBControllers->push_back(ctrl);
11993 }
11994
11995 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11996 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11997 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11998 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11999 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12000 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12001 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12002}
12003
12004/**
12005 * Returns whether the given storage controller is hotplug capable.
12006 *
12007 * @returns true if the controller supports hotplugging
12008 * false otherwise.
12009 * @param enmCtrlType The controller type to check for.
12010 */
12011bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12012{
12013 ComPtr<ISystemProperties> systemProperties;
12014 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12015 if (FAILED(rc))
12016 return false;
12017
12018 BOOL aHotplugCapable = FALSE;
12019 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12020
12021 return RT_BOOL(aHotplugCapable);
12022}
12023
12024#ifdef VBOX_WITH_RESOURCE_USAGE_API
12025
12026void Machine::i_getDiskList(MediaList &list)
12027{
12028 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12029 it != mMediaData->mAttachments.end();
12030 ++it)
12031 {
12032 MediumAttachment* pAttach = *it;
12033 /* just in case */
12034 AssertStmt(pAttach, continue);
12035
12036 AutoCaller localAutoCallerA(pAttach);
12037 if (FAILED(localAutoCallerA.rc())) continue;
12038
12039 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12040
12041 if (pAttach->i_getType() == DeviceType_HardDisk)
12042 list.push_back(pAttach->i_getMedium());
12043 }
12044}
12045
12046void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12047{
12048 AssertReturnVoid(isWriteLockOnCurrentThread());
12049 AssertPtrReturnVoid(aCollector);
12050
12051 pm::CollectorHAL *hal = aCollector->getHAL();
12052 /* Create sub metrics */
12053 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12054 "Percentage of processor time spent in user mode by the VM process.");
12055 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12056 "Percentage of processor time spent in kernel mode by the VM process.");
12057 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12058 "Size of resident portion of VM process in memory.");
12059 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12060 "Actual size of all VM disks combined.");
12061 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12062 "Network receive rate.");
12063 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12064 "Network transmit rate.");
12065 /* Create and register base metrics */
12066 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12067 cpuLoadUser, cpuLoadKernel);
12068 aCollector->registerBaseMetric(cpuLoad);
12069 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12070 ramUsageUsed);
12071 aCollector->registerBaseMetric(ramUsage);
12072 MediaList disks;
12073 i_getDiskList(disks);
12074 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12075 diskUsageUsed);
12076 aCollector->registerBaseMetric(diskUsage);
12077
12078 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12079 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12080 new pm::AggregateAvg()));
12081 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12082 new pm::AggregateMin()));
12083 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12084 new pm::AggregateMax()));
12085 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12086 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12087 new pm::AggregateAvg()));
12088 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12089 new pm::AggregateMin()));
12090 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12091 new pm::AggregateMax()));
12092
12093 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12094 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12095 new pm::AggregateAvg()));
12096 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12097 new pm::AggregateMin()));
12098 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12099 new pm::AggregateMax()));
12100
12101 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12102 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12103 new pm::AggregateAvg()));
12104 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12105 new pm::AggregateMin()));
12106 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12107 new pm::AggregateMax()));
12108
12109
12110 /* Guest metrics collector */
12111 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12112 aCollector->registerGuest(mCollectorGuest);
12113 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12114 this, __PRETTY_FUNCTION__, mCollectorGuest));
12115
12116 /* Create sub metrics */
12117 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12118 "Percentage of processor time spent in user mode as seen by the guest.");
12119 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12120 "Percentage of processor time spent in kernel mode as seen by the guest.");
12121 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12122 "Percentage of processor time spent idling as seen by the guest.");
12123
12124 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12125 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12126 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12127 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12128 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12129 pm::SubMetric *guestMemCache = new pm::SubMetric(
12130 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12131
12132 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12133 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12134
12135 /* Create and register base metrics */
12136 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12137 machineNetRx, machineNetTx);
12138 aCollector->registerBaseMetric(machineNetRate);
12139
12140 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12141 guestLoadUser, guestLoadKernel, guestLoadIdle);
12142 aCollector->registerBaseMetric(guestCpuLoad);
12143
12144 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12145 guestMemTotal, guestMemFree,
12146 guestMemBalloon, guestMemShared,
12147 guestMemCache, guestPagedTotal);
12148 aCollector->registerBaseMetric(guestCpuMem);
12149
12150 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12151 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12152 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12153 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12154
12155 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12156 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12157 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12158 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12159
12160 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12161 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12162 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12163 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12164
12165 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12166 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12167 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12168 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12169
12170 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12171 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12172 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12173 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12174
12175 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12176 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12177 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12178 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12179
12180 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12181 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12182 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12183 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12184
12185 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12186 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12187 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12188 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12189
12190 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12191 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12192 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12193 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12194
12195 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12196 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12197 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12198 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12199
12200 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12201 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12202 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12203 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12204}
12205
12206void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12207{
12208 AssertReturnVoid(isWriteLockOnCurrentThread());
12209
12210 if (aCollector)
12211 {
12212 aCollector->unregisterMetricsFor(aMachine);
12213 aCollector->unregisterBaseMetricsFor(aMachine);
12214 }
12215}
12216
12217#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12218
12219
12220////////////////////////////////////////////////////////////////////////////////
12221
12222DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12223
12224HRESULT SessionMachine::FinalConstruct()
12225{
12226 LogFlowThisFunc(("\n"));
12227
12228 mClientToken = NULL;
12229
12230 return BaseFinalConstruct();
12231}
12232
12233void SessionMachine::FinalRelease()
12234{
12235 LogFlowThisFunc(("\n"));
12236
12237 Assert(!mClientToken);
12238 /* paranoia, should not hang around any more */
12239 if (mClientToken)
12240 {
12241 delete mClientToken;
12242 mClientToken = NULL;
12243 }
12244
12245 uninit(Uninit::Unexpected);
12246
12247 BaseFinalRelease();
12248}
12249
12250/**
12251 * @note Must be called only by Machine::LockMachine() from its own write lock.
12252 */
12253HRESULT SessionMachine::init(Machine *aMachine)
12254{
12255 LogFlowThisFuncEnter();
12256 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12257
12258 AssertReturn(aMachine, E_INVALIDARG);
12259
12260 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12261
12262 /* Enclose the state transition NotReady->InInit->Ready */
12263 AutoInitSpan autoInitSpan(this);
12264 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12265
12266 HRESULT rc = S_OK;
12267
12268 /* create the machine client token */
12269 try
12270 {
12271 mClientToken = new ClientToken(aMachine, this);
12272 if (!mClientToken->isReady())
12273 {
12274 delete mClientToken;
12275 mClientToken = NULL;
12276 rc = E_FAIL;
12277 }
12278 }
12279 catch (std::bad_alloc &)
12280 {
12281 rc = E_OUTOFMEMORY;
12282 }
12283 if (FAILED(rc))
12284 return rc;
12285
12286 /* memorize the peer Machine */
12287 unconst(mPeer) = aMachine;
12288 /* share the parent pointer */
12289 unconst(mParent) = aMachine->mParent;
12290
12291 /* take the pointers to data to share */
12292 mData.share(aMachine->mData);
12293 mSSData.share(aMachine->mSSData);
12294
12295 mUserData.share(aMachine->mUserData);
12296 mHWData.share(aMachine->mHWData);
12297 mMediaData.share(aMachine->mMediaData);
12298
12299 mStorageControllers.allocate();
12300 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12301 it != aMachine->mStorageControllers->end();
12302 ++it)
12303 {
12304 ComObjPtr<StorageController> ctl;
12305 ctl.createObject();
12306 ctl->init(this, *it);
12307 mStorageControllers->push_back(ctl);
12308 }
12309
12310 mUSBControllers.allocate();
12311 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12312 it != aMachine->mUSBControllers->end();
12313 ++it)
12314 {
12315 ComObjPtr<USBController> ctl;
12316 ctl.createObject();
12317 ctl->init(this, *it);
12318 mUSBControllers->push_back(ctl);
12319 }
12320
12321 unconst(mBIOSSettings).createObject();
12322 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12323 /* create another VRDEServer object that will be mutable */
12324 unconst(mVRDEServer).createObject();
12325 mVRDEServer->init(this, aMachine->mVRDEServer);
12326 /* create another audio adapter object that will be mutable */
12327 unconst(mAudioAdapter).createObject();
12328 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12329 /* create a list of serial ports that will be mutable */
12330 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12331 {
12332 unconst(mSerialPorts[slot]).createObject();
12333 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12334 }
12335 /* create a list of parallel ports that will be mutable */
12336 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12337 {
12338 unconst(mParallelPorts[slot]).createObject();
12339 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12340 }
12341
12342 /* create another USB device filters object that will be mutable */
12343 unconst(mUSBDeviceFilters).createObject();
12344 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12345
12346 /* create a list of network adapters that will be mutable */
12347 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12348 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12349 {
12350 unconst(mNetworkAdapters[slot]).createObject();
12351 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12352 }
12353
12354 /* create another bandwidth control object that will be mutable */
12355 unconst(mBandwidthControl).createObject();
12356 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12357
12358 /* default is to delete saved state on Saved -> PoweredOff transition */
12359 mRemoveSavedState = true;
12360
12361 /* Confirm a successful initialization when it's the case */
12362 autoInitSpan.setSucceeded();
12363
12364 miNATNetworksStarted = 0;
12365
12366 LogFlowThisFuncLeave();
12367 return rc;
12368}
12369
12370/**
12371 * Uninitializes this session object. If the reason is other than
12372 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12373 * or the client watcher code.
12374 *
12375 * @param aReason uninitialization reason
12376 *
12377 * @note Locks mParent + this object for writing.
12378 */
12379void SessionMachine::uninit(Uninit::Reason aReason)
12380{
12381 LogFlowThisFuncEnter();
12382 LogFlowThisFunc(("reason=%d\n", aReason));
12383
12384 /*
12385 * Strongly reference ourselves to prevent this object deletion after
12386 * mData->mSession.mMachine.setNull() below (which can release the last
12387 * reference and call the destructor). Important: this must be done before
12388 * accessing any members (and before AutoUninitSpan that does it as well).
12389 * This self reference will be released as the very last step on return.
12390 */
12391 ComObjPtr<SessionMachine> selfRef = this;
12392
12393 /* Enclose the state transition Ready->InUninit->NotReady */
12394 AutoUninitSpan autoUninitSpan(this);
12395 if (autoUninitSpan.uninitDone())
12396 {
12397 LogFlowThisFunc(("Already uninitialized\n"));
12398 LogFlowThisFuncLeave();
12399 return;
12400 }
12401
12402 if (autoUninitSpan.initFailed())
12403 {
12404 /* We've been called by init() because it's failed. It's not really
12405 * necessary (nor it's safe) to perform the regular uninit sequence
12406 * below, the following is enough.
12407 */
12408 LogFlowThisFunc(("Initialization failed.\n"));
12409 /* destroy the machine client token */
12410 if (mClientToken)
12411 {
12412 delete mClientToken;
12413 mClientToken = NULL;
12414 }
12415 uninitDataAndChildObjects();
12416 mData.free();
12417 unconst(mParent) = NULL;
12418 unconst(mPeer) = NULL;
12419 LogFlowThisFuncLeave();
12420 return;
12421 }
12422
12423 MachineState_T lastState;
12424 {
12425 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12426 lastState = mData->mMachineState;
12427 }
12428 NOREF(lastState);
12429
12430#ifdef VBOX_WITH_USB
12431 // release all captured USB devices, but do this before requesting the locks below
12432 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12433 {
12434 /* Console::captureUSBDevices() is called in the VM process only after
12435 * setting the machine state to Starting or Restoring.
12436 * Console::detachAllUSBDevices() will be called upon successful
12437 * termination. So, we need to release USB devices only if there was
12438 * an abnormal termination of a running VM.
12439 *
12440 * This is identical to SessionMachine::DetachAllUSBDevices except
12441 * for the aAbnormal argument. */
12442 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12443 AssertComRC(rc);
12444 NOREF(rc);
12445
12446 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12447 if (service)
12448 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12449 }
12450#endif /* VBOX_WITH_USB */
12451
12452 // we need to lock this object in uninit() because the lock is shared
12453 // with mPeer (as well as data we modify below). mParent lock is needed
12454 // by several calls to it, and USB needs host lock.
12455 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12456
12457#ifdef VBOX_WITH_RESOURCE_USAGE_API
12458 /*
12459 * It is safe to call Machine::i_unregisterMetrics() here because
12460 * PerformanceCollector::samplerCallback no longer accesses guest methods
12461 * holding the lock.
12462 */
12463 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12464 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12465 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12466 this, __PRETTY_FUNCTION__, mCollectorGuest));
12467 if (mCollectorGuest)
12468 {
12469 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12470 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12471 mCollectorGuest = NULL;
12472 }
12473#endif
12474
12475 if (aReason == Uninit::Abnormal)
12476 {
12477 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12478 Global::IsOnlineOrTransient(lastState)));
12479
12480 /* reset the state to Aborted */
12481 if (mData->mMachineState != MachineState_Aborted)
12482 i_setMachineState(MachineState_Aborted);
12483 }
12484
12485 // any machine settings modified?
12486 if (mData->flModifications)
12487 {
12488 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12489 i_rollback(false /* aNotify */);
12490 }
12491
12492 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12493 || !mConsoleTaskData.mSnapshot);
12494 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12495 {
12496 LogWarningThisFunc(("canceling failed save state request!\n"));
12497 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12498 }
12499 else if (!mConsoleTaskData.mSnapshot.isNull())
12500 {
12501 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12502
12503 /* delete all differencing hard disks created (this will also attach
12504 * their parents back by rolling back mMediaData) */
12505 i_rollbackMedia();
12506
12507 // delete the saved state file (it might have been already created)
12508 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12509 // think it's still in use
12510 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12511 mConsoleTaskData.mSnapshot->uninit();
12512 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12513 }
12514
12515 mData->mSession.mPID = NIL_RTPROCESS;
12516
12517 if (aReason == Uninit::Unexpected)
12518 {
12519 /* Uninitialization didn't come from #checkForDeath(), so tell the
12520 * client watcher thread to update the set of machines that have open
12521 * sessions. */
12522 mParent->i_updateClientWatcher();
12523 }
12524
12525 /* uninitialize all remote controls */
12526 if (mData->mSession.mRemoteControls.size())
12527 {
12528 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12529 mData->mSession.mRemoteControls.size()));
12530
12531 Data::Session::RemoteControlList::iterator it =
12532 mData->mSession.mRemoteControls.begin();
12533 while (it != mData->mSession.mRemoteControls.end())
12534 {
12535 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12536 HRESULT rc = (*it)->Uninitialize();
12537 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12538 if (FAILED(rc))
12539 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12540 ++it;
12541 }
12542 mData->mSession.mRemoteControls.clear();
12543 }
12544
12545 /* Remove all references to the NAT network service. The service will stop
12546 * if all references (also from other VMs) are removed. */
12547 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12548 {
12549 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12550 {
12551 NetworkAttachmentType_T type;
12552 HRESULT hrc;
12553
12554 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12555 if ( SUCCEEDED(hrc)
12556 && type == NetworkAttachmentType_NATNetwork)
12557 {
12558 Bstr name;
12559 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12560 if (SUCCEEDED(hrc))
12561 {
12562 multilock.release();
12563 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12564 mUserData->s.strName.c_str(), name.raw()));
12565 mParent->i_natNetworkRefDec(name.raw());
12566 multilock.acquire();
12567 }
12568 }
12569 }
12570 }
12571
12572 /*
12573 * An expected uninitialization can come only from #checkForDeath().
12574 * Otherwise it means that something's gone really wrong (for example,
12575 * the Session implementation has released the VirtualBox reference
12576 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12577 * etc). However, it's also possible, that the client releases the IPC
12578 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12579 * but the VirtualBox release event comes first to the server process.
12580 * This case is practically possible, so we should not assert on an
12581 * unexpected uninit, just log a warning.
12582 */
12583
12584 if ((aReason == Uninit::Unexpected))
12585 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12586
12587 if (aReason != Uninit::Normal)
12588 {
12589 mData->mSession.mDirectControl.setNull();
12590 }
12591 else
12592 {
12593 /* this must be null here (see #OnSessionEnd()) */
12594 Assert(mData->mSession.mDirectControl.isNull());
12595 Assert(mData->mSession.mState == SessionState_Unlocking);
12596 Assert(!mData->mSession.mProgress.isNull());
12597 }
12598 if (mData->mSession.mProgress)
12599 {
12600 if (aReason == Uninit::Normal)
12601 mData->mSession.mProgress->i_notifyComplete(S_OK);
12602 else
12603 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12604 COM_IIDOF(ISession),
12605 getComponentName(),
12606 tr("The VM session was aborted"));
12607 mData->mSession.mProgress.setNull();
12608 }
12609
12610 /* remove the association between the peer machine and this session machine */
12611 Assert( (SessionMachine*)mData->mSession.mMachine == this
12612 || aReason == Uninit::Unexpected);
12613
12614 /* reset the rest of session data */
12615 mData->mSession.mMachine.setNull();
12616 mData->mSession.mState = SessionState_Unlocked;
12617 mData->mSession.mType.setNull();
12618
12619 /* destroy the machine client token before leaving the exclusive lock */
12620 if (mClientToken)
12621 {
12622 delete mClientToken;
12623 mClientToken = NULL;
12624 }
12625
12626 /* fire an event */
12627 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12628
12629 uninitDataAndChildObjects();
12630
12631 /* free the essential data structure last */
12632 mData.free();
12633
12634 /* release the exclusive lock before setting the below two to NULL */
12635 multilock.release();
12636
12637 unconst(mParent) = NULL;
12638 unconst(mPeer) = NULL;
12639
12640 LogFlowThisFuncLeave();
12641}
12642
12643// util::Lockable interface
12644////////////////////////////////////////////////////////////////////////////////
12645
12646/**
12647 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12648 * with the primary Machine instance (mPeer).
12649 */
12650RWLockHandle *SessionMachine::lockHandle() const
12651{
12652 AssertReturn(mPeer != NULL, NULL);
12653 return mPeer->lockHandle();
12654}
12655
12656// IInternalMachineControl methods
12657////////////////////////////////////////////////////////////////////////////////
12658
12659/**
12660 * Passes collected guest statistics to performance collector object
12661 */
12662HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12663 ULONG aCpuKernel, ULONG aCpuIdle,
12664 ULONG aMemTotal, ULONG aMemFree,
12665 ULONG aMemBalloon, ULONG aMemShared,
12666 ULONG aMemCache, ULONG aPageTotal,
12667 ULONG aAllocVMM, ULONG aFreeVMM,
12668 ULONG aBalloonedVMM, ULONG aSharedVMM,
12669 ULONG aVmNetRx, ULONG aVmNetTx)
12670{
12671#ifdef VBOX_WITH_RESOURCE_USAGE_API
12672 if (mCollectorGuest)
12673 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12674 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12675 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12676 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12677
12678 return S_OK;
12679#else
12680 NOREF(aValidStats);
12681 NOREF(aCpuUser);
12682 NOREF(aCpuKernel);
12683 NOREF(aCpuIdle);
12684 NOREF(aMemTotal);
12685 NOREF(aMemFree);
12686 NOREF(aMemBalloon);
12687 NOREF(aMemShared);
12688 NOREF(aMemCache);
12689 NOREF(aPageTotal);
12690 NOREF(aAllocVMM);
12691 NOREF(aFreeVMM);
12692 NOREF(aBalloonedVMM);
12693 NOREF(aSharedVMM);
12694 NOREF(aVmNetRx);
12695 NOREF(aVmNetTx);
12696 return E_NOTIMPL;
12697#endif
12698}
12699
12700/**
12701 * @note Locks this object for writing.
12702 */
12703HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12704{
12705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12706
12707 mRemoveSavedState = RT_BOOL(aRemove);
12708
12709 return S_OK;
12710}
12711
12712/**
12713 * @note Locks the same as #i_setMachineState() does.
12714 */
12715HRESULT SessionMachine::updateState(MachineState_T aState)
12716{
12717 return i_setMachineState(aState);
12718}
12719
12720/**
12721 * @note Locks this object for writing.
12722 */
12723HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12724{
12725 IProgress* pProgress(aProgress);
12726
12727 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12728
12729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12730
12731 if (mData->mSession.mState != SessionState_Locked)
12732 return VBOX_E_INVALID_OBJECT_STATE;
12733
12734 if (!mData->mSession.mProgress.isNull())
12735 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12736
12737 /* If we didn't reference the NAT network service yet, add a reference to
12738 * force a start */
12739 if (miNATNetworksStarted < 1)
12740 {
12741 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12742 {
12743 NetworkAttachmentType_T type;
12744 HRESULT hrc;
12745 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12746 if ( SUCCEEDED(hrc)
12747 && type == NetworkAttachmentType_NATNetwork)
12748 {
12749 Bstr name;
12750 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12751 if (SUCCEEDED(hrc))
12752 {
12753 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12754 mUserData->s.strName.c_str(), name.raw()));
12755 mPeer->lockHandle()->unlockWrite();
12756 mParent->i_natNetworkRefInc(name.raw());
12757#ifdef RT_LOCK_STRICT
12758 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12759#else
12760 mPeer->lockHandle()->lockWrite();
12761#endif
12762 }
12763 }
12764 }
12765 miNATNetworksStarted++;
12766 }
12767
12768 LogFlowThisFunc(("returns S_OK.\n"));
12769 return S_OK;
12770}
12771
12772/**
12773 * @note Locks this object for writing.
12774 */
12775HRESULT SessionMachine::endPowerUp(LONG aResult)
12776{
12777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12778
12779 if (mData->mSession.mState != SessionState_Locked)
12780 return VBOX_E_INVALID_OBJECT_STATE;
12781
12782 /* Finalize the LaunchVMProcess progress object. */
12783 if (mData->mSession.mProgress)
12784 {
12785 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12786 mData->mSession.mProgress.setNull();
12787 }
12788
12789 if (SUCCEEDED((HRESULT)aResult))
12790 {
12791#ifdef VBOX_WITH_RESOURCE_USAGE_API
12792 /* The VM has been powered up successfully, so it makes sense
12793 * now to offer the performance metrics for a running machine
12794 * object. Doing it earlier wouldn't be safe. */
12795 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12796 mData->mSession.mPID);
12797#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12798 }
12799
12800 return S_OK;
12801}
12802
12803/**
12804 * @note Locks this object for writing.
12805 */
12806HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12807{
12808 LogFlowThisFuncEnter();
12809
12810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12811
12812 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12813 E_FAIL);
12814
12815 /* create a progress object to track operation completion */
12816 ComObjPtr<Progress> pProgress;
12817 pProgress.createObject();
12818 pProgress->init(i_getVirtualBox(),
12819 static_cast<IMachine *>(this) /* aInitiator */,
12820 Bstr(tr("Stopping the virtual machine")).raw(),
12821 FALSE /* aCancelable */);
12822
12823 /* fill in the console task data */
12824 mConsoleTaskData.mLastState = mData->mMachineState;
12825 mConsoleTaskData.mProgress = pProgress;
12826
12827 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12828 i_setMachineState(MachineState_Stopping);
12829
12830 pProgress.queryInterfaceTo(aProgress.asOutParam());
12831
12832 return S_OK;
12833}
12834
12835/**
12836 * @note Locks this object for writing.
12837 */
12838HRESULT SessionMachine::endPoweringDown(LONG aResult,
12839 const com::Utf8Str &aErrMsg)
12840{
12841 LogFlowThisFuncEnter();
12842
12843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12844
12845 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12846 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12847 && mConsoleTaskData.mLastState != MachineState_Null,
12848 E_FAIL);
12849
12850 /*
12851 * On failure, set the state to the state we had when BeginPoweringDown()
12852 * was called (this is expected by Console::PowerDown() and the associated
12853 * task). On success the VM process already changed the state to
12854 * MachineState_PoweredOff, so no need to do anything.
12855 */
12856 if (FAILED(aResult))
12857 i_setMachineState(mConsoleTaskData.mLastState);
12858
12859 /* notify the progress object about operation completion */
12860 Assert(mConsoleTaskData.mProgress);
12861 if (SUCCEEDED(aResult))
12862 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12863 else
12864 {
12865 if (aErrMsg.length())
12866 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12867 COM_IIDOF(ISession),
12868 getComponentName(),
12869 aErrMsg.c_str());
12870 else
12871 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12872 }
12873
12874 /* clear out the temporary saved state data */
12875 mConsoleTaskData.mLastState = MachineState_Null;
12876 mConsoleTaskData.mProgress.setNull();
12877
12878 LogFlowThisFuncLeave();
12879 return S_OK;
12880}
12881
12882
12883/**
12884 * Goes through the USB filters of the given machine to see if the given
12885 * device matches any filter or not.
12886 *
12887 * @note Locks the same as USBController::hasMatchingFilter() does.
12888 */
12889HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12890 BOOL *aMatched,
12891 ULONG *aMaskedInterfaces)
12892{
12893 LogFlowThisFunc(("\n"));
12894
12895#ifdef VBOX_WITH_USB
12896 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12897#else
12898 NOREF(aDevice);
12899 NOREF(aMaskedInterfaces);
12900 *aMatched = FALSE;
12901#endif
12902
12903 return S_OK;
12904}
12905
12906/**
12907 * @note Locks the same as Host::captureUSBDevice() does.
12908 */
12909HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
12910{
12911 LogFlowThisFunc(("\n"));
12912
12913#ifdef VBOX_WITH_USB
12914 /* if captureDeviceForVM() fails, it must have set extended error info */
12915 clearError();
12916 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12917 if (FAILED(rc)) return rc;
12918
12919 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12920 AssertReturn(service, E_FAIL);
12921 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
12922#else
12923 NOREF(aId);
12924 return E_NOTIMPL;
12925#endif
12926}
12927
12928/**
12929 * @note Locks the same as Host::detachUSBDevice() does.
12930 */
12931HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12932 BOOL aDone)
12933{
12934 LogFlowThisFunc(("\n"));
12935
12936#ifdef VBOX_WITH_USB
12937 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12938 AssertReturn(service, E_FAIL);
12939 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12940#else
12941 NOREF(aId);
12942 NOREF(aDone);
12943 return E_NOTIMPL;
12944#endif
12945}
12946
12947/**
12948 * Inserts all machine filters to the USB proxy service and then calls
12949 * Host::autoCaptureUSBDevices().
12950 *
12951 * Called by Console from the VM process upon VM startup.
12952 *
12953 * @note Locks what called methods lock.
12954 */
12955HRESULT SessionMachine::autoCaptureUSBDevices()
12956{
12957 LogFlowThisFunc(("\n"));
12958
12959#ifdef VBOX_WITH_USB
12960 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12961 AssertComRC(rc);
12962 NOREF(rc);
12963
12964 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12965 AssertReturn(service, E_FAIL);
12966 return service->autoCaptureDevicesForVM(this);
12967#else
12968 return S_OK;
12969#endif
12970}
12971
12972/**
12973 * Removes all machine filters from the USB proxy service and then calls
12974 * Host::detachAllUSBDevices().
12975 *
12976 * Called by Console from the VM process upon normal VM termination or by
12977 * SessionMachine::uninit() upon abnormal VM termination (from under the
12978 * Machine/SessionMachine lock).
12979 *
12980 * @note Locks what called methods lock.
12981 */
12982HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12983{
12984 LogFlowThisFunc(("\n"));
12985
12986#ifdef VBOX_WITH_USB
12987 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12988 AssertComRC(rc);
12989 NOREF(rc);
12990
12991 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12992 AssertReturn(service, E_FAIL);
12993 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12994#else
12995 NOREF(aDone);
12996 return S_OK;
12997#endif
12998}
12999
13000/**
13001 * @note Locks this object for writing.
13002 */
13003HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13004 ComPtr<IProgress> &aProgress)
13005{
13006 LogFlowThisFuncEnter();
13007
13008 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13009 /*
13010 * We don't assert below because it might happen that a non-direct session
13011 * informs us it is closed right after we've been uninitialized -- it's ok.
13012 */
13013
13014 /* get IInternalSessionControl interface */
13015 ComPtr<IInternalSessionControl> control(aSession);
13016
13017 ComAssertRet(!control.isNull(), E_INVALIDARG);
13018
13019 /* Creating a Progress object requires the VirtualBox lock, and
13020 * thus locking it here is required by the lock order rules. */
13021 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13022
13023 if (control == mData->mSession.mDirectControl)
13024 {
13025 /* The direct session is being normally closed by the client process
13026 * ----------------------------------------------------------------- */
13027
13028 /* go to the closing state (essential for all open*Session() calls and
13029 * for #checkForDeath()) */
13030 Assert(mData->mSession.mState == SessionState_Locked);
13031 mData->mSession.mState = SessionState_Unlocking;
13032
13033 /* set direct control to NULL to release the remote instance */
13034 mData->mSession.mDirectControl.setNull();
13035 LogFlowThisFunc(("Direct control is set to NULL\n"));
13036
13037 if (mData->mSession.mProgress)
13038 {
13039 /* finalize the progress, someone might wait if a frontend
13040 * closes the session before powering on the VM. */
13041 mData->mSession.mProgress->notifyComplete(E_FAIL,
13042 COM_IIDOF(ISession),
13043 getComponentName(),
13044 tr("The VM session was closed before any attempt to power it on"));
13045 mData->mSession.mProgress.setNull();
13046 }
13047
13048 /* Create the progress object the client will use to wait until
13049 * #checkForDeath() is called to uninitialize this session object after
13050 * it releases the IPC semaphore.
13051 * Note! Because we're "reusing" mProgress here, this must be a proxy
13052 * object just like for LaunchVMProcess. */
13053 Assert(mData->mSession.mProgress.isNull());
13054 ComObjPtr<ProgressProxy> progress;
13055 progress.createObject();
13056 ComPtr<IUnknown> pPeer(mPeer);
13057 progress->init(mParent, pPeer,
13058 Bstr(tr("Closing session")).raw(),
13059 FALSE /* aCancelable */);
13060 progress.queryInterfaceTo(aProgress.asOutParam());
13061 mData->mSession.mProgress = progress;
13062 }
13063 else
13064 {
13065 /* the remote session is being normally closed */
13066 Data::Session::RemoteControlList::iterator it =
13067 mData->mSession.mRemoteControls.begin();
13068 while (it != mData->mSession.mRemoteControls.end())
13069 {
13070 if (control == *it)
13071 break;
13072 ++it;
13073 }
13074 BOOL found = it != mData->mSession.mRemoteControls.end();
13075 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13076 E_INVALIDARG);
13077 // This MUST be erase(it), not remove(*it) as the latter triggers a
13078 // very nasty use after free due to the place where the value "lives".
13079 mData->mSession.mRemoteControls.erase(it);
13080 }
13081
13082 /* signal the client watcher thread, because the client is going away */
13083 mParent->i_updateClientWatcher();
13084
13085 LogFlowThisFuncLeave();
13086 return S_OK;
13087}
13088
13089/**
13090 * @note Locks this object for writing.
13091 */
13092HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
13093 com::Utf8Str &aStateFilePath)
13094{
13095 LogFlowThisFuncEnter();
13096
13097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13098
13099 AssertReturn( mData->mMachineState == MachineState_Paused
13100 && mConsoleTaskData.mLastState == MachineState_Null
13101 && mConsoleTaskData.strStateFilePath.isEmpty(),
13102 E_FAIL);
13103
13104 /* create a progress object to track operation completion */
13105 ComObjPtr<Progress> pProgress;
13106 pProgress.createObject();
13107 pProgress->init(i_getVirtualBox(),
13108 static_cast<IMachine *>(this) /* aInitiator */,
13109 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13110 FALSE /* aCancelable */);
13111
13112 /* stateFilePath is null when the machine is not running */
13113 if (mData->mMachineState == MachineState_Paused)
13114 i_composeSavedStateFilename(aStateFilePath);
13115
13116 /* fill in the console task data */
13117 mConsoleTaskData.mLastState = mData->mMachineState;
13118 mConsoleTaskData.strStateFilePath = aStateFilePath;
13119 mConsoleTaskData.mProgress = pProgress;
13120
13121 /* set the state to Saving (this is expected by Console::SaveState()) */
13122 i_setMachineState(MachineState_Saving);
13123
13124 pProgress.queryInterfaceTo(aProgress.asOutParam());
13125
13126 return S_OK;
13127}
13128
13129/**
13130 * @note Locks mParent + this object for writing.
13131 */
13132HRESULT SessionMachine::endSavingState(LONG aResult,
13133 const com::Utf8Str &aErrMsg)
13134{
13135 LogFlowThisFunc(("\n"));
13136
13137 /* endSavingState() need mParent lock */
13138 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13139
13140 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13141 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13142 && mConsoleTaskData.mLastState != MachineState_Null
13143 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13144 E_FAIL);
13145
13146 /*
13147 * On failure, set the state to the state we had when BeginSavingState()
13148 * was called (this is expected by Console::SaveState() and the associated
13149 * task). On success the VM process already changed the state to
13150 * MachineState_Saved, so no need to do anything.
13151 */
13152 if (FAILED(aResult))
13153 i_setMachineState(mConsoleTaskData.mLastState);
13154
13155 return i_endSavingState(aResult, aErrMsg);
13156}
13157
13158/**
13159 * @note Locks this object for writing.
13160 */
13161HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13162{
13163 LogFlowThisFunc(("\n"));
13164
13165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13166
13167 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13168 || mData->mMachineState == MachineState_Teleported
13169 || mData->mMachineState == MachineState_Aborted
13170 , E_FAIL); /** @todo setError. */
13171
13172 com::Utf8Str stateFilePathFull;
13173 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13174 if (RT_FAILURE(vrc))
13175 return setError(VBOX_E_FILE_ERROR,
13176 tr("Invalid saved state file path '%s' (%Rrc)"),
13177 aSavedStateFile.c_str(),
13178 vrc);
13179
13180 mSSData->strStateFilePath = stateFilePathFull;
13181
13182 /* The below i_setMachineState() will detect the state transition and will
13183 * update the settings file */
13184
13185 return i_setMachineState(MachineState_Saved);
13186}
13187
13188HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13189 std::vector<com::Utf8Str> &aValues,
13190 std::vector<LONG64> &aTimestamps,
13191 std::vector<com::Utf8Str> &aFlags)
13192{
13193 LogFlowThisFunc(("\n"));
13194
13195#ifdef VBOX_WITH_GUEST_PROPS
13196 using namespace guestProp;
13197
13198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13199
13200 size_t cEntries = mHWData->mGuestProperties.size();
13201 aNames.resize(cEntries);
13202 aValues.resize(cEntries);
13203 aTimestamps.resize(cEntries);
13204 aFlags.resize(cEntries);
13205
13206 size_t i = 0;
13207 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13208 it != mHWData->mGuestProperties.end();
13209 ++it, ++i)
13210 {
13211 char szFlags[MAX_FLAGS_LEN + 1];
13212 aNames[i] = it->first;
13213 aValues[i] = it->second.strValue;
13214 aTimestamps[i] = it->second.mTimestamp;
13215
13216 /* If it is NULL, keep it NULL. */
13217 if (it->second.mFlags)
13218 {
13219 writeFlags(it->second.mFlags, szFlags);
13220 aFlags[i] = szFlags;
13221 }
13222 else
13223 aFlags[i] = "";
13224 }
13225 return S_OK;
13226#else
13227 ReturnComNotImplemented();
13228#endif
13229}
13230
13231HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13232 const com::Utf8Str &aValue,
13233 LONG64 aTimestamp,
13234 const com::Utf8Str &aFlags)
13235{
13236 LogFlowThisFunc(("\n"));
13237
13238#ifdef VBOX_WITH_GUEST_PROPS
13239 using namespace guestProp;
13240
13241 try
13242 {
13243 /*
13244 * Convert input up front.
13245 */
13246 uint32_t fFlags = NILFLAG;
13247 if (aFlags.length())
13248 {
13249 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13250 AssertRCReturn(vrc, E_INVALIDARG);
13251 }
13252
13253 /*
13254 * Now grab the object lock, validate the state and do the update.
13255 */
13256
13257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13258
13259 switch (mData->mMachineState)
13260 {
13261 case MachineState_Paused:
13262 case MachineState_Running:
13263 case MachineState_Teleporting:
13264 case MachineState_TeleportingPausedVM:
13265 case MachineState_LiveSnapshotting:
13266 case MachineState_DeletingSnapshotOnline:
13267 case MachineState_DeletingSnapshotPaused:
13268 case MachineState_Saving:
13269 case MachineState_Stopping:
13270 break;
13271
13272 default:
13273 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13274 VBOX_E_INVALID_VM_STATE);
13275 }
13276
13277 i_setModified(IsModified_MachineData);
13278 mHWData.backup();
13279
13280 bool fDelete = !aValue.length();
13281 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13282 if (it != mHWData->mGuestProperties.end())
13283 {
13284 if (!fDelete)
13285 {
13286 it->second.strValue = aValue;
13287 it->second.mTimestamp = aTimestamp;
13288 it->second.mFlags = fFlags;
13289 }
13290 else
13291 mHWData->mGuestProperties.erase(it);
13292
13293 mData->mGuestPropertiesModified = TRUE;
13294 }
13295 else if (!fDelete)
13296 {
13297 HWData::GuestProperty prop;
13298 prop.strValue = aValue;
13299 prop.mTimestamp = aTimestamp;
13300 prop.mFlags = fFlags;
13301
13302 mHWData->mGuestProperties[aName] = prop;
13303 mData->mGuestPropertiesModified = TRUE;
13304 }
13305
13306 /*
13307 * Send a callback notification if appropriate
13308 */
13309 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13310 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13311 RTSTR_MAX,
13312 aName.c_str(),
13313 RTSTR_MAX, NULL)
13314 )
13315 {
13316 alock.release();
13317
13318 mParent->i_onGuestPropertyChange(mData->mUuid,
13319 Bstr(aName).raw(),
13320 Bstr(aValue).raw(),
13321 Bstr(aFlags).raw());
13322 }
13323 }
13324 catch (...)
13325 {
13326 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13327 }
13328 return S_OK;
13329#else
13330 ReturnComNotImplemented();
13331#endif
13332}
13333
13334
13335HRESULT SessionMachine::lockMedia()
13336{
13337 AutoMultiWriteLock2 alock(this->lockHandle(),
13338 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13339
13340 AssertReturn( mData->mMachineState == MachineState_Starting
13341 || mData->mMachineState == MachineState_Restoring
13342 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13343
13344 clearError();
13345 alock.release();
13346 return i_lockMedia();
13347}
13348
13349HRESULT SessionMachine::unlockMedia()
13350{
13351 HRESULT hrc = i_unlockMedia();
13352 return hrc;
13353}
13354
13355HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13356 ComPtr<IMediumAttachment> &aNewAttachment)
13357{
13358 // request the host lock first, since might be calling Host methods for getting host drives;
13359 // next, protect the media tree all the while we're in here, as well as our member variables
13360 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13361 this->lockHandle(),
13362 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13363
13364 IMediumAttachment *iAttach = aAttachment;
13365 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13366
13367 Bstr ctrlName;
13368 LONG lPort;
13369 LONG lDevice;
13370 bool fTempEject;
13371 {
13372 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13373
13374 /* Need to query the details first, as the IMediumAttachment reference
13375 * might be to the original settings, which we are going to change. */
13376 ctrlName = pAttach->i_getControllerName();
13377 lPort = pAttach->i_getPort();
13378 lDevice = pAttach->i_getDevice();
13379 fTempEject = pAttach->i_getTempEject();
13380 }
13381
13382 if (!fTempEject)
13383 {
13384 /* Remember previously mounted medium. The medium before taking the
13385 * backup is not necessarily the same thing. */
13386 ComObjPtr<Medium> oldmedium;
13387 oldmedium = pAttach->i_getMedium();
13388
13389 i_setModified(IsModified_Storage);
13390 mMediaData.backup();
13391
13392 // The backup operation makes the pAttach reference point to the
13393 // old settings. Re-get the correct reference.
13394 pAttach = i_findAttachment(mMediaData->mAttachments,
13395 ctrlName.raw(),
13396 lPort,
13397 lDevice);
13398
13399 {
13400 AutoCaller autoAttachCaller(this);
13401 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13402
13403 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13404 if (!oldmedium.isNull())
13405 oldmedium->i_removeBackReference(mData->mUuid);
13406
13407 pAttach->i_updateMedium(NULL);
13408 pAttach->i_updateEjected();
13409 }
13410
13411 i_setModified(IsModified_Storage);
13412 }
13413 else
13414 {
13415 {
13416 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13417 pAttach->i_updateEjected();
13418 }
13419 }
13420
13421 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13422
13423 return S_OK;
13424}
13425
13426// public methods only for internal purposes
13427/////////////////////////////////////////////////////////////////////////////
13428
13429#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13430/**
13431 * Called from the client watcher thread to check for expected or unexpected
13432 * death of the client process that has a direct session to this machine.
13433 *
13434 * On Win32 and on OS/2, this method is called only when we've got the
13435 * mutex (i.e. the client has either died or terminated normally) so it always
13436 * returns @c true (the client is terminated, the session machine is
13437 * uninitialized).
13438 *
13439 * On other platforms, the method returns @c true if the client process has
13440 * terminated normally or abnormally and the session machine was uninitialized,
13441 * and @c false if the client process is still alive.
13442 *
13443 * @note Locks this object for writing.
13444 */
13445bool SessionMachine::i_checkForDeath()
13446{
13447 Uninit::Reason reason;
13448 bool terminated = false;
13449
13450 /* Enclose autoCaller with a block because calling uninit() from under it
13451 * will deadlock. */
13452 {
13453 AutoCaller autoCaller(this);
13454 if (!autoCaller.isOk())
13455 {
13456 /* return true if not ready, to cause the client watcher to exclude
13457 * the corresponding session from watching */
13458 LogFlowThisFunc(("Already uninitialized!\n"));
13459 return true;
13460 }
13461
13462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13463
13464 /* Determine the reason of death: if the session state is Closing here,
13465 * everything is fine. Otherwise it means that the client did not call
13466 * OnSessionEnd() before it released the IPC semaphore. This may happen
13467 * either because the client process has abnormally terminated, or
13468 * because it simply forgot to call ISession::Close() before exiting. We
13469 * threat the latter also as an abnormal termination (see
13470 * Session::uninit() for details). */
13471 reason = mData->mSession.mState == SessionState_Unlocking ?
13472 Uninit::Normal :
13473 Uninit::Abnormal;
13474
13475 if (mClientToken)
13476 terminated = mClientToken->release();
13477 } /* AutoCaller block */
13478
13479 if (terminated)
13480 uninit(reason);
13481
13482 return terminated;
13483}
13484
13485void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13486{
13487 LogFlowThisFunc(("\n"));
13488
13489 strTokenId.setNull();
13490
13491 AutoCaller autoCaller(this);
13492 AssertComRCReturnVoid(autoCaller.rc());
13493
13494 Assert(mClientToken);
13495 if (mClientToken)
13496 mClientToken->getId(strTokenId);
13497}
13498#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13499IToken *SessionMachine::i_getToken()
13500{
13501 LogFlowThisFunc(("\n"));
13502
13503 AutoCaller autoCaller(this);
13504 AssertComRCReturn(autoCaller.rc(), NULL);
13505
13506 Assert(mClientToken);
13507 if (mClientToken)
13508 return mClientToken->getToken();
13509 else
13510 return NULL;
13511}
13512#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13513
13514Machine::ClientToken *SessionMachine::i_getClientToken()
13515{
13516 LogFlowThisFunc(("\n"));
13517
13518 AutoCaller autoCaller(this);
13519 AssertComRCReturn(autoCaller.rc(), NULL);
13520
13521 return mClientToken;
13522}
13523
13524
13525/**
13526 * @note Locks this object for reading.
13527 */
13528HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13529{
13530 LogFlowThisFunc(("\n"));
13531
13532 AutoCaller autoCaller(this);
13533 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13534
13535 ComPtr<IInternalSessionControl> directControl;
13536 {
13537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13538 directControl = mData->mSession.mDirectControl;
13539 }
13540
13541 /* ignore notifications sent after #OnSessionEnd() is called */
13542 if (!directControl)
13543 return S_OK;
13544
13545 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13546}
13547
13548/**
13549 * @note Locks this object for reading.
13550 */
13551HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13552 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13553 IN_BSTR aGuestIp, LONG aGuestPort)
13554{
13555 LogFlowThisFunc(("\n"));
13556
13557 AutoCaller autoCaller(this);
13558 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13559
13560 ComPtr<IInternalSessionControl> directControl;
13561 {
13562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13563 directControl = mData->mSession.mDirectControl;
13564 }
13565
13566 /* ignore notifications sent after #OnSessionEnd() is called */
13567 if (!directControl)
13568 return S_OK;
13569 /*
13570 * instead acting like callback we ask IVirtualBox deliver corresponding event
13571 */
13572
13573 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13574 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13575 return S_OK;
13576}
13577
13578/**
13579 * @note Locks this object for reading.
13580 */
13581HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13582{
13583 LogFlowThisFunc(("\n"));
13584
13585 AutoCaller autoCaller(this);
13586 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13587
13588 ComPtr<IInternalSessionControl> directControl;
13589 {
13590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13591 directControl = mData->mSession.mDirectControl;
13592 }
13593
13594 /* ignore notifications sent after #OnSessionEnd() is called */
13595 if (!directControl)
13596 return S_OK;
13597
13598 return directControl->OnSerialPortChange(serialPort);
13599}
13600
13601/**
13602 * @note Locks this object for reading.
13603 */
13604HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13605{
13606 LogFlowThisFunc(("\n"));
13607
13608 AutoCaller autoCaller(this);
13609 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13610
13611 ComPtr<IInternalSessionControl> directControl;
13612 {
13613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13614 directControl = mData->mSession.mDirectControl;
13615 }
13616
13617 /* ignore notifications sent after #OnSessionEnd() is called */
13618 if (!directControl)
13619 return S_OK;
13620
13621 return directControl->OnParallelPortChange(parallelPort);
13622}
13623
13624/**
13625 * @note Locks this object for reading.
13626 */
13627HRESULT SessionMachine::i_onStorageControllerChange()
13628{
13629 LogFlowThisFunc(("\n"));
13630
13631 AutoCaller autoCaller(this);
13632 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13633
13634 ComPtr<IInternalSessionControl> directControl;
13635 {
13636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13637 directControl = mData->mSession.mDirectControl;
13638 }
13639
13640 /* ignore notifications sent after #OnSessionEnd() is called */
13641 if (!directControl)
13642 return S_OK;
13643
13644 return directControl->OnStorageControllerChange();
13645}
13646
13647/**
13648 * @note Locks this object for reading.
13649 */
13650HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13651{
13652 LogFlowThisFunc(("\n"));
13653
13654 AutoCaller autoCaller(this);
13655 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13656
13657 ComPtr<IInternalSessionControl> directControl;
13658 {
13659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13660 directControl = mData->mSession.mDirectControl;
13661 }
13662
13663 /* ignore notifications sent after #OnSessionEnd() is called */
13664 if (!directControl)
13665 return S_OK;
13666
13667 return directControl->OnMediumChange(aAttachment, aForce);
13668}
13669
13670/**
13671 * @note Locks this object for reading.
13672 */
13673HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13674{
13675 LogFlowThisFunc(("\n"));
13676
13677 AutoCaller autoCaller(this);
13678 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13679
13680 ComPtr<IInternalSessionControl> directControl;
13681 {
13682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13683 directControl = mData->mSession.mDirectControl;
13684 }
13685
13686 /* ignore notifications sent after #OnSessionEnd() is called */
13687 if (!directControl)
13688 return S_OK;
13689
13690 return directControl->OnCPUChange(aCPU, aRemove);
13691}
13692
13693HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13694{
13695 LogFlowThisFunc(("\n"));
13696
13697 AutoCaller autoCaller(this);
13698 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13699
13700 ComPtr<IInternalSessionControl> directControl;
13701 {
13702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13703 directControl = mData->mSession.mDirectControl;
13704 }
13705
13706 /* ignore notifications sent after #OnSessionEnd() is called */
13707 if (!directControl)
13708 return S_OK;
13709
13710 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13711}
13712
13713/**
13714 * @note Locks this object for reading.
13715 */
13716HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13717{
13718 LogFlowThisFunc(("\n"));
13719
13720 AutoCaller autoCaller(this);
13721 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13722
13723 ComPtr<IInternalSessionControl> directControl;
13724 {
13725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13726 directControl = mData->mSession.mDirectControl;
13727 }
13728
13729 /* ignore notifications sent after #OnSessionEnd() is called */
13730 if (!directControl)
13731 return S_OK;
13732
13733 return directControl->OnVRDEServerChange(aRestart);
13734}
13735
13736/**
13737 * @note Locks this object for reading.
13738 */
13739HRESULT SessionMachine::i_onVideoCaptureChange()
13740{
13741 LogFlowThisFunc(("\n"));
13742
13743 AutoCaller autoCaller(this);
13744 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13745
13746 ComPtr<IInternalSessionControl> directControl;
13747 {
13748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13749 directControl = mData->mSession.mDirectControl;
13750 }
13751
13752 /* ignore notifications sent after #OnSessionEnd() is called */
13753 if (!directControl)
13754 return S_OK;
13755
13756 return directControl->OnVideoCaptureChange();
13757}
13758
13759/**
13760 * @note Locks this object for reading.
13761 */
13762HRESULT SessionMachine::i_onUSBControllerChange()
13763{
13764 LogFlowThisFunc(("\n"));
13765
13766 AutoCaller autoCaller(this);
13767 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13768
13769 ComPtr<IInternalSessionControl> directControl;
13770 {
13771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13772 directControl = mData->mSession.mDirectControl;
13773 }
13774
13775 /* ignore notifications sent after #OnSessionEnd() is called */
13776 if (!directControl)
13777 return S_OK;
13778
13779 return directControl->OnUSBControllerChange();
13780}
13781
13782/**
13783 * @note Locks this object for reading.
13784 */
13785HRESULT SessionMachine::i_onSharedFolderChange()
13786{
13787 LogFlowThisFunc(("\n"));
13788
13789 AutoCaller autoCaller(this);
13790 AssertComRCReturnRC(autoCaller.rc());
13791
13792 ComPtr<IInternalSessionControl> directControl;
13793 {
13794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13795 directControl = mData->mSession.mDirectControl;
13796 }
13797
13798 /* ignore notifications sent after #OnSessionEnd() is called */
13799 if (!directControl)
13800 return S_OK;
13801
13802 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13803}
13804
13805/**
13806 * @note Locks this object for reading.
13807 */
13808HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13809{
13810 LogFlowThisFunc(("\n"));
13811
13812 AutoCaller autoCaller(this);
13813 AssertComRCReturnRC(autoCaller.rc());
13814
13815 ComPtr<IInternalSessionControl> directControl;
13816 {
13817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13818 directControl = mData->mSession.mDirectControl;
13819 }
13820
13821 /* ignore notifications sent after #OnSessionEnd() is called */
13822 if (!directControl)
13823 return S_OK;
13824
13825 return directControl->OnClipboardModeChange(aClipboardMode);
13826}
13827
13828/**
13829 * @note Locks this object for reading.
13830 */
13831HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13832{
13833 LogFlowThisFunc(("\n"));
13834
13835 AutoCaller autoCaller(this);
13836 AssertComRCReturnRC(autoCaller.rc());
13837
13838 ComPtr<IInternalSessionControl> directControl;
13839 {
13840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13841 directControl = mData->mSession.mDirectControl;
13842 }
13843
13844 /* ignore notifications sent after #OnSessionEnd() is called */
13845 if (!directControl)
13846 return S_OK;
13847
13848 return directControl->OnDnDModeChange(aDnDMode);
13849}
13850
13851/**
13852 * @note Locks this object for reading.
13853 */
13854HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13855{
13856 LogFlowThisFunc(("\n"));
13857
13858 AutoCaller autoCaller(this);
13859 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13860
13861 ComPtr<IInternalSessionControl> directControl;
13862 {
13863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13864 directControl = mData->mSession.mDirectControl;
13865 }
13866
13867 /* ignore notifications sent after #OnSessionEnd() is called */
13868 if (!directControl)
13869 return S_OK;
13870
13871 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13872}
13873
13874/**
13875 * @note Locks this object for reading.
13876 */
13877HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13878{
13879 LogFlowThisFunc(("\n"));
13880
13881 AutoCaller autoCaller(this);
13882 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13883
13884 ComPtr<IInternalSessionControl> directControl;
13885 {
13886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13887 directControl = mData->mSession.mDirectControl;
13888 }
13889
13890 /* ignore notifications sent after #OnSessionEnd() is called */
13891 if (!directControl)
13892 return S_OK;
13893
13894 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13895}
13896
13897/**
13898 * Returns @c true if this machine's USB controller reports it has a matching
13899 * filter for the given USB device and @c false otherwise.
13900 *
13901 * @note locks this object for reading.
13902 */
13903bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13904{
13905 AutoCaller autoCaller(this);
13906 /* silently return if not ready -- this method may be called after the
13907 * direct machine session has been called */
13908 if (!autoCaller.isOk())
13909 return false;
13910
13911#ifdef VBOX_WITH_USB
13912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13913
13914 switch (mData->mMachineState)
13915 {
13916 case MachineState_Starting:
13917 case MachineState_Restoring:
13918 case MachineState_TeleportingIn:
13919 case MachineState_Paused:
13920 case MachineState_Running:
13921 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13922 * elsewhere... */
13923 alock.release();
13924 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13925 default: break;
13926 }
13927#else
13928 NOREF(aDevice);
13929 NOREF(aMaskedIfs);
13930#endif
13931 return false;
13932}
13933
13934/**
13935 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13936 */
13937HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13938 IVirtualBoxErrorInfo *aError,
13939 ULONG aMaskedIfs,
13940 const com::Utf8Str &aCaptureFilename)
13941{
13942 LogFlowThisFunc(("\n"));
13943
13944 AutoCaller autoCaller(this);
13945
13946 /* This notification may happen after the machine object has been
13947 * uninitialized (the session was closed), so don't assert. */
13948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13949
13950 ComPtr<IInternalSessionControl> directControl;
13951 {
13952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13953 directControl = mData->mSession.mDirectControl;
13954 }
13955
13956 /* fail on notifications sent after #OnSessionEnd() is called, it is
13957 * expected by the caller */
13958 if (!directControl)
13959 return E_FAIL;
13960
13961 /* No locks should be held at this point. */
13962 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13963 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13964
13965 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
13966}
13967
13968/**
13969 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13970 */
13971HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13972 IVirtualBoxErrorInfo *aError)
13973{
13974 LogFlowThisFunc(("\n"));
13975
13976 AutoCaller autoCaller(this);
13977
13978 /* This notification may happen after the machine object has been
13979 * uninitialized (the session was closed), so don't assert. */
13980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13981
13982 ComPtr<IInternalSessionControl> directControl;
13983 {
13984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 /* fail on notifications sent after #OnSessionEnd() is called, it is
13989 * expected by the caller */
13990 if (!directControl)
13991 return E_FAIL;
13992
13993 /* No locks should be held at this point. */
13994 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13995 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13996
13997 return directControl->OnUSBDeviceDetach(aId, aError);
13998}
13999
14000// protected methods
14001/////////////////////////////////////////////////////////////////////////////
14002
14003/**
14004 * Helper method to finalize saving the state.
14005 *
14006 * @note Must be called from under this object's lock.
14007 *
14008 * @param aRc S_OK if the snapshot has been taken successfully
14009 * @param aErrMsg human readable error message for failure
14010 *
14011 * @note Locks mParent + this objects for writing.
14012 */
14013HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14014{
14015 LogFlowThisFuncEnter();
14016
14017 AutoCaller autoCaller(this);
14018 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14019
14020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14021
14022 HRESULT rc = S_OK;
14023
14024 if (SUCCEEDED(aRc))
14025 {
14026 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14027
14028 /* save all VM settings */
14029 rc = i_saveSettings(NULL);
14030 // no need to check whether VirtualBox.xml needs saving also since
14031 // we can't have a name change pending at this point
14032 }
14033 else
14034 {
14035 // delete the saved state file (it might have been already created);
14036 // we need not check whether this is shared with a snapshot here because
14037 // we certainly created this saved state file here anew
14038 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14039 }
14040
14041 /* notify the progress object about operation completion */
14042 Assert(mConsoleTaskData.mProgress);
14043 if (SUCCEEDED(aRc))
14044 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
14045 else
14046 {
14047 if (aErrMsg.length())
14048 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
14049 COM_IIDOF(ISession),
14050 getComponentName(),
14051 aErrMsg.c_str());
14052 else
14053 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
14054 }
14055
14056 /* clear out the temporary saved state data */
14057 mConsoleTaskData.mLastState = MachineState_Null;
14058 mConsoleTaskData.strStateFilePath.setNull();
14059 mConsoleTaskData.mProgress.setNull();
14060
14061 LogFlowThisFuncLeave();
14062 return rc;
14063}
14064
14065/**
14066 * Deletes the given file if it is no longer in use by either the current machine state
14067 * (if the machine is "saved") or any of the machine's snapshots.
14068 *
14069 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14070 * but is different for each SnapshotMachine. When calling this, the order of calling this
14071 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14072 * is therefore critical. I know, it's all rather messy.
14073 *
14074 * @param strStateFile
14075 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14076 * the test for whether the saved state file is in use.
14077 */
14078void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14079 Snapshot *pSnapshotToIgnore)
14080{
14081 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14082 if ( (strStateFile.isNotEmpty())
14083 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14084 )
14085 // ... and it must also not be shared with other snapshots
14086 if ( !mData->mFirstSnapshot
14087 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14088 // this checks the SnapshotMachine's state file paths
14089 )
14090 RTFileDelete(strStateFile.c_str());
14091}
14092
14093/**
14094 * Locks the attached media.
14095 *
14096 * All attached hard disks are locked for writing and DVD/floppy are locked for
14097 * reading. Parents of attached hard disks (if any) are locked for reading.
14098 *
14099 * This method also performs accessibility check of all media it locks: if some
14100 * media is inaccessible, the method will return a failure and a bunch of
14101 * extended error info objects per each inaccessible medium.
14102 *
14103 * Note that this method is atomic: if it returns a success, all media are
14104 * locked as described above; on failure no media is locked at all (all
14105 * succeeded individual locks will be undone).
14106 *
14107 * The caller is responsible for doing the necessary state sanity checks.
14108 *
14109 * The locks made by this method must be undone by calling #unlockMedia() when
14110 * no more needed.
14111 */
14112HRESULT SessionMachine::i_lockMedia()
14113{
14114 AutoCaller autoCaller(this);
14115 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14116
14117 AutoMultiWriteLock2 alock(this->lockHandle(),
14118 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14119
14120 /* bail out if trying to lock things with already set up locking */
14121 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14122
14123 MultiResult mrc(S_OK);
14124
14125 /* Collect locking information for all medium objects attached to the VM. */
14126 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14127 it != mMediaData->mAttachments.end();
14128 ++it)
14129 {
14130 MediumAttachment* pAtt = *it;
14131 DeviceType_T devType = pAtt->i_getType();
14132 Medium *pMedium = pAtt->i_getMedium();
14133
14134 MediumLockList *pMediumLockList(new MediumLockList());
14135 // There can be attachments without a medium (floppy/dvd), and thus
14136 // it's impossible to create a medium lock list. It still makes sense
14137 // to have the empty medium lock list in the map in case a medium is
14138 // attached later.
14139 if (pMedium != NULL)
14140 {
14141 MediumType_T mediumType = pMedium->i_getType();
14142 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14143 || mediumType == MediumType_Shareable;
14144 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14145
14146 alock.release();
14147 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14148 !fIsReadOnlyLock /* fMediumLockWrite */,
14149 false /* fMediumLockWriteAll */,
14150 NULL,
14151 *pMediumLockList);
14152 alock.acquire();
14153 if (FAILED(mrc))
14154 {
14155 delete pMediumLockList;
14156 mData->mSession.mLockedMedia.Clear();
14157 break;
14158 }
14159 }
14160
14161 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14162 if (FAILED(rc))
14163 {
14164 mData->mSession.mLockedMedia.Clear();
14165 mrc = setError(rc,
14166 tr("Collecting locking information for all attached media failed"));
14167 break;
14168 }
14169 }
14170
14171 if (SUCCEEDED(mrc))
14172 {
14173 /* Now lock all media. If this fails, nothing is locked. */
14174 alock.release();
14175 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14176 alock.acquire();
14177 if (FAILED(rc))
14178 {
14179 mrc = setError(rc,
14180 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14181 }
14182 }
14183
14184 return mrc;
14185}
14186
14187/**
14188 * Undoes the locks made by by #lockMedia().
14189 */
14190HRESULT SessionMachine::i_unlockMedia()
14191{
14192 AutoCaller autoCaller(this);
14193 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14194
14195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14196
14197 /* we may be holding important error info on the current thread;
14198 * preserve it */
14199 ErrorInfoKeeper eik;
14200
14201 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14202 AssertComRC(rc);
14203 return rc;
14204}
14205
14206/**
14207 * Helper to change the machine state (reimplementation).
14208 *
14209 * @note Locks this object for writing.
14210 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14211 * it can cause crashes in random places due to unexpectedly committing
14212 * the current settings. The caller is responsible for that. The call
14213 * to saveStateSettings is fine, because this method does not commit.
14214 */
14215HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14216{
14217 LogFlowThisFuncEnter();
14218 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14219
14220 AutoCaller autoCaller(this);
14221 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14222
14223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14224
14225 MachineState_T oldMachineState = mData->mMachineState;
14226
14227 AssertMsgReturn(oldMachineState != aMachineState,
14228 ("oldMachineState=%s, aMachineState=%s\n",
14229 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14230 E_FAIL);
14231
14232 HRESULT rc = S_OK;
14233
14234 int stsFlags = 0;
14235 bool deleteSavedState = false;
14236
14237 /* detect some state transitions */
14238
14239 if ( ( oldMachineState == MachineState_Saved
14240 && aMachineState == MachineState_Restoring)
14241 || ( ( oldMachineState == MachineState_PoweredOff
14242 || oldMachineState == MachineState_Teleported
14243 || oldMachineState == MachineState_Aborted
14244 )
14245 && ( aMachineState == MachineState_TeleportingIn
14246 || aMachineState == MachineState_Starting
14247 )
14248 )
14249 )
14250 {
14251 /* The EMT thread is about to start */
14252
14253 /* Nothing to do here for now... */
14254
14255 /// @todo NEWMEDIA don't let mDVDDrive and other children
14256 /// change anything when in the Starting/Restoring state
14257 }
14258 else if ( ( oldMachineState == MachineState_Running
14259 || oldMachineState == MachineState_Paused
14260 || oldMachineState == MachineState_Teleporting
14261 || oldMachineState == MachineState_LiveSnapshotting
14262 || oldMachineState == MachineState_Stuck
14263 || oldMachineState == MachineState_Starting
14264 || oldMachineState == MachineState_Stopping
14265 || oldMachineState == MachineState_Saving
14266 || oldMachineState == MachineState_Restoring
14267 || oldMachineState == MachineState_TeleportingPausedVM
14268 || oldMachineState == MachineState_TeleportingIn
14269 )
14270 && ( aMachineState == MachineState_PoweredOff
14271 || aMachineState == MachineState_Saved
14272 || aMachineState == MachineState_Teleported
14273 || aMachineState == MachineState_Aborted
14274 )
14275 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14276 * snapshot */
14277 && ( mConsoleTaskData.mSnapshot.isNull()
14278 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14279 )
14280 )
14281 {
14282 /* The EMT thread has just stopped, unlock attached media. Note that as
14283 * opposed to locking that is done from Console, we do unlocking here
14284 * because the VM process may have aborted before having a chance to
14285 * properly unlock all media it locked. */
14286
14287 unlockMedia();
14288 }
14289
14290 if (oldMachineState == MachineState_Restoring)
14291 {
14292 if (aMachineState != MachineState_Saved)
14293 {
14294 /*
14295 * delete the saved state file once the machine has finished
14296 * restoring from it (note that Console sets the state from
14297 * Restoring to Saved if the VM couldn't restore successfully,
14298 * to give the user an ability to fix an error and retry --
14299 * we keep the saved state file in this case)
14300 */
14301 deleteSavedState = true;
14302 }
14303 }
14304 else if ( oldMachineState == MachineState_Saved
14305 && ( aMachineState == MachineState_PoweredOff
14306 || aMachineState == MachineState_Aborted
14307 || aMachineState == MachineState_Teleported
14308 )
14309 )
14310 {
14311 /*
14312 * delete the saved state after Console::ForgetSavedState() is called
14313 * or if the VM process (owning a direct VM session) crashed while the
14314 * VM was Saved
14315 */
14316
14317 /// @todo (dmik)
14318 // Not sure that deleting the saved state file just because of the
14319 // client death before it attempted to restore the VM is a good
14320 // thing. But when it crashes we need to go to the Aborted state
14321 // which cannot have the saved state file associated... The only
14322 // way to fix this is to make the Aborted condition not a VM state
14323 // but a bool flag: i.e., when a crash occurs, set it to true and
14324 // change the state to PoweredOff or Saved depending on the
14325 // saved state presence.
14326
14327 deleteSavedState = true;
14328 mData->mCurrentStateModified = TRUE;
14329 stsFlags |= SaveSTS_CurStateModified;
14330 }
14331
14332 if ( aMachineState == MachineState_Starting
14333 || aMachineState == MachineState_Restoring
14334 || aMachineState == MachineState_TeleportingIn
14335 )
14336 {
14337 /* set the current state modified flag to indicate that the current
14338 * state is no more identical to the state in the
14339 * current snapshot */
14340 if (!mData->mCurrentSnapshot.isNull())
14341 {
14342 mData->mCurrentStateModified = TRUE;
14343 stsFlags |= SaveSTS_CurStateModified;
14344 }
14345 }
14346
14347 if (deleteSavedState)
14348 {
14349 if (mRemoveSavedState)
14350 {
14351 Assert(!mSSData->strStateFilePath.isEmpty());
14352
14353 // it is safe to delete the saved state file if ...
14354 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14355 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14356 // ... none of the snapshots share the saved state file
14357 )
14358 RTFileDelete(mSSData->strStateFilePath.c_str());
14359 }
14360
14361 mSSData->strStateFilePath.setNull();
14362 stsFlags |= SaveSTS_StateFilePath;
14363 }
14364
14365 /* redirect to the underlying peer machine */
14366 mPeer->i_setMachineState(aMachineState);
14367
14368 if ( aMachineState == MachineState_PoweredOff
14369 || aMachineState == MachineState_Teleported
14370 || aMachineState == MachineState_Aborted
14371 || aMachineState == MachineState_Saved)
14372 {
14373 /* the machine has stopped execution
14374 * (or the saved state file was adopted) */
14375 stsFlags |= SaveSTS_StateTimeStamp;
14376 }
14377
14378 if ( ( oldMachineState == MachineState_PoweredOff
14379 || oldMachineState == MachineState_Aborted
14380 || oldMachineState == MachineState_Teleported
14381 )
14382 && aMachineState == MachineState_Saved)
14383 {
14384 /* the saved state file was adopted */
14385 Assert(!mSSData->strStateFilePath.isEmpty());
14386 stsFlags |= SaveSTS_StateFilePath;
14387 }
14388
14389#ifdef VBOX_WITH_GUEST_PROPS
14390 if ( aMachineState == MachineState_PoweredOff
14391 || aMachineState == MachineState_Aborted
14392 || aMachineState == MachineState_Teleported)
14393 {
14394 /* Make sure any transient guest properties get removed from the
14395 * property store on shutdown. */
14396 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14397
14398 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14399 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14400 while (it != llGuestProperties.end())
14401 {
14402 const settings::GuestProperty &prop = *it;
14403 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14404 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14405 {
14406 it = llGuestProperties.erase(it);
14407 fNeedsSaving = true;
14408 }
14409 else
14410 {
14411 ++it;
14412 }
14413 }
14414
14415 if (fNeedsSaving)
14416 {
14417 mData->mCurrentStateModified = TRUE;
14418 stsFlags |= SaveSTS_CurStateModified;
14419 }
14420 }
14421#endif /* VBOX_WITH_GUEST_PROPS */
14422
14423 rc = i_saveStateSettings(stsFlags);
14424
14425 if ( ( oldMachineState != MachineState_PoweredOff
14426 && oldMachineState != MachineState_Aborted
14427 && oldMachineState != MachineState_Teleported
14428 )
14429 && ( aMachineState == MachineState_PoweredOff
14430 || aMachineState == MachineState_Aborted
14431 || aMachineState == MachineState_Teleported
14432 )
14433 )
14434 {
14435 /* we've been shut down for any reason */
14436 /* no special action so far */
14437 }
14438
14439 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14440 LogFlowThisFuncLeave();
14441 return rc;
14442}
14443
14444/**
14445 * Sends the current machine state value to the VM process.
14446 *
14447 * @note Locks this object for reading, then calls a client process.
14448 */
14449HRESULT SessionMachine::i_updateMachineStateOnClient()
14450{
14451 AutoCaller autoCaller(this);
14452 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14453
14454 ComPtr<IInternalSessionControl> directControl;
14455 {
14456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14457 AssertReturn(!!mData, E_FAIL);
14458 directControl = mData->mSession.mDirectControl;
14459
14460 /* directControl may be already set to NULL here in #OnSessionEnd()
14461 * called too early by the direct session process while there is still
14462 * some operation (like deleting the snapshot) in progress. The client
14463 * process in this case is waiting inside Session::close() for the
14464 * "end session" process object to complete, while #uninit() called by
14465 * #checkForDeath() on the Watcher thread is waiting for the pending
14466 * operation to complete. For now, we accept this inconsistent behavior
14467 * and simply do nothing here. */
14468
14469 if (mData->mSession.mState == SessionState_Unlocking)
14470 return S_OK;
14471
14472 AssertReturn(!directControl.isNull(), E_FAIL);
14473 }
14474
14475 return directControl->UpdateMachineState(mData->mMachineState);
14476}
14477
14478HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14479{
14480 NOREF(aRemove);
14481 ReturnComNotImplemented();
14482}
14483
14484HRESULT Machine::updateState(MachineState_T aState)
14485{
14486 NOREF(aState);
14487 ReturnComNotImplemented();
14488}
14489
14490HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14491{
14492 NOREF(aProgress);
14493 ReturnComNotImplemented();
14494}
14495
14496HRESULT Machine::endPowerUp(LONG aResult)
14497{
14498 NOREF(aResult);
14499 ReturnComNotImplemented();
14500}
14501
14502HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14503{
14504 NOREF(aProgress);
14505 ReturnComNotImplemented();
14506}
14507
14508HRESULT Machine::endPoweringDown(LONG aResult,
14509 const com::Utf8Str &aErrMsg)
14510{
14511 NOREF(aResult);
14512 NOREF(aErrMsg);
14513 ReturnComNotImplemented();
14514}
14515
14516HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14517 BOOL *aMatched,
14518 ULONG *aMaskedInterfaces)
14519{
14520 NOREF(aDevice);
14521 NOREF(aMatched);
14522 NOREF(aMaskedInterfaces);
14523 ReturnComNotImplemented();
14524
14525}
14526
14527HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14528{
14529 NOREF(aId); NOREF(aCaptureFilename);
14530 ReturnComNotImplemented();
14531}
14532
14533HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14534 BOOL aDone)
14535{
14536 NOREF(aId);
14537 NOREF(aDone);
14538 ReturnComNotImplemented();
14539}
14540
14541HRESULT Machine::autoCaptureUSBDevices()
14542{
14543 ReturnComNotImplemented();
14544}
14545
14546HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14547{
14548 NOREF(aDone);
14549 ReturnComNotImplemented();
14550}
14551
14552HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14553 ComPtr<IProgress> &aProgress)
14554{
14555 NOREF(aSession);
14556 NOREF(aProgress);
14557 ReturnComNotImplemented();
14558}
14559
14560HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14561 com::Utf8Str &aStateFilePath)
14562{
14563 NOREF(aProgress);
14564 NOREF(aStateFilePath);
14565 ReturnComNotImplemented();
14566}
14567
14568HRESULT Machine::endSavingState(LONG aResult,
14569 const com::Utf8Str &aErrMsg)
14570{
14571 NOREF(aResult);
14572 NOREF(aErrMsg);
14573 ReturnComNotImplemented();
14574}
14575
14576HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14577{
14578 NOREF(aSavedStateFile);
14579 ReturnComNotImplemented();
14580}
14581
14582HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14583 const com::Utf8Str &aName,
14584 const com::Utf8Str &aDescription,
14585 const ComPtr<IProgress> &aConsoleProgress,
14586 BOOL aFTakingSnapshotOnline,
14587 com::Utf8Str &aStateFilePath)
14588{
14589 NOREF(aInitiator);
14590 NOREF(aName);
14591 NOREF(aDescription);
14592 NOREF(aConsoleProgress);
14593 NOREF(aFTakingSnapshotOnline);
14594 NOREF(aStateFilePath);
14595 ReturnComNotImplemented();
14596}
14597
14598HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14599{
14600 NOREF(aSuccess);
14601 ReturnComNotImplemented();
14602}
14603
14604HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14605 const com::Guid &aStartId,
14606 const com::Guid &aEndId,
14607 BOOL aDeleteAllChildren,
14608 MachineState_T *aMachineState,
14609 ComPtr<IProgress> &aProgress)
14610{
14611 NOREF(aInitiator);
14612 NOREF(aStartId);
14613 NOREF(aEndId);
14614 NOREF(aDeleteAllChildren);
14615 NOREF(aMachineState);
14616 NOREF(aProgress);
14617 ReturnComNotImplemented();
14618}
14619
14620HRESULT Machine::finishOnlineMergeMedium()
14621{
14622 ReturnComNotImplemented();
14623}
14624
14625HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14626 const ComPtr<ISnapshot> &aSnapshot,
14627 MachineState_T *aMachineState,
14628 ComPtr<IProgress> &aProgress)
14629{
14630 NOREF(aInitiator);
14631 NOREF(aSnapshot);
14632 NOREF(aMachineState);
14633 NOREF(aProgress);
14634 ReturnComNotImplemented();
14635}
14636
14637HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14638 std::vector<com::Utf8Str> &aValues,
14639 std::vector<LONG64> &aTimestamps,
14640 std::vector<com::Utf8Str> &aFlags)
14641{
14642 NOREF(aNames);
14643 NOREF(aValues);
14644 NOREF(aTimestamps);
14645 NOREF(aFlags);
14646 ReturnComNotImplemented();
14647}
14648
14649HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14650 const com::Utf8Str &aValue,
14651 LONG64 aTimestamp,
14652 const com::Utf8Str &aFlags)
14653{
14654 NOREF(aName);
14655 NOREF(aValue);
14656 NOREF(aTimestamp);
14657 NOREF(aFlags);
14658 ReturnComNotImplemented();
14659}
14660
14661HRESULT Machine::lockMedia()
14662{
14663 ReturnComNotImplemented();
14664}
14665
14666HRESULT Machine::unlockMedia()
14667{
14668 ReturnComNotImplemented();
14669}
14670
14671HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14672 ComPtr<IMediumAttachment> &aNewAttachment)
14673{
14674 NOREF(aAttachment);
14675 NOREF(aNewAttachment);
14676 ReturnComNotImplemented();
14677}
14678
14679HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14680 ULONG aCpuUser,
14681 ULONG aCpuKernel,
14682 ULONG aCpuIdle,
14683 ULONG aMemTotal,
14684 ULONG aMemFree,
14685 ULONG aMemBalloon,
14686 ULONG aMemShared,
14687 ULONG aMemCache,
14688 ULONG aPagedTotal,
14689 ULONG aMemAllocTotal,
14690 ULONG aMemFreeTotal,
14691 ULONG aMemBalloonTotal,
14692 ULONG aMemSharedTotal,
14693 ULONG aVmNetRx,
14694 ULONG aVmNetTx)
14695{
14696 NOREF(aValidStats);
14697 NOREF(aCpuUser);
14698 NOREF(aCpuKernel);
14699 NOREF(aCpuIdle);
14700 NOREF(aMemTotal);
14701 NOREF(aMemFree);
14702 NOREF(aMemBalloon);
14703 NOREF(aMemShared);
14704 NOREF(aMemCache);
14705 NOREF(aPagedTotal);
14706 NOREF(aMemAllocTotal);
14707 NOREF(aMemFreeTotal);
14708 NOREF(aMemBalloonTotal);
14709 NOREF(aMemSharedTotal);
14710 NOREF(aVmNetRx);
14711 NOREF(aVmNetTx);
14712 ReturnComNotImplemented();
14713}
Note: See TracBrowser for help on using the repository browser.

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