VirtualBox

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

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

Main/Machine: fix long-standing bug with setting extradata (it isn't allowed to use an immutable IMachine instance, see corresponding VBoxManage fix), and while at it fix a long-standing bug which allowed changing most settings at runtime (the i_checkStateDependency logic was broken)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 501.0 KB
Line 
1/* $Id: MachineImpl.cpp 54791 2015-03-16 16:04:03Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#ifdef VBOX_WITH_DTRACE_R3_MAIN
90# include "dtrace/VBoxAPI.h"
91#endif
92
93#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
94# define HOSTSUFF_EXE ".exe"
95#else /* !RT_OS_WINDOWS */
96# define HOSTSUFF_EXE ""
97#endif /* !RT_OS_WINDOWS */
98
99// defines / prototypes
100/////////////////////////////////////////////////////////////////////////////
101
102/////////////////////////////////////////////////////////////////////////////
103// Machine::Data structure
104/////////////////////////////////////////////////////////////////////////////
105
106Machine::Data::Data()
107{
108 mRegistered = FALSE;
109 pMachineConfigFile = NULL;
110 /* Contains hints on what has changed when the user is using the VM (config
111 * changes, running the VM, ...). This is used to decide if a config needs
112 * to be written to disk. */
113 flModifications = 0;
114 /* VM modification usually also trigger setting the current state to
115 * "Modified". Although this is not always the case. An e.g. is the VM
116 * initialization phase or when snapshot related data is changed. The
117 * actually behavior is controlled by the following flag. */
118 m_fAllowStateModification = false;
119 mAccessible = FALSE;
120 /* mUuid is initialized in Machine::init() */
121
122 mMachineState = MachineState_PoweredOff;
123 RTTimeNow(&mLastStateChange);
124
125 mMachineStateDeps = 0;
126 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
127 mMachineStateChangePending = 0;
128
129 mCurrentStateModified = TRUE;
130 mGuestPropertiesModified = FALSE;
131
132 mSession.mPID = NIL_RTPROCESS;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mTripleFaultReset = false;
197 mHPETEnabled = false;
198
199 /* default boot order: floppy - DVD - HDD */
200 mBootOrder[0] = DeviceType_Floppy;
201 mBootOrder[1] = DeviceType_DVD;
202 mBootOrder[2] = DeviceType_HardDisk;
203 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
204 mBootOrder[i] = DeviceType_Null;
205
206 mClipboardMode = ClipboardMode_Disabled;
207 mDnDMode = DnDMode_Disabled;
208 mGuestPropertyNotificationPatterns = "";
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222
223 /* Maximum CPU execution cap by default. */
224 mCpuExecutionCap = 100;
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HDData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::MediaData::MediaData()
236{
237}
238
239Machine::MediaData::~MediaData()
240{
241}
242
243/////////////////////////////////////////////////////////////////////////////
244// Machine class
245/////////////////////////////////////////////////////////////////////////////
246
247// constructor / destructor
248/////////////////////////////////////////////////////////////////////////////
249
250Machine::Machine() :
251#ifdef VBOX_WITH_RESOURCE_USAGE_API
252 mCollectorGuest(NULL),
253#endif
254 mPeer(NULL),
255 mParent(NULL),
256 mSerialPorts(),
257 mParallelPorts(),
258 uRegistryNeedsSaving(0)
259{}
260
261Machine::~Machine()
262{}
263
264HRESULT Machine::FinalConstruct()
265{
266 LogFlowThisFunc(("\n"));
267 return BaseFinalConstruct();
268}
269
270void Machine::FinalRelease()
271{
272 LogFlowThisFunc(("\n"));
273 uninit();
274 BaseFinalRelease();
275}
276
277/**
278 * Initializes a new machine instance; this init() variant creates a new, empty machine.
279 * This gets called from VirtualBox::CreateMachine().
280 *
281 * @param aParent Associated parent object
282 * @param strConfigFile Local file system path to the VM settings file (can
283 * be relative to the VirtualBox config directory).
284 * @param strName name for the machine
285 * @param llGroups list of groups for the machine
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Apply BIOS defaults */
349 mBIOSSettings->i_applyDefaults(aOsType);
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Let the OS type select 64-bit ness. */
360 mHWData->mLongMode = aOsType->i_is64Bit()
361 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 }
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 i_allowStateModification();
367
368 /* commit all changes made during the initialization */
369 i_commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param aConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = i_registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 i_allowStateModification();
469
470 i_commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->i_unregisterMachineMedia(i_getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent
607 * @return
608 */
609HRESULT Machine::initImpl(VirtualBox *aParent,
610 const Utf8Str &strConfigFile)
611{
612 LogFlowThisFuncEnter();
613
614 AssertReturn(aParent, E_INVALIDARG);
615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
616
617 HRESULT rc = S_OK;
618
619 /* share the parent weakly */
620 unconst(mParent) = aParent;
621
622 /* allocate the essential machine data structure (the rest will be
623 * allocated later by initDataAndChildObjects() */
624 mData.allocate();
625
626 /* memorize the config file name (as provided) */
627 mData->m_strConfigFile = strConfigFile;
628
629 /* get the full file name */
630 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
631 if (RT_FAILURE(vrc1))
632 return setError(VBOX_E_FILE_ERROR,
633 tr("Invalid machine settings file name '%s' (%Rrc)"),
634 strConfigFile.c_str(),
635 vrc1);
636
637 LogFlowThisFuncLeave();
638
639 return rc;
640}
641
642/**
643 * Tries to create a machine settings file in the path stored in the machine
644 * instance data. Used when a new machine is created to fail gracefully if
645 * the settings file could not be written (e.g. because machine dir is read-only).
646 * @return
647 */
648HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
649{
650 HRESULT rc = S_OK;
651
652 // when we create a new machine, we must be able to create the settings file
653 RTFILE f = NIL_RTFILE;
654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
655 if ( RT_SUCCESS(vrc)
656 || vrc == VERR_SHARING_VIOLATION
657 )
658 {
659 if (RT_SUCCESS(vrc))
660 RTFileClose(f);
661 if (!fForceOverwrite)
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Machine settings file '%s' already exists"),
664 mData->m_strConfigFileFull.c_str());
665 else
666 {
667 /* try to delete the config file, as otherwise the creation
668 * of a new settings file will fail. */
669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
670 if (RT_FAILURE(vrc2))
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Could not delete the existing settings file '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(), vrc2);
674 }
675 }
676 else if ( vrc != VERR_FILE_NOT_FOUND
677 && vrc != VERR_PATH_NOT_FOUND
678 )
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Invalid machine settings file name '%s' (%Rrc)"),
681 mData->m_strConfigFileFull.c_str(),
682 vrc);
683 return rc;
684}
685
686/**
687 * Initializes the registered machine by loading the settings file.
688 * This method is separated from #init() in order to make it possible to
689 * retry the operation after VirtualBox startup instead of refusing to
690 * startup the whole VirtualBox server in case if the settings file of some
691 * registered VM is invalid or inaccessible.
692 *
693 * @note Must be always called from this object's write lock
694 * (unless called from #init() that doesn't need any locking).
695 * @note Locks the mUSBController method for writing.
696 * @note Subclasses must not call this method.
697 */
698HRESULT Machine::i_registeredInit()
699{
700 AssertReturn(!i_isSessionMachine(), E_FAIL);
701 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
702 AssertReturn(mData->mUuid.isValid(), E_FAIL);
703 AssertReturn(!mData->mAccessible, E_FAIL);
704
705 HRESULT rc = initDataAndChildObjects();
706
707 if (SUCCEEDED(rc))
708 {
709 /* Temporarily reset the registered flag in order to let setters
710 * potentially called from loadSettings() succeed (isMutable() used in
711 * all setters will return FALSE for a Machine instance if mRegistered
712 * is TRUE). */
713 mData->mRegistered = FALSE;
714
715 try
716 {
717 // load and parse machine XML; this will throw on XML or logic errors
718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
719
720 if (mData->mUuid != mData->pMachineConfigFile->uuid)
721 throw setError(E_FAIL,
722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
723 mData->pMachineConfigFile->uuid.raw(),
724 mData->m_strConfigFileFull.c_str(),
725 mData->mUuid.toString().c_str(),
726 mParent->i_settingsFilePath().c_str());
727
728 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
729 NULL /* const Guid *puuidRegistry */);
730 if (FAILED(rc)) throw rc;
731 }
732 catch (HRESULT err)
733 {
734 /* we assume that error info is set by the thrower */
735 rc = err;
736 }
737 catch (...)
738 {
739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
740 }
741
742 /* Restore the registered flag (even on failure) */
743 mData->mRegistered = TRUE;
744 }
745
746 if (SUCCEEDED(rc))
747 {
748 /* Set mAccessible to TRUE only if we successfully locked and loaded
749 * the settings file */
750 mData->mAccessible = TRUE;
751
752 /* commit all changes made during loading the settings file */
753 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
754 /// @todo r=klaus for some reason the settings loading logic backs up
755 // the settings, and therefore a commit is needed. Should probably be changed.
756 }
757 else
758 {
759 /* If the machine is registered, then, instead of returning a
760 * failure, we mark it as inaccessible and set the result to
761 * success to give it a try later */
762
763 /* fetch the current error info */
764 mData->mAccessError = com::ErrorInfo();
765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
766 mData->mUuid.raw(),
767 mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 i_rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->i_unregisterMachineMedia(i_getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 i_saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!i_isSnapshotMachine());
820 Assert(!i_isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it does
835 * VirtualBox::addCaller() for the duration of the
836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 LogWarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(i_getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->i_unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
879 i_rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// Wrapped IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
894{
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 aParent = pVirtualBox;
898
899 return S_OK;
900}
901
902
903HRESULT Machine::getAccessible(BOOL *aAccessible)
904{
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->i_dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = i_registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->i_onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
959 {
960 /* return shortly */
961 aAccessError = NULL;
962 return S_OK;
963 }
964
965 HRESULT rc = S_OK;
966
967 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
968 rc = errorInfo.createObject();
969 if (SUCCEEDED(rc))
970 {
971 errorInfo->init(mData->mAccessError.getResultCode(),
972 mData->mAccessError.getInterfaceID().ref(),
973 Utf8Str(mData->mAccessError.getComponent()).c_str(),
974 Utf8Str(mData->mAccessError.getText()));
975 aAccessError = errorInfo;
976 }
977
978 return rc;
979}
980
981HRESULT Machine::getName(com::Utf8Str &aName)
982{
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 aName = mUserData->s.strName;
986
987 return S_OK;
988}
989
990HRESULT Machine::setName(const com::Utf8Str &aName)
991{
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995
996 if (test.isValid())
997 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
998
999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 HRESULT rc = i_checkStateDependency(MutableStateDep);
1002 if (FAILED(rc)) return rc;
1003
1004 i_setModified(IsModified_MachineData);
1005 mUserData.backup();
1006 mUserData->s.strName = aName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1012{
1013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 aDescription = mUserData->s.strDescription;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1021{
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 // this can be done in principle in any state as it doesn't affect the VM
1025 // significantly, but play safe by not messing around while complex
1026 // activities are going on
1027 HRESULT rc = i_checkStateDependency(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(MutableStateDep);
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_Minimal:
1257 break;
1258
1259 /* Resolve dynamic provider types to the effective types. */
1260 default:
1261 {
1262 ComPtr<IGuestOSType> ptrGuestOSType;
1263 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1264 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1265
1266 Bstr guestTypeFamilyId;
1267 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1268 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1269 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1270
1271 switch (mHWData->mParavirtProvider)
1272 {
1273 case ParavirtProvider_Legacy:
1274 {
1275 if (fOsXGuest)
1276 *aParavirtProvider = ParavirtProvider_Minimal;
1277 else
1278 *aParavirtProvider = ParavirtProvider_None;
1279 break;
1280 }
1281
1282 case ParavirtProvider_Default:
1283 {
1284 if (fOsXGuest)
1285 *aParavirtProvider = ParavirtProvider_Minimal;
1286 else if ( mUserData->s.strOsType == "Windows10"
1287 || mUserData->s.strOsType == "Windows10_64"
1288 || mUserData->s.strOsType == "Windows81"
1289 || mUserData->s.strOsType == "Windows81_64"
1290 || mUserData->s.strOsType == "Windows8"
1291 || mUserData->s.strOsType == "Windows8_64"
1292 || mUserData->s.strOsType == "Windows7"
1293 || mUserData->s.strOsType == "Windows7_64"
1294 || mUserData->s.strOsType == "WindowsVista"
1295 || mUserData->s.strOsType == "WindowsVista_64"
1296 || mUserData->s.strOsType == "Windows2012"
1297 || mUserData->s.strOsType == "Windows2012_64"
1298 || mUserData->s.strOsType == "Windows2008"
1299 || mUserData->s.strOsType == "Windows2008_64")
1300 {
1301 *aParavirtProvider = ParavirtProvider_HyperV;
1302 }
1303 else
1304 *aParavirtProvider = ParavirtProvider_None;
1305 break;
1306 }
1307 }
1308 break;
1309 }
1310 }
1311
1312 Assert( *aParavirtProvider == ParavirtProvider_None
1313 || *aParavirtProvider == ParavirtProvider_Minimal
1314 || *aParavirtProvider == ParavirtProvider_HyperV);
1315 return S_OK;
1316}
1317
1318HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1319{
1320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1321
1322 aHardwareVersion = mHWData->mHWVersion;
1323
1324 return S_OK;
1325}
1326
1327HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1328{
1329 /* check known version */
1330 Utf8Str hwVersion = aHardwareVersion;
1331 if ( hwVersion.compare("1") != 0
1332 && hwVersion.compare("2") != 0)
1333 return setError(E_INVALIDARG,
1334 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1335
1336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 HRESULT rc = i_checkStateDependency(MutableStateDep);
1339 if (FAILED(rc)) return rc;
1340
1341 i_setModified(IsModified_MachineData);
1342 mHWData.backup();
1343 mHWData->mHWVersion = aHardwareVersion;
1344
1345 return S_OK;
1346}
1347
1348HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1349{
1350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1351
1352 if (!mHWData->mHardwareUUID.isZero())
1353 aHardwareUUID = mHWData->mHardwareUUID;
1354 else
1355 aHardwareUUID = mData->mUuid;
1356
1357 return S_OK;
1358}
1359
1360HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1361{
1362 if (!aHardwareUUID.isValid())
1363 return E_INVALIDARG;
1364
1365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 HRESULT rc = i_checkStateDependency(MutableStateDep);
1368 if (FAILED(rc)) return rc;
1369
1370 i_setModified(IsModified_MachineData);
1371 mHWData.backup();
1372 if (aHardwareUUID == mData->mUuid)
1373 mHWData->mHardwareUUID.clear();
1374 else
1375 mHWData->mHardwareUUID = aHardwareUUID;
1376
1377 return S_OK;
1378}
1379
1380HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1381{
1382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 *aMemorySize = mHWData->mMemorySize;
1385
1386 return S_OK;
1387}
1388
1389HRESULT Machine::setMemorySize(ULONG aMemorySize)
1390{
1391 /* check RAM limits */
1392 if ( aMemorySize < MM_RAM_MIN_IN_MB
1393 || aMemorySize > MM_RAM_MAX_IN_MB
1394 )
1395 return setError(E_INVALIDARG,
1396 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1397 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1398
1399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1400
1401 HRESULT rc = i_checkStateDependency(MutableStateDep);
1402 if (FAILED(rc)) return rc;
1403
1404 i_setModified(IsModified_MachineData);
1405 mHWData.backup();
1406 mHWData->mMemorySize = aMemorySize;
1407
1408 return S_OK;
1409}
1410
1411HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1412{
1413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 *aCPUCount = mHWData->mCPUCount;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::setCPUCount(ULONG aCPUCount)
1421{
1422 /* check CPU limits */
1423 if ( aCPUCount < SchemaDefs::MinCPUCount
1424 || aCPUCount > SchemaDefs::MaxCPUCount
1425 )
1426 return setError(E_INVALIDARG,
1427 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1428 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1429
1430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1433 if (mHWData->mCPUHotPlugEnabled)
1434 {
1435 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1436 {
1437 if (mHWData->mCPUAttached[idx])
1438 return setError(E_INVALIDARG,
1439 tr("There is still a CPU attached to socket %lu."
1440 "Detach the CPU before removing the socket"),
1441 aCPUCount, idx+1);
1442 }
1443 }
1444
1445 HRESULT rc = i_checkStateDependency(MutableStateDep);
1446 if (FAILED(rc)) return rc;
1447
1448 i_setModified(IsModified_MachineData);
1449 mHWData.backup();
1450 mHWData->mCPUCount = aCPUCount;
1451
1452 return S_OK;
1453}
1454
1455HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1456{
1457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1458
1459 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1460
1461 return S_OK;
1462}
1463
1464HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1465{
1466 HRESULT rc = S_OK;
1467
1468 /* check throttle limits */
1469 if ( aCPUExecutionCap < 1
1470 || aCPUExecutionCap > 100
1471 )
1472 return setError(E_INVALIDARG,
1473 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1474 aCPUExecutionCap, 1, 100);
1475
1476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1477
1478 alock.release();
1479 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1480 alock.acquire();
1481 if (FAILED(rc)) return rc;
1482
1483 i_setModified(IsModified_MachineData);
1484 mHWData.backup();
1485 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1486
1487 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1488 if (Global::IsOnline(mData->mMachineState))
1489 i_saveSettings(NULL);
1490
1491 return S_OK;
1492}
1493
1494HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1495{
1496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1497
1498 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1499
1500 return S_OK;
1501}
1502
1503HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1504{
1505 HRESULT rc = S_OK;
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 rc = i_checkStateDependency(MutableStateDep);
1510 if (FAILED(rc)) return rc;
1511
1512 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1513 {
1514 if (aCPUHotPlugEnabled)
1515 {
1516 i_setModified(IsModified_MachineData);
1517 mHWData.backup();
1518
1519 /* Add the amount of CPUs currently attached */
1520 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1521 mHWData->mCPUAttached[i] = true;
1522 }
1523 else
1524 {
1525 /*
1526 * We can disable hotplug only if the amount of maximum CPUs is equal
1527 * to the amount of attached CPUs
1528 */
1529 unsigned cCpusAttached = 0;
1530 unsigned iHighestId = 0;
1531
1532 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1533 {
1534 if (mHWData->mCPUAttached[i])
1535 {
1536 cCpusAttached++;
1537 iHighestId = i;
1538 }
1539 }
1540
1541 if ( (cCpusAttached != mHWData->mCPUCount)
1542 || (iHighestId >= mHWData->mCPUCount))
1543 return setError(E_INVALIDARG,
1544 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1545
1546 i_setModified(IsModified_MachineData);
1547 mHWData.backup();
1548 }
1549 }
1550
1551 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1552
1553 return rc;
1554}
1555
1556HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1557{
1558#ifdef VBOX_WITH_USB_CARDREADER
1559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1560
1561 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1562
1563 return S_OK;
1564#else
1565 NOREF(aEmulatedUSBCardReaderEnabled);
1566 return E_NOTIMPL;
1567#endif
1568}
1569
1570HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1571{
1572#ifdef VBOX_WITH_USB_CARDREADER
1573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1574
1575 HRESULT rc = i_checkStateDependency(MutableStateDep);
1576 if (FAILED(rc)) return rc;
1577
1578 i_setModified(IsModified_MachineData);
1579 mHWData.backup();
1580 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1581
1582 return S_OK;
1583#else
1584 NOREF(aEmulatedUSBCardReaderEnabled);
1585 return E_NOTIMPL;
1586#endif
1587}
1588
1589HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1590{
1591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1592
1593 *aHPETEnabled = mHWData->mHPETEnabled;
1594
1595 return S_OK;
1596}
1597
1598HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1599{
1600 HRESULT rc = S_OK;
1601
1602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1603
1604 rc = i_checkStateDependency(MutableStateDep);
1605 if (FAILED(rc)) return rc;
1606
1607 i_setModified(IsModified_MachineData);
1608 mHWData.backup();
1609
1610 mHWData->mHPETEnabled = aHPETEnabled;
1611
1612 return rc;
1613}
1614
1615HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1616{
1617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1618
1619 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1620 return S_OK;
1621}
1622
1623HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1624{
1625 HRESULT rc = S_OK;
1626
1627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1628
1629 i_setModified(IsModified_MachineData);
1630 mHWData.backup();
1631 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1632
1633 alock.release();
1634 rc = i_onVideoCaptureChange();
1635 alock.acquire();
1636 if (FAILED(rc))
1637 {
1638 /*
1639 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1640 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1641 * determine if it should start or stop capturing. Therefore we need to manually
1642 * undo change.
1643 */
1644 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1645 return rc;
1646 }
1647
1648 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1649 if (Global::IsOnline(mData->mMachineState))
1650 i_saveSettings(NULL);
1651
1652 return rc;
1653}
1654
1655HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1656{
1657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1658 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1659 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1660 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1661 return S_OK;
1662}
1663
1664HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1665{
1666 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1667 bool fChanged = false;
1668
1669 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1672 {
1673 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1674 {
1675 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1676 fChanged = true;
1677 }
1678 }
1679 if (fChanged)
1680 {
1681 alock.release();
1682 HRESULT rc = i_onVideoCaptureChange();
1683 alock.acquire();
1684 if (FAILED(rc)) return rc;
1685 i_setModified(IsModified_MachineData);
1686
1687 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1688 if (Global::IsOnline(mData->mMachineState))
1689 i_saveSettings(NULL);
1690 }
1691
1692 return S_OK;
1693}
1694
1695HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1696{
1697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1698 if (mHWData->mVideoCaptureFile.isEmpty())
1699 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1700 else
1701 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1702 return S_OK;
1703}
1704
1705HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1706{
1707 Utf8Str strFile(aVideoCaptureFile);
1708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1709
1710 if ( Global::IsOnline(mData->mMachineState)
1711 && mHWData->mVideoCaptureEnabled)
1712 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1713
1714 if (!RTPathStartsWithRoot(strFile.c_str()))
1715 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1716
1717 if (!strFile.isEmpty())
1718 {
1719 Utf8Str defaultFile;
1720 i_getDefaultVideoCaptureFile(defaultFile);
1721 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1722 strFile.setNull();
1723 }
1724
1725 i_setModified(IsModified_MachineData);
1726 mHWData.backup();
1727 mHWData->mVideoCaptureFile = strFile;
1728
1729 return S_OK;
1730}
1731
1732HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1733{
1734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1735 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1736 return S_OK;
1737}
1738
1739HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1740{
1741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1742
1743 if ( Global::IsOnline(mData->mMachineState)
1744 && mHWData->mVideoCaptureEnabled)
1745 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1746
1747 i_setModified(IsModified_MachineData);
1748 mHWData.backup();
1749 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1750
1751 return S_OK;
1752}
1753
1754HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1755{
1756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1757 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1758 return S_OK;
1759}
1760
1761HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1762{
1763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1764
1765 if ( Global::IsOnline(mData->mMachineState)
1766 && mHWData->mVideoCaptureEnabled)
1767 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1768
1769 i_setModified(IsModified_MachineData);
1770 mHWData.backup();
1771 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1772
1773 return S_OK;
1774}
1775
1776HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1777{
1778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1779 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1780 return S_OK;
1781}
1782
1783HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1784{
1785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 if ( Global::IsOnline(mData->mMachineState)
1788 && mHWData->mVideoCaptureEnabled)
1789 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1790
1791 i_setModified(IsModified_MachineData);
1792 mHWData.backup();
1793 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1794
1795 return S_OK;
1796}
1797
1798HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1802 return S_OK;
1803}
1804
1805HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1806{
1807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1808
1809 if ( Global::IsOnline(mData->mMachineState)
1810 && mHWData->mVideoCaptureEnabled)
1811 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1812
1813 i_setModified(IsModified_MachineData);
1814 mHWData.backup();
1815 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1816
1817 return S_OK;
1818}
1819
1820HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1824 return S_OK;
1825}
1826
1827HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1828{
1829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1830
1831 if ( Global::IsOnline(mData->mMachineState)
1832 && mHWData->mVideoCaptureEnabled)
1833 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1834
1835 i_setModified(IsModified_MachineData);
1836 mHWData.backup();
1837 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1838
1839 return S_OK;
1840}
1841
1842HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1843{
1844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1845 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1846 return S_OK;
1847}
1848
1849HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1850{
1851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1852
1853 if ( Global::IsOnline(mData->mMachineState)
1854 && mHWData->mVideoCaptureEnabled)
1855 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1856
1857 i_setModified(IsModified_MachineData);
1858 mHWData.backup();
1859 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1860
1861 return S_OK;
1862}
1863
1864HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1865{
1866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1867
1868 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1869 return S_OK;
1870}
1871
1872HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1873{
1874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1875
1876 if ( Global::IsOnline(mData->mMachineState)
1877 && mHWData->mVideoCaptureEnabled)
1878 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1879
1880 i_setModified(IsModified_MachineData);
1881 mHWData.backup();
1882 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1883
1884 return S_OK;
1885}
1886
1887HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1888{
1889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1890
1891 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1892
1893 return S_OK;
1894}
1895
1896HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1897{
1898 switch (aGraphicsControllerType)
1899 {
1900 case GraphicsControllerType_Null:
1901 case GraphicsControllerType_VBoxVGA:
1902#ifdef VBOX_WITH_VMSVGA
1903 case GraphicsControllerType_VMSVGA:
1904#endif
1905 break;
1906 default:
1907 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1908 }
1909
1910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1911
1912 HRESULT rc = i_checkStateDependency(MutableStateDep);
1913 if (FAILED(rc)) return rc;
1914
1915 i_setModified(IsModified_MachineData);
1916 mHWData.backup();
1917 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1918
1919 return S_OK;
1920}
1921
1922HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1923{
1924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 *aVRAMSize = mHWData->mVRAMSize;
1927
1928 return S_OK;
1929}
1930
1931HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1932{
1933 /* check VRAM limits */
1934 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1935 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1936 return setError(E_INVALIDARG,
1937 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1938 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1939
1940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1941
1942 HRESULT rc = i_checkStateDependency(MutableStateDep);
1943 if (FAILED(rc)) return rc;
1944
1945 i_setModified(IsModified_MachineData);
1946 mHWData.backup();
1947 mHWData->mVRAMSize = aVRAMSize;
1948
1949 return S_OK;
1950}
1951
1952/** @todo this method should not be public */
1953HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1954{
1955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1958
1959 return S_OK;
1960}
1961
1962/**
1963 * Set the memory balloon size.
1964 *
1965 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1966 * we have to make sure that we never call IGuest from here.
1967 */
1968HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1969{
1970 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1971#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1972 /* check limits */
1973 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1974 return setError(E_INVALIDARG,
1975 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1976 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1977
1978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1979
1980 i_setModified(IsModified_MachineData);
1981 mHWData.backup();
1982 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1983
1984 return S_OK;
1985#else
1986 NOREF(aMemoryBalloonSize);
1987 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1988#endif
1989}
1990
1991HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1992{
1993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1994
1995 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1996 return S_OK;
1997}
1998
1999HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2000{
2001#ifdef VBOX_WITH_PAGE_SHARING
2002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2005 i_setModified(IsModified_MachineData);
2006 mHWData.backup();
2007 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2008 return S_OK;
2009#else
2010 NOREF(aPageFusionEnabled);
2011 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2012#endif
2013}
2014
2015HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2016{
2017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2018
2019 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2020
2021 return S_OK;
2022}
2023
2024HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2025{
2026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2027
2028 HRESULT rc = i_checkStateDependency(MutableStateDep);
2029 if (FAILED(rc)) return rc;
2030
2031 /** @todo check validity! */
2032
2033 i_setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2036
2037 return S_OK;
2038}
2039
2040
2041HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2042{
2043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2044
2045 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2046
2047 return S_OK;
2048}
2049
2050HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2051{
2052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2053
2054 HRESULT rc = i_checkStateDependency(MutableStateDep);
2055 if (FAILED(rc)) return rc;
2056
2057 /** @todo check validity! */
2058 i_setModified(IsModified_MachineData);
2059 mHWData.backup();
2060 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2061
2062 return S_OK;
2063}
2064
2065HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2066{
2067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2068
2069 *aMonitorCount = mHWData->mMonitorCount;
2070
2071 return S_OK;
2072}
2073
2074HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2075{
2076 /* make sure monitor count is a sensible number */
2077 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2078 return setError(E_INVALIDARG,
2079 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2080 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2081
2082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2083
2084 HRESULT rc = i_checkStateDependency(MutableStateDep);
2085 if (FAILED(rc)) return rc;
2086
2087 i_setModified(IsModified_MachineData);
2088 mHWData.backup();
2089 mHWData->mMonitorCount = aMonitorCount;
2090
2091 return S_OK;
2092}
2093
2094HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2095{
2096 /* mBIOSSettings is constant during life time, no need to lock */
2097 aBIOSSettings = mBIOSSettings;
2098
2099 return S_OK;
2100}
2101
2102HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2103{
2104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2105
2106 switch (aProperty)
2107 {
2108 case CPUPropertyType_PAE:
2109 *aValue = mHWData->mPAEEnabled;
2110 break;
2111
2112 case CPUPropertyType_Synthetic:
2113 *aValue = mHWData->mSyntheticCpu;
2114 break;
2115
2116 case CPUPropertyType_LongMode:
2117 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2118 *aValue = TRUE;
2119 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2120 *aValue = FALSE;
2121#if HC_ARCH_BITS == 64
2122 else
2123 *aValue = TRUE;
2124#else
2125 else
2126 {
2127 *aValue = FALSE;
2128
2129 ComPtr<IGuestOSType> ptrGuestOSType;
2130 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2131 if (SUCCEEDED(hrc2))
2132 {
2133 BOOL fIs64Bit = FALSE;
2134 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2135 if (SUCCEEDED(hrc2) && fIs64Bit)
2136 {
2137 ComObjPtr<Host> ptrHost = mParent->i_host();
2138 alock.release();
2139
2140 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2141 if (FAILED(hrc2))
2142 *aValue = FALSE;
2143 }
2144 }
2145 }
2146#endif
2147 break;
2148
2149 case CPUPropertyType_TripleFaultReset:
2150 *aValue = mHWData->mTripleFaultReset;
2151 break;
2152
2153 default:
2154 return E_INVALIDARG;
2155 }
2156 return S_OK;
2157}
2158
2159HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2160{
2161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 HRESULT rc = i_checkStateDependency(MutableStateDep);
2164 if (FAILED(rc)) return rc;
2165
2166 switch (aProperty)
2167 {
2168 case CPUPropertyType_PAE:
2169 i_setModified(IsModified_MachineData);
2170 mHWData.backup();
2171 mHWData->mPAEEnabled = !!aValue;
2172 break;
2173
2174 case CPUPropertyType_Synthetic:
2175 i_setModified(IsModified_MachineData);
2176 mHWData.backup();
2177 mHWData->mSyntheticCpu = !!aValue;
2178 break;
2179
2180 case CPUPropertyType_LongMode:
2181 i_setModified(IsModified_MachineData);
2182 mHWData.backup();
2183 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2184 break;
2185
2186 case CPUPropertyType_TripleFaultReset:
2187 i_setModified(IsModified_MachineData);
2188 mHWData.backup();
2189 mHWData->mTripleFaultReset = !!aValue;
2190 break;
2191
2192 default:
2193 return E_INVALIDARG;
2194 }
2195 return S_OK;
2196}
2197
2198HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2199{
2200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2201
2202 switch(aId)
2203 {
2204 case 0x0:
2205 case 0x1:
2206 case 0x2:
2207 case 0x3:
2208 case 0x4:
2209 case 0x5:
2210 case 0x6:
2211 case 0x7:
2212 case 0x8:
2213 case 0x9:
2214 case 0xA:
2215 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2216 return E_INVALIDARG;
2217
2218 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2219 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2220 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2221 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2222 break;
2223
2224 case 0x80000000:
2225 case 0x80000001:
2226 case 0x80000002:
2227 case 0x80000003:
2228 case 0x80000004:
2229 case 0x80000005:
2230 case 0x80000006:
2231 case 0x80000007:
2232 case 0x80000008:
2233 case 0x80000009:
2234 case 0x8000000A:
2235 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2236 return E_INVALIDARG;
2237
2238 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2239 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2240 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2241 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2242 break;
2243
2244 default:
2245 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2246 }
2247 return S_OK;
2248}
2249
2250
2251HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2252{
2253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2254
2255 HRESULT rc = i_checkStateDependency(MutableStateDep);
2256 if (FAILED(rc)) return rc;
2257
2258 switch(aId)
2259 {
2260 case 0x0:
2261 case 0x1:
2262 case 0x2:
2263 case 0x3:
2264 case 0x4:
2265 case 0x5:
2266 case 0x6:
2267 case 0x7:
2268 case 0x8:
2269 case 0x9:
2270 case 0xA:
2271 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2272 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2273 i_setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2276 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2277 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2278 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2279 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2280 break;
2281
2282 case 0x80000000:
2283 case 0x80000001:
2284 case 0x80000002:
2285 case 0x80000003:
2286 case 0x80000004:
2287 case 0x80000005:
2288 case 0x80000006:
2289 case 0x80000007:
2290 case 0x80000008:
2291 case 0x80000009:
2292 case 0x8000000A:
2293 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2294 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2295 i_setModified(IsModified_MachineData);
2296 mHWData.backup();
2297 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2298 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2299 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2300 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2301 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2302 break;
2303
2304 default:
2305 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2306 }
2307 return S_OK;
2308}
2309
2310HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2311{
2312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2313
2314 HRESULT rc = i_checkStateDependency(MutableStateDep);
2315 if (FAILED(rc)) return rc;
2316
2317 switch(aId)
2318 {
2319 case 0x0:
2320 case 0x1:
2321 case 0x2:
2322 case 0x3:
2323 case 0x4:
2324 case 0x5:
2325 case 0x6:
2326 case 0x7:
2327 case 0x8:
2328 case 0x9:
2329 case 0xA:
2330 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2331 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2332 i_setModified(IsModified_MachineData);
2333 mHWData.backup();
2334 /* Invalidate leaf. */
2335 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2336 break;
2337
2338 case 0x80000000:
2339 case 0x80000001:
2340 case 0x80000002:
2341 case 0x80000003:
2342 case 0x80000004:
2343 case 0x80000005:
2344 case 0x80000006:
2345 case 0x80000007:
2346 case 0x80000008:
2347 case 0x80000009:
2348 case 0x8000000A:
2349 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2350 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2351 i_setModified(IsModified_MachineData);
2352 mHWData.backup();
2353 /* Invalidate leaf. */
2354 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2355 break;
2356
2357 default:
2358 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2359 }
2360 return S_OK;
2361}
2362
2363HRESULT Machine::removeAllCPUIDLeaves()
2364{
2365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2366
2367 HRESULT rc = i_checkStateDependency(MutableStateDep);
2368 if (FAILED(rc)) return rc;
2369
2370 i_setModified(IsModified_MachineData);
2371 mHWData.backup();
2372
2373 /* Invalidate all standard leafs. */
2374 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2375 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2376
2377 /* Invalidate all extended leafs. */
2378 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2379 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2380
2381 return S_OK;
2382}
2383HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2384{
2385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2386
2387 switch(aProperty)
2388 {
2389 case HWVirtExPropertyType_Enabled:
2390 *aValue = mHWData->mHWVirtExEnabled;
2391 break;
2392
2393 case HWVirtExPropertyType_VPID:
2394 *aValue = mHWData->mHWVirtExVPIDEnabled;
2395 break;
2396
2397 case HWVirtExPropertyType_NestedPaging:
2398 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2399 break;
2400
2401 case HWVirtExPropertyType_UnrestrictedExecution:
2402 *aValue = mHWData->mHWVirtExUXEnabled;
2403 break;
2404
2405 case HWVirtExPropertyType_LargePages:
2406 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2407#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2408 *aValue = FALSE;
2409#endif
2410 break;
2411
2412 case HWVirtExPropertyType_Force:
2413 *aValue = mHWData->mHWVirtExForceEnabled;
2414 break;
2415
2416 default:
2417 return E_INVALIDARG;
2418 }
2419 return S_OK;
2420}
2421
2422HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2423{
2424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2425
2426 HRESULT rc = i_checkStateDependency(MutableStateDep);
2427 if (FAILED(rc)) return rc;
2428
2429 switch(aProperty)
2430 {
2431 case HWVirtExPropertyType_Enabled:
2432 i_setModified(IsModified_MachineData);
2433 mHWData.backup();
2434 mHWData->mHWVirtExEnabled = !!aValue;
2435 break;
2436
2437 case HWVirtExPropertyType_VPID:
2438 i_setModified(IsModified_MachineData);
2439 mHWData.backup();
2440 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2441 break;
2442
2443 case HWVirtExPropertyType_NestedPaging:
2444 i_setModified(IsModified_MachineData);
2445 mHWData.backup();
2446 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2447 break;
2448
2449 case HWVirtExPropertyType_UnrestrictedExecution:
2450 i_setModified(IsModified_MachineData);
2451 mHWData.backup();
2452 mHWData->mHWVirtExUXEnabled = !!aValue;
2453 break;
2454
2455 case HWVirtExPropertyType_LargePages:
2456 i_setModified(IsModified_MachineData);
2457 mHWData.backup();
2458 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2459 break;
2460
2461 case HWVirtExPropertyType_Force:
2462 i_setModified(IsModified_MachineData);
2463 mHWData.backup();
2464 mHWData->mHWVirtExForceEnabled = !!aValue;
2465 break;
2466
2467 default:
2468 return E_INVALIDARG;
2469 }
2470
2471 return S_OK;
2472}
2473
2474HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2475{
2476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2479
2480 return S_OK;
2481}
2482
2483HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2484{
2485 /* @todo (r=dmik):
2486 * 1. Allow to change the name of the snapshot folder containing snapshots
2487 * 2. Rename the folder on disk instead of just changing the property
2488 * value (to be smart and not to leave garbage). Note that it cannot be
2489 * done here because the change may be rolled back. Thus, the right
2490 * place is #saveSettings().
2491 */
2492
2493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2494
2495 HRESULT rc = i_checkStateDependency(MutableStateDep);
2496 if (FAILED(rc)) return rc;
2497
2498 if (!mData->mCurrentSnapshot.isNull())
2499 return setError(E_FAIL,
2500 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2501
2502 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2503
2504 if (strSnapshotFolder.isEmpty())
2505 strSnapshotFolder = "Snapshots";
2506 int vrc = i_calculateFullPath(strSnapshotFolder,
2507 strSnapshotFolder);
2508 if (RT_FAILURE(vrc))
2509 return setError(E_FAIL,
2510 tr("Invalid snapshot folder '%s' (%Rrc)"),
2511 strSnapshotFolder.c_str(), vrc);
2512
2513 i_setModified(IsModified_MachineData);
2514 mUserData.backup();
2515
2516 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2517
2518 return S_OK;
2519}
2520
2521HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2522{
2523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 aMediumAttachments.resize(mMediaData->mAttachments.size());
2526 size_t i = 0;
2527 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2528 it != mMediaData->mAttachments.end(); ++it, ++i)
2529 aMediumAttachments[i] = *it;
2530
2531 return S_OK;
2532}
2533
2534HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2535{
2536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2537
2538 Assert(!!mVRDEServer);
2539
2540 aVRDEServer = mVRDEServer;
2541
2542 return S_OK;
2543}
2544
2545HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2546{
2547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 aAudioAdapter = mAudioAdapter;
2550
2551 return S_OK;
2552}
2553
2554HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2555{
2556#ifdef VBOX_WITH_VUSB
2557 clearError();
2558 MultiResult rc(S_OK);
2559
2560# ifdef VBOX_WITH_USB
2561 rc = mParent->i_host()->i_checkUSBProxyService();
2562 if (FAILED(rc)) return rc;
2563# endif
2564
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 USBControllerList data = *mUSBControllers.data();
2568 aUSBControllers.resize(data.size());
2569 size_t i = 0;
2570 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2571 aUSBControllers[i] = *it;
2572
2573 return S_OK;
2574#else
2575 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2576 * extended error info to indicate that USB is simply not available
2577 * (w/o treating it as a failure), for example, as in OSE */
2578 NOREF(aUSBControllers);
2579 ReturnComNotImplemented();
2580#endif /* VBOX_WITH_VUSB */
2581}
2582
2583HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2584{
2585#ifdef VBOX_WITH_VUSB
2586 clearError();
2587 MultiResult rc(S_OK);
2588
2589# ifdef VBOX_WITH_USB
2590 rc = mParent->i_host()->i_checkUSBProxyService();
2591 if (FAILED(rc)) return rc;
2592# endif
2593
2594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2595
2596 aUSBDeviceFilters = mUSBDeviceFilters;
2597 return rc;
2598#else
2599 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2600 * extended error info to indicate that USB is simply not available
2601 * (w/o treating it as a failure), for example, as in OSE */
2602 NOREF(aUSBDeviceFilters);
2603 ReturnComNotImplemented();
2604#endif /* VBOX_WITH_VUSB */
2605}
2606
2607HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2608{
2609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2610
2611 aSettingsFilePath = mData->m_strConfigFileFull;
2612
2613 return S_OK;
2614}
2615
2616HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2617{
2618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2619
2620 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2621 if (FAILED(rc)) return rc;
2622
2623 if (!mData->pMachineConfigFile->fileExists())
2624 // this is a new machine, and no config file exists yet:
2625 *aSettingsModified = TRUE;
2626 else
2627 *aSettingsModified = (mData->flModifications != 0);
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2633{
2634
2635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2636
2637 *aSessionState = mData->mSession.mState;
2638
2639 return S_OK;
2640}
2641
2642HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2643{
2644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 aSessionType = mData->mSession.mType;
2647
2648 return S_OK;
2649}
2650
2651HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2652{
2653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2654
2655 *aSessionPID = mData->mSession.mPID;
2656
2657 return S_OK;
2658}
2659
2660HRESULT Machine::getState(MachineState_T *aState)
2661{
2662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2663
2664 *aState = mData->mMachineState;
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2679{
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 aStateFilePath = mSSData->strStateFilePath;
2683
2684 return S_OK;
2685}
2686
2687HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2688{
2689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2690
2691 i_getLogFolder(aLogFolder);
2692
2693 return S_OK;
2694}
2695
2696HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2697{
2698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 aCurrentSnapshot = mData->mCurrentSnapshot;
2701
2702 return S_OK;
2703}
2704
2705HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2706{
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2710 ? 0
2711 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2712
2713 return S_OK;
2714}
2715
2716HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2717{
2718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2719
2720 /* Note: for machines with no snapshots, we always return FALSE
2721 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2722 * reasons :) */
2723
2724 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2725 ? FALSE
2726 : mData->mCurrentStateModified;
2727
2728 return S_OK;
2729}
2730
2731HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2732{
2733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2734
2735 aSharedFolders.resize(mHWData->mSharedFolders.size());
2736 size_t i = 0;
2737 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2738 it != mHWData->mSharedFolders.end(); ++i, ++it)
2739 aSharedFolders[i] = *it;
2740
2741 return S_OK;
2742}
2743
2744HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2745{
2746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2747
2748 *aClipboardMode = mHWData->mClipboardMode;
2749
2750 return S_OK;
2751}
2752
2753HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2754{
2755 HRESULT rc = S_OK;
2756
2757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2758
2759 alock.release();
2760 rc = i_onClipboardModeChange(aClipboardMode);
2761 alock.acquire();
2762 if (FAILED(rc)) return rc;
2763
2764 i_setModified(IsModified_MachineData);
2765 mHWData.backup();
2766 mHWData->mClipboardMode = aClipboardMode;
2767
2768 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2769 if (Global::IsOnline(mData->mMachineState))
2770 i_saveSettings(NULL);
2771
2772 return S_OK;
2773}
2774
2775HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 *aDnDMode = mHWData->mDnDMode;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2785{
2786 HRESULT rc = S_OK;
2787
2788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 alock.release();
2791 rc = i_onDnDModeChange(aDnDMode);
2792
2793 alock.acquire();
2794 if (FAILED(rc)) return rc;
2795
2796 i_setModified(IsModified_MachineData);
2797 mHWData.backup();
2798 mHWData->mDnDMode = aDnDMode;
2799
2800 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2801 if (Global::IsOnline(mData->mMachineState))
2802 i_saveSettings(NULL);
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 try
2812 {
2813 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2814 }
2815 catch (...)
2816 {
2817 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2818 }
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2824{
2825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2828 if (FAILED(rc)) return rc;
2829
2830 i_setModified(IsModified_MachineData);
2831 mHWData.backup();
2832 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2833 return rc;
2834}
2835
2836HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2837{
2838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2839 StorageControllerList data = *mStorageControllers.data();
2840 size_t i = 0;
2841 aStorageControllers.resize(data.size());
2842 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2843 aStorageControllers[i] = *it;
2844 return S_OK;
2845}
2846
2847HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2848{
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 *aEnabled = mUserData->s.fTeleporterEnabled;
2852
2853 return S_OK;
2854}
2855
2856HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2857{
2858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2859
2860 /* Only allow it to be set to true when PoweredOff or Aborted.
2861 (Clearing it is always permitted.) */
2862 if ( aTeleporterEnabled
2863 && mData->mRegistered
2864 && ( !i_isSessionMachine()
2865 || ( mData->mMachineState != MachineState_PoweredOff
2866 && mData->mMachineState != MachineState_Teleported
2867 && mData->mMachineState != MachineState_Aborted
2868 )
2869 )
2870 )
2871 return setError(VBOX_E_INVALID_VM_STATE,
2872 tr("The machine is not powered off (state is %s)"),
2873 Global::stringifyMachineState(mData->mMachineState));
2874
2875 i_setModified(IsModified_MachineData);
2876 mUserData.backup();
2877 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2878
2879 return S_OK;
2880}
2881
2882HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2883{
2884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2885
2886 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2887
2888 return S_OK;
2889}
2890
2891HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2892{
2893 if (aTeleporterPort >= _64K)
2894 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2895
2896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2897
2898 HRESULT rc = i_checkStateDependency(MutableStateDep);
2899 if (FAILED(rc)) return rc;
2900
2901 i_setModified(IsModified_MachineData);
2902 mUserData.backup();
2903 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2904
2905 return S_OK;
2906}
2907
2908HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2909{
2910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2913
2914 return S_OK;
2915}
2916
2917HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2918{
2919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2920
2921 HRESULT rc = i_checkStateDependency(MutableStateDep);
2922 if (FAILED(rc)) return rc;
2923
2924 i_setModified(IsModified_MachineData);
2925 mUserData.backup();
2926 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2927
2928 return S_OK;
2929}
2930
2931HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2932{
2933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2934 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2935
2936 return S_OK;
2937}
2938
2939HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2940{
2941 /*
2942 * Hash the password first.
2943 */
2944 com::Utf8Str aT = aTeleporterPassword;
2945
2946 if (!aT.isEmpty())
2947 {
2948 if (VBoxIsPasswordHashed(&aT))
2949 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2950 VBoxHashPassword(&aT);
2951 }
2952
2953 /*
2954 * Do the update.
2955 */
2956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2957 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2958 if (SUCCEEDED(hrc))
2959 {
2960 i_setModified(IsModified_MachineData);
2961 mUserData.backup();
2962 mUserData->s.strTeleporterPassword = aT;
2963 }
2964
2965 return hrc;
2966}
2967
2968HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2969{
2970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2971
2972 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2973 return S_OK;
2974}
2975
2976HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2977{
2978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2979
2980 /* @todo deal with running state change. */
2981 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2982 if (FAILED(rc)) return rc;
2983
2984 i_setModified(IsModified_MachineData);
2985 mUserData.backup();
2986 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2987 return S_OK;
2988}
2989
2990HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2991{
2992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2993
2994 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2995 return S_OK;
2996}
2997
2998HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2999{
3000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3001
3002 /* @todo deal with running state change. */
3003 HRESULT rc = i_checkStateDependency(MutableStateDep);
3004 if (FAILED(rc)) return rc;
3005
3006 i_setModified(IsModified_MachineData);
3007 mUserData.backup();
3008 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3009 return S_OK;
3010}
3011
3012HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3013{
3014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3015
3016 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3017 return S_OK;
3018}
3019
3020HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3021{
3022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3023
3024 /* @todo deal with running state change. */
3025 HRESULT rc = i_checkStateDependency(MutableStateDep);
3026 if (FAILED(rc)) return rc;
3027
3028 i_setModified(IsModified_MachineData);
3029 mUserData.backup();
3030 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3031 return S_OK;
3032}
3033
3034HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3035{
3036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3037
3038 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3039
3040 return S_OK;
3041}
3042
3043HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3044{
3045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 /* @todo deal with running state change. */
3048 HRESULT rc = i_checkStateDependency(MutableStateDep);
3049 if (FAILED(rc)) return rc;
3050
3051 i_setModified(IsModified_MachineData);
3052 mUserData.backup();
3053 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3054
3055 return S_OK;
3056}
3057
3058HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3059{
3060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3061
3062 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3063 return S_OK;
3064}
3065
3066HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3067{
3068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3069
3070 /* @todo deal with running state change. */
3071 HRESULT rc = i_checkStateDependency(MutableStateDep);
3072 if (FAILED(rc)) return rc;
3073
3074 i_setModified(IsModified_MachineData);
3075 mUserData.backup();
3076 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3077 return S_OK;
3078}
3079
3080HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3081{
3082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3083
3084 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3085
3086 return S_OK;
3087}
3088
3089HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3090{
3091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 /* Only allow it to be set to true when PoweredOff or Aborted.
3094 (Clearing it is always permitted.) */
3095 if ( aRTCUseUTC
3096 && mData->mRegistered
3097 && ( !i_isSessionMachine()
3098 || ( mData->mMachineState != MachineState_PoweredOff
3099 && mData->mMachineState != MachineState_Teleported
3100 && mData->mMachineState != MachineState_Aborted
3101 )
3102 )
3103 )
3104 return setError(VBOX_E_INVALID_VM_STATE,
3105 tr("The machine is not powered off (state is %s)"),
3106 Global::stringifyMachineState(mData->mMachineState));
3107
3108 i_setModified(IsModified_MachineData);
3109 mUserData.backup();
3110 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3111
3112 return S_OK;
3113}
3114
3115HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3116{
3117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3118
3119 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3120
3121 return S_OK;
3122}
3123
3124HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3125{
3126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3127
3128 HRESULT rc = i_checkStateDependency(MutableStateDep);
3129 if (FAILED(rc)) return rc;
3130
3131 i_setModified(IsModified_MachineData);
3132 mHWData.backup();
3133 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3134
3135 return S_OK;
3136}
3137
3138HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3139{
3140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3141
3142 *aIOCacheSize = mHWData->mIOCacheSize;
3143
3144 return S_OK;
3145}
3146
3147HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3148{
3149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 HRESULT rc = i_checkStateDependency(MutableStateDep);
3152 if (FAILED(rc)) return rc;
3153
3154 i_setModified(IsModified_MachineData);
3155 mHWData.backup();
3156 mHWData->mIOCacheSize = aIOCacheSize;
3157
3158 return S_OK;
3159}
3160
3161
3162/**
3163 * @note Locks objects!
3164 */
3165HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3166 LockType_T aLockType)
3167
3168{
3169 /* check the session state */
3170 SessionState_T state;
3171 HRESULT rc = aSession->COMGETTER(State)(&state);
3172 if (FAILED(rc)) return rc;
3173
3174 if (state != SessionState_Unlocked)
3175 return setError(VBOX_E_INVALID_OBJECT_STATE,
3176 tr("The given session is busy"));
3177
3178 // get the client's IInternalSessionControl interface
3179 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3180 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3181 E_INVALIDARG);
3182
3183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3184
3185 if (!mData->mRegistered)
3186 return setError(E_UNEXPECTED,
3187 tr("The machine '%s' is not registered"),
3188 mUserData->s.strName.c_str());
3189
3190 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3191
3192 SessionState_T oldState = mData->mSession.mState;
3193 /* Hack: in case the session is closing and there is a progress object
3194 * which allows waiting for the session to be closed, take the opportunity
3195 * and do a limited wait (max. 1 second). This helps a lot when the system
3196 * is busy and thus session closing can take a little while. */
3197 if ( mData->mSession.mState == SessionState_Unlocking
3198 && mData->mSession.mProgress)
3199 {
3200 alock.release();
3201 mData->mSession.mProgress->WaitForCompletion(1000);
3202 alock.acquire();
3203 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3204 }
3205
3206 // try again now
3207 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3208 // (i.e. session machine exists)
3209 && (aLockType == LockType_Shared) // caller wants a shared link to the
3210 // existing session that holds the write lock:
3211 )
3212 {
3213 // OK, share the session... we are now dealing with three processes:
3214 // 1) VBoxSVC (where this code runs);
3215 // 2) process C: the caller's client process (who wants a shared session);
3216 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3217
3218 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3219 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3220 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3221 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3222 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3223
3224 /*
3225 * Release the lock before calling the client process. It's safe here
3226 * since the only thing to do after we get the lock again is to add
3227 * the remote control to the list (which doesn't directly influence
3228 * anything).
3229 */
3230 alock.release();
3231
3232 // get the console of the session holding the write lock (this is a remote call)
3233 ComPtr<IConsole> pConsoleW;
3234 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3235 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3236 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3237 if (FAILED(rc))
3238 // the failure may occur w/o any error info (from RPC), so provide one
3239 return setError(VBOX_E_VM_ERROR,
3240 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3241
3242 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3243
3244 // share the session machine and W's console with the caller's session
3245 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3246 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3247 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3248
3249 if (FAILED(rc))
3250 // the failure may occur w/o any error info (from RPC), so provide one
3251 return setError(VBOX_E_VM_ERROR,
3252 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3253 alock.acquire();
3254
3255 // need to revalidate the state after acquiring the lock again
3256 if (mData->mSession.mState != SessionState_Locked)
3257 {
3258 pSessionControl->Uninitialize();
3259 return setError(VBOX_E_INVALID_SESSION_STATE,
3260 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3261 mUserData->s.strName.c_str());
3262 }
3263
3264 // add the caller's session to the list
3265 mData->mSession.mRemoteControls.push_back(pSessionControl);
3266 }
3267 else if ( mData->mSession.mState == SessionState_Locked
3268 || mData->mSession.mState == SessionState_Unlocking
3269 )
3270 {
3271 // sharing not permitted, or machine still unlocking:
3272 return setError(VBOX_E_INVALID_OBJECT_STATE,
3273 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3274 mUserData->s.strName.c_str());
3275 }
3276 else
3277 {
3278 // machine is not locked: then write-lock the machine (create the session machine)
3279
3280 // must not be busy
3281 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3282
3283 // get the caller's session PID
3284 RTPROCESS pid = NIL_RTPROCESS;
3285 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3286 pSessionControl->GetPID((ULONG*)&pid);
3287 Assert(pid != NIL_RTPROCESS);
3288
3289 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3290
3291 if (fLaunchingVMProcess)
3292 {
3293 if (mData->mSession.mPID == NIL_RTPROCESS)
3294 {
3295 // two or more clients racing for a lock, the one which set the
3296 // session state to Spawning will win, the others will get an
3297 // error as we can't decide here if waiting a little would help
3298 // (only for shared locks this would avoid an error)
3299 return setError(VBOX_E_INVALID_OBJECT_STATE,
3300 tr("The machine '%s' already has a lock request pending"),
3301 mUserData->s.strName.c_str());
3302 }
3303
3304 // this machine is awaiting for a spawning session to be opened:
3305 // then the calling process must be the one that got started by
3306 // LaunchVMProcess()
3307
3308 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3309 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3310
3311#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3312 /* Hardened windows builds spawns three processes when a VM is
3313 launched, the 3rd one is the one that will end up here. */
3314 RTPROCESS ppid;
3315 int rc = RTProcQueryParent(pid, &ppid);
3316 if (RT_SUCCESS(rc))
3317 rc = RTProcQueryParent(ppid, &ppid);
3318 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3319 || rc == VERR_ACCESS_DENIED)
3320 {
3321 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3322 mData->mSession.mPID = pid;
3323 }
3324#endif
3325
3326 if (mData->mSession.mPID != pid)
3327 return setError(E_ACCESSDENIED,
3328 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3329 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3330 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3331 }
3332
3333 // create the mutable SessionMachine from the current machine
3334 ComObjPtr<SessionMachine> sessionMachine;
3335 sessionMachine.createObject();
3336 rc = sessionMachine->init(this);
3337 AssertComRC(rc);
3338
3339 /* NOTE: doing return from this function after this point but
3340 * before the end is forbidden since it may call SessionMachine::uninit()
3341 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3342 * lock while still holding the Machine lock in alock so that a deadlock
3343 * is possible due to the wrong lock order. */
3344
3345 if (SUCCEEDED(rc))
3346 {
3347 /*
3348 * Set the session state to Spawning to protect against subsequent
3349 * attempts to open a session and to unregister the machine after
3350 * we release the lock.
3351 */
3352 SessionState_T origState = mData->mSession.mState;
3353 mData->mSession.mState = SessionState_Spawning;
3354
3355#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3356 /* Get the client token ID to be passed to the client process */
3357 Utf8Str strTokenId;
3358 sessionMachine->i_getTokenId(strTokenId);
3359 Assert(!strTokenId.isEmpty());
3360#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3361 /* Get the client token to be passed to the client process */
3362 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3363 /* The token is now "owned" by pToken, fix refcount */
3364 if (!pToken.isNull())
3365 pToken->Release();
3366#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3367
3368 /*
3369 * Release the lock before calling the client process -- it will call
3370 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3371 * because the state is Spawning, so that LaunchVMProcess() and
3372 * LockMachine() calls will fail. This method, called before we
3373 * acquire the lock again, will fail because of the wrong PID.
3374 *
3375 * Note that mData->mSession.mRemoteControls accessed outside
3376 * the lock may not be modified when state is Spawning, so it's safe.
3377 */
3378 alock.release();
3379
3380 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3381#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3382 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3383#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3384 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3385 /* Now the token is owned by the client process. */
3386 pToken.setNull();
3387#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3388 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3389
3390 /* The failure may occur w/o any error info (from RPC), so provide one */
3391 if (FAILED(rc))
3392 setError(VBOX_E_VM_ERROR,
3393 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3394
3395 if ( SUCCEEDED(rc)
3396 && fLaunchingVMProcess
3397 )
3398 {
3399 /* complete the remote session initialization */
3400
3401 /* get the console from the direct session */
3402 ComPtr<IConsole> console;
3403 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3404 ComAssertComRC(rc);
3405
3406 if (SUCCEEDED(rc) && !console)
3407 {
3408 ComAssert(!!console);
3409 rc = E_FAIL;
3410 }
3411
3412 /* assign machine & console to the remote session */
3413 if (SUCCEEDED(rc))
3414 {
3415 /*
3416 * after LaunchVMProcess(), the first and the only
3417 * entry in remoteControls is that remote session
3418 */
3419 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3420 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3421 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3422
3423 /* The failure may occur w/o any error info (from RPC), so provide one */
3424 if (FAILED(rc))
3425 setError(VBOX_E_VM_ERROR,
3426 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3427 }
3428
3429 if (FAILED(rc))
3430 pSessionControl->Uninitialize();
3431 }
3432
3433 /* acquire the lock again */
3434 alock.acquire();
3435
3436 /* Restore the session state */
3437 mData->mSession.mState = origState;
3438 }
3439
3440 // finalize spawning anyway (this is why we don't return on errors above)
3441 if (fLaunchingVMProcess)
3442 {
3443 /* Note that the progress object is finalized later */
3444 /** @todo Consider checking mData->mSession.mProgress for cancellation
3445 * around here. */
3446
3447 /* We don't reset mSession.mPID here because it is necessary for
3448 * SessionMachine::uninit() to reap the child process later. */
3449
3450 if (FAILED(rc))
3451 {
3452 /* Close the remote session, remove the remote control from the list
3453 * and reset session state to Closed (@note keep the code in sync
3454 * with the relevant part in checkForSpawnFailure()). */
3455
3456 Assert(mData->mSession.mRemoteControls.size() == 1);
3457 if (mData->mSession.mRemoteControls.size() == 1)
3458 {
3459 ErrorInfoKeeper eik;
3460 mData->mSession.mRemoteControls.front()->Uninitialize();
3461 }
3462
3463 mData->mSession.mRemoteControls.clear();
3464 mData->mSession.mState = SessionState_Unlocked;
3465 }
3466 }
3467 else
3468 {
3469 /* memorize PID of the directly opened session */
3470 if (SUCCEEDED(rc))
3471 mData->mSession.mPID = pid;
3472 }
3473
3474 if (SUCCEEDED(rc))
3475 {
3476 /* memorize the direct session control and cache IUnknown for it */
3477 mData->mSession.mDirectControl = pSessionControl;
3478 mData->mSession.mState = SessionState_Locked;
3479 /* associate the SessionMachine with this Machine */
3480 mData->mSession.mMachine = sessionMachine;
3481
3482 /* request an IUnknown pointer early from the remote party for later
3483 * identity checks (it will be internally cached within mDirectControl
3484 * at least on XPCOM) */
3485 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3486 NOREF(unk);
3487 }
3488
3489 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3490 * would break the lock order */
3491 alock.release();
3492
3493 /* uninitialize the created session machine on failure */
3494 if (FAILED(rc))
3495 sessionMachine->uninit();
3496
3497 }
3498
3499 if (SUCCEEDED(rc))
3500 {
3501 /*
3502 * tell the client watcher thread to update the set of
3503 * machines that have open sessions
3504 */
3505 mParent->i_updateClientWatcher();
3506
3507 if (oldState != SessionState_Locked)
3508 /* fire an event */
3509 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3510 }
3511
3512 return rc;
3513}
3514
3515/**
3516 * @note Locks objects!
3517 */
3518HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3519 const com::Utf8Str &aType,
3520 const com::Utf8Str &aEnvironment,
3521 ComPtr<IProgress> &aProgress)
3522{
3523 Utf8Str strFrontend(aType);
3524 /* "emergencystop" doesn't need the session, so skip the checks/interface
3525 * retrieval. This code doesn't quite fit in here, but introducing a
3526 * special API method would be even more effort, and would require explicit
3527 * support by every API client. It's better to hide the feature a bit. */
3528 if (strFrontend != "emergencystop")
3529 CheckComArgNotNull(aSession);
3530
3531 HRESULT rc = S_OK;
3532 if (strFrontend.isEmpty())
3533 {
3534 Bstr bstrFrontend;
3535 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3536 if (FAILED(rc))
3537 return rc;
3538 strFrontend = bstrFrontend;
3539 if (strFrontend.isEmpty())
3540 {
3541 ComPtr<ISystemProperties> systemProperties;
3542 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3543 if (FAILED(rc))
3544 return rc;
3545 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3546 if (FAILED(rc))
3547 return rc;
3548 strFrontend = bstrFrontend;
3549 }
3550 /* paranoia - emergencystop is not a valid default */
3551 if (strFrontend == "emergencystop")
3552 strFrontend = Utf8Str::Empty;
3553 }
3554 /* default frontend: Qt GUI */
3555 if (strFrontend.isEmpty())
3556 strFrontend = "GUI/Qt";
3557
3558 if (strFrontend != "emergencystop")
3559 {
3560 /* check the session state */
3561 SessionState_T state;
3562 rc = aSession->COMGETTER(State)(&state);
3563 if (FAILED(rc))
3564 return rc;
3565
3566 if (state != SessionState_Unlocked)
3567 return setError(VBOX_E_INVALID_OBJECT_STATE,
3568 tr("The given session is busy"));
3569
3570 /* get the IInternalSessionControl interface */
3571 ComPtr<IInternalSessionControl> control(aSession);
3572 ComAssertMsgRet(!control.isNull(),
3573 ("No IInternalSessionControl interface"),
3574 E_INVALIDARG);
3575
3576 /* get the teleporter enable state for the progress object init. */
3577 BOOL fTeleporterEnabled;
3578 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3579 if (FAILED(rc))
3580 return rc;
3581
3582 /* create a progress object */
3583 ComObjPtr<ProgressProxy> progress;
3584 progress.createObject();
3585 rc = progress->init(mParent,
3586 static_cast<IMachine*>(this),
3587 Bstr(tr("Starting VM")).raw(),
3588 TRUE /* aCancelable */,
3589 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3590 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3591 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3592 2 /* uFirstOperationWeight */,
3593 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3594
3595 if (SUCCEEDED(rc))
3596 {
3597 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3598 if (SUCCEEDED(rc))
3599 {
3600 aProgress = progress;
3601
3602 /* signal the client watcher thread */
3603 mParent->i_updateClientWatcher();
3604
3605 /* fire an event */
3606 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3607 }
3608 }
3609 }
3610 else
3611 {
3612 /* no progress object - either instant success or failure */
3613 aProgress = NULL;
3614
3615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3616
3617 if (mData->mSession.mState != SessionState_Locked)
3618 return setError(VBOX_E_INVALID_OBJECT_STATE,
3619 tr("The machine '%s' is not locked by a session"),
3620 mUserData->s.strName.c_str());
3621
3622 /* must have a VM process associated - do not kill normal API clients
3623 * with an open session */
3624 if (!Global::IsOnline(mData->mMachineState))
3625 return setError(VBOX_E_INVALID_OBJECT_STATE,
3626 tr("The machine '%s' does not have a VM process"),
3627 mUserData->s.strName.c_str());
3628
3629 /* forcibly terminate the VM process */
3630 if (mData->mSession.mPID != NIL_RTPROCESS)
3631 RTProcTerminate(mData->mSession.mPID);
3632
3633 /* signal the client watcher thread, as most likely the client has
3634 * been terminated */
3635 mParent->i_updateClientWatcher();
3636 }
3637
3638 return rc;
3639}
3640
3641HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3642{
3643 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3644 return setError(E_INVALIDARG,
3645 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3646 aPosition, SchemaDefs::MaxBootPosition);
3647
3648 if (aDevice == DeviceType_USB)
3649 return setError(E_NOTIMPL,
3650 tr("Booting from USB device is currently not supported"));
3651
3652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3653
3654 HRESULT rc = i_checkStateDependency(MutableStateDep);
3655 if (FAILED(rc)) return rc;
3656
3657 i_setModified(IsModified_MachineData);
3658 mHWData.backup();
3659 mHWData->mBootOrder[aPosition - 1] = aDevice;
3660
3661 return S_OK;
3662}
3663
3664HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3665{
3666 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3667 return setError(E_INVALIDARG,
3668 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3669 aPosition, SchemaDefs::MaxBootPosition);
3670
3671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3672
3673 *aDevice = mHWData->mBootOrder[aPosition - 1];
3674
3675 return S_OK;
3676}
3677
3678HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3679 LONG aControllerPort,
3680 LONG aDevice,
3681 DeviceType_T aType,
3682 const ComPtr<IMedium> &aMedium)
3683{
3684 IMedium *aM = aMedium;
3685 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3686 aName.c_str(), aControllerPort, aDevice, aType, aM));
3687
3688 // request the host lock first, since might be calling Host methods for getting host drives;
3689 // next, protect the media tree all the while we're in here, as well as our member variables
3690 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3691 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3692
3693 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3694 if (FAILED(rc)) return rc;
3695
3696 /// @todo NEWMEDIA implicit machine registration
3697 if (!mData->mRegistered)
3698 return setError(VBOX_E_INVALID_OBJECT_STATE,
3699 tr("Cannot attach storage devices to an unregistered machine"));
3700
3701 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3702
3703 /* Check for an existing controller. */
3704 ComObjPtr<StorageController> ctl;
3705 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3706 if (FAILED(rc)) return rc;
3707
3708 StorageControllerType_T ctrlType;
3709 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3710 if (FAILED(rc))
3711 return setError(E_FAIL,
3712 tr("Could not get type of controller '%s'"),
3713 aName.c_str());
3714
3715 bool fSilent = false;
3716 Utf8Str strReconfig;
3717
3718 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3719 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3720 if ( mData->mMachineState == MachineState_Paused
3721 && strReconfig == "1")
3722 fSilent = true;
3723
3724 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3725 bool fHotplug = false;
3726 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3727 fHotplug = true;
3728
3729 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3730 return setError(VBOX_E_INVALID_VM_STATE,
3731 tr("Controller '%s' does not support hotplugging"),
3732 aName.c_str());
3733
3734 // check that the port and device are not out of range
3735 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3736 if (FAILED(rc)) return rc;
3737
3738 /* check if the device slot is already busy */
3739 MediumAttachment *pAttachTemp;
3740 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3741 Bstr(aName).raw(),
3742 aControllerPort,
3743 aDevice)))
3744 {
3745 Medium *pMedium = pAttachTemp->i_getMedium();
3746 if (pMedium)
3747 {
3748 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3749 return setError(VBOX_E_OBJECT_IN_USE,
3750 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3751 pMedium->i_getLocationFull().c_str(),
3752 aControllerPort,
3753 aDevice,
3754 aName.c_str());
3755 }
3756 else
3757 return setError(VBOX_E_OBJECT_IN_USE,
3758 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3759 aControllerPort, aDevice, aName.c_str());
3760 }
3761
3762 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3763 if (aMedium && medium.isNull())
3764 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3765
3766 AutoCaller mediumCaller(medium);
3767 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3768
3769 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3770
3771 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3772 && !medium.isNull()
3773 )
3774 return setError(VBOX_E_OBJECT_IN_USE,
3775 tr("Medium '%s' is already attached to this virtual machine"),
3776 medium->i_getLocationFull().c_str());
3777
3778 if (!medium.isNull())
3779 {
3780 MediumType_T mtype = medium->i_getType();
3781 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3782 // For DVDs it's not written to the config file, so needs no global config
3783 // version bump. For floppies it's a new attribute "type", which is ignored
3784 // by older VirtualBox version, so needs no global config version bump either.
3785 // For hard disks this type is not accepted.
3786 if (mtype == MediumType_MultiAttach)
3787 {
3788 // This type is new with VirtualBox 4.0 and therefore requires settings
3789 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3790 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3791 // two reasons: The medium type is a property of the media registry tree, which
3792 // can reside in the global config file (for pre-4.0 media); we would therefore
3793 // possibly need to bump the global config version. We don't want to do that though
3794 // because that might make downgrading to pre-4.0 impossible.
3795 // As a result, we can only use these two new types if the medium is NOT in the
3796 // global registry:
3797 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3798 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3799 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3800 )
3801 return setError(VBOX_E_INVALID_OBJECT_STATE,
3802 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3803 "to machines that were created with VirtualBox 4.0 or later"),
3804 medium->i_getLocationFull().c_str());
3805 }
3806 }
3807
3808 bool fIndirect = false;
3809 if (!medium.isNull())
3810 fIndirect = medium->i_isReadOnly();
3811 bool associate = true;
3812
3813 do
3814 {
3815 if ( aType == DeviceType_HardDisk
3816 && mMediaData.isBackedUp())
3817 {
3818 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3819
3820 /* check if the medium was attached to the VM before we started
3821 * changing attachments in which case the attachment just needs to
3822 * be restored */
3823 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3824 {
3825 AssertReturn(!fIndirect, E_FAIL);
3826
3827 /* see if it's the same bus/channel/device */
3828 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3829 {
3830 /* the simplest case: restore the whole attachment
3831 * and return, nothing else to do */
3832 mMediaData->mAttachments.push_back(pAttachTemp);
3833
3834 /* Reattach the medium to the VM. */
3835 if (fHotplug || fSilent)
3836 {
3837 mediumLock.release();
3838 treeLock.release();
3839 alock.release();
3840
3841 MediumLockList *pMediumLockList(new MediumLockList());
3842
3843 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3844 true /* fMediumLockWrite */,
3845 false /* fMediumLockWriteAll */,
3846 NULL,
3847 *pMediumLockList);
3848 alock.acquire();
3849 if (FAILED(rc))
3850 delete pMediumLockList;
3851 else
3852 {
3853 mData->mSession.mLockedMedia.Unlock();
3854 alock.release();
3855 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3856 mData->mSession.mLockedMedia.Lock();
3857 alock.acquire();
3858 }
3859 alock.release();
3860
3861 if (SUCCEEDED(rc))
3862 {
3863 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3864 /* Remove lock list in case of error. */
3865 if (FAILED(rc))
3866 {
3867 mData->mSession.mLockedMedia.Unlock();
3868 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3869 mData->mSession.mLockedMedia.Lock();
3870 }
3871 }
3872 }
3873
3874 return S_OK;
3875 }
3876
3877 /* bus/channel/device differ; we need a new attachment object,
3878 * but don't try to associate it again */
3879 associate = false;
3880 break;
3881 }
3882 }
3883
3884 /* go further only if the attachment is to be indirect */
3885 if (!fIndirect)
3886 break;
3887
3888 /* perform the so called smart attachment logic for indirect
3889 * attachments. Note that smart attachment is only applicable to base
3890 * hard disks. */
3891
3892 if (medium->i_getParent().isNull())
3893 {
3894 /* first, investigate the backup copy of the current hard disk
3895 * attachments to make it possible to re-attach existing diffs to
3896 * another device slot w/o losing their contents */
3897 if (mMediaData.isBackedUp())
3898 {
3899 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3900
3901 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3902 uint32_t foundLevel = 0;
3903
3904 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3905 {
3906 uint32_t level = 0;
3907 MediumAttachment *pAttach = *it;
3908 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3909 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3910 if (pMedium.isNull())
3911 continue;
3912
3913 if (pMedium->i_getBase(&level) == medium)
3914 {
3915 /* skip the hard disk if its currently attached (we
3916 * cannot attach the same hard disk twice) */
3917 if (i_findAttachment(mMediaData->mAttachments,
3918 pMedium))
3919 continue;
3920
3921 /* matched device, channel and bus (i.e. attached to the
3922 * same place) will win and immediately stop the search;
3923 * otherwise the attachment that has the youngest
3924 * descendant of medium will be used
3925 */
3926 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3927 {
3928 /* the simplest case: restore the whole attachment
3929 * and return, nothing else to do */
3930 mMediaData->mAttachments.push_back(*it);
3931
3932 /* Reattach the medium to the VM. */
3933 if (fHotplug || fSilent)
3934 {
3935 mediumLock.release();
3936 treeLock.release();
3937 alock.release();
3938
3939 MediumLockList *pMediumLockList(new MediumLockList());
3940
3941 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3942 true /* fMediumLockWrite */,
3943 false /* fMediumLockWriteAll */,
3944 NULL,
3945 *pMediumLockList);
3946 alock.acquire();
3947 if (FAILED(rc))
3948 delete pMediumLockList;
3949 else
3950 {
3951 mData->mSession.mLockedMedia.Unlock();
3952 alock.release();
3953 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3954 mData->mSession.mLockedMedia.Lock();
3955 alock.acquire();
3956 }
3957 alock.release();
3958
3959 if (SUCCEEDED(rc))
3960 {
3961 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3962 /* Remove lock list in case of error. */
3963 if (FAILED(rc))
3964 {
3965 mData->mSession.mLockedMedia.Unlock();
3966 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3967 mData->mSession.mLockedMedia.Lock();
3968 }
3969 }
3970 }
3971
3972 return S_OK;
3973 }
3974 else if ( foundIt == oldAtts.end()
3975 || level > foundLevel /* prefer younger */
3976 )
3977 {
3978 foundIt = it;
3979 foundLevel = level;
3980 }
3981 }
3982 }
3983
3984 if (foundIt != oldAtts.end())
3985 {
3986 /* use the previously attached hard disk */
3987 medium = (*foundIt)->i_getMedium();
3988 mediumCaller.attach(medium);
3989 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3990 mediumLock.attach(medium);
3991 /* not implicit, doesn't require association with this VM */
3992 fIndirect = false;
3993 associate = false;
3994 /* go right to the MediumAttachment creation */
3995 break;
3996 }
3997 }
3998
3999 /* must give up the medium lock and medium tree lock as below we
4000 * go over snapshots, which needs a lock with higher lock order. */
4001 mediumLock.release();
4002 treeLock.release();
4003
4004 /* then, search through snapshots for the best diff in the given
4005 * hard disk's chain to base the new diff on */
4006
4007 ComObjPtr<Medium> base;
4008 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4009 while (snap)
4010 {
4011 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4012
4013 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4014
4015 MediumAttachment *pAttachFound = NULL;
4016 uint32_t foundLevel = 0;
4017
4018 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4019 {
4020 MediumAttachment *pAttach = *it;
4021 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4022 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4023 if (pMedium.isNull())
4024 continue;
4025
4026 uint32_t level = 0;
4027 if (pMedium->i_getBase(&level) == medium)
4028 {
4029 /* matched device, channel and bus (i.e. attached to the
4030 * same place) will win and immediately stop the search;
4031 * otherwise the attachment that has the youngest
4032 * descendant of medium will be used
4033 */
4034 if ( pAttach->i_getDevice() == aDevice
4035 && pAttach->i_getPort() == aControllerPort
4036 && pAttach->i_getControllerName() == aName
4037 )
4038 {
4039 pAttachFound = pAttach;
4040 break;
4041 }
4042 else if ( !pAttachFound
4043 || level > foundLevel /* prefer younger */
4044 )
4045 {
4046 pAttachFound = pAttach;
4047 foundLevel = level;
4048 }
4049 }
4050 }
4051
4052 if (pAttachFound)
4053 {
4054 base = pAttachFound->i_getMedium();
4055 break;
4056 }
4057
4058 snap = snap->i_getParent();
4059 }
4060
4061 /* re-lock medium tree and the medium, as we need it below */
4062 treeLock.acquire();
4063 mediumLock.acquire();
4064
4065 /* found a suitable diff, use it as a base */
4066 if (!base.isNull())
4067 {
4068 medium = base;
4069 mediumCaller.attach(medium);
4070 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4071 mediumLock.attach(medium);
4072 }
4073 }
4074
4075 Utf8Str strFullSnapshotFolder;
4076 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4077
4078 ComObjPtr<Medium> diff;
4079 diff.createObject();
4080 // store this diff in the same registry as the parent
4081 Guid uuidRegistryParent;
4082 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4083 {
4084 // parent image has no registry: this can happen if we're attaching a new immutable
4085 // image that has not yet been attached (medium then points to the base and we're
4086 // creating the diff image for the immutable, and the parent is not yet registered);
4087 // put the parent in the machine registry then
4088 mediumLock.release();
4089 treeLock.release();
4090 alock.release();
4091 i_addMediumToRegistry(medium);
4092 alock.acquire();
4093 treeLock.acquire();
4094 mediumLock.acquire();
4095 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4096 }
4097 rc = diff->init(mParent,
4098 medium->i_getPreferredDiffFormat(),
4099 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4100 uuidRegistryParent,
4101 DeviceType_HardDisk);
4102 if (FAILED(rc)) return rc;
4103
4104 /* Apply the normal locking logic to the entire chain. */
4105 MediumLockList *pMediumLockList(new MediumLockList());
4106 mediumLock.release();
4107 treeLock.release();
4108 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4109 true /* fMediumLockWrite */,
4110 false /* fMediumLockWriteAll */,
4111 medium,
4112 *pMediumLockList);
4113 treeLock.acquire();
4114 mediumLock.acquire();
4115 if (SUCCEEDED(rc))
4116 {
4117 mediumLock.release();
4118 treeLock.release();
4119 rc = pMediumLockList->Lock();
4120 treeLock.acquire();
4121 mediumLock.acquire();
4122 if (FAILED(rc))
4123 setError(rc,
4124 tr("Could not lock medium when creating diff '%s'"),
4125 diff->i_getLocationFull().c_str());
4126 else
4127 {
4128 /* will release the lock before the potentially lengthy
4129 * operation, so protect with the special state */
4130 MachineState_T oldState = mData->mMachineState;
4131 i_setMachineState(MachineState_SettingUp);
4132
4133 mediumLock.release();
4134 treeLock.release();
4135 alock.release();
4136
4137 rc = medium->i_createDiffStorage(diff,
4138 MediumVariant_Standard,
4139 pMediumLockList,
4140 NULL /* aProgress */,
4141 true /* aWait */);
4142
4143 alock.acquire();
4144 treeLock.acquire();
4145 mediumLock.acquire();
4146
4147 i_setMachineState(oldState);
4148 }
4149 }
4150
4151 /* Unlock the media and free the associated memory. */
4152 delete pMediumLockList;
4153
4154 if (FAILED(rc)) return rc;
4155
4156 /* use the created diff for the actual attachment */
4157 medium = diff;
4158 mediumCaller.attach(medium);
4159 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4160 mediumLock.attach(medium);
4161 }
4162 while (0);
4163
4164 ComObjPtr<MediumAttachment> attachment;
4165 attachment.createObject();
4166 rc = attachment->init(this,
4167 medium,
4168 aName,
4169 aControllerPort,
4170 aDevice,
4171 aType,
4172 fIndirect,
4173 false /* fPassthrough */,
4174 false /* fTempEject */,
4175 false /* fNonRotational */,
4176 false /* fDiscard */,
4177 fHotplug /* fHotPluggable */,
4178 Utf8Str::Empty);
4179 if (FAILED(rc)) return rc;
4180
4181 if (associate && !medium.isNull())
4182 {
4183 // as the last step, associate the medium to the VM
4184 rc = medium->i_addBackReference(mData->mUuid);
4185 // here we can fail because of Deleting, or being in process of creating a Diff
4186 if (FAILED(rc)) return rc;
4187
4188 mediumLock.release();
4189 treeLock.release();
4190 alock.release();
4191 i_addMediumToRegistry(medium);
4192 alock.acquire();
4193 treeLock.acquire();
4194 mediumLock.acquire();
4195 }
4196
4197 /* success: finally remember the attachment */
4198 i_setModified(IsModified_Storage);
4199 mMediaData.backup();
4200 mMediaData->mAttachments.push_back(attachment);
4201
4202 mediumLock.release();
4203 treeLock.release();
4204 alock.release();
4205
4206 if (fHotplug || fSilent)
4207 {
4208 if (!medium.isNull())
4209 {
4210 MediumLockList *pMediumLockList(new MediumLockList());
4211
4212 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4213 true /* fMediumLockWrite */,
4214 false /* fMediumLockWriteAll */,
4215 NULL,
4216 *pMediumLockList);
4217 alock.acquire();
4218 if (FAILED(rc))
4219 delete pMediumLockList;
4220 else
4221 {
4222 mData->mSession.mLockedMedia.Unlock();
4223 alock.release();
4224 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4225 mData->mSession.mLockedMedia.Lock();
4226 alock.acquire();
4227 }
4228 alock.release();
4229 }
4230
4231 if (SUCCEEDED(rc))
4232 {
4233 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4234 /* Remove lock list in case of error. */
4235 if (FAILED(rc))
4236 {
4237 mData->mSession.mLockedMedia.Unlock();
4238 mData->mSession.mLockedMedia.Remove(attachment);
4239 mData->mSession.mLockedMedia.Lock();
4240 }
4241 }
4242 }
4243
4244 mParent->i_saveModifiedRegistries();
4245
4246 return rc;
4247}
4248
4249HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4250 LONG aDevice)
4251{
4252 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4253 aName.c_str(), aControllerPort, aDevice));
4254
4255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4256
4257 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4258 if (FAILED(rc)) return rc;
4259
4260 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4261
4262 /* Check for an existing controller. */
4263 ComObjPtr<StorageController> ctl;
4264 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4265 if (FAILED(rc)) return rc;
4266
4267 StorageControllerType_T ctrlType;
4268 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4269 if (FAILED(rc))
4270 return setError(E_FAIL,
4271 tr("Could not get type of controller '%s'"),
4272 aName.c_str());
4273
4274 bool fSilent = false;
4275 Utf8Str strReconfig;
4276
4277 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4278 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4279 if ( mData->mMachineState == MachineState_Paused
4280 && strReconfig == "1")
4281 fSilent = true;
4282
4283 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4284 bool fHotplug = false;
4285 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4286 fHotplug = true;
4287
4288 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4289 return setError(VBOX_E_INVALID_VM_STATE,
4290 tr("Controller '%s' does not support hotplugging"),
4291 aName.c_str());
4292
4293 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4294 Bstr(aName).raw(),
4295 aControllerPort,
4296 aDevice);
4297 if (!pAttach)
4298 return setError(VBOX_E_OBJECT_NOT_FOUND,
4299 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4300 aDevice, aControllerPort, aName.c_str());
4301
4302 if (fHotplug && !pAttach->i_getHotPluggable())
4303 return setError(VBOX_E_NOT_SUPPORTED,
4304 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4305 aDevice, aControllerPort, aName.c_str());
4306
4307 /*
4308 * The VM has to detach the device before we delete any implicit diffs.
4309 * If this fails we can roll back without loosing data.
4310 */
4311 if (fHotplug || fSilent)
4312 {
4313 alock.release();
4314 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4315 alock.acquire();
4316 }
4317 if (FAILED(rc)) return rc;
4318
4319 /* If we are here everything went well and we can delete the implicit now. */
4320 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4321
4322 alock.release();
4323
4324 mParent->i_saveModifiedRegistries();
4325
4326 return rc;
4327}
4328
4329HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4330 LONG aDevice, BOOL aPassthrough)
4331{
4332 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4333 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4334
4335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4336
4337 HRESULT rc = i_checkStateDependency(MutableStateDep);
4338 if (FAILED(rc)) return rc;
4339
4340 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4341
4342 if (Global::IsOnlineOrTransient(mData->mMachineState))
4343 return setError(VBOX_E_INVALID_VM_STATE,
4344 tr("Invalid machine state: %s"),
4345 Global::stringifyMachineState(mData->mMachineState));
4346
4347 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4348 Bstr(aName).raw(),
4349 aControllerPort,
4350 aDevice);
4351 if (!pAttach)
4352 return setError(VBOX_E_OBJECT_NOT_FOUND,
4353 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4354 aDevice, aControllerPort, aName.c_str());
4355
4356
4357 i_setModified(IsModified_Storage);
4358 mMediaData.backup();
4359
4360 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4361
4362 if (pAttach->i_getType() != DeviceType_DVD)
4363 return setError(E_INVALIDARG,
4364 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4365 aDevice, aControllerPort, aName.c_str());
4366 pAttach->i_updatePassthrough(!!aPassthrough);
4367
4368 return S_OK;
4369}
4370
4371HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4372 LONG aDevice, BOOL aTemporaryEject)
4373{
4374
4375 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4376 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4377
4378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4379
4380 HRESULT rc = i_checkStateDependency(MutableStateDep);
4381 if (FAILED(rc)) return rc;
4382
4383 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4384 Bstr(aName).raw(),
4385 aControllerPort,
4386 aDevice);
4387 if (!pAttach)
4388 return setError(VBOX_E_OBJECT_NOT_FOUND,
4389 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4390 aDevice, aControllerPort, aName.c_str());
4391
4392
4393 i_setModified(IsModified_Storage);
4394 mMediaData.backup();
4395
4396 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4397
4398 if (pAttach->i_getType() != DeviceType_DVD)
4399 return setError(E_INVALIDARG,
4400 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4401 aDevice, aControllerPort, aName.c_str());
4402 pAttach->i_updateTempEject(!!aTemporaryEject);
4403
4404 return S_OK;
4405}
4406
4407HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4408 LONG aDevice, BOOL aNonRotational)
4409{
4410
4411 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4412 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4413
4414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4415
4416 HRESULT rc = i_checkStateDependency(MutableStateDep);
4417 if (FAILED(rc)) return rc;
4418
4419 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4420
4421 if (Global::IsOnlineOrTransient(mData->mMachineState))
4422 return setError(VBOX_E_INVALID_VM_STATE,
4423 tr("Invalid machine state: %s"),
4424 Global::stringifyMachineState(mData->mMachineState));
4425
4426 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4427 Bstr(aName).raw(),
4428 aControllerPort,
4429 aDevice);
4430 if (!pAttach)
4431 return setError(VBOX_E_OBJECT_NOT_FOUND,
4432 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4433 aDevice, aControllerPort, aName.c_str());
4434
4435
4436 i_setModified(IsModified_Storage);
4437 mMediaData.backup();
4438
4439 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4440
4441 if (pAttach->i_getType() != DeviceType_HardDisk)
4442 return setError(E_INVALIDARG,
4443 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"),
4444 aDevice, aControllerPort, aName.c_str());
4445 pAttach->i_updateNonRotational(!!aNonRotational);
4446
4447 return S_OK;
4448}
4449
4450HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4451 LONG aDevice, BOOL aDiscard)
4452{
4453
4454 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4455 aName.c_str(), aControllerPort, aDevice, aDiscard));
4456
4457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4458
4459 HRESULT rc = i_checkStateDependency(MutableStateDep);
4460 if (FAILED(rc)) return rc;
4461
4462 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4463
4464 if (Global::IsOnlineOrTransient(mData->mMachineState))
4465 return setError(VBOX_E_INVALID_VM_STATE,
4466 tr("Invalid machine state: %s"),
4467 Global::stringifyMachineState(mData->mMachineState));
4468
4469 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4470 Bstr(aName).raw(),
4471 aControllerPort,
4472 aDevice);
4473 if (!pAttach)
4474 return setError(VBOX_E_OBJECT_NOT_FOUND,
4475 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4476 aDevice, aControllerPort, aName.c_str());
4477
4478
4479 i_setModified(IsModified_Storage);
4480 mMediaData.backup();
4481
4482 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4483
4484 if (pAttach->i_getType() != DeviceType_HardDisk)
4485 return setError(E_INVALIDARG,
4486 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"),
4487 aDevice, aControllerPort, aName.c_str());
4488 pAttach->i_updateDiscard(!!aDiscard);
4489
4490 return S_OK;
4491}
4492
4493HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4494 LONG aDevice, BOOL aHotPluggable)
4495{
4496 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4497 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4498
4499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4500
4501 HRESULT rc = i_checkStateDependency(MutableStateDep);
4502 if (FAILED(rc)) return rc;
4503
4504 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4505
4506 if (Global::IsOnlineOrTransient(mData->mMachineState))
4507 return setError(VBOX_E_INVALID_VM_STATE,
4508 tr("Invalid machine state: %s"),
4509 Global::stringifyMachineState(mData->mMachineState));
4510
4511 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4512 Bstr(aName).raw(),
4513 aControllerPort,
4514 aDevice);
4515 if (!pAttach)
4516 return setError(VBOX_E_OBJECT_NOT_FOUND,
4517 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4518 aDevice, aControllerPort, aName.c_str());
4519
4520 /* Check for an existing controller. */
4521 ComObjPtr<StorageController> ctl;
4522 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4523 if (FAILED(rc)) return rc;
4524
4525 StorageControllerType_T ctrlType;
4526 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4527 if (FAILED(rc))
4528 return setError(E_FAIL,
4529 tr("Could not get type of controller '%s'"),
4530 aName.c_str());
4531
4532 if (!i_isControllerHotplugCapable(ctrlType))
4533 return setError(VBOX_E_NOT_SUPPORTED,
4534 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4535 aName.c_str());
4536
4537 i_setModified(IsModified_Storage);
4538 mMediaData.backup();
4539
4540 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4541
4542 if (pAttach->i_getType() == DeviceType_Floppy)
4543 return setError(E_INVALIDARG,
4544 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"),
4545 aDevice, aControllerPort, aName.c_str());
4546 pAttach->i_updateHotPluggable(!!aHotPluggable);
4547
4548 return S_OK;
4549}
4550
4551HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4552 LONG aDevice)
4553{
4554 int rc = S_OK;
4555 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4556 aName.c_str(), aControllerPort, aDevice));
4557
4558 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4559
4560 return rc;
4561}
4562
4563HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4564 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4565{
4566 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4567 aName.c_str(), aControllerPort, aDevice));
4568
4569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4570
4571 HRESULT rc = i_checkStateDependency(MutableStateDep);
4572 if (FAILED(rc)) return rc;
4573
4574 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4575
4576 if (Global::IsOnlineOrTransient(mData->mMachineState))
4577 return setError(VBOX_E_INVALID_VM_STATE,
4578 tr("Invalid machine state: %s"),
4579 Global::stringifyMachineState(mData->mMachineState));
4580
4581 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4582 Bstr(aName).raw(),
4583 aControllerPort,
4584 aDevice);
4585 if (!pAttach)
4586 return setError(VBOX_E_OBJECT_NOT_FOUND,
4587 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4588 aDevice, aControllerPort, aName.c_str());
4589
4590
4591 i_setModified(IsModified_Storage);
4592 mMediaData.backup();
4593
4594 IBandwidthGroup *iB = aBandwidthGroup;
4595 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4596 if (aBandwidthGroup && group.isNull())
4597 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4598
4599 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4600
4601 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4602 if (strBandwidthGroupOld.isNotEmpty())
4603 {
4604 /* Get the bandwidth group object and release it - this must not fail. */
4605 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4606 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4607 Assert(SUCCEEDED(rc));
4608
4609 pBandwidthGroupOld->i_release();
4610 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4611 }
4612
4613 if (!group.isNull())
4614 {
4615 group->i_reference();
4616 pAttach->i_updateBandwidthGroup(group->i_getName());
4617 }
4618
4619 return S_OK;
4620}
4621
4622HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4623 LONG aControllerPort,
4624 LONG aDevice,
4625 DeviceType_T aType)
4626{
4627 HRESULT rc = S_OK;
4628
4629 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4630 aName.c_str(), aControllerPort, aDevice, aType));
4631
4632 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4633
4634 return rc;
4635}
4636
4637
4638HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4639 LONG aControllerPort,
4640 LONG aDevice,
4641 BOOL aForce)
4642{
4643 int rc = S_OK;
4644 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4645 aName.c_str(), aControllerPort, aForce));
4646
4647 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4648
4649 return rc;
4650}
4651
4652HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4653 LONG aControllerPort,
4654 LONG aDevice,
4655 const ComPtr<IMedium> &aMedium,
4656 BOOL aForce)
4657{
4658 int rc = S_OK;
4659 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4660 aName.c_str(), aControllerPort, aDevice, aForce));
4661
4662 // request the host lock first, since might be calling Host methods for getting host drives;
4663 // next, protect the media tree all the while we're in here, as well as our member variables
4664 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4665 this->lockHandle(),
4666 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4667
4668 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4669 Bstr(aName).raw(),
4670 aControllerPort,
4671 aDevice);
4672 if (pAttach.isNull())
4673 return setError(VBOX_E_OBJECT_NOT_FOUND,
4674 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4675 aDevice, aControllerPort, aName.c_str());
4676
4677 /* Remember previously mounted medium. The medium before taking the
4678 * backup is not necessarily the same thing. */
4679 ComObjPtr<Medium> oldmedium;
4680 oldmedium = pAttach->i_getMedium();
4681
4682 IMedium *iM = aMedium;
4683 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4684 if (aMedium && pMedium.isNull())
4685 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4686
4687 AutoCaller mediumCaller(pMedium);
4688 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4689
4690 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4691 if (pMedium)
4692 {
4693 DeviceType_T mediumType = pAttach->i_getType();
4694 switch (mediumType)
4695 {
4696 case DeviceType_DVD:
4697 case DeviceType_Floppy:
4698 break;
4699
4700 default:
4701 return setError(VBOX_E_INVALID_OBJECT_STATE,
4702 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4703 aControllerPort,
4704 aDevice,
4705 aName.c_str());
4706 }
4707 }
4708
4709 i_setModified(IsModified_Storage);
4710 mMediaData.backup();
4711
4712 {
4713 // The backup operation makes the pAttach reference point to the
4714 // old settings. Re-get the correct reference.
4715 pAttach = i_findAttachment(mMediaData->mAttachments,
4716 Bstr(aName).raw(),
4717 aControllerPort,
4718 aDevice);
4719 if (!oldmedium.isNull())
4720 oldmedium->i_removeBackReference(mData->mUuid);
4721 if (!pMedium.isNull())
4722 {
4723 pMedium->i_addBackReference(mData->mUuid);
4724
4725 mediumLock.release();
4726 multiLock.release();
4727 i_addMediumToRegistry(pMedium);
4728 multiLock.acquire();
4729 mediumLock.acquire();
4730 }
4731
4732 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4733 pAttach->i_updateMedium(pMedium);
4734 }
4735
4736 i_setModified(IsModified_Storage);
4737
4738 mediumLock.release();
4739 multiLock.release();
4740 rc = i_onMediumChange(pAttach, aForce);
4741 multiLock.acquire();
4742 mediumLock.acquire();
4743
4744 /* On error roll back this change only. */
4745 if (FAILED(rc))
4746 {
4747 if (!pMedium.isNull())
4748 pMedium->i_removeBackReference(mData->mUuid);
4749 pAttach = i_findAttachment(mMediaData->mAttachments,
4750 Bstr(aName).raw(),
4751 aControllerPort,
4752 aDevice);
4753 /* If the attachment is gone in the meantime, bail out. */
4754 if (pAttach.isNull())
4755 return rc;
4756 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4757 if (!oldmedium.isNull())
4758 oldmedium->i_addBackReference(mData->mUuid);
4759 pAttach->i_updateMedium(oldmedium);
4760 }
4761
4762 mediumLock.release();
4763 multiLock.release();
4764
4765 mParent->i_saveModifiedRegistries();
4766
4767 return rc;
4768}
4769HRESULT Machine::getMedium(const com::Utf8Str &aName,
4770 LONG aControllerPort,
4771 LONG aDevice,
4772 ComPtr<IMedium> &aMedium)
4773{
4774 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4775 aName.c_str(), aControllerPort, aDevice));
4776
4777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4778
4779 aMedium = NULL;
4780
4781 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4782 Bstr(aName).raw(),
4783 aControllerPort,
4784 aDevice);
4785 if (pAttach.isNull())
4786 return setError(VBOX_E_OBJECT_NOT_FOUND,
4787 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4788 aDevice, aControllerPort, aName.c_str());
4789
4790 aMedium = pAttach->i_getMedium();
4791
4792 return S_OK;
4793}
4794
4795HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4796{
4797
4798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4799
4800 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4801
4802 return S_OK;
4803}
4804
4805HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4806{
4807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4808
4809 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4810
4811 return S_OK;
4812}
4813
4814HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4815{
4816 /* Do not assert if slot is out of range, just return the advertised
4817 status. testdriver/vbox.py triggers this in logVmInfo. */
4818 if (aSlot >= mNetworkAdapters.size())
4819 return setError(E_INVALIDARG,
4820 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4821 aSlot, mNetworkAdapters.size());
4822
4823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4824
4825 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4826
4827 return S_OK;
4828}
4829
4830HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4831{
4832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4833
4834 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4835 size_t i = 0;
4836 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4837 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4838 ++it, ++i)
4839 aKeys[i] = it->first;
4840
4841 return S_OK;
4842}
4843
4844 /**
4845 * @note Locks this object for reading.
4846 */
4847HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4848 com::Utf8Str &aValue)
4849{
4850 /* start with nothing found */
4851 aValue = "";
4852
4853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4854
4855 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4856 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4857 // found:
4858 aValue = it->second; // source is a Utf8Str
4859
4860 /* return the result to caller (may be empty) */
4861 return S_OK;
4862}
4863
4864 /**
4865 * @note Locks mParent for writing + this object for writing.
4866 */
4867HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4868{
4869 Utf8Str strOldValue; // empty
4870
4871 // locking note: we only hold the read lock briefly to look up the old value,
4872 // then release it and call the onExtraCanChange callbacks. There is a small
4873 // chance of a race insofar as the callback might be called twice if two callers
4874 // change the same key at the same time, but that's a much better solution
4875 // than the deadlock we had here before. The actual changing of the extradata
4876 // is then performed under the write lock and race-free.
4877
4878 // look up the old value first; if nothing has changed then we need not do anything
4879 {
4880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4881
4882 // For snapshots don't even think about allowing changes, extradata
4883 // is global for a machine, so there is nothing snapshot specific.
4884 if (i_isSnapshotMachine())
4885 return setError(VBOX_E_INVALID_VM_STATE,
4886 tr("Cannot set extradata for a snapshot"));
4887
4888 // check if the right IMachine instance is used
4889 if (!i_isSessionMachine())
4890 return setError(VBOX_E_INVALID_VM_STATE,
4891 tr("Cannot set extradata for an immutable machine"));
4892
4893 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4894 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4895 strOldValue = it->second;
4896 }
4897
4898 bool fChanged;
4899 if ((fChanged = (strOldValue != aValue)))
4900 {
4901 // ask for permission from all listeners outside the locks;
4902 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4903 // lock to copy the list of callbacks to invoke
4904 Bstr error;
4905 Bstr bstrValue(aValue);
4906
4907 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4908 {
4909 const char *sep = error.isEmpty() ? "" : ": ";
4910 CBSTR err = error.raw();
4911 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4912 sep, err));
4913 return setError(E_ACCESSDENIED,
4914 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4915 aKey.c_str(),
4916 aValue.c_str(),
4917 sep,
4918 err);
4919 }
4920
4921 // data is changing and change not vetoed: then write it out under the lock
4922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4923
4924 if (aValue.isEmpty())
4925 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4926 else
4927 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4928 // creates a new key if needed
4929
4930 bool fNeedsGlobalSaveSettings = false;
4931 // This saving of settings is tricky: there is no "old state" for the
4932 // extradata items at all (unlike all other settings), so the old/new
4933 // settings comparison would give a wrong result!
4934 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4935
4936 if (fNeedsGlobalSaveSettings)
4937 {
4938 // save the global settings; for that we should hold only the VirtualBox lock
4939 alock.release();
4940 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4941 mParent->i_saveSettings();
4942 }
4943 }
4944
4945 // fire notification outside the lock
4946 if (fChanged)
4947 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4948
4949 return S_OK;
4950}
4951
4952HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4953{
4954 aProgress = NULL;
4955 NOREF(aSettingsFilePath);
4956 ReturnComNotImplemented();
4957}
4958
4959HRESULT Machine::saveSettings()
4960{
4961 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4962
4963 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4964 if (FAILED(rc)) return rc;
4965
4966 /* the settings file path may never be null */
4967 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4968
4969 /* save all VM data excluding snapshots */
4970 bool fNeedsGlobalSaveSettings = false;
4971 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4972 mlock.release();
4973
4974 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4975 {
4976 // save the global settings; for that we should hold only the VirtualBox lock
4977 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4978 rc = mParent->i_saveSettings();
4979 }
4980
4981 return rc;
4982}
4983
4984
4985HRESULT Machine::discardSettings()
4986{
4987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4988
4989 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4990 if (FAILED(rc)) return rc;
4991
4992 /*
4993 * during this rollback, the session will be notified if data has
4994 * been actually changed
4995 */
4996 i_rollback(true /* aNotify */);
4997
4998 return S_OK;
4999}
5000
5001/** @note Locks objects! */
5002HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5003 std::vector<ComPtr<IMedium> > &aMedia)
5004{
5005 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5006 AutoLimitedCaller autoCaller(this);
5007 AssertComRCReturnRC(autoCaller.rc());
5008
5009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5010
5011 Guid id(i_getId());
5012
5013 if (mData->mSession.mState != SessionState_Unlocked)
5014 return setError(VBOX_E_INVALID_OBJECT_STATE,
5015 tr("Cannot unregister the machine '%s' while it is locked"),
5016 mUserData->s.strName.c_str());
5017
5018 // wait for state dependents to drop to zero
5019 i_ensureNoStateDependencies();
5020
5021 if (!mData->mAccessible)
5022 {
5023 // inaccessible maschines can only be unregistered; uninitialize ourselves
5024 // here because currently there may be no unregistered that are inaccessible
5025 // (this state combination is not supported). Note releasing the caller and
5026 // leaving the lock before calling uninit()
5027 alock.release();
5028 autoCaller.release();
5029
5030 uninit();
5031
5032 mParent->i_unregisterMachine(this, id);
5033 // calls VirtualBox::i_saveSettings()
5034
5035 return S_OK;
5036 }
5037
5038 HRESULT rc = S_OK;
5039
5040 // discard saved state
5041 if (mData->mMachineState == MachineState_Saved)
5042 {
5043 // add the saved state file to the list of files the caller should delete
5044 Assert(!mSSData->strStateFilePath.isEmpty());
5045 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5046
5047 mSSData->strStateFilePath.setNull();
5048
5049 // unconditionally set the machine state to powered off, we now
5050 // know no session has locked the machine
5051 mData->mMachineState = MachineState_PoweredOff;
5052 }
5053
5054 size_t cSnapshots = 0;
5055 if (mData->mFirstSnapshot)
5056 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5057 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5058 // fail now before we start detaching media
5059 return setError(VBOX_E_INVALID_OBJECT_STATE,
5060 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5061 mUserData->s.strName.c_str(), cSnapshots);
5062
5063 // This list collects the medium objects from all medium attachments
5064 // which we will detach from the machine and its snapshots, in a specific
5065 // order which allows for closing all media without getting "media in use"
5066 // errors, simply by going through the list from the front to the back:
5067 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5068 // and must be closed before the parent media from the snapshots, or closing the parents
5069 // will fail because they still have children);
5070 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5071 // the root ("first") snapshot of the machine.
5072 MediaList llMedia;
5073
5074 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5075 && mMediaData->mAttachments.size()
5076 )
5077 {
5078 // we have media attachments: detach them all and add the Medium objects to our list
5079 if (aCleanupMode != CleanupMode_UnregisterOnly)
5080 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5081 else
5082 return setError(VBOX_E_INVALID_OBJECT_STATE,
5083 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5084 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5085 }
5086
5087 if (cSnapshots)
5088 {
5089 // autoCleanup must be true here, or we would have failed above
5090
5091 // add the media from the medium attachments of the snapshots to llMedia
5092 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5093 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5094 // into the children first
5095
5096 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5097 MachineState_T oldState = mData->mMachineState;
5098 mData->mMachineState = MachineState_DeletingSnapshot;
5099
5100 // make a copy of the first snapshot so the refcount does not drop to 0
5101 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5102 // because of the AutoCaller voodoo)
5103 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5104
5105 // GO!
5106 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5107
5108 mData->mMachineState = oldState;
5109 }
5110
5111 if (FAILED(rc))
5112 {
5113 i_rollbackMedia();
5114 return rc;
5115 }
5116
5117 // commit all the media changes made above
5118 i_commitMedia();
5119
5120 mData->mRegistered = false;
5121
5122 // machine lock no longer needed
5123 alock.release();
5124
5125 // return media to caller
5126 size_t i = 0;
5127 aMedia.resize(llMedia.size());
5128 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5129 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5130
5131 mParent->i_unregisterMachine(this, id);
5132 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5133
5134 return S_OK;
5135}
5136
5137struct Machine::DeleteTask
5138{
5139 ComObjPtr<Machine> pMachine;
5140 RTCList<ComPtr<IMedium> > llMediums;
5141 StringsList llFilesToDelete;
5142 ComObjPtr<Progress> pProgress;
5143};
5144
5145HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5146{
5147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5148
5149 HRESULT rc = i_checkStateDependency(MutableStateDep);
5150 if (FAILED(rc)) return rc;
5151
5152 if (mData->mRegistered)
5153 return setError(VBOX_E_INVALID_VM_STATE,
5154 tr("Cannot delete settings of a registered machine"));
5155
5156 DeleteTask *pTask = new DeleteTask;
5157 pTask->pMachine = this;
5158
5159 // collect files to delete
5160 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5161
5162 for (size_t i = 0; i < aMedia.size(); ++i)
5163 {
5164 IMedium *pIMedium(aMedia[i]);
5165 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5166 if (pMedium.isNull())
5167 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5168 SafeArray<BSTR> ids;
5169 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5170 if (FAILED(rc)) return rc;
5171 /* At this point the medium should not have any back references
5172 * anymore. If it has it is attached to another VM and *must* not
5173 * deleted. */
5174 if (ids.size() < 1)
5175 pTask->llMediums.append(pMedium);
5176 }
5177 if (mData->pMachineConfigFile->fileExists())
5178 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5179
5180 pTask->pProgress.createObject();
5181 pTask->pProgress->init(i_getVirtualBox(),
5182 static_cast<IMachine*>(this) /* aInitiator */,
5183 Bstr(tr("Deleting files")).raw(),
5184 true /* fCancellable */,
5185 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5186 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5187
5188 int vrc = RTThreadCreate(NULL,
5189 Machine::deleteThread,
5190 (void*)pTask,
5191 0,
5192 RTTHREADTYPE_MAIN_WORKER,
5193 0,
5194 "MachineDelete");
5195
5196 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5197
5198 if (RT_FAILURE(vrc))
5199 {
5200 delete pTask;
5201 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5202 }
5203
5204 LogFlowFuncLeave();
5205
5206 return S_OK;
5207}
5208
5209/**
5210 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5211 * calls Machine::deleteTaskWorker() on the actual machine object.
5212 * @param Thread
5213 * @param pvUser
5214 * @return
5215 */
5216/*static*/
5217DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5218{
5219 LogFlowFuncEnter();
5220
5221 DeleteTask *pTask = (DeleteTask*)pvUser;
5222 Assert(pTask);
5223 Assert(pTask->pMachine);
5224 Assert(pTask->pProgress);
5225
5226 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5227 pTask->pProgress->i_notifyComplete(rc);
5228
5229 delete pTask;
5230
5231 LogFlowFuncLeave();
5232
5233 NOREF(Thread);
5234
5235 return VINF_SUCCESS;
5236}
5237
5238/**
5239 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5240 * @param task
5241 * @return
5242 */
5243HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5244{
5245 AutoCaller autoCaller(this);
5246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5247
5248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5249
5250 HRESULT rc = S_OK;
5251
5252 try
5253 {
5254 ULONG uLogHistoryCount = 3;
5255 ComPtr<ISystemProperties> systemProperties;
5256 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5257 if (FAILED(rc)) throw rc;
5258
5259 if (!systemProperties.isNull())
5260 {
5261 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5262 if (FAILED(rc)) throw rc;
5263 }
5264
5265 MachineState_T oldState = mData->mMachineState;
5266 i_setMachineState(MachineState_SettingUp);
5267 alock.release();
5268 for (size_t i = 0; i < task.llMediums.size(); ++i)
5269 {
5270 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5271 {
5272 AutoCaller mac(pMedium);
5273 if (FAILED(mac.rc())) throw mac.rc();
5274 Utf8Str strLocation = pMedium->i_getLocationFull();
5275 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5276 if (FAILED(rc)) throw rc;
5277 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5278 }
5279 if (pMedium->i_isMediumFormatFile())
5280 {
5281 ComPtr<IProgress> pProgress2;
5282 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5283 if (FAILED(rc)) throw rc;
5284 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5285 if (FAILED(rc)) throw rc;
5286 /* Check the result of the asynchronous process. */
5287 LONG iRc;
5288 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5289 if (FAILED(rc)) throw rc;
5290 /* If the thread of the progress object has an error, then
5291 * retrieve the error info from there, or it'll be lost. */
5292 if (FAILED(iRc))
5293 throw setError(ProgressErrorInfo(pProgress2));
5294 }
5295
5296 /* Close the medium, deliberately without checking the return
5297 * code, and without leaving any trace in the error info, as
5298 * a failure here is a very minor issue, which shouldn't happen
5299 * as above we even managed to delete the medium. */
5300 {
5301 ErrorInfoKeeper eik;
5302 pMedium->Close();
5303 }
5304 }
5305 i_setMachineState(oldState);
5306 alock.acquire();
5307
5308 // delete the files pushed on the task list by Machine::Delete()
5309 // (this includes saved states of the machine and snapshots and
5310 // medium storage files from the IMedium list passed in, and the
5311 // machine XML file)
5312 StringsList::const_iterator it = task.llFilesToDelete.begin();
5313 while (it != task.llFilesToDelete.end())
5314 {
5315 const Utf8Str &strFile = *it;
5316 LogFunc(("Deleting file %s\n", strFile.c_str()));
5317 int vrc = RTFileDelete(strFile.c_str());
5318 if (RT_FAILURE(vrc))
5319 throw setError(VBOX_E_IPRT_ERROR,
5320 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5321
5322 ++it;
5323 if (it == task.llFilesToDelete.end())
5324 {
5325 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5326 if (FAILED(rc)) throw rc;
5327 break;
5328 }
5329
5330 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5331 if (FAILED(rc)) throw rc;
5332 }
5333
5334 /* delete the settings only when the file actually exists */
5335 if (mData->pMachineConfigFile->fileExists())
5336 {
5337 /* Delete any backup or uncommitted XML files. Ignore failures.
5338 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5339 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5340 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5341 RTFileDelete(otherXml.c_str());
5342 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5343 RTFileDelete(otherXml.c_str());
5344
5345 /* delete the Logs folder, nothing important should be left
5346 * there (we don't check for errors because the user might have
5347 * some private files there that we don't want to delete) */
5348 Utf8Str logFolder;
5349 getLogFolder(logFolder);
5350 Assert(logFolder.length());
5351 if (RTDirExists(logFolder.c_str()))
5352 {
5353 /* Delete all VBox.log[.N] files from the Logs folder
5354 * (this must be in sync with the rotation logic in
5355 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5356 * files that may have been created by the GUI. */
5357 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5358 logFolder.c_str(), RTPATH_DELIMITER);
5359 RTFileDelete(log.c_str());
5360 log = Utf8StrFmt("%s%cVBox.png",
5361 logFolder.c_str(), RTPATH_DELIMITER);
5362 RTFileDelete(log.c_str());
5363 for (int i = uLogHistoryCount; i > 0; i--)
5364 {
5365 log = Utf8StrFmt("%s%cVBox.log.%d",
5366 logFolder.c_str(), RTPATH_DELIMITER, i);
5367 RTFileDelete(log.c_str());
5368 log = Utf8StrFmt("%s%cVBox.png.%d",
5369 logFolder.c_str(), RTPATH_DELIMITER, i);
5370 RTFileDelete(log.c_str());
5371 }
5372#if defined(RT_OS_WINDOWS)
5373 log = Utf8StrFmt("%s%cVBoxStartup.log",
5374 logFolder.c_str(), RTPATH_DELIMITER);
5375 RTFileDelete(log.c_str());
5376#endif
5377
5378 RTDirRemove(logFolder.c_str());
5379 }
5380
5381 /* delete the Snapshots folder, nothing important should be left
5382 * there (we don't check for errors because the user might have
5383 * some private files there that we don't want to delete) */
5384 Utf8Str strFullSnapshotFolder;
5385 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5386 Assert(!strFullSnapshotFolder.isEmpty());
5387 if (RTDirExists(strFullSnapshotFolder.c_str()))
5388 RTDirRemove(strFullSnapshotFolder.c_str());
5389
5390 // delete the directory that contains the settings file, but only
5391 // if it matches the VM name
5392 Utf8Str settingsDir;
5393 if (i_isInOwnDir(&settingsDir))
5394 RTDirRemove(settingsDir.c_str());
5395 }
5396
5397 alock.release();
5398
5399 mParent->i_saveModifiedRegistries();
5400 }
5401 catch (HRESULT aRC) { rc = aRC; }
5402
5403 return rc;
5404}
5405
5406HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5407{
5408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5409
5410 ComObjPtr<Snapshot> pSnapshot;
5411 HRESULT rc;
5412
5413 if (aNameOrId.isEmpty())
5414 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5415 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5416 else
5417 {
5418 Guid uuid(aNameOrId);
5419 if (uuid.isValid())
5420 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5421 else
5422 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5423 }
5424 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5425
5426 return rc;
5427}
5428
5429HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5430{
5431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5432
5433 HRESULT rc = i_checkStateDependency(MutableStateDep);
5434 if (FAILED(rc)) return rc;
5435
5436 ComObjPtr<SharedFolder> sharedFolder;
5437 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5438 if (SUCCEEDED(rc))
5439 return setError(VBOX_E_OBJECT_IN_USE,
5440 tr("Shared folder named '%s' already exists"),
5441 aName.c_str());
5442
5443 sharedFolder.createObject();
5444 rc = sharedFolder->init(i_getMachine(),
5445 aName,
5446 aHostPath,
5447 !!aWritable,
5448 !!aAutomount,
5449 true /* fFailOnError */);
5450 if (FAILED(rc)) return rc;
5451
5452 i_setModified(IsModified_SharedFolders);
5453 mHWData.backup();
5454 mHWData->mSharedFolders.push_back(sharedFolder);
5455
5456 /* inform the direct session if any */
5457 alock.release();
5458 i_onSharedFolderChange();
5459
5460 return S_OK;
5461}
5462
5463HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5464{
5465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5466
5467 HRESULT rc = i_checkStateDependency(MutableStateDep);
5468 if (FAILED(rc)) return rc;
5469
5470 ComObjPtr<SharedFolder> sharedFolder;
5471 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5472 if (FAILED(rc)) return rc;
5473
5474 i_setModified(IsModified_SharedFolders);
5475 mHWData.backup();
5476 mHWData->mSharedFolders.remove(sharedFolder);
5477
5478 /* inform the direct session if any */
5479 alock.release();
5480 i_onSharedFolderChange();
5481
5482 return S_OK;
5483}
5484
5485HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5486{
5487 /* start with No */
5488 *aCanShow = FALSE;
5489
5490 ComPtr<IInternalSessionControl> directControl;
5491 {
5492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5493
5494 if (mData->mSession.mState != SessionState_Locked)
5495 return setError(VBOX_E_INVALID_VM_STATE,
5496 tr("Machine is not locked for session (session state: %s)"),
5497 Global::stringifySessionState(mData->mSession.mState));
5498
5499 directControl = mData->mSession.mDirectControl;
5500 }
5501
5502 /* ignore calls made after #OnSessionEnd() is called */
5503 if (!directControl)
5504 return S_OK;
5505
5506 LONG64 dummy;
5507 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5508}
5509
5510HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5511{
5512 ComPtr<IInternalSessionControl> directControl;
5513 {
5514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5515
5516 if (mData->mSession.mState != SessionState_Locked)
5517 return setError(E_FAIL,
5518 tr("Machine is not locked for session (session state: %s)"),
5519 Global::stringifySessionState(mData->mSession.mState));
5520
5521 directControl = mData->mSession.mDirectControl;
5522 }
5523
5524 /* ignore calls made after #OnSessionEnd() is called */
5525 if (!directControl)
5526 return S_OK;
5527
5528 BOOL dummy;
5529 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5530}
5531
5532#ifdef VBOX_WITH_GUEST_PROPS
5533/**
5534 * Look up a guest property in VBoxSVC's internal structures.
5535 */
5536HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5537 com::Utf8Str &aValue,
5538 LONG64 *aTimestamp,
5539 com::Utf8Str &aFlags) const
5540{
5541 using namespace guestProp;
5542
5543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5544 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5545
5546 if (it != mHWData->mGuestProperties.end())
5547 {
5548 char szFlags[MAX_FLAGS_LEN + 1];
5549 aValue = it->second.strValue;
5550 *aTimestamp = it->second.mTimestamp;
5551 writeFlags(it->second.mFlags, szFlags);
5552 aFlags = Utf8Str(szFlags);
5553 }
5554
5555 return S_OK;
5556}
5557
5558/**
5559 * Query the VM that a guest property belongs to for the property.
5560 * @returns E_ACCESSDENIED if the VM process is not available or not
5561 * currently handling queries and the lookup should then be done in
5562 * VBoxSVC.
5563 */
5564HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5565 com::Utf8Str &aValue,
5566 LONG64 *aTimestamp,
5567 com::Utf8Str &aFlags) const
5568{
5569 HRESULT rc = S_OK;
5570 BSTR bValue = NULL;
5571 BSTR bFlags = NULL;
5572
5573 ComPtr<IInternalSessionControl> directControl;
5574 directControl = mData->mSession.mDirectControl;
5575
5576 /* fail if we were called after #OnSessionEnd() is called. This is a
5577 * silly race condition. */
5578
5579 /** @todo This code is bothering API clients (like python script clients) with
5580 * the AccessGuestProperty call, creating unncessary IPC. Need to
5581 * have a way of figuring out which kind of direct session it is... */
5582 if (!directControl)
5583 rc = E_ACCESSDENIED;
5584 else
5585 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5586 0 /* accessMode */,
5587 &bValue, aTimestamp, &bFlags);
5588
5589 aValue = bValue;
5590 aFlags = bFlags;
5591
5592 return rc;
5593}
5594#endif // VBOX_WITH_GUEST_PROPS
5595
5596HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5597 com::Utf8Str &aValue,
5598 LONG64 *aTimestamp,
5599 com::Utf8Str &aFlags)
5600{
5601#ifndef VBOX_WITH_GUEST_PROPS
5602 ReturnComNotImplemented();
5603#else // VBOX_WITH_GUEST_PROPS
5604
5605 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5606
5607 if (rc == E_ACCESSDENIED)
5608 /* The VM is not running or the service is not (yet) accessible */
5609 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5610 return rc;
5611#endif // VBOX_WITH_GUEST_PROPS
5612}
5613
5614HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5615{
5616 LONG64 dummyTimestamp;
5617 com::Utf8Str dummyFlags;
5618 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5619 return rc;
5620
5621}
5622HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5623{
5624 com::Utf8Str dummyFlags;
5625 com::Utf8Str dummyValue;
5626 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5627 return rc;
5628}
5629
5630#ifdef VBOX_WITH_GUEST_PROPS
5631/**
5632 * Set a guest property in VBoxSVC's internal structures.
5633 */
5634HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5635 const com::Utf8Str &aFlags, bool fDelete)
5636{
5637 using namespace guestProp;
5638
5639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5640 HRESULT rc = S_OK;
5641
5642 rc = i_checkStateDependency(MutableOrSavedStateDep);
5643 if (FAILED(rc)) return rc;
5644
5645 try
5646 {
5647 uint32_t fFlags = NILFLAG;
5648 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5649 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5650
5651 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5652 if (it == mHWData->mGuestProperties.end())
5653 {
5654 if (!fDelete)
5655 {
5656 i_setModified(IsModified_MachineData);
5657 mHWData.backupEx();
5658
5659 RTTIMESPEC time;
5660 HWData::GuestProperty prop;
5661 prop.strValue = Bstr(aValue).raw();
5662 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5663 prop.mFlags = fFlags;
5664 mHWData->mGuestProperties[aName] = prop;
5665 }
5666 }
5667 else
5668 {
5669 if (it->second.mFlags & (RDONLYHOST))
5670 {
5671 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5672 }
5673 else
5674 {
5675 i_setModified(IsModified_MachineData);
5676 mHWData.backupEx();
5677
5678 /* The backupEx() operation invalidates our iterator,
5679 * so get a new one. */
5680 it = mHWData->mGuestProperties.find(aName);
5681 Assert(it != mHWData->mGuestProperties.end());
5682
5683 if (!fDelete)
5684 {
5685 RTTIMESPEC time;
5686 it->second.strValue = aValue;
5687 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5688 it->second.mFlags = fFlags;
5689 }
5690 else
5691 mHWData->mGuestProperties.erase(it);
5692 }
5693 }
5694
5695 if ( SUCCEEDED(rc)
5696 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5697 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5698 RTSTR_MAX,
5699 aName.c_str(),
5700 RTSTR_MAX,
5701 NULL)
5702 )
5703 )
5704 {
5705 alock.release();
5706
5707 mParent->i_onGuestPropertyChange(mData->mUuid,
5708 Bstr(aName).raw(),
5709 Bstr(aValue).raw(),
5710 Bstr(aFlags).raw());
5711 }
5712 }
5713 catch (std::bad_alloc &)
5714 {
5715 rc = E_OUTOFMEMORY;
5716 }
5717
5718 return rc;
5719}
5720
5721/**
5722 * Set a property on the VM that that property belongs to.
5723 * @returns E_ACCESSDENIED if the VM process is not available or not
5724 * currently handling queries and the setting should then be done in
5725 * VBoxSVC.
5726 */
5727HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5728 const com::Utf8Str &aFlags, bool fDelete)
5729{
5730 HRESULT rc;
5731
5732 try
5733 {
5734 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5735
5736 BSTR dummy = NULL; /* will not be changed (setter) */
5737 LONG64 dummy64;
5738 if (!directControl)
5739 rc = E_ACCESSDENIED;
5740 else
5741 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5742 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5743 fDelete? 2: 1 /* accessMode */,
5744 &dummy, &dummy64, &dummy);
5745 }
5746 catch (std::bad_alloc &)
5747 {
5748 rc = E_OUTOFMEMORY;
5749 }
5750
5751 return rc;
5752}
5753#endif // VBOX_WITH_GUEST_PROPS
5754
5755HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5756 const com::Utf8Str &aFlags)
5757{
5758#ifndef VBOX_WITH_GUEST_PROPS
5759 ReturnComNotImplemented();
5760#else // VBOX_WITH_GUEST_PROPS
5761 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5762 if (rc == E_ACCESSDENIED)
5763 /* The VM is not running or the service is not (yet) accessible */
5764 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5765 return rc;
5766#endif // VBOX_WITH_GUEST_PROPS
5767}
5768
5769HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5770{
5771 return setGuestProperty(aProperty, aValue, "");
5772}
5773
5774HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5775{
5776#ifndef VBOX_WITH_GUEST_PROPS
5777 ReturnComNotImplemented();
5778#else // VBOX_WITH_GUEST_PROPS
5779 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5780 if (rc == E_ACCESSDENIED)
5781 /* The VM is not running or the service is not (yet) accessible */
5782 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5783 return rc;
5784#endif // VBOX_WITH_GUEST_PROPS
5785}
5786
5787#ifdef VBOX_WITH_GUEST_PROPS
5788/**
5789 * Enumerate the guest properties in VBoxSVC's internal structures.
5790 */
5791HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5792 std::vector<com::Utf8Str> &aNames,
5793 std::vector<com::Utf8Str> &aValues,
5794 std::vector<LONG64> &aTimestamps,
5795 std::vector<com::Utf8Str> &aFlags)
5796{
5797 using namespace guestProp;
5798
5799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5800 Utf8Str strPatterns(aPatterns);
5801
5802 HWData::GuestPropertyMap propMap;
5803
5804 /*
5805 * Look for matching patterns and build up a list.
5806 */
5807 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5808 while (it != mHWData->mGuestProperties.end())
5809 {
5810 if ( strPatterns.isEmpty()
5811 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5812 RTSTR_MAX,
5813 it->first.c_str(),
5814 RTSTR_MAX,
5815 NULL)
5816 )
5817 propMap.insert(*it);
5818 it++;
5819 }
5820
5821 alock.release();
5822
5823 /*
5824 * And build up the arrays for returning the property information.
5825 */
5826 size_t cEntries = propMap.size();
5827
5828 aNames.resize(cEntries);
5829 aValues.resize(cEntries);
5830 aTimestamps.resize(cEntries);
5831 aFlags.resize(cEntries);
5832
5833 char szFlags[MAX_FLAGS_LEN + 1];
5834 size_t i= 0;
5835 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5836 {
5837 aNames[i] = it->first;
5838 aValues[i] = it->second.strValue;
5839 aTimestamps[i] = it->second.mTimestamp;
5840 writeFlags(it->second.mFlags, szFlags);
5841 aFlags[i] = Utf8Str(szFlags);
5842 }
5843
5844 return S_OK;
5845}
5846
5847/**
5848 * Enumerate the properties managed by a VM.
5849 * @returns E_ACCESSDENIED if the VM process is not available or not
5850 * currently handling queries and the setting should then be done in
5851 * VBoxSVC.
5852 */
5853HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5854 std::vector<com::Utf8Str> &aNames,
5855 std::vector<com::Utf8Str> &aValues,
5856 std::vector<LONG64> &aTimestamps,
5857 std::vector<com::Utf8Str> &aFlags)
5858{
5859 HRESULT rc;
5860 ComPtr<IInternalSessionControl> directControl;
5861 directControl = mData->mSession.mDirectControl;
5862
5863
5864 com::SafeArray<BSTR> bNames;
5865 com::SafeArray<BSTR> bValues;
5866 com::SafeArray<LONG64> bTimestamps;
5867 com::SafeArray<BSTR> bFlags;
5868
5869 if (!directControl)
5870 rc = E_ACCESSDENIED;
5871 else
5872 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5873 ComSafeArrayAsOutParam(bNames),
5874 ComSafeArrayAsOutParam(bValues),
5875 ComSafeArrayAsOutParam(bTimestamps),
5876 ComSafeArrayAsOutParam(bFlags));
5877 size_t i;
5878 aNames.resize(bNames.size());
5879 for (i = 0; i < bNames.size(); ++i)
5880 aNames[i] = Utf8Str(bNames[i]);
5881 aValues.resize(bValues.size());
5882 for (i = 0; i < bValues.size(); ++i)
5883 aValues[i] = Utf8Str(bValues[i]);
5884 aTimestamps.resize(bTimestamps.size());
5885 for (i = 0; i < bTimestamps.size(); ++i)
5886 aTimestamps[i] = bTimestamps[i];
5887 aFlags.resize(bFlags.size());
5888 for (i = 0; i < bFlags.size(); ++i)
5889 aFlags[i] = Utf8Str(bFlags[i]);
5890
5891 return rc;
5892}
5893#endif // VBOX_WITH_GUEST_PROPS
5894HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5895 std::vector<com::Utf8Str> &aNames,
5896 std::vector<com::Utf8Str> &aValues,
5897 std::vector<LONG64> &aTimestamps,
5898 std::vector<com::Utf8Str> &aFlags)
5899{
5900#ifndef VBOX_WITH_GUEST_PROPS
5901 ReturnComNotImplemented();
5902#else // VBOX_WITH_GUEST_PROPS
5903
5904 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5905
5906 if (rc == E_ACCESSDENIED)
5907 /* The VM is not running or the service is not (yet) accessible */
5908 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5909 return rc;
5910#endif // VBOX_WITH_GUEST_PROPS
5911}
5912
5913HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5914 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5915{
5916 MediaData::AttachmentList atts;
5917
5918 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5919 if (FAILED(rc)) return rc;
5920
5921 size_t i = 0;
5922 aMediumAttachments.resize(atts.size());
5923 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5924 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5925
5926 return S_OK;
5927}
5928
5929HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5930 LONG aControllerPort,
5931 LONG aDevice,
5932 ComPtr<IMediumAttachment> &aAttachment)
5933{
5934 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5935 aName.c_str(), aControllerPort, aDevice));
5936
5937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5938
5939 aAttachment = NULL;
5940
5941 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5942 Bstr(aName).raw(),
5943 aControllerPort,
5944 aDevice);
5945 if (pAttach.isNull())
5946 return setError(VBOX_E_OBJECT_NOT_FOUND,
5947 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5948 aDevice, aControllerPort, aName.c_str());
5949
5950 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5951
5952 return S_OK;
5953}
5954
5955
5956HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5957 StorageBus_T aConnectionType,
5958 ComPtr<IStorageController> &aController)
5959{
5960 if ( (aConnectionType <= StorageBus_Null)
5961 || (aConnectionType > StorageBus_USB))
5962 return setError(E_INVALIDARG,
5963 tr("Invalid connection type: %d"),
5964 aConnectionType);
5965
5966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5967
5968 HRESULT rc = i_checkStateDependency(MutableStateDep);
5969 if (FAILED(rc)) return rc;
5970
5971 /* try to find one with the name first. */
5972 ComObjPtr<StorageController> ctrl;
5973
5974 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5975 if (SUCCEEDED(rc))
5976 return setError(VBOX_E_OBJECT_IN_USE,
5977 tr("Storage controller named '%s' already exists"),
5978 aName.c_str());
5979
5980 ctrl.createObject();
5981
5982 /* get a new instance number for the storage controller */
5983 ULONG ulInstance = 0;
5984 bool fBootable = true;
5985 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5986 it != mStorageControllers->end();
5987 ++it)
5988 {
5989 if ((*it)->i_getStorageBus() == aConnectionType)
5990 {
5991 ULONG ulCurInst = (*it)->i_getInstance();
5992
5993 if (ulCurInst >= ulInstance)
5994 ulInstance = ulCurInst + 1;
5995
5996 /* Only one controller of each type can be marked as bootable. */
5997 if ((*it)->i_getBootable())
5998 fBootable = false;
5999 }
6000 }
6001
6002 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6003 if (FAILED(rc)) return rc;
6004
6005 i_setModified(IsModified_Storage);
6006 mStorageControllers.backup();
6007 mStorageControllers->push_back(ctrl);
6008
6009 ctrl.queryInterfaceTo(aController.asOutParam());
6010
6011 /* inform the direct session if any */
6012 alock.release();
6013 i_onStorageControllerChange();
6014
6015 return S_OK;
6016}
6017
6018HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6019 ComPtr<IStorageController> &aStorageController)
6020{
6021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6022
6023 ComObjPtr<StorageController> ctrl;
6024
6025 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6026 if (SUCCEEDED(rc))
6027 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6028
6029 return rc;
6030}
6031
6032HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6033 ComPtr<IStorageController> &aStorageController)
6034{
6035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6036
6037 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6038 it != mStorageControllers->end();
6039 ++it)
6040 {
6041 if ((*it)->i_getInstance() == aInstance)
6042 {
6043 (*it).queryInterfaceTo(aStorageController.asOutParam());
6044 return S_OK;
6045 }
6046 }
6047
6048 return setError(VBOX_E_OBJECT_NOT_FOUND,
6049 tr("Could not find a storage controller with instance number '%lu'"),
6050 aInstance);
6051}
6052
6053HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6054{
6055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6056
6057 HRESULT rc = i_checkStateDependency(MutableStateDep);
6058 if (FAILED(rc)) return rc;
6059
6060 ComObjPtr<StorageController> ctrl;
6061
6062 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6063 if (SUCCEEDED(rc))
6064 {
6065 /* Ensure that only one controller of each type is marked as bootable. */
6066 if (aBootable == TRUE)
6067 {
6068 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6069 it != mStorageControllers->end();
6070 ++it)
6071 {
6072 ComObjPtr<StorageController> aCtrl = (*it);
6073
6074 if ( (aCtrl->i_getName() != aName)
6075 && aCtrl->i_getBootable() == TRUE
6076 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6077 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6078 {
6079 aCtrl->i_setBootable(FALSE);
6080 break;
6081 }
6082 }
6083 }
6084
6085 if (SUCCEEDED(rc))
6086 {
6087 ctrl->i_setBootable(aBootable);
6088 i_setModified(IsModified_Storage);
6089 }
6090 }
6091
6092 if (SUCCEEDED(rc))
6093 {
6094 /* inform the direct session if any */
6095 alock.release();
6096 i_onStorageControllerChange();
6097 }
6098
6099 return rc;
6100}
6101
6102HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6103{
6104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6105
6106 HRESULT rc = i_checkStateDependency(MutableStateDep);
6107 if (FAILED(rc)) return rc;
6108
6109 ComObjPtr<StorageController> ctrl;
6110 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6111 if (FAILED(rc)) return rc;
6112
6113 {
6114 /* find all attached devices to the appropriate storage controller and detach them all */
6115 // make a temporary list because detachDevice invalidates iterators into
6116 // mMediaData->mAttachments
6117 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6118
6119 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6120 it != llAttachments2.end();
6121 ++it)
6122 {
6123 MediumAttachment *pAttachTemp = *it;
6124
6125 AutoCaller localAutoCaller(pAttachTemp);
6126 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6127
6128 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6129
6130 if (pAttachTemp->i_getControllerName() == aName)
6131 {
6132 rc = i_detachDevice(pAttachTemp, alock, NULL);
6133 if (FAILED(rc)) return rc;
6134 }
6135 }
6136 }
6137
6138 /* We can remove it now. */
6139 i_setModified(IsModified_Storage);
6140 mStorageControllers.backup();
6141
6142 ctrl->i_unshare();
6143
6144 mStorageControllers->remove(ctrl);
6145
6146 /* inform the direct session if any */
6147 alock.release();
6148 i_onStorageControllerChange();
6149
6150 return S_OK;
6151}
6152
6153HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6154 ComPtr<IUSBController> &aController)
6155{
6156 if ( (aType <= USBControllerType_Null)
6157 || (aType >= USBControllerType_Last))
6158 return setError(E_INVALIDARG,
6159 tr("Invalid USB controller type: %d"),
6160 aType);
6161
6162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6163
6164 HRESULT rc = i_checkStateDependency(MutableStateDep);
6165 if (FAILED(rc)) return rc;
6166
6167 /* try to find one with the same type first. */
6168 ComObjPtr<USBController> ctrl;
6169
6170 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6171 if (SUCCEEDED(rc))
6172 return setError(VBOX_E_OBJECT_IN_USE,
6173 tr("USB controller named '%s' already exists"),
6174 aName.c_str());
6175
6176 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6177 ULONG maxInstances;
6178 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6179 if (FAILED(rc))
6180 return rc;
6181
6182 ULONG cInstances = i_getUSBControllerCountByType(aType);
6183 if (cInstances >= maxInstances)
6184 return setError(E_INVALIDARG,
6185 tr("Too many USB controllers of this type"));
6186
6187 ctrl.createObject();
6188
6189 rc = ctrl->init(this, aName, aType);
6190 if (FAILED(rc)) return rc;
6191
6192 i_setModified(IsModified_USB);
6193 mUSBControllers.backup();
6194 mUSBControllers->push_back(ctrl);
6195
6196 ctrl.queryInterfaceTo(aController.asOutParam());
6197
6198 /* inform the direct session if any */
6199 alock.release();
6200 i_onUSBControllerChange();
6201
6202 return S_OK;
6203}
6204
6205HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6206{
6207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6208
6209 ComObjPtr<USBController> ctrl;
6210
6211 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6212 if (SUCCEEDED(rc))
6213 ctrl.queryInterfaceTo(aController.asOutParam());
6214
6215 return rc;
6216}
6217
6218HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6219 ULONG *aControllers)
6220{
6221 if ( (aType <= USBControllerType_Null)
6222 || (aType >= USBControllerType_Last))
6223 return setError(E_INVALIDARG,
6224 tr("Invalid USB controller type: %d"),
6225 aType);
6226
6227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6228
6229 ComObjPtr<USBController> ctrl;
6230
6231 *aControllers = i_getUSBControllerCountByType(aType);
6232
6233 return S_OK;
6234}
6235
6236HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6237{
6238
6239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6240
6241 HRESULT rc = i_checkStateDependency(MutableStateDep);
6242 if (FAILED(rc)) return rc;
6243
6244 ComObjPtr<USBController> ctrl;
6245 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6246 if (FAILED(rc)) return rc;
6247
6248 i_setModified(IsModified_USB);
6249 mUSBControllers.backup();
6250
6251 ctrl->i_unshare();
6252
6253 mUSBControllers->remove(ctrl);
6254
6255 /* inform the direct session if any */
6256 alock.release();
6257 i_onUSBControllerChange();
6258
6259 return S_OK;
6260}
6261
6262HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6263 ULONG *aOriginX,
6264 ULONG *aOriginY,
6265 ULONG *aWidth,
6266 ULONG *aHeight,
6267 BOOL *aEnabled)
6268{
6269 uint32_t u32OriginX= 0;
6270 uint32_t u32OriginY= 0;
6271 uint32_t u32Width = 0;
6272 uint32_t u32Height = 0;
6273 uint16_t u16Flags = 0;
6274
6275 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6276 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6277 if (RT_FAILURE(vrc))
6278 {
6279#ifdef RT_OS_WINDOWS
6280 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6281 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6282 * So just assign fEnable to TRUE again.
6283 * The right fix would be to change GUI API wrappers to make sure that parameters
6284 * are changed only if API succeeds.
6285 */
6286 *aEnabled = TRUE;
6287#endif
6288 return setError(VBOX_E_IPRT_ERROR,
6289 tr("Saved guest size is not available (%Rrc)"),
6290 vrc);
6291 }
6292
6293 *aOriginX = u32OriginX;
6294 *aOriginY = u32OriginY;
6295 *aWidth = u32Width;
6296 *aHeight = u32Height;
6297 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6298
6299 return S_OK;
6300}
6301
6302HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6303{
6304 if (aScreenId != 0)
6305 return E_NOTIMPL;
6306
6307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6308
6309 uint8_t *pu8Data = NULL;
6310 uint32_t cbData = 0;
6311 uint32_t u32Width = 0;
6312 uint32_t u32Height = 0;
6313
6314 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6315
6316 if (RT_FAILURE(vrc))
6317 return setError(VBOX_E_IPRT_ERROR,
6318 tr("Saved screenshot data is not available (%Rrc)"),
6319 vrc);
6320
6321 *aSize = cbData;
6322 *aWidth = u32Width;
6323 *aHeight = u32Height;
6324
6325 freeSavedDisplayScreenshot(pu8Data);
6326
6327 return S_OK;
6328}
6329
6330HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6331 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6332{
6333 if (aScreenId != 0)
6334 return E_NOTIMPL;
6335
6336 if ( aBitmapFormat != BitmapFormat_BGR0
6337 && aBitmapFormat != BitmapFormat_BGRA
6338 && aBitmapFormat != BitmapFormat_RGBA
6339 && aBitmapFormat != BitmapFormat_PNG)
6340 return setError(E_NOTIMPL,
6341 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6342
6343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6344
6345 uint8_t *pu8Data = NULL;
6346 uint32_t cbData = 0;
6347 uint32_t u32Width = 0;
6348 uint32_t u32Height = 0;
6349
6350 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6351
6352 if (RT_FAILURE(vrc))
6353 return setError(VBOX_E_IPRT_ERROR,
6354 tr("Saved thumbnail data is not available (%Rrc)"),
6355 vrc);
6356
6357 HRESULT hr = S_OK;
6358
6359 *aWidth = u32Width;
6360 *aHeight = u32Height;
6361
6362 if (cbData > 0)
6363 {
6364 /* Convert pixels to the format expected by the API caller. */
6365 if (aBitmapFormat == BitmapFormat_BGR0)
6366 {
6367 /* [0] B, [1] G, [2] R, [3] 0. */
6368 aData.resize(cbData);
6369 memcpy(&aData.front(), pu8Data, cbData);
6370 }
6371 else if (aBitmapFormat == BitmapFormat_BGRA)
6372 {
6373 /* [0] B, [1] G, [2] R, [3] A. */
6374 aData.resize(cbData);
6375 for (uint32_t i = 0; i < cbData; i += 4)
6376 {
6377 aData[i] = pu8Data[i];
6378 aData[i + 1] = pu8Data[i + 1];
6379 aData[i + 2] = pu8Data[i + 2];
6380 aData[i + 3] = 0xff;
6381 }
6382 }
6383 else if (aBitmapFormat == BitmapFormat_RGBA)
6384 {
6385 /* [0] R, [1] G, [2] B, [3] A. */
6386 aData.resize(cbData);
6387 for (uint32_t i = 0; i < cbData; i += 4)
6388 {
6389 aData[i] = pu8Data[i + 2];
6390 aData[i + 1] = pu8Data[i + 1];
6391 aData[i + 2] = pu8Data[i];
6392 aData[i + 3] = 0xff;
6393 }
6394 }
6395 else if (aBitmapFormat == BitmapFormat_PNG)
6396 {
6397 uint8_t *pu8PNG = NULL;
6398 uint32_t cbPNG = 0;
6399 uint32_t cxPNG = 0;
6400 uint32_t cyPNG = 0;
6401
6402 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6403
6404 if (RT_SUCCESS(vrc))
6405 {
6406 aData.resize(cbPNG);
6407 if (cbPNG)
6408 memcpy(&aData.front(), pu8PNG, cbPNG);
6409 }
6410 else
6411 hr = setError(VBOX_E_IPRT_ERROR,
6412 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6413 vrc);
6414
6415 RTMemFree(pu8PNG);
6416 }
6417 }
6418
6419 freeSavedDisplayScreenshot(pu8Data);
6420
6421 return hr;
6422}
6423
6424HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6425{
6426 if (aScreenId != 0)
6427 return E_NOTIMPL;
6428
6429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6430
6431 uint8_t *pu8Data = NULL;
6432 uint32_t cbData = 0;
6433 uint32_t u32Width = 0;
6434 uint32_t u32Height = 0;
6435
6436 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6437
6438 if (RT_FAILURE(vrc))
6439 return setError(VBOX_E_IPRT_ERROR,
6440 tr("Saved screenshot data is not available (%Rrc)"),
6441 vrc);
6442
6443 *aSize = cbData;
6444 *aWidth = u32Width;
6445 *aHeight = u32Height;
6446
6447 freeSavedDisplayScreenshot(pu8Data);
6448
6449 return S_OK;
6450}
6451
6452HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6453{
6454 if (aScreenId != 0)
6455 return E_NOTIMPL;
6456
6457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6458
6459 uint8_t *pu8Data = NULL;
6460 uint32_t cbData = 0;
6461 uint32_t u32Width = 0;
6462 uint32_t u32Height = 0;
6463
6464 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6465
6466 if (RT_FAILURE(vrc))
6467 return setError(VBOX_E_IPRT_ERROR,
6468 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6469 vrc);
6470
6471 *aWidth = u32Width;
6472 *aHeight = u32Height;
6473
6474 aData.resize(cbData);
6475 if (cbData)
6476 memcpy(&aData.front(), pu8Data, cbData);
6477
6478 freeSavedDisplayScreenshot(pu8Data);
6479
6480 return S_OK;
6481}
6482
6483HRESULT Machine::hotPlugCPU(ULONG aCpu)
6484{
6485 HRESULT rc = S_OK;
6486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6487
6488 if (!mHWData->mCPUHotPlugEnabled)
6489 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6490
6491 if (aCpu >= mHWData->mCPUCount)
6492 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6493
6494 if (mHWData->mCPUAttached[aCpu])
6495 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6496
6497 alock.release();
6498 rc = i_onCPUChange(aCpu, false);
6499 alock.acquire();
6500 if (FAILED(rc)) return rc;
6501
6502 i_setModified(IsModified_MachineData);
6503 mHWData.backup();
6504 mHWData->mCPUAttached[aCpu] = true;
6505
6506 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6507 if (Global::IsOnline(mData->mMachineState))
6508 i_saveSettings(NULL);
6509
6510 return S_OK;
6511}
6512
6513HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6514{
6515 HRESULT rc = S_OK;
6516
6517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6518
6519 if (!mHWData->mCPUHotPlugEnabled)
6520 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6521
6522 if (aCpu >= SchemaDefs::MaxCPUCount)
6523 return setError(E_INVALIDARG,
6524 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6525 SchemaDefs::MaxCPUCount);
6526
6527 if (!mHWData->mCPUAttached[aCpu])
6528 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6529
6530 /* CPU 0 can't be detached */
6531 if (aCpu == 0)
6532 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6533
6534 alock.release();
6535 rc = i_onCPUChange(aCpu, true);
6536 alock.acquire();
6537 if (FAILED(rc)) return rc;
6538
6539 i_setModified(IsModified_MachineData);
6540 mHWData.backup();
6541 mHWData->mCPUAttached[aCpu] = false;
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::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6551{
6552 *aAttached = false;
6553
6554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 /* If hotplug is enabled the CPU is always enabled. */
6557 if (!mHWData->mCPUHotPlugEnabled)
6558 {
6559 if (aCpu < mHWData->mCPUCount)
6560 *aAttached = true;
6561 }
6562 else
6563 {
6564 if (aCpu < SchemaDefs::MaxCPUCount)
6565 *aAttached = mHWData->mCPUAttached[aCpu];
6566 }
6567
6568 return S_OK;
6569}
6570
6571HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6572{
6573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6574
6575 Utf8Str log = i_queryLogFilename(aIdx);
6576 if (!RTFileExists(log.c_str()))
6577 log.setNull();
6578 aFilename = log;
6579
6580 return S_OK;
6581}
6582
6583HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6584{
6585 if (aSize < 0)
6586 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6587
6588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6589
6590 HRESULT rc = S_OK;
6591 Utf8Str log = i_queryLogFilename(aIdx);
6592
6593 /* do not unnecessarily hold the lock while doing something which does
6594 * not need the lock and potentially takes a long time. */
6595 alock.release();
6596
6597 /* Limit the chunk size to 32K for now, as that gives better performance
6598 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6599 * One byte expands to approx. 25 bytes of breathtaking XML. */
6600 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6601 aData.resize(cbData);
6602
6603 RTFILE LogFile;
6604 int vrc = RTFileOpen(&LogFile, log.c_str(),
6605 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6606 if (RT_SUCCESS(vrc))
6607 {
6608 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6609 if (RT_SUCCESS(vrc))
6610 aData.resize(cbData);
6611 else
6612 rc = setError(VBOX_E_IPRT_ERROR,
6613 tr("Could not read log file '%s' (%Rrc)"),
6614 log.c_str(), vrc);
6615 RTFileClose(LogFile);
6616 }
6617 else
6618 rc = setError(VBOX_E_IPRT_ERROR,
6619 tr("Could not open log file '%s' (%Rrc)"),
6620 log.c_str(), vrc);
6621
6622 if (FAILED(rc))
6623 aData.resize(0);
6624
6625 return rc;
6626}
6627
6628
6629/**
6630 * Currently this method doesn't attach device to the running VM,
6631 * just makes sure it's plugged on next VM start.
6632 */
6633HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6634{
6635 // lock scope
6636 {
6637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6638
6639 HRESULT rc = i_checkStateDependency(MutableStateDep);
6640 if (FAILED(rc)) return rc;
6641
6642 ChipsetType_T aChipset = ChipsetType_PIIX3;
6643 COMGETTER(ChipsetType)(&aChipset);
6644
6645 if (aChipset != ChipsetType_ICH9)
6646 {
6647 return setError(E_INVALIDARG,
6648 tr("Host PCI attachment only supported with ICH9 chipset"));
6649 }
6650
6651 // check if device with this host PCI address already attached
6652 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6653 it != mHWData->mPCIDeviceAssignments.end();
6654 ++it)
6655 {
6656 LONG iHostAddress = -1;
6657 ComPtr<PCIDeviceAttachment> pAttach;
6658 pAttach = *it;
6659 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6660 if (iHostAddress == aHostAddress)
6661 return setError(E_INVALIDARG,
6662 tr("Device with host PCI address already attached to this VM"));
6663 }
6664
6665 ComObjPtr<PCIDeviceAttachment> pda;
6666 char name[32];
6667
6668 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6669 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6670 Bstr bname(name);
6671 pda.createObject();
6672 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6673 i_setModified(IsModified_MachineData);
6674 mHWData.backup();
6675 mHWData->mPCIDeviceAssignments.push_back(pda);
6676 }
6677
6678 return S_OK;
6679}
6680
6681/**
6682 * Currently this method doesn't detach device from the running VM,
6683 * just makes sure it's not plugged on next VM start.
6684 */
6685HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6686{
6687 ComObjPtr<PCIDeviceAttachment> pAttach;
6688 bool fRemoved = false;
6689 HRESULT rc;
6690
6691 // lock scope
6692 {
6693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6694
6695 rc = i_checkStateDependency(MutableStateDep);
6696 if (FAILED(rc)) return rc;
6697
6698 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6699 it != mHWData->mPCIDeviceAssignments.end();
6700 ++it)
6701 {
6702 LONG iHostAddress = -1;
6703 pAttach = *it;
6704 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6705 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6706 {
6707 i_setModified(IsModified_MachineData);
6708 mHWData.backup();
6709 mHWData->mPCIDeviceAssignments.remove(pAttach);
6710 fRemoved = true;
6711 break;
6712 }
6713 }
6714 }
6715
6716
6717 /* Fire event outside of the lock */
6718 if (fRemoved)
6719 {
6720 Assert(!pAttach.isNull());
6721 ComPtr<IEventSource> es;
6722 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6723 Assert(SUCCEEDED(rc));
6724 Bstr mid;
6725 rc = this->COMGETTER(Id)(mid.asOutParam());
6726 Assert(SUCCEEDED(rc));
6727 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6728 }
6729
6730 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6731 tr("No host PCI device %08x attached"),
6732 aHostAddress
6733 );
6734}
6735
6736HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6737{
6738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6739
6740 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6741
6742 size_t i = 0;
6743 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6744 it != mHWData->mPCIDeviceAssignments.end();
6745 ++i, ++it)
6746 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6747
6748 return S_OK;
6749}
6750
6751HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6752{
6753 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6754
6755 return S_OK;
6756}
6757
6758HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6759{
6760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6761
6762 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6763
6764 return S_OK;
6765}
6766
6767HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6768{
6769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6770 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6771 if (SUCCEEDED(hrc))
6772 {
6773 hrc = mHWData.backupEx();
6774 if (SUCCEEDED(hrc))
6775 {
6776 i_setModified(IsModified_MachineData);
6777 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6778 }
6779 }
6780 return hrc;
6781}
6782
6783HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6784{
6785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6786 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6787 return S_OK;
6788}
6789
6790HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6791{
6792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6793 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6794 if (SUCCEEDED(hrc))
6795 {
6796 hrc = mHWData.backupEx();
6797 if (SUCCEEDED(hrc))
6798 {
6799 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6800 if (SUCCEEDED(hrc))
6801 i_setModified(IsModified_MachineData);
6802 }
6803 }
6804 return hrc;
6805}
6806
6807HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6808{
6809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6810
6811 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6812
6813 return S_OK;
6814}
6815
6816HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6817{
6818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6819 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6820 if (SUCCEEDED(hrc))
6821 {
6822 hrc = mHWData.backupEx();
6823 if (SUCCEEDED(hrc))
6824 {
6825 i_setModified(IsModified_MachineData);
6826 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6827 }
6828 }
6829 return hrc;
6830}
6831
6832HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6833{
6834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6835
6836 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6837
6838 return S_OK;
6839}
6840
6841HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6842{
6843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6844
6845 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6846 if ( SUCCEEDED(hrc)
6847 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6848 {
6849 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6850 int vrc;
6851
6852 if (aAutostartEnabled)
6853 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6854 else
6855 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6856
6857 if (RT_SUCCESS(vrc))
6858 {
6859 hrc = mHWData.backupEx();
6860 if (SUCCEEDED(hrc))
6861 {
6862 i_setModified(IsModified_MachineData);
6863 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6864 }
6865 }
6866 else if (vrc == VERR_NOT_SUPPORTED)
6867 hrc = setError(VBOX_E_NOT_SUPPORTED,
6868 tr("The VM autostart feature is not supported on this platform"));
6869 else if (vrc == VERR_PATH_NOT_FOUND)
6870 hrc = setError(E_FAIL,
6871 tr("The path to the autostart database is not set"));
6872 else
6873 hrc = setError(E_UNEXPECTED,
6874 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6875 aAutostartEnabled ? "Adding" : "Removing",
6876 mUserData->s.strName.c_str(), vrc);
6877 }
6878 return hrc;
6879}
6880
6881HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6882{
6883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6884
6885 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6886
6887 return S_OK;
6888}
6889
6890HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6891{
6892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6893 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6894 if (SUCCEEDED(hrc))
6895 {
6896 hrc = mHWData.backupEx();
6897 if (SUCCEEDED(hrc))
6898 {
6899 i_setModified(IsModified_MachineData);
6900 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6901 }
6902 }
6903 return hrc;
6904}
6905
6906HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6907{
6908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6909
6910 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6911
6912 return S_OK;
6913}
6914
6915HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6916{
6917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6918 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6919 if ( SUCCEEDED(hrc)
6920 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6921 {
6922 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6923 int vrc;
6924
6925 if (aAutostopType != AutostopType_Disabled)
6926 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6927 else
6928 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6929
6930 if (RT_SUCCESS(vrc))
6931 {
6932 hrc = mHWData.backupEx();
6933 if (SUCCEEDED(hrc))
6934 {
6935 i_setModified(IsModified_MachineData);
6936 mHWData->mAutostart.enmAutostopType = aAutostopType;
6937 }
6938 }
6939 else if (vrc == VERR_NOT_SUPPORTED)
6940 hrc = setError(VBOX_E_NOT_SUPPORTED,
6941 tr("The VM autostop feature is not supported on this platform"));
6942 else if (vrc == VERR_PATH_NOT_FOUND)
6943 hrc = setError(E_FAIL,
6944 tr("The path to the autostart database is not set"));
6945 else
6946 hrc = setError(E_UNEXPECTED,
6947 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6948 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6949 mUserData->s.strName.c_str(), vrc);
6950 }
6951 return hrc;
6952}
6953
6954HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6955{
6956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6957
6958 aDefaultFrontend = mHWData->mDefaultFrontend;
6959
6960 return S_OK;
6961}
6962
6963HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6964{
6965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6966 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6967 if (SUCCEEDED(hrc))
6968 {
6969 hrc = mHWData.backupEx();
6970 if (SUCCEEDED(hrc))
6971 {
6972 i_setModified(IsModified_MachineData);
6973 mHWData->mDefaultFrontend = aDefaultFrontend;
6974 }
6975 }
6976 return hrc;
6977}
6978
6979HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6980{
6981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6982 size_t cbIcon = mUserData->mIcon.size();
6983 aIcon.resize(cbIcon);
6984 if (cbIcon)
6985 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
6986 return S_OK;
6987}
6988
6989HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6990{
6991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6992 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6993 if (SUCCEEDED(hrc))
6994 {
6995 i_setModified(IsModified_MachineData);
6996 mUserData.backup();
6997 size_t cbIcon = aIcon.size();
6998 mUserData->mIcon.resize(cbIcon);
6999 if (cbIcon)
7000 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7001 }
7002 return hrc;
7003}
7004
7005HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7006{
7007#ifdef VBOX_WITH_USB
7008 *aUSBProxyAvailable = true;
7009#else
7010 *aUSBProxyAvailable = false;
7011#endif
7012 return S_OK;
7013}
7014
7015HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7016 ComPtr<IProgress> &aProgress)
7017{
7018 ComObjPtr<Progress> pP;
7019 Progress *ppP = pP;
7020 IProgress *iP = static_cast<IProgress *>(ppP);
7021 IProgress **pProgress = &iP;
7022
7023 IMachine *pTarget = aTarget;
7024
7025 /* Convert the options. */
7026 RTCList<CloneOptions_T> optList;
7027 if (aOptions.size())
7028 for (size_t i = 0; i < aOptions.size(); ++i)
7029 optList.append(aOptions[i]);
7030
7031 if (optList.contains(CloneOptions_Link))
7032 {
7033 if (!i_isSnapshotMachine())
7034 return setError(E_INVALIDARG,
7035 tr("Linked clone can only be created from a snapshot"));
7036 if (aMode != CloneMode_MachineState)
7037 return setError(E_INVALIDARG,
7038 tr("Linked clone can only be created for a single machine state"));
7039 }
7040 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7041
7042 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7043
7044 HRESULT rc = pWorker->start(pProgress);
7045
7046 pP = static_cast<Progress *>(*pProgress);
7047 pP.queryInterfaceTo(aProgress.asOutParam());
7048
7049 return rc;
7050
7051}
7052
7053// public methods for internal purposes
7054/////////////////////////////////////////////////////////////////////////////
7055
7056/**
7057 * Adds the given IsModified_* flag to the dirty flags of the machine.
7058 * This must be called either during i_loadSettings or under the machine write lock.
7059 * @param fl
7060 */
7061void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7062{
7063 mData->flModifications |= fl;
7064 if (fAllowStateModification && i_isStateModificationAllowed())
7065 mData->mCurrentStateModified = true;
7066}
7067
7068/**
7069 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7070 * care of the write locking.
7071 *
7072 * @param fModifications The flag to add.
7073 */
7074void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7075{
7076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7077 i_setModified(fModification, fAllowStateModification);
7078}
7079
7080/**
7081 * Saves the registry entry of this machine to the given configuration node.
7082 *
7083 * @param aEntryNode Node to save the registry entry to.
7084 *
7085 * @note locks this object for reading.
7086 */
7087HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7088{
7089 AutoLimitedCaller autoCaller(this);
7090 AssertComRCReturnRC(autoCaller.rc());
7091
7092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7093
7094 data.uuid = mData->mUuid;
7095 data.strSettingsFile = mData->m_strConfigFile;
7096
7097 return S_OK;
7098}
7099
7100/**
7101 * Calculates the absolute path of the given path taking the directory of the
7102 * machine settings file as the current directory.
7103 *
7104 * @param aPath Path to calculate the absolute path for.
7105 * @param aResult Where to put the result (used only on success, can be the
7106 * same Utf8Str instance as passed in @a aPath).
7107 * @return IPRT result.
7108 *
7109 * @note Locks this object for reading.
7110 */
7111int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7112{
7113 AutoCaller autoCaller(this);
7114 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7115
7116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7117
7118 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7119
7120 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7121
7122 strSettingsDir.stripFilename();
7123 char folder[RTPATH_MAX];
7124 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7125 if (RT_SUCCESS(vrc))
7126 aResult = folder;
7127
7128 return vrc;
7129}
7130
7131/**
7132 * Copies strSource to strTarget, making it relative to the machine folder
7133 * if it is a subdirectory thereof, or simply copying it otherwise.
7134 *
7135 * @param strSource Path to evaluate and copy.
7136 * @param strTarget Buffer to receive target path.
7137 *
7138 * @note Locks this object for reading.
7139 */
7140void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7141 Utf8Str &strTarget)
7142{
7143 AutoCaller autoCaller(this);
7144 AssertComRCReturn(autoCaller.rc(), (void)0);
7145
7146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7147
7148 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7149 // use strTarget as a temporary buffer to hold the machine settings dir
7150 strTarget = mData->m_strConfigFileFull;
7151 strTarget.stripFilename();
7152 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7153 {
7154 // is relative: then append what's left
7155 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7156 // for empty paths (only possible for subdirs) use "." to avoid
7157 // triggering default settings for not present config attributes.
7158 if (strTarget.isEmpty())
7159 strTarget = ".";
7160 }
7161 else
7162 // is not relative: then overwrite
7163 strTarget = strSource;
7164}
7165
7166/**
7167 * Returns the full path to the machine's log folder in the
7168 * \a aLogFolder argument.
7169 */
7170void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7171{
7172 AutoCaller autoCaller(this);
7173 AssertComRCReturnVoid(autoCaller.rc());
7174
7175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7176
7177 char szTmp[RTPATH_MAX];
7178 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7179 if (RT_SUCCESS(vrc))
7180 {
7181 if (szTmp[0] && !mUserData.isNull())
7182 {
7183 char szTmp2[RTPATH_MAX];
7184 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7185 if (RT_SUCCESS(vrc))
7186 aLogFolder = BstrFmt("%s%c%s",
7187 szTmp2,
7188 RTPATH_DELIMITER,
7189 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7190 }
7191 else
7192 vrc = VERR_PATH_IS_RELATIVE;
7193 }
7194
7195 if (RT_FAILURE(vrc))
7196 {
7197 // fallback if VBOX_USER_LOGHOME is not set or invalid
7198 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7199 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7200 aLogFolder.append(RTPATH_DELIMITER);
7201 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7202 }
7203}
7204
7205/**
7206 * Returns the full path to the machine's log file for an given index.
7207 */
7208Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7209 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7210{
7211 Utf8Str logFolder;
7212 getLogFolder(logFolder);
7213 Assert(logFolder.length());
7214 Utf8Str log;
7215 if (idx == 0)
7216 log = Utf8StrFmt("%s%cVBox.log",
7217 logFolder.c_str(), RTPATH_DELIMITER);
7218 else
7219 log = Utf8StrFmt("%s%cVBox.log.%d",
7220 logFolder.c_str(), RTPATH_DELIMITER, idx);
7221 return log;
7222}
7223
7224/**
7225 * Returns the full path to the machine's (hardened) startup log file.
7226 */
7227Utf8Str Machine::i_getStartupLogFilename(void)
7228{
7229 Utf8Str strFilename;
7230 getLogFolder(strFilename);
7231 Assert(strFilename.length());
7232 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7233 return strFilename;
7234}
7235
7236
7237/**
7238 * Composes a unique saved state filename based on the current system time. The filename is
7239 * granular to the second so this will work so long as no more than one snapshot is taken on
7240 * a machine per second.
7241 *
7242 * Before version 4.1, we used this formula for saved state files:
7243 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7244 * which no longer works because saved state files can now be shared between the saved state of the
7245 * "saved" machine and an online snapshot, and the following would cause problems:
7246 * 1) save machine
7247 * 2) create online snapshot from that machine state --> reusing saved state file
7248 * 3) save machine again --> filename would be reused, breaking the online snapshot
7249 *
7250 * So instead we now use a timestamp.
7251 *
7252 * @param str
7253 */
7254
7255void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7256{
7257 AutoCaller autoCaller(this);
7258 AssertComRCReturnVoid(autoCaller.rc());
7259
7260 {
7261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7262 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7263 }
7264
7265 RTTIMESPEC ts;
7266 RTTimeNow(&ts);
7267 RTTIME time;
7268 RTTimeExplode(&time, &ts);
7269
7270 strStateFilePath += RTPATH_DELIMITER;
7271 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7272 time.i32Year, time.u8Month, time.u8MonthDay,
7273 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7274}
7275
7276/**
7277 * Returns the full path to the default video capture file.
7278 */
7279void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7280{
7281 AutoCaller autoCaller(this);
7282 AssertComRCReturnVoid(autoCaller.rc());
7283
7284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7285
7286 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7287 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7288 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7289}
7290
7291/**
7292 * Returns whether at least one USB controller is present for the VM.
7293 */
7294bool Machine::i_isUSBControllerPresent()
7295{
7296 AutoCaller autoCaller(this);
7297 AssertComRCReturn(autoCaller.rc(), false);
7298
7299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7300
7301 return (mUSBControllers->size() > 0);
7302}
7303
7304/**
7305 * @note Locks this object for writing, calls the client process
7306 * (inside the lock).
7307 */
7308HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7309 const Utf8Str &strFrontend,
7310 const Utf8Str &strEnvironment,
7311 ProgressProxy *aProgress)
7312{
7313 LogFlowThisFuncEnter();
7314
7315 AssertReturn(aControl, E_FAIL);
7316 AssertReturn(aProgress, E_FAIL);
7317 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7318
7319 AutoCaller autoCaller(this);
7320 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7321
7322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7323
7324 if (!mData->mRegistered)
7325 return setError(E_UNEXPECTED,
7326 tr("The machine '%s' is not registered"),
7327 mUserData->s.strName.c_str());
7328
7329 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7330
7331 if ( mData->mSession.mState == SessionState_Locked
7332 || mData->mSession.mState == SessionState_Spawning
7333 || mData->mSession.mState == SessionState_Unlocking)
7334 return setError(VBOX_E_INVALID_OBJECT_STATE,
7335 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7336 mUserData->s.strName.c_str());
7337
7338 /* may not be busy */
7339 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7340
7341 /* get the path to the executable */
7342 char szPath[RTPATH_MAX];
7343 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7344 size_t cchBufLeft = strlen(szPath);
7345 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7346 szPath[cchBufLeft] = 0;
7347 char *pszNamePart = szPath + cchBufLeft;
7348 cchBufLeft = sizeof(szPath) - cchBufLeft;
7349
7350 int vrc = VINF_SUCCESS;
7351 RTPROCESS pid = NIL_RTPROCESS;
7352
7353 RTENV env = RTENV_DEFAULT;
7354
7355 if (!strEnvironment.isEmpty())
7356 {
7357 char *newEnvStr = NULL;
7358
7359 do
7360 {
7361 /* clone the current environment */
7362 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7363 AssertRCBreakStmt(vrc2, vrc = vrc2);
7364
7365 newEnvStr = RTStrDup(strEnvironment.c_str());
7366 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7367
7368 /* put new variables to the environment
7369 * (ignore empty variable names here since RTEnv API
7370 * intentionally doesn't do that) */
7371 char *var = newEnvStr;
7372 for (char *p = newEnvStr; *p; ++p)
7373 {
7374 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7375 {
7376 *p = '\0';
7377 if (*var)
7378 {
7379 char *val = strchr(var, '=');
7380 if (val)
7381 {
7382 *val++ = '\0';
7383 vrc2 = RTEnvSetEx(env, var, val);
7384 }
7385 else
7386 vrc2 = RTEnvUnsetEx(env, var);
7387 if (RT_FAILURE(vrc2))
7388 break;
7389 }
7390 var = p + 1;
7391 }
7392 }
7393 if (RT_SUCCESS(vrc2) && *var)
7394 vrc2 = RTEnvPutEx(env, var);
7395
7396 AssertRCBreakStmt(vrc2, vrc = vrc2);
7397 }
7398 while (0);
7399
7400 if (newEnvStr != NULL)
7401 RTStrFree(newEnvStr);
7402 }
7403
7404 /* Hardened startup logging */
7405#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7406 Utf8Str strSupStartLogArg("--sup-startup-log=");
7407 {
7408 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7409 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7410 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7411 {
7412 Utf8Str strStartupLogDir = strStartupLogFile;
7413 strStartupLogDir.stripFilename();
7414 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7415 file without stripping the file. */
7416 }
7417 strSupStartLogArg.append(strStartupLogFile);
7418 }
7419 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7420#else
7421 const char *pszSupStartupLogArg = NULL;
7422#endif
7423
7424
7425#ifdef VBOX_WITH_QTGUI
7426 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7427 {
7428# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7429 /* Modify the base path so that we don't need to use ".." below. */
7430 RTPathStripTrailingSlash(szPath);
7431 RTPathStripFilename(szPath);
7432 cchBufLeft = strlen(szPath);
7433 pszNamePart = szPath + cchBufLeft;
7434 cchBufLeft = sizeof(szPath) - cchBufLeft;
7435
7436# define OSX_APP_NAME "VirtualBoxVM"
7437# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7438
7439 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7440 if ( strAppOverride.contains(".")
7441 || strAppOverride.contains("/")
7442 || strAppOverride.contains("\\")
7443 || strAppOverride.contains(":"))
7444 strAppOverride.setNull();
7445 Utf8Str strAppPath;
7446 if (!strAppOverride.isEmpty())
7447 {
7448 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7449 Utf8Str strFullPath(szPath);
7450 strFullPath.append(strAppPath);
7451 /* there is a race, but people using this deserve the failure */
7452 if (!RTFileExists(strFullPath.c_str()))
7453 strAppOverride.setNull();
7454 }
7455 if (strAppOverride.isEmpty())
7456 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7457 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7458 strcpy(pszNamePart, strAppPath.c_str());
7459# else
7460 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7461 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7462 strcpy(pszNamePart, s_szVirtualBox_exe);
7463# endif
7464
7465 Utf8Str idStr = mData->mUuid.toString();
7466 const char *apszArgs[] =
7467 {
7468 szPath,
7469 "--comment", mUserData->s.strName.c_str(),
7470 "--startvm", idStr.c_str(),
7471 "--no-startvm-errormsgbox",
7472 pszSupStartupLogArg,
7473 NULL
7474 };
7475 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7476 }
7477#else /* !VBOX_WITH_QTGUI */
7478 if (0)
7479 ;
7480#endif /* VBOX_WITH_QTGUI */
7481
7482 else
7483
7484#ifdef VBOX_WITH_VBOXSDL
7485 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7486 {
7487 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7488 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7489 strcpy(pszNamePart, s_szVBoxSDL_exe);
7490
7491 Utf8Str idStr = mData->mUuid.toString();
7492 const char *apszArgs[] =
7493 {
7494 szPath,
7495 "--comment", mUserData->s.strName.c_str(),
7496 "--startvm", idStr.c_str(),
7497 pszSupStartupLogArg,
7498 NULL
7499 };
7500 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7501 }
7502#else /* !VBOX_WITH_VBOXSDL */
7503 if (0)
7504 ;
7505#endif /* !VBOX_WITH_VBOXSDL */
7506
7507 else
7508
7509#ifdef VBOX_WITH_HEADLESS
7510 if ( strFrontend == "headless"
7511 || strFrontend == "capture"
7512 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7513 )
7514 {
7515 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7516 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7517 * and a VM works even if the server has not been installed.
7518 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7519 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7520 * differently in 4.0 and 3.x.
7521 */
7522 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7523 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7524 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7525
7526 Utf8Str idStr = mData->mUuid.toString();
7527 const char *apszArgs[] =
7528 {
7529 szPath,
7530 "--comment", mUserData->s.strName.c_str(),
7531 "--startvm", idStr.c_str(),
7532 "--vrde", "config",
7533 0, /* For "--capture". */
7534 0, /* For "--sup-startup-log". */
7535 0
7536 };
7537 unsigned iArg = 7;
7538 if (strFrontend == "capture")
7539 apszArgs[iArg++] = "--capture";
7540 apszArgs[iArg++] = pszSupStartupLogArg;
7541
7542# ifdef RT_OS_WINDOWS
7543 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7544# else
7545 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7546# endif
7547 }
7548#else /* !VBOX_WITH_HEADLESS */
7549 if (0)
7550 ;
7551#endif /* !VBOX_WITH_HEADLESS */
7552 else
7553 {
7554 RTEnvDestroy(env);
7555 return setError(E_INVALIDARG,
7556 tr("Invalid frontend name: '%s'"),
7557 strFrontend.c_str());
7558 }
7559
7560 RTEnvDestroy(env);
7561
7562 if (RT_FAILURE(vrc))
7563 return setError(VBOX_E_IPRT_ERROR,
7564 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7565 mUserData->s.strName.c_str(), vrc);
7566
7567 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7568
7569 /*
7570 * Note that we don't release the lock here before calling the client,
7571 * because it doesn't need to call us back if called with a NULL argument.
7572 * Releasing the lock here is dangerous because we didn't prepare the
7573 * launch data yet, but the client we've just started may happen to be
7574 * too fast and call LockMachine() that will fail (because of PID, etc.),
7575 * so that the Machine will never get out of the Spawning session state.
7576 */
7577
7578 /* inform the session that it will be a remote one */
7579 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7580#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7581 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7582#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7583 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7584#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7585 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7586
7587 if (FAILED(rc))
7588 {
7589 /* restore the session state */
7590 mData->mSession.mState = SessionState_Unlocked;
7591 alock.release();
7592 mParent->i_addProcessToReap(pid);
7593 /* The failure may occur w/o any error info (from RPC), so provide one */
7594 return setError(VBOX_E_VM_ERROR,
7595 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7596 }
7597
7598 /* attach launch data to the machine */
7599 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7600 mData->mSession.mRemoteControls.push_back(aControl);
7601 mData->mSession.mProgress = aProgress;
7602 mData->mSession.mPID = pid;
7603 mData->mSession.mState = SessionState_Spawning;
7604 mData->mSession.mType = strFrontend;
7605
7606 alock.release();
7607 mParent->i_addProcessToReap(pid);
7608
7609 LogFlowThisFuncLeave();
7610 return S_OK;
7611}
7612
7613/**
7614 * Returns @c true if the given session machine instance has an open direct
7615 * session (and optionally also for direct sessions which are closing) and
7616 * returns the session control machine instance if so.
7617 *
7618 * Note that when the method returns @c false, the arguments remain unchanged.
7619 *
7620 * @param aMachine Session machine object.
7621 * @param aControl Direct session control object (optional).
7622 * @param aAllowClosing If true then additionally a session which is currently
7623 * being closed will also be allowed.
7624 *
7625 * @note locks this object for reading.
7626 */
7627bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7628 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7629 bool aAllowClosing /*= false*/)
7630{
7631 AutoLimitedCaller autoCaller(this);
7632 AssertComRCReturn(autoCaller.rc(), false);
7633
7634 /* just return false for inaccessible machines */
7635 if (getObjectState().getState() != ObjectState::Ready)
7636 return false;
7637
7638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7639
7640 if ( mData->mSession.mState == SessionState_Locked
7641 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7642 )
7643 {
7644 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7645
7646 aMachine = mData->mSession.mMachine;
7647
7648 if (aControl != NULL)
7649 *aControl = mData->mSession.mDirectControl;
7650
7651 return true;
7652 }
7653
7654 return false;
7655}
7656
7657/**
7658 * Returns @c true if the given machine has an spawning direct session.
7659 *
7660 * @note locks this object for reading.
7661 */
7662bool Machine::i_isSessionSpawning()
7663{
7664 AutoLimitedCaller autoCaller(this);
7665 AssertComRCReturn(autoCaller.rc(), false);
7666
7667 /* just return false for inaccessible machines */
7668 if (getObjectState().getState() != ObjectState::Ready)
7669 return false;
7670
7671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7672
7673 if (mData->mSession.mState == SessionState_Spawning)
7674 return true;
7675
7676 return false;
7677}
7678
7679/**
7680 * Called from the client watcher thread to check for unexpected client process
7681 * death during Session_Spawning state (e.g. before it successfully opened a
7682 * direct session).
7683 *
7684 * On Win32 and on OS/2, this method is called only when we've got the
7685 * direct client's process termination notification, so it always returns @c
7686 * true.
7687 *
7688 * On other platforms, this method returns @c true if the client process is
7689 * terminated and @c false if it's still alive.
7690 *
7691 * @note Locks this object for writing.
7692 */
7693bool Machine::i_checkForSpawnFailure()
7694{
7695 AutoCaller autoCaller(this);
7696 if (!autoCaller.isOk())
7697 {
7698 /* nothing to do */
7699 LogFlowThisFunc(("Already uninitialized!\n"));
7700 return true;
7701 }
7702
7703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7704
7705 if (mData->mSession.mState != SessionState_Spawning)
7706 {
7707 /* nothing to do */
7708 LogFlowThisFunc(("Not spawning any more!\n"));
7709 return true;
7710 }
7711
7712 HRESULT rc = S_OK;
7713
7714 /* PID not yet initialized, skip check. */
7715 if (mData->mSession.mPID == NIL_RTPROCESS)
7716 return false;
7717
7718 RTPROCSTATUS status;
7719 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7720
7721 if (vrc != VERR_PROCESS_RUNNING)
7722 {
7723 Utf8Str strExtraInfo;
7724
7725#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7726 /* If the startup logfile exists and is of non-zero length, tell the
7727 user to look there for more details to encourage them to attach it
7728 when reporting startup issues. */
7729 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7730 uint64_t cbStartupLogFile = 0;
7731 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7732 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7733 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7734#endif
7735
7736 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7737 rc = setError(E_FAIL,
7738 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7739 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7740 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7741 rc = setError(E_FAIL,
7742 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7743 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7744 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7745 rc = setError(E_FAIL,
7746 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7747 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7748 else
7749 rc = setError(E_FAIL,
7750 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7751 i_getName().c_str(), vrc, strExtraInfo.c_str());
7752 }
7753
7754 if (FAILED(rc))
7755 {
7756 /* Close the remote session, remove the remote control from the list
7757 * and reset session state to Closed (@note keep the code in sync with
7758 * the relevant part in LockMachine()). */
7759
7760 Assert(mData->mSession.mRemoteControls.size() == 1);
7761 if (mData->mSession.mRemoteControls.size() == 1)
7762 {
7763 ErrorInfoKeeper eik;
7764 mData->mSession.mRemoteControls.front()->Uninitialize();
7765 }
7766
7767 mData->mSession.mRemoteControls.clear();
7768 mData->mSession.mState = SessionState_Unlocked;
7769
7770 /* finalize the progress after setting the state */
7771 if (!mData->mSession.mProgress.isNull())
7772 {
7773 mData->mSession.mProgress->notifyComplete(rc);
7774 mData->mSession.mProgress.setNull();
7775 }
7776
7777 mData->mSession.mPID = NIL_RTPROCESS;
7778
7779 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7780 return true;
7781 }
7782
7783 return false;
7784}
7785
7786/**
7787 * Checks whether the machine can be registered. If so, commits and saves
7788 * all settings.
7789 *
7790 * @note Must be called from mParent's write lock. Locks this object and
7791 * children for writing.
7792 */
7793HRESULT Machine::i_prepareRegister()
7794{
7795 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7796
7797 AutoLimitedCaller autoCaller(this);
7798 AssertComRCReturnRC(autoCaller.rc());
7799
7800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7801
7802 /* wait for state dependents to drop to zero */
7803 i_ensureNoStateDependencies();
7804
7805 if (!mData->mAccessible)
7806 return setError(VBOX_E_INVALID_OBJECT_STATE,
7807 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7808 mUserData->s.strName.c_str(),
7809 mData->mUuid.toString().c_str());
7810
7811 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7812
7813 if (mData->mRegistered)
7814 return setError(VBOX_E_INVALID_OBJECT_STATE,
7815 tr("The machine '%s' with UUID {%s} is already registered"),
7816 mUserData->s.strName.c_str(),
7817 mData->mUuid.toString().c_str());
7818
7819 HRESULT rc = S_OK;
7820
7821 // Ensure the settings are saved. If we are going to be registered and
7822 // no config file exists yet, create it by calling i_saveSettings() too.
7823 if ( (mData->flModifications)
7824 || (!mData->pMachineConfigFile->fileExists())
7825 )
7826 {
7827 rc = i_saveSettings(NULL);
7828 // no need to check whether VirtualBox.xml needs saving too since
7829 // we can't have a machine XML file rename pending
7830 if (FAILED(rc)) return rc;
7831 }
7832
7833 /* more config checking goes here */
7834
7835 if (SUCCEEDED(rc))
7836 {
7837 /* we may have had implicit modifications we want to fix on success */
7838 i_commit();
7839
7840 mData->mRegistered = true;
7841 }
7842 else
7843 {
7844 /* we may have had implicit modifications we want to cancel on failure*/
7845 i_rollback(false /* aNotify */);
7846 }
7847
7848 return rc;
7849}
7850
7851/**
7852 * Increases the number of objects dependent on the machine state or on the
7853 * registered state. Guarantees that these two states will not change at least
7854 * until #releaseStateDependency() is called.
7855 *
7856 * Depending on the @a aDepType value, additional state checks may be made.
7857 * These checks will set extended error info on failure. See
7858 * #checkStateDependency() for more info.
7859 *
7860 * If this method returns a failure, the dependency is not added and the caller
7861 * is not allowed to rely on any particular machine state or registration state
7862 * value and may return the failed result code to the upper level.
7863 *
7864 * @param aDepType Dependency type to add.
7865 * @param aState Current machine state (NULL if not interested).
7866 * @param aRegistered Current registered state (NULL if not interested).
7867 *
7868 * @note Locks this object for writing.
7869 */
7870HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7871 MachineState_T *aState /* = NULL */,
7872 BOOL *aRegistered /* = NULL */)
7873{
7874 AutoCaller autoCaller(this);
7875 AssertComRCReturnRC(autoCaller.rc());
7876
7877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7878
7879 HRESULT rc = i_checkStateDependency(aDepType);
7880 if (FAILED(rc)) return rc;
7881
7882 {
7883 if (mData->mMachineStateChangePending != 0)
7884 {
7885 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7886 * drop to zero so don't add more. It may make sense to wait a bit
7887 * and retry before reporting an error (since the pending state
7888 * transition should be really quick) but let's just assert for
7889 * now to see if it ever happens on practice. */
7890
7891 AssertFailed();
7892
7893 return setError(E_ACCESSDENIED,
7894 tr("Machine state change is in progress. Please retry the operation later."));
7895 }
7896
7897 ++mData->mMachineStateDeps;
7898 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7899 }
7900
7901 if (aState)
7902 *aState = mData->mMachineState;
7903 if (aRegistered)
7904 *aRegistered = mData->mRegistered;
7905
7906 return S_OK;
7907}
7908
7909/**
7910 * Decreases the number of objects dependent on the machine state.
7911 * Must always complete the #addStateDependency() call after the state
7912 * dependency is no more necessary.
7913 */
7914void Machine::i_releaseStateDependency()
7915{
7916 AutoCaller autoCaller(this);
7917 AssertComRCReturnVoid(autoCaller.rc());
7918
7919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7920
7921 /* releaseStateDependency() w/o addStateDependency()? */
7922 AssertReturnVoid(mData->mMachineStateDeps != 0);
7923 -- mData->mMachineStateDeps;
7924
7925 if (mData->mMachineStateDeps == 0)
7926 {
7927 /* inform i_ensureNoStateDependencies() that there are no more deps */
7928 if (mData->mMachineStateChangePending != 0)
7929 {
7930 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7931 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7932 }
7933 }
7934}
7935
7936Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7937{
7938 /* start with nothing found */
7939 Utf8Str strResult("");
7940
7941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7942
7943 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7944 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7945 // found:
7946 strResult = it->second; // source is a Utf8Str
7947
7948 return strResult;
7949}
7950
7951// protected methods
7952/////////////////////////////////////////////////////////////////////////////
7953
7954/**
7955 * Performs machine state checks based on the @a aDepType value. If a check
7956 * fails, this method will set extended error info, otherwise it will return
7957 * S_OK. It is supposed, that on failure, the caller will immediately return
7958 * the return value of this method to the upper level.
7959 *
7960 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7961 *
7962 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7963 * current state of this machine object allows to change settings of the
7964 * machine (i.e. the machine is not registered, or registered but not running
7965 * and not saved). It is useful to call this method from Machine setters
7966 * before performing any change.
7967 *
7968 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7969 * as for MutableStateDep except that if the machine is saved, S_OK is also
7970 * returned. This is useful in setters which allow changing machine
7971 * properties when it is in the saved state.
7972 *
7973 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7974 * if the current state of this machine object allows to change runtime
7975 * changeable settings of the machine (i.e. the machine is not registered, or
7976 * registered but either running or not running and not saved). It is useful
7977 * to call this method from Machine setters before performing any changes to
7978 * runtime changeable settings.
7979 *
7980 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7981 * the same as for MutableOrRunningStateDep except that if the machine is
7982 * saved, S_OK is also returned. This is useful in setters which allow
7983 * changing runtime and saved state changeable machine properties.
7984 *
7985 * @param aDepType Dependency type to check.
7986 *
7987 * @note Non Machine based classes should use #addStateDependency() and
7988 * #releaseStateDependency() methods or the smart AutoStateDependency
7989 * template.
7990 *
7991 * @note This method must be called from under this object's read or write
7992 * lock.
7993 */
7994HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7995{
7996 switch (aDepType)
7997 {
7998 case AnyStateDep:
7999 {
8000 break;
8001 }
8002 case MutableStateDep:
8003 {
8004 if ( mData->mRegistered
8005 && ( !i_isSessionMachine()
8006 || ( mData->mMachineState != MachineState_Aborted
8007 && mData->mMachineState != MachineState_Teleported
8008 && mData->mMachineState != MachineState_PoweredOff
8009 )
8010 )
8011 )
8012 return setError(VBOX_E_INVALID_VM_STATE,
8013 tr("The machine is not mutable (state is %s)"),
8014 Global::stringifyMachineState(mData->mMachineState));
8015 break;
8016 }
8017 case MutableOrSavedStateDep:
8018 {
8019 if ( mData->mRegistered
8020 && ( !i_isSessionMachine()
8021 || ( mData->mMachineState != MachineState_Aborted
8022 && mData->mMachineState != MachineState_Teleported
8023 && mData->mMachineState != MachineState_Saved
8024 && mData->mMachineState != MachineState_PoweredOff
8025 )
8026 )
8027 )
8028 return setError(VBOX_E_INVALID_VM_STATE,
8029 tr("The machine is not mutable (state is %s)"),
8030 Global::stringifyMachineState(mData->mMachineState));
8031 break;
8032 }
8033 case MutableOrRunningStateDep:
8034 {
8035 if ( mData->mRegistered
8036 && ( !i_isSessionMachine()
8037 || ( mData->mMachineState != MachineState_Aborted
8038 && mData->mMachineState != MachineState_Teleported
8039 && mData->mMachineState != MachineState_PoweredOff
8040 && !Global::IsOnline(mData->mMachineState)
8041 )
8042 )
8043 )
8044 return setError(VBOX_E_INVALID_VM_STATE,
8045 tr("The machine is not mutable (state is %s)"),
8046 Global::stringifyMachineState(mData->mMachineState));
8047 break;
8048 }
8049 case MutableOrSavedOrRunningStateDep:
8050 {
8051 if ( mData->mRegistered
8052 && ( !i_isSessionMachine()
8053 || ( mData->mMachineState != MachineState_Aborted
8054 && mData->mMachineState != MachineState_Teleported
8055 && mData->mMachineState != MachineState_Saved
8056 && mData->mMachineState != MachineState_PoweredOff
8057 && !Global::IsOnline(mData->mMachineState)
8058 )
8059 )
8060 )
8061 return setError(VBOX_E_INVALID_VM_STATE,
8062 tr("The machine is not mutable (state is %s)"),
8063 Global::stringifyMachineState(mData->mMachineState));
8064 break;
8065 }
8066 }
8067
8068 return S_OK;
8069}
8070
8071/**
8072 * Helper to initialize all associated child objects and allocate data
8073 * structures.
8074 *
8075 * This method must be called as a part of the object's initialization procedure
8076 * (usually done in the #init() method).
8077 *
8078 * @note Must be called only from #init() or from #registeredInit().
8079 */
8080HRESULT Machine::initDataAndChildObjects()
8081{
8082 AutoCaller autoCaller(this);
8083 AssertComRCReturnRC(autoCaller.rc());
8084 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8085 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8086
8087 AssertReturn(!mData->mAccessible, E_FAIL);
8088
8089 /* allocate data structures */
8090 mSSData.allocate();
8091 mUserData.allocate();
8092 mHWData.allocate();
8093 mMediaData.allocate();
8094 mStorageControllers.allocate();
8095 mUSBControllers.allocate();
8096
8097 /* initialize mOSTypeId */
8098 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8099
8100 /* create associated BIOS settings object */
8101 unconst(mBIOSSettings).createObject();
8102 mBIOSSettings->init(this);
8103
8104 /* create an associated VRDE object (default is disabled) */
8105 unconst(mVRDEServer).createObject();
8106 mVRDEServer->init(this);
8107
8108 /* create associated serial port objects */
8109 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8110 {
8111 unconst(mSerialPorts[slot]).createObject();
8112 mSerialPorts[slot]->init(this, slot);
8113 }
8114
8115 /* create associated parallel port objects */
8116 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8117 {
8118 unconst(mParallelPorts[slot]).createObject();
8119 mParallelPorts[slot]->init(this, slot);
8120 }
8121
8122 /* create the audio adapter object (always present, default is disabled) */
8123 unconst(mAudioAdapter).createObject();
8124 mAudioAdapter->init(this);
8125
8126 /* create the USB device filters object (always present) */
8127 unconst(mUSBDeviceFilters).createObject();
8128 mUSBDeviceFilters->init(this);
8129
8130 /* create associated network adapter objects */
8131 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8132 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8133 {
8134 unconst(mNetworkAdapters[slot]).createObject();
8135 mNetworkAdapters[slot]->init(this, slot);
8136 }
8137
8138 /* create the bandwidth control */
8139 unconst(mBandwidthControl).createObject();
8140 mBandwidthControl->init(this);
8141
8142 return S_OK;
8143}
8144
8145/**
8146 * Helper to uninitialize all associated child objects and to free all data
8147 * structures.
8148 *
8149 * This method must be called as a part of the object's uninitialization
8150 * procedure (usually done in the #uninit() method).
8151 *
8152 * @note Must be called only from #uninit() or from #registeredInit().
8153 */
8154void Machine::uninitDataAndChildObjects()
8155{
8156 AutoCaller autoCaller(this);
8157 AssertComRCReturnVoid(autoCaller.rc());
8158 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8159 || getObjectState().getState() == ObjectState::Limited);
8160
8161 /* tell all our other child objects we've been uninitialized */
8162 if (mBandwidthControl)
8163 {
8164 mBandwidthControl->uninit();
8165 unconst(mBandwidthControl).setNull();
8166 }
8167
8168 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8169 {
8170 if (mNetworkAdapters[slot])
8171 {
8172 mNetworkAdapters[slot]->uninit();
8173 unconst(mNetworkAdapters[slot]).setNull();
8174 }
8175 }
8176
8177 if (mUSBDeviceFilters)
8178 {
8179 mUSBDeviceFilters->uninit();
8180 unconst(mUSBDeviceFilters).setNull();
8181 }
8182
8183 if (mAudioAdapter)
8184 {
8185 mAudioAdapter->uninit();
8186 unconst(mAudioAdapter).setNull();
8187 }
8188
8189 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8190 {
8191 if (mParallelPorts[slot])
8192 {
8193 mParallelPorts[slot]->uninit();
8194 unconst(mParallelPorts[slot]).setNull();
8195 }
8196 }
8197
8198 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8199 {
8200 if (mSerialPorts[slot])
8201 {
8202 mSerialPorts[slot]->uninit();
8203 unconst(mSerialPorts[slot]).setNull();
8204 }
8205 }
8206
8207 if (mVRDEServer)
8208 {
8209 mVRDEServer->uninit();
8210 unconst(mVRDEServer).setNull();
8211 }
8212
8213 if (mBIOSSettings)
8214 {
8215 mBIOSSettings->uninit();
8216 unconst(mBIOSSettings).setNull();
8217 }
8218
8219 /* Deassociate media (only when a real Machine or a SnapshotMachine
8220 * instance is uninitialized; SessionMachine instances refer to real
8221 * Machine media). This is necessary for a clean re-initialization of
8222 * the VM after successfully re-checking the accessibility state. Note
8223 * that in case of normal Machine or SnapshotMachine uninitialization (as
8224 * a result of unregistering or deleting the snapshot), outdated media
8225 * attachments will already be uninitialized and deleted, so this
8226 * code will not affect them. */
8227 if ( !!mMediaData
8228 && (!i_isSessionMachine())
8229 )
8230 {
8231 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8232 it != mMediaData->mAttachments.end();
8233 ++it)
8234 {
8235 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8236 if (pMedium.isNull())
8237 continue;
8238 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8239 AssertComRC(rc);
8240 }
8241 }
8242
8243 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8244 {
8245 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8246 if (mData->mFirstSnapshot)
8247 {
8248 // snapshots tree is protected by machine write lock; strictly
8249 // this isn't necessary here since we're deleting the entire
8250 // machine, but otherwise we assert in Snapshot::uninit()
8251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8252 mData->mFirstSnapshot->uninit();
8253 mData->mFirstSnapshot.setNull();
8254 }
8255
8256 mData->mCurrentSnapshot.setNull();
8257 }
8258
8259 /* free data structures (the essential mData structure is not freed here
8260 * since it may be still in use) */
8261 mMediaData.free();
8262 mStorageControllers.free();
8263 mUSBControllers.free();
8264 mHWData.free();
8265 mUserData.free();
8266 mSSData.free();
8267}
8268
8269/**
8270 * Returns a pointer to the Machine object for this machine that acts like a
8271 * parent for complex machine data objects such as shared folders, etc.
8272 *
8273 * For primary Machine objects and for SnapshotMachine objects, returns this
8274 * object's pointer itself. For SessionMachine objects, returns the peer
8275 * (primary) machine pointer.
8276 */
8277Machine* Machine::i_getMachine()
8278{
8279 if (i_isSessionMachine())
8280 return (Machine*)mPeer;
8281 return this;
8282}
8283
8284/**
8285 * Makes sure that there are no machine state dependents. If necessary, waits
8286 * for the number of dependents to drop to zero.
8287 *
8288 * Make sure this method is called from under this object's write lock to
8289 * guarantee that no new dependents may be added when this method returns
8290 * control to the caller.
8291 *
8292 * @note Locks this object for writing. The lock will be released while waiting
8293 * (if necessary).
8294 *
8295 * @warning To be used only in methods that change the machine state!
8296 */
8297void Machine::i_ensureNoStateDependencies()
8298{
8299 AssertReturnVoid(isWriteLockOnCurrentThread());
8300
8301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8302
8303 /* Wait for all state dependents if necessary */
8304 if (mData->mMachineStateDeps != 0)
8305 {
8306 /* lazy semaphore creation */
8307 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8308 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8309
8310 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8311 mData->mMachineStateDeps));
8312
8313 ++mData->mMachineStateChangePending;
8314
8315 /* reset the semaphore before waiting, the last dependent will signal
8316 * it */
8317 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8318
8319 alock.release();
8320
8321 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8322
8323 alock.acquire();
8324
8325 -- mData->mMachineStateChangePending;
8326 }
8327}
8328
8329/**
8330 * Changes the machine state and informs callbacks.
8331 *
8332 * This method is not intended to fail so it either returns S_OK or asserts (and
8333 * returns a failure).
8334 *
8335 * @note Locks this object for writing.
8336 */
8337HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8338{
8339 LogFlowThisFuncEnter();
8340 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8341
8342 AutoCaller autoCaller(this);
8343 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8344
8345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8346
8347 /* wait for state dependents to drop to zero */
8348 i_ensureNoStateDependencies();
8349
8350 MachineState_T const enmOldState = mData->mMachineState;
8351 if (enmOldState != aMachineState)
8352 {
8353 mData->mMachineState = aMachineState;
8354 RTTimeNow(&mData->mLastStateChange);
8355
8356#ifdef VBOX_WITH_DTRACE_R3_MAIN
8357 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8358#endif
8359 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8360 }
8361
8362 LogFlowThisFuncLeave();
8363 return S_OK;
8364}
8365
8366/**
8367 * Searches for a shared folder with the given logical name
8368 * in the collection of shared folders.
8369 *
8370 * @param aName logical name of the shared folder
8371 * @param aSharedFolder where to return the found object
8372 * @param aSetError whether to set the error info if the folder is
8373 * not found
8374 * @return
8375 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8376 *
8377 * @note
8378 * must be called from under the object's lock!
8379 */
8380HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8381 ComObjPtr<SharedFolder> &aSharedFolder,
8382 bool aSetError /* = false */)
8383{
8384 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8385 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8386 it != mHWData->mSharedFolders.end();
8387 ++it)
8388 {
8389 SharedFolder *pSF = *it;
8390 AutoCaller autoCaller(pSF);
8391 if (pSF->i_getName() == aName)
8392 {
8393 aSharedFolder = pSF;
8394 rc = S_OK;
8395 break;
8396 }
8397 }
8398
8399 if (aSetError && FAILED(rc))
8400 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8401
8402 return rc;
8403}
8404
8405/**
8406 * Initializes all machine instance data from the given settings structures
8407 * from XML. The exception is the machine UUID which needs special handling
8408 * depending on the caller's use case, so the caller needs to set that herself.
8409 *
8410 * This gets called in several contexts during machine initialization:
8411 *
8412 * -- When machine XML exists on disk already and needs to be loaded into memory,
8413 * for example, from registeredInit() to load all registered machines on
8414 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8415 * attached to the machine should be part of some media registry already.
8416 *
8417 * -- During OVF import, when a machine config has been constructed from an
8418 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8419 * ensure that the media listed as attachments in the config (which have
8420 * been imported from the OVF) receive the correct registry ID.
8421 *
8422 * -- During VM cloning.
8423 *
8424 * @param config Machine settings from XML.
8425 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8426 * for each attached medium in the config.
8427 * @return
8428 */
8429HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8430 const Guid *puuidRegistry)
8431{
8432 // copy name, description, OS type, teleporter, UTC etc.
8433 mUserData->s = config.machineUserData;
8434
8435 // Decode the Icon overide data from config userdata and set onto Machine.
8436 #define DECODE_STR_MAX _1M
8437 const char* pszStr = config.machineUserData.ovIcon.c_str();
8438 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8439 if (cbOut > DECODE_STR_MAX)
8440 return setError(E_FAIL,
8441 tr("Icon Data too long.'%d' > '%d'"),
8442 cbOut,
8443 DECODE_STR_MAX);
8444 mUserData->mIcon.resize(cbOut);
8445 int vrc = VINF_SUCCESS;
8446 if (cbOut)
8447 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8448 if (RT_FAILURE(vrc))
8449 {
8450 mUserData->mIcon.resize(0);
8451 return setError(E_FAIL,
8452 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8453 pszStr,
8454 vrc);
8455 }
8456
8457 // look up the object by Id to check it is valid
8458 ComPtr<IGuestOSType> guestOSType;
8459 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8460 guestOSType.asOutParam());
8461 if (FAILED(rc)) return rc;
8462
8463 // stateFile (optional)
8464 if (config.strStateFile.isEmpty())
8465 mSSData->strStateFilePath.setNull();
8466 else
8467 {
8468 Utf8Str stateFilePathFull(config.strStateFile);
8469 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8470 if (RT_FAILURE(vrc))
8471 return setError(E_FAIL,
8472 tr("Invalid saved state file path '%s' (%Rrc)"),
8473 config.strStateFile.c_str(),
8474 vrc);
8475 mSSData->strStateFilePath = stateFilePathFull;
8476 }
8477
8478 // snapshot folder needs special processing so set it again
8479 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8480 if (FAILED(rc)) return rc;
8481
8482 /* Copy the extra data items (Not in any case config is already the same as
8483 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8484 * make sure the extra data map is copied). */
8485 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8486
8487 /* currentStateModified (optional, default is true) */
8488 mData->mCurrentStateModified = config.fCurrentStateModified;
8489
8490 mData->mLastStateChange = config.timeLastStateChange;
8491
8492 /*
8493 * note: all mUserData members must be assigned prior this point because
8494 * we need to commit changes in order to let mUserData be shared by all
8495 * snapshot machine instances.
8496 */
8497 mUserData.commitCopy();
8498
8499 // machine registry, if present (must be loaded before snapshots)
8500 if (config.canHaveOwnMediaRegistry())
8501 {
8502 // determine machine folder
8503 Utf8Str strMachineFolder = i_getSettingsFileFull();
8504 strMachineFolder.stripFilename();
8505 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8506 config.mediaRegistry,
8507 strMachineFolder);
8508 if (FAILED(rc)) return rc;
8509 }
8510
8511 /* Snapshot node (optional) */
8512 size_t cRootSnapshots;
8513 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8514 {
8515 // there must be only one root snapshot
8516 Assert(cRootSnapshots == 1);
8517
8518 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8519
8520 rc = i_loadSnapshot(snap,
8521 config.uuidCurrentSnapshot,
8522 NULL); // no parent == first snapshot
8523 if (FAILED(rc)) return rc;
8524 }
8525
8526 // hardware data
8527 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8528 if (FAILED(rc)) return rc;
8529
8530 // load storage controllers
8531 rc = i_loadStorageControllers(config.storageMachine,
8532 puuidRegistry,
8533 NULL /* puuidSnapshot */);
8534 if (FAILED(rc)) return rc;
8535
8536 /*
8537 * NOTE: the assignment below must be the last thing to do,
8538 * otherwise it will be not possible to change the settings
8539 * somewhere in the code above because all setters will be
8540 * blocked by i_checkStateDependency(MutableStateDep).
8541 */
8542
8543 /* set the machine state to Aborted or Saved when appropriate */
8544 if (config.fAborted)
8545 {
8546 mSSData->strStateFilePath.setNull();
8547
8548 /* no need to use i_setMachineState() during init() */
8549 mData->mMachineState = MachineState_Aborted;
8550 }
8551 else if (!mSSData->strStateFilePath.isEmpty())
8552 {
8553 /* no need to use i_setMachineState() during init() */
8554 mData->mMachineState = MachineState_Saved;
8555 }
8556
8557 // after loading settings, we are no longer different from the XML on disk
8558 mData->flModifications = 0;
8559
8560 return S_OK;
8561}
8562
8563/**
8564 * Recursively loads all snapshots starting from the given.
8565 *
8566 * @param aNode <Snapshot> node.
8567 * @param aCurSnapshotId Current snapshot ID from the settings file.
8568 * @param aParentSnapshot Parent snapshot.
8569 */
8570HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8571 const Guid &aCurSnapshotId,
8572 Snapshot *aParentSnapshot)
8573{
8574 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8575 AssertReturn(!i_isSessionMachine(), E_FAIL);
8576
8577 HRESULT rc = S_OK;
8578
8579 Utf8Str strStateFile;
8580 if (!data.strStateFile.isEmpty())
8581 {
8582 /* optional */
8583 strStateFile = data.strStateFile;
8584 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8585 if (RT_FAILURE(vrc))
8586 return setError(E_FAIL,
8587 tr("Invalid saved state file path '%s' (%Rrc)"),
8588 strStateFile.c_str(),
8589 vrc);
8590 }
8591
8592 /* create a snapshot machine object */
8593 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8594 pSnapshotMachine.createObject();
8595 rc = pSnapshotMachine->initFromSettings(this,
8596 data.hardware,
8597 &data.debugging,
8598 &data.autostart,
8599 data.storage,
8600 data.uuid.ref(),
8601 strStateFile);
8602 if (FAILED(rc)) return rc;
8603
8604 /* create a snapshot object */
8605 ComObjPtr<Snapshot> pSnapshot;
8606 pSnapshot.createObject();
8607 /* initialize the snapshot */
8608 rc = pSnapshot->init(mParent, // VirtualBox object
8609 data.uuid,
8610 data.strName,
8611 data.strDescription,
8612 data.timestamp,
8613 pSnapshotMachine,
8614 aParentSnapshot);
8615 if (FAILED(rc)) return rc;
8616
8617 /* memorize the first snapshot if necessary */
8618 if (!mData->mFirstSnapshot)
8619 mData->mFirstSnapshot = pSnapshot;
8620
8621 /* memorize the current snapshot when appropriate */
8622 if ( !mData->mCurrentSnapshot
8623 && pSnapshot->i_getId() == aCurSnapshotId
8624 )
8625 mData->mCurrentSnapshot = pSnapshot;
8626
8627 // now create the children
8628 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8629 it != data.llChildSnapshots.end();
8630 ++it)
8631 {
8632 const settings::Snapshot &childData = *it;
8633 // recurse
8634 rc = i_loadSnapshot(childData,
8635 aCurSnapshotId,
8636 pSnapshot); // parent = the one we created above
8637 if (FAILED(rc)) return rc;
8638 }
8639
8640 return rc;
8641}
8642
8643/**
8644 * Loads settings into mHWData.
8645 *
8646 * @param data Reference to the hardware settings.
8647 * @param pDbg Pointer to the debugging settings.
8648 * @param pAutostart Pointer to the autostart settings.
8649 */
8650HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8651 const settings::Autostart *pAutostart)
8652{
8653 AssertReturn(!i_isSessionMachine(), E_FAIL);
8654
8655 HRESULT rc = S_OK;
8656
8657 try
8658 {
8659 /* The hardware version attribute (optional). */
8660 mHWData->mHWVersion = data.strVersion;
8661 mHWData->mHardwareUUID = data.uuid;
8662
8663 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8664 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8665 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8666 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8667 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8668 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8669 mHWData->mPAEEnabled = data.fPAE;
8670 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8671 mHWData->mLongMode = data.enmLongMode;
8672 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8673 mHWData->mCPUCount = data.cCPUs;
8674 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8675 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8676
8677 // cpu
8678 if (mHWData->mCPUHotPlugEnabled)
8679 {
8680 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8681 it != data.llCpus.end();
8682 ++it)
8683 {
8684 const settings::Cpu &cpu = *it;
8685
8686 mHWData->mCPUAttached[cpu.ulId] = true;
8687 }
8688 }
8689
8690 // cpuid leafs
8691 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8692 it != data.llCpuIdLeafs.end();
8693 ++it)
8694 {
8695 const settings::CpuIdLeaf &leaf = *it;
8696
8697 switch (leaf.ulId)
8698 {
8699 case 0x0:
8700 case 0x1:
8701 case 0x2:
8702 case 0x3:
8703 case 0x4:
8704 case 0x5:
8705 case 0x6:
8706 case 0x7:
8707 case 0x8:
8708 case 0x9:
8709 case 0xA:
8710 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8711 break;
8712
8713 case 0x80000000:
8714 case 0x80000001:
8715 case 0x80000002:
8716 case 0x80000003:
8717 case 0x80000004:
8718 case 0x80000005:
8719 case 0x80000006:
8720 case 0x80000007:
8721 case 0x80000008:
8722 case 0x80000009:
8723 case 0x8000000A:
8724 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8725 break;
8726
8727 default:
8728 /* just ignore */
8729 break;
8730 }
8731 }
8732
8733 mHWData->mMemorySize = data.ulMemorySizeMB;
8734 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8735
8736 // boot order
8737 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8738 {
8739 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8740 if (it == data.mapBootOrder.end())
8741 mHWData->mBootOrder[i] = DeviceType_Null;
8742 else
8743 mHWData->mBootOrder[i] = it->second;
8744 }
8745
8746 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8747 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8748 mHWData->mMonitorCount = data.cMonitors;
8749 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8750 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8751 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8752 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8753 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8754 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8755 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8756 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8757 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8758 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8759 if (!data.strVideoCaptureFile.isEmpty())
8760 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8761 else
8762 mHWData->mVideoCaptureFile.setNull();
8763 mHWData->mFirmwareType = data.firmwareType;
8764 mHWData->mPointingHIDType = data.pointingHIDType;
8765 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8766 mHWData->mChipsetType = data.chipsetType;
8767 mHWData->mParavirtProvider = data.paravirtProvider;
8768 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8769 mHWData->mHPETEnabled = data.fHPETEnabled;
8770
8771 /* VRDEServer */
8772 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8773 if (FAILED(rc)) return rc;
8774
8775 /* BIOS */
8776 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8777 if (FAILED(rc)) return rc;
8778
8779 // Bandwidth control (must come before network adapters)
8780 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8781 if (FAILED(rc)) return rc;
8782
8783 /* Shared folders */
8784 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8785 it != data.usbSettings.llUSBControllers.end();
8786 ++it)
8787 {
8788 const settings::USBController &settingsCtrl = *it;
8789 ComObjPtr<USBController> newCtrl;
8790
8791 newCtrl.createObject();
8792 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8793 mUSBControllers->push_back(newCtrl);
8794 }
8795
8796 /* USB device filters */
8797 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8798 if (FAILED(rc)) return rc;
8799
8800 // network adapters
8801 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8802 size_t oldCount = mNetworkAdapters.size();
8803 if (newCount > oldCount)
8804 {
8805 mNetworkAdapters.resize(newCount);
8806 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8807 {
8808 unconst(mNetworkAdapters[slot]).createObject();
8809 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8810 }
8811 }
8812 else if (newCount < oldCount)
8813 mNetworkAdapters.resize(newCount);
8814 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8815 it != data.llNetworkAdapters.end();
8816 ++it)
8817 {
8818 const settings::NetworkAdapter &nic = *it;
8819
8820 /* slot unicity is guaranteed by XML Schema */
8821 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8822 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8823 if (FAILED(rc)) return rc;
8824 }
8825
8826 // serial ports
8827 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8828 it != data.llSerialPorts.end();
8829 ++it)
8830 {
8831 const settings::SerialPort &s = *it;
8832
8833 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8834 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8835 if (FAILED(rc)) return rc;
8836 }
8837
8838 // parallel ports (optional)
8839 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8840 it != data.llParallelPorts.end();
8841 ++it)
8842 {
8843 const settings::ParallelPort &p = *it;
8844
8845 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8846 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8847 if (FAILED(rc)) return rc;
8848 }
8849
8850 /* AudioAdapter */
8851 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8852 if (FAILED(rc)) return rc;
8853
8854 /* Shared folders */
8855 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8856 it != data.llSharedFolders.end();
8857 ++it)
8858 {
8859 const settings::SharedFolder &sf = *it;
8860
8861 ComObjPtr<SharedFolder> sharedFolder;
8862 /* Check for double entries. Not allowed! */
8863 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8864 if (SUCCEEDED(rc))
8865 return setError(VBOX_E_OBJECT_IN_USE,
8866 tr("Shared folder named '%s' already exists"),
8867 sf.strName.c_str());
8868
8869 /* Create the new shared folder. Don't break on error. This will be
8870 * reported when the machine starts. */
8871 sharedFolder.createObject();
8872 rc = sharedFolder->init(i_getMachine(),
8873 sf.strName,
8874 sf.strHostPath,
8875 RT_BOOL(sf.fWritable),
8876 RT_BOOL(sf.fAutoMount),
8877 false /* fFailOnError */);
8878 if (FAILED(rc)) return rc;
8879 mHWData->mSharedFolders.push_back(sharedFolder);
8880 }
8881
8882 // Clipboard
8883 mHWData->mClipboardMode = data.clipboardMode;
8884
8885 // drag'n'drop
8886 mHWData->mDnDMode = data.dndMode;
8887
8888 // guest settings
8889 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8890
8891 // IO settings
8892 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8893 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8894
8895 // Host PCI devices
8896 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8897 it != data.pciAttachments.end();
8898 ++it)
8899 {
8900 const settings::HostPCIDeviceAttachment &hpda = *it;
8901 ComObjPtr<PCIDeviceAttachment> pda;
8902
8903 pda.createObject();
8904 pda->i_loadSettings(this, hpda);
8905 mHWData->mPCIDeviceAssignments.push_back(pda);
8906 }
8907
8908 /*
8909 * (The following isn't really real hardware, but it lives in HWData
8910 * for reasons of convenience.)
8911 */
8912
8913#ifdef VBOX_WITH_GUEST_PROPS
8914 /* Guest properties (optional) */
8915 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8916 it != data.llGuestProperties.end();
8917 ++it)
8918 {
8919 const settings::GuestProperty &prop = *it;
8920 uint32_t fFlags = guestProp::NILFLAG;
8921 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8922 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8923 mHWData->mGuestProperties[prop.strName] = property;
8924 }
8925
8926 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8927#endif /* VBOX_WITH_GUEST_PROPS defined */
8928
8929 rc = i_loadDebugging(pDbg);
8930 if (FAILED(rc))
8931 return rc;
8932
8933 mHWData->mAutostart = *pAutostart;
8934
8935 /* default frontend */
8936 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8937 }
8938 catch(std::bad_alloc &)
8939 {
8940 return E_OUTOFMEMORY;
8941 }
8942
8943 AssertComRC(rc);
8944 return rc;
8945}
8946
8947/**
8948 * Called from Machine::loadHardware() to load the debugging settings of the
8949 * machine.
8950 *
8951 * @param pDbg Pointer to the settings.
8952 */
8953HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8954{
8955 mHWData->mDebugging = *pDbg;
8956 /* no more processing currently required, this will probably change. */
8957 return S_OK;
8958}
8959
8960/**
8961 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8962 *
8963 * @param data
8964 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8965 * @param puuidSnapshot
8966 * @return
8967 */
8968HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8969 const Guid *puuidRegistry,
8970 const Guid *puuidSnapshot)
8971{
8972 AssertReturn(!i_isSessionMachine(), E_FAIL);
8973
8974 HRESULT rc = S_OK;
8975
8976 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8977 it != data.llStorageControllers.end();
8978 ++it)
8979 {
8980 const settings::StorageController &ctlData = *it;
8981
8982 ComObjPtr<StorageController> pCtl;
8983 /* Try to find one with the name first. */
8984 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8985 if (SUCCEEDED(rc))
8986 return setError(VBOX_E_OBJECT_IN_USE,
8987 tr("Storage controller named '%s' already exists"),
8988 ctlData.strName.c_str());
8989
8990 pCtl.createObject();
8991 rc = pCtl->init(this,
8992 ctlData.strName,
8993 ctlData.storageBus,
8994 ctlData.ulInstance,
8995 ctlData.fBootable);
8996 if (FAILED(rc)) return rc;
8997
8998 mStorageControllers->push_back(pCtl);
8999
9000 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9001 if (FAILED(rc)) return rc;
9002
9003 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9004 if (FAILED(rc)) return rc;
9005
9006 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9007 if (FAILED(rc)) return rc;
9008
9009 /* Set IDE emulation settings (only for AHCI controller). */
9010 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9011 {
9012 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9013 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9014 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9015 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9016 )
9017 return rc;
9018 }
9019
9020 /* Load the attached devices now. */
9021 rc = i_loadStorageDevices(pCtl,
9022 ctlData,
9023 puuidRegistry,
9024 puuidSnapshot);
9025 if (FAILED(rc)) return rc;
9026 }
9027
9028 return S_OK;
9029}
9030
9031/**
9032 * Called from i_loadStorageControllers for a controller's devices.
9033 *
9034 * @param aStorageController
9035 * @param data
9036 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9037 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9038 * @return
9039 */
9040HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9041 const settings::StorageController &data,
9042 const Guid *puuidRegistry,
9043 const Guid *puuidSnapshot)
9044{
9045 HRESULT rc = S_OK;
9046
9047 /* paranoia: detect duplicate attachments */
9048 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9049 it != data.llAttachedDevices.end();
9050 ++it)
9051 {
9052 const settings::AttachedDevice &ad = *it;
9053
9054 for (settings::AttachedDevicesList::const_iterator it2 = it;
9055 it2 != data.llAttachedDevices.end();
9056 ++it2)
9057 {
9058 if (it == it2)
9059 continue;
9060
9061 const settings::AttachedDevice &ad2 = *it2;
9062
9063 if ( ad.lPort == ad2.lPort
9064 && ad.lDevice == ad2.lDevice)
9065 {
9066 return setError(E_FAIL,
9067 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9068 aStorageController->i_getName().c_str(),
9069 ad.lPort,
9070 ad.lDevice,
9071 mUserData->s.strName.c_str());
9072 }
9073 }
9074 }
9075
9076 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9077 it != data.llAttachedDevices.end();
9078 ++it)
9079 {
9080 const settings::AttachedDevice &dev = *it;
9081 ComObjPtr<Medium> medium;
9082
9083 switch (dev.deviceType)
9084 {
9085 case DeviceType_Floppy:
9086 case DeviceType_DVD:
9087 if (dev.strHostDriveSrc.isNotEmpty())
9088 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9089 false /* fRefresh */, medium);
9090 else
9091 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9092 dev.uuid,
9093 false /* fRefresh */,
9094 false /* aSetError */,
9095 medium);
9096 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9097 // This is not an error. The host drive or UUID might have vanished, so just go
9098 // ahead without this removeable medium attachment
9099 rc = S_OK;
9100 break;
9101
9102 case DeviceType_HardDisk:
9103 {
9104 /* find a hard disk by UUID */
9105 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9106 if (FAILED(rc))
9107 {
9108 if (i_isSnapshotMachine())
9109 {
9110 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9111 // so the user knows that the bad disk is in a snapshot somewhere
9112 com::ErrorInfo info;
9113 return setError(E_FAIL,
9114 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9115 puuidSnapshot->raw(),
9116 info.getText().raw());
9117 }
9118 else
9119 return rc;
9120 }
9121
9122 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9123
9124 if (medium->i_getType() == MediumType_Immutable)
9125 {
9126 if (i_isSnapshotMachine())
9127 return setError(E_FAIL,
9128 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9129 "of the virtual machine '%s' ('%s')"),
9130 medium->i_getLocationFull().c_str(),
9131 dev.uuid.raw(),
9132 puuidSnapshot->raw(),
9133 mUserData->s.strName.c_str(),
9134 mData->m_strConfigFileFull.c_str());
9135
9136 return setError(E_FAIL,
9137 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9138 medium->i_getLocationFull().c_str(),
9139 dev.uuid.raw(),
9140 mUserData->s.strName.c_str(),
9141 mData->m_strConfigFileFull.c_str());
9142 }
9143
9144 if (medium->i_getType() == MediumType_MultiAttach)
9145 {
9146 if (i_isSnapshotMachine())
9147 return setError(E_FAIL,
9148 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9149 "of the virtual machine '%s' ('%s')"),
9150 medium->i_getLocationFull().c_str(),
9151 dev.uuid.raw(),
9152 puuidSnapshot->raw(),
9153 mUserData->s.strName.c_str(),
9154 mData->m_strConfigFileFull.c_str());
9155
9156 return setError(E_FAIL,
9157 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9158 medium->i_getLocationFull().c_str(),
9159 dev.uuid.raw(),
9160 mUserData->s.strName.c_str(),
9161 mData->m_strConfigFileFull.c_str());
9162 }
9163
9164 if ( !i_isSnapshotMachine()
9165 && medium->i_getChildren().size() != 0
9166 )
9167 return setError(E_FAIL,
9168 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9169 "because it has %d differencing child hard disks"),
9170 medium->i_getLocationFull().c_str(),
9171 dev.uuid.raw(),
9172 mUserData->s.strName.c_str(),
9173 mData->m_strConfigFileFull.c_str(),
9174 medium->i_getChildren().size());
9175
9176 if (i_findAttachment(mMediaData->mAttachments,
9177 medium))
9178 return setError(E_FAIL,
9179 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9180 medium->i_getLocationFull().c_str(),
9181 dev.uuid.raw(),
9182 mUserData->s.strName.c_str(),
9183 mData->m_strConfigFileFull.c_str());
9184
9185 break;
9186 }
9187
9188 default:
9189 return setError(E_FAIL,
9190 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9191 medium->i_getLocationFull().c_str(),
9192 mUserData->s.strName.c_str(),
9193 mData->m_strConfigFileFull.c_str());
9194 }
9195
9196 if (FAILED(rc))
9197 break;
9198
9199 /* Bandwidth groups are loaded at this point. */
9200 ComObjPtr<BandwidthGroup> pBwGroup;
9201
9202 if (!dev.strBwGroup.isEmpty())
9203 {
9204 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9205 if (FAILED(rc))
9206 return setError(E_FAIL,
9207 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9208 medium->i_getLocationFull().c_str(),
9209 dev.strBwGroup.c_str(),
9210 mUserData->s.strName.c_str(),
9211 mData->m_strConfigFileFull.c_str());
9212 pBwGroup->i_reference();
9213 }
9214
9215 const Bstr controllerName = aStorageController->i_getName();
9216 ComObjPtr<MediumAttachment> pAttachment;
9217 pAttachment.createObject();
9218 rc = pAttachment->init(this,
9219 medium,
9220 controllerName,
9221 dev.lPort,
9222 dev.lDevice,
9223 dev.deviceType,
9224 false,
9225 dev.fPassThrough,
9226 dev.fTempEject,
9227 dev.fNonRotational,
9228 dev.fDiscard,
9229 dev.fHotPluggable,
9230 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9231 if (FAILED(rc)) break;
9232
9233 /* associate the medium with this machine and snapshot */
9234 if (!medium.isNull())
9235 {
9236 AutoCaller medCaller(medium);
9237 if (FAILED(medCaller.rc())) return medCaller.rc();
9238 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9239
9240 if (i_isSnapshotMachine())
9241 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9242 else
9243 rc = medium->i_addBackReference(mData->mUuid);
9244 /* If the medium->addBackReference fails it sets an appropriate
9245 * error message, so no need to do any guesswork here. */
9246
9247 if (puuidRegistry)
9248 // caller wants registry ID to be set on all attached media (OVF import case)
9249 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9250 }
9251
9252 if (FAILED(rc))
9253 break;
9254
9255 /* back up mMediaData to let registeredInit() properly rollback on failure
9256 * (= limited accessibility) */
9257 i_setModified(IsModified_Storage);
9258 mMediaData.backup();
9259 mMediaData->mAttachments.push_back(pAttachment);
9260 }
9261
9262 return rc;
9263}
9264
9265/**
9266 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9267 *
9268 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9269 * @param aSnapshot where to return the found snapshot
9270 * @param aSetError true to set extended error info on failure
9271 */
9272HRESULT Machine::i_findSnapshotById(const Guid &aId,
9273 ComObjPtr<Snapshot> &aSnapshot,
9274 bool aSetError /* = false */)
9275{
9276 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9277
9278 if (!mData->mFirstSnapshot)
9279 {
9280 if (aSetError)
9281 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9282 return E_FAIL;
9283 }
9284
9285 if (aId.isZero())
9286 aSnapshot = mData->mFirstSnapshot;
9287 else
9288 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9289
9290 if (!aSnapshot)
9291 {
9292 if (aSetError)
9293 return setError(E_FAIL,
9294 tr("Could not find a snapshot with UUID {%s}"),
9295 aId.toString().c_str());
9296 return E_FAIL;
9297 }
9298
9299 return S_OK;
9300}
9301
9302/**
9303 * Returns the snapshot with the given name or fails of no such snapshot.
9304 *
9305 * @param aName snapshot name to find
9306 * @param aSnapshot where to return the found snapshot
9307 * @param aSetError true to set extended error info on failure
9308 */
9309HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9310 ComObjPtr<Snapshot> &aSnapshot,
9311 bool aSetError /* = false */)
9312{
9313 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9314
9315 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9316
9317 if (!mData->mFirstSnapshot)
9318 {
9319 if (aSetError)
9320 return setError(VBOX_E_OBJECT_NOT_FOUND,
9321 tr("This machine does not have any snapshots"));
9322 return VBOX_E_OBJECT_NOT_FOUND;
9323 }
9324
9325 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9326
9327 if (!aSnapshot)
9328 {
9329 if (aSetError)
9330 return setError(VBOX_E_OBJECT_NOT_FOUND,
9331 tr("Could not find a snapshot named '%s'"), strName.c_str());
9332 return VBOX_E_OBJECT_NOT_FOUND;
9333 }
9334
9335 return S_OK;
9336}
9337
9338/**
9339 * Returns a storage controller object with the given name.
9340 *
9341 * @param aName storage controller name to find
9342 * @param aStorageController where to return the found storage controller
9343 * @param aSetError true to set extended error info on failure
9344 */
9345HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9346 ComObjPtr<StorageController> &aStorageController,
9347 bool aSetError /* = false */)
9348{
9349 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9350
9351 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9352 it != mStorageControllers->end();
9353 ++it)
9354 {
9355 if ((*it)->i_getName() == aName)
9356 {
9357 aStorageController = (*it);
9358 return S_OK;
9359 }
9360 }
9361
9362 if (aSetError)
9363 return setError(VBOX_E_OBJECT_NOT_FOUND,
9364 tr("Could not find a storage controller named '%s'"),
9365 aName.c_str());
9366 return VBOX_E_OBJECT_NOT_FOUND;
9367}
9368
9369/**
9370 * Returns a USB controller object with the given name.
9371 *
9372 * @param aName USB controller name to find
9373 * @param aUSBController where to return the found USB controller
9374 * @param aSetError true to set extended error info on failure
9375 */
9376HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9377 ComObjPtr<USBController> &aUSBController,
9378 bool aSetError /* = false */)
9379{
9380 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9381
9382 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9383 it != mUSBControllers->end();
9384 ++it)
9385 {
9386 if ((*it)->i_getName() == aName)
9387 {
9388 aUSBController = (*it);
9389 return S_OK;
9390 }
9391 }
9392
9393 if (aSetError)
9394 return setError(VBOX_E_OBJECT_NOT_FOUND,
9395 tr("Could not find a storage controller named '%s'"),
9396 aName.c_str());
9397 return VBOX_E_OBJECT_NOT_FOUND;
9398}
9399
9400/**
9401 * Returns the number of USB controller instance of the given type.
9402 *
9403 * @param enmType USB controller type.
9404 */
9405ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9406{
9407 ULONG cCtrls = 0;
9408
9409 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9410 it != mUSBControllers->end();
9411 ++it)
9412 {
9413 if ((*it)->i_getControllerType() == enmType)
9414 cCtrls++;
9415 }
9416
9417 return cCtrls;
9418}
9419
9420HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9421 MediaData::AttachmentList &atts)
9422{
9423 AutoCaller autoCaller(this);
9424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9425
9426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9427
9428 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9429 it != mMediaData->mAttachments.end();
9430 ++it)
9431 {
9432 const ComObjPtr<MediumAttachment> &pAtt = *it;
9433 // should never happen, but deal with NULL pointers in the list.
9434 AssertStmt(!pAtt.isNull(), continue);
9435
9436 // getControllerName() needs caller+read lock
9437 AutoCaller autoAttCaller(pAtt);
9438 if (FAILED(autoAttCaller.rc()))
9439 {
9440 atts.clear();
9441 return autoAttCaller.rc();
9442 }
9443 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9444
9445 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9446 atts.push_back(pAtt);
9447 }
9448
9449 return S_OK;
9450}
9451
9452
9453/**
9454 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9455 * file if the machine name was changed and about creating a new settings file
9456 * if this is a new machine.
9457 *
9458 * @note Must be never called directly but only from #saveSettings().
9459 */
9460HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9461{
9462 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9463
9464 HRESULT rc = S_OK;
9465
9466 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9467
9468 /// @todo need to handle primary group change, too
9469
9470 /* attempt to rename the settings file if machine name is changed */
9471 if ( mUserData->s.fNameSync
9472 && mUserData.isBackedUp()
9473 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9474 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9475 )
9476 {
9477 bool dirRenamed = false;
9478 bool fileRenamed = false;
9479
9480 Utf8Str configFile, newConfigFile;
9481 Utf8Str configFilePrev, newConfigFilePrev;
9482 Utf8Str configDir, newConfigDir;
9483
9484 do
9485 {
9486 int vrc = VINF_SUCCESS;
9487
9488 Utf8Str name = mUserData.backedUpData()->s.strName;
9489 Utf8Str newName = mUserData->s.strName;
9490 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9491 if (group == "/")
9492 group.setNull();
9493 Utf8Str newGroup = mUserData->s.llGroups.front();
9494 if (newGroup == "/")
9495 newGroup.setNull();
9496
9497 configFile = mData->m_strConfigFileFull;
9498
9499 /* first, rename the directory if it matches the group and machine name */
9500 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9501 group.c_str(), RTPATH_DELIMITER, name.c_str());
9502 /** @todo hack, make somehow use of ComposeMachineFilename */
9503 if (mUserData->s.fDirectoryIncludesUUID)
9504 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9505 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9506 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9507 /** @todo hack, make somehow use of ComposeMachineFilename */
9508 if (mUserData->s.fDirectoryIncludesUUID)
9509 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9510 configDir = configFile;
9511 configDir.stripFilename();
9512 newConfigDir = configDir;
9513 if ( configDir.length() >= groupPlusName.length()
9514 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9515 groupPlusName.c_str()))
9516 {
9517 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9518 Utf8Str newConfigBaseDir(newConfigDir);
9519 newConfigDir.append(newGroupPlusName);
9520 /* consistency: use \ if appropriate on the platform */
9521 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9522 /* new dir and old dir cannot be equal here because of 'if'
9523 * above and because name != newName */
9524 Assert(configDir != newConfigDir);
9525 if (!fSettingsFileIsNew)
9526 {
9527 /* perform real rename only if the machine is not new */
9528 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9529 if ( vrc == VERR_FILE_NOT_FOUND
9530 || vrc == VERR_PATH_NOT_FOUND)
9531 {
9532 /* create the parent directory, then retry renaming */
9533 Utf8Str parent(newConfigDir);
9534 parent.stripFilename();
9535 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9536 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9537 }
9538 if (RT_FAILURE(vrc))
9539 {
9540 rc = setError(E_FAIL,
9541 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9542 configDir.c_str(),
9543 newConfigDir.c_str(),
9544 vrc);
9545 break;
9546 }
9547 /* delete subdirectories which are no longer needed */
9548 Utf8Str dir(configDir);
9549 dir.stripFilename();
9550 while (dir != newConfigBaseDir && dir != ".")
9551 {
9552 vrc = RTDirRemove(dir.c_str());
9553 if (RT_FAILURE(vrc))
9554 break;
9555 dir.stripFilename();
9556 }
9557 dirRenamed = true;
9558 }
9559 }
9560
9561 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9562 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9563
9564 /* then try to rename the settings file itself */
9565 if (newConfigFile != configFile)
9566 {
9567 /* get the path to old settings file in renamed directory */
9568 configFile = Utf8StrFmt("%s%c%s",
9569 newConfigDir.c_str(),
9570 RTPATH_DELIMITER,
9571 RTPathFilename(configFile.c_str()));
9572 if (!fSettingsFileIsNew)
9573 {
9574 /* perform real rename only if the machine is not new */
9575 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9576 if (RT_FAILURE(vrc))
9577 {
9578 rc = setError(E_FAIL,
9579 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9580 configFile.c_str(),
9581 newConfigFile.c_str(),
9582 vrc);
9583 break;
9584 }
9585 fileRenamed = true;
9586 configFilePrev = configFile;
9587 configFilePrev += "-prev";
9588 newConfigFilePrev = newConfigFile;
9589 newConfigFilePrev += "-prev";
9590 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9591 }
9592 }
9593
9594 // update m_strConfigFileFull amd mConfigFile
9595 mData->m_strConfigFileFull = newConfigFile;
9596 // compute the relative path too
9597 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9598
9599 // store the old and new so that VirtualBox::i_saveSettings() can update
9600 // the media registry
9601 if ( mData->mRegistered
9602 && (configDir != newConfigDir || configFile != newConfigFile))
9603 {
9604 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9605
9606 if (pfNeedsGlobalSaveSettings)
9607 *pfNeedsGlobalSaveSettings = true;
9608 }
9609
9610 // in the saved state file path, replace the old directory with the new directory
9611 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9612 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9613
9614 // and do the same thing for the saved state file paths of all the online snapshots
9615 if (mData->mFirstSnapshot)
9616 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9617 newConfigDir.c_str());
9618 }
9619 while (0);
9620
9621 if (FAILED(rc))
9622 {
9623 /* silently try to rename everything back */
9624 if (fileRenamed)
9625 {
9626 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9627 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9628 }
9629 if (dirRenamed)
9630 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9631 }
9632
9633 if (FAILED(rc)) return rc;
9634 }
9635
9636 if (fSettingsFileIsNew)
9637 {
9638 /* create a virgin config file */
9639 int vrc = VINF_SUCCESS;
9640
9641 /* ensure the settings directory exists */
9642 Utf8Str path(mData->m_strConfigFileFull);
9643 path.stripFilename();
9644 if (!RTDirExists(path.c_str()))
9645 {
9646 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9647 if (RT_FAILURE(vrc))
9648 {
9649 return setError(E_FAIL,
9650 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9651 path.c_str(),
9652 vrc);
9653 }
9654 }
9655
9656 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9657 path = Utf8Str(mData->m_strConfigFileFull);
9658 RTFILE f = NIL_RTFILE;
9659 vrc = RTFileOpen(&f, path.c_str(),
9660 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9661 if (RT_FAILURE(vrc))
9662 return setError(E_FAIL,
9663 tr("Could not create the settings file '%s' (%Rrc)"),
9664 path.c_str(),
9665 vrc);
9666 RTFileClose(f);
9667 }
9668
9669 return rc;
9670}
9671
9672/**
9673 * Saves and commits machine data, user data and hardware data.
9674 *
9675 * Note that on failure, the data remains uncommitted.
9676 *
9677 * @a aFlags may combine the following flags:
9678 *
9679 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9680 * Used when saving settings after an operation that makes them 100%
9681 * correspond to the settings from the current snapshot.
9682 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9683 * #isReallyModified() returns false. This is necessary for cases when we
9684 * change machine data directly, not through the backup()/commit() mechanism.
9685 * - SaveS_Force: settings will be saved without doing a deep compare of the
9686 * settings structures. This is used when this is called because snapshots
9687 * have changed to avoid the overhead of the deep compare.
9688 *
9689 * @note Must be called from under this object's write lock. Locks children for
9690 * writing.
9691 *
9692 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9693 * initialized to false and that will be set to true by this function if
9694 * the caller must invoke VirtualBox::i_saveSettings() because the global
9695 * settings have changed. This will happen if a machine rename has been
9696 * saved and the global machine and media registries will therefore need
9697 * updating.
9698 */
9699HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9700 int aFlags /*= 0*/)
9701{
9702 LogFlowThisFuncEnter();
9703
9704 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9705
9706 /* make sure child objects are unable to modify the settings while we are
9707 * saving them */
9708 i_ensureNoStateDependencies();
9709
9710 AssertReturn(!i_isSnapshotMachine(),
9711 E_FAIL);
9712
9713 HRESULT rc = S_OK;
9714 bool fNeedsWrite = false;
9715
9716 /* First, prepare to save settings. It will care about renaming the
9717 * settings directory and file if the machine name was changed and about
9718 * creating a new settings file if this is a new machine. */
9719 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9720 if (FAILED(rc)) return rc;
9721
9722 // keep a pointer to the current settings structures
9723 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9724 settings::MachineConfigFile *pNewConfig = NULL;
9725
9726 try
9727 {
9728 // make a fresh one to have everyone write stuff into
9729 pNewConfig = new settings::MachineConfigFile(NULL);
9730 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9731
9732 // now go and copy all the settings data from COM to the settings structures
9733 // (this calles i_saveSettings() on all the COM objects in the machine)
9734 i_copyMachineDataToSettings(*pNewConfig);
9735
9736 if (aFlags & SaveS_ResetCurStateModified)
9737 {
9738 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9739 mData->mCurrentStateModified = FALSE;
9740 fNeedsWrite = true; // always, no need to compare
9741 }
9742 else if (aFlags & SaveS_Force)
9743 {
9744 fNeedsWrite = true; // always, no need to compare
9745 }
9746 else
9747 {
9748 if (!mData->mCurrentStateModified)
9749 {
9750 // do a deep compare of the settings that we just saved with the settings
9751 // previously stored in the config file; this invokes MachineConfigFile::operator==
9752 // which does a deep compare of all the settings, which is expensive but less expensive
9753 // than writing out XML in vain
9754 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9755
9756 // could still be modified if any settings changed
9757 mData->mCurrentStateModified = fAnySettingsChanged;
9758
9759 fNeedsWrite = fAnySettingsChanged;
9760 }
9761 else
9762 fNeedsWrite = true;
9763 }
9764
9765 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9766
9767 if (fNeedsWrite)
9768 // now spit it all out!
9769 pNewConfig->write(mData->m_strConfigFileFull);
9770
9771 mData->pMachineConfigFile = pNewConfig;
9772 delete pOldConfig;
9773 i_commit();
9774
9775 // after saving settings, we are no longer different from the XML on disk
9776 mData->flModifications = 0;
9777 }
9778 catch (HRESULT err)
9779 {
9780 // we assume that error info is set by the thrower
9781 rc = err;
9782
9783 // restore old config
9784 delete pNewConfig;
9785 mData->pMachineConfigFile = pOldConfig;
9786 }
9787 catch (...)
9788 {
9789 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9790 }
9791
9792 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9793 {
9794 /* Fire the data change event, even on failure (since we've already
9795 * committed all data). This is done only for SessionMachines because
9796 * mutable Machine instances are always not registered (i.e. private
9797 * to the client process that creates them) and thus don't need to
9798 * inform callbacks. */
9799 if (i_isSessionMachine())
9800 mParent->i_onMachineDataChange(mData->mUuid);
9801 }
9802
9803 LogFlowThisFunc(("rc=%08X\n", rc));
9804 LogFlowThisFuncLeave();
9805 return rc;
9806}
9807
9808/**
9809 * Implementation for saving the machine settings into the given
9810 * settings::MachineConfigFile instance. This copies machine extradata
9811 * from the previous machine config file in the instance data, if any.
9812 *
9813 * This gets called from two locations:
9814 *
9815 * -- Machine::i_saveSettings(), during the regular XML writing;
9816 *
9817 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9818 * exported to OVF and we write the VirtualBox proprietary XML
9819 * into a <vbox:Machine> tag.
9820 *
9821 * This routine fills all the fields in there, including snapshots, *except*
9822 * for the following:
9823 *
9824 * -- fCurrentStateModified. There is some special logic associated with that.
9825 *
9826 * The caller can then call MachineConfigFile::write() or do something else
9827 * with it.
9828 *
9829 * Caller must hold the machine lock!
9830 *
9831 * This throws XML errors and HRESULT, so the caller must have a catch block!
9832 */
9833void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9834{
9835 // deep copy extradata
9836 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9837
9838 config.uuid = mData->mUuid;
9839
9840 // copy name, description, OS type, teleport, UTC etc.
9841 config.machineUserData = mUserData->s;
9842
9843 // Encode the Icon Override data from Machine and store on config userdata.
9844 std::vector<BYTE> iconByte;
9845 getIcon(iconByte);
9846 ssize_t cbData = iconByte.size();
9847 if (cbData > 0)
9848 {
9849 ssize_t cchOut = RTBase64EncodedLength(cbData);
9850 Utf8Str strIconData;
9851 strIconData.reserve(cchOut+1);
9852 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9853 strIconData.mutableRaw(), strIconData.capacity(),
9854 NULL);
9855 if (RT_FAILURE(vrc))
9856 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9857 strIconData.jolt();
9858 config.machineUserData.ovIcon = strIconData;
9859 }
9860 else
9861 config.machineUserData.ovIcon.setNull();
9862
9863 if ( mData->mMachineState == MachineState_Saved
9864 || mData->mMachineState == MachineState_Restoring
9865 // when deleting a snapshot we may or may not have a saved state in the current state,
9866 // so let's not assert here please
9867 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9868 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9869 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9870 && (!mSSData->strStateFilePath.isEmpty())
9871 )
9872 )
9873 {
9874 Assert(!mSSData->strStateFilePath.isEmpty());
9875 /* try to make the file name relative to the settings file dir */
9876 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9877 }
9878 else
9879 {
9880 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9881 config.strStateFile.setNull();
9882 }
9883
9884 if (mData->mCurrentSnapshot)
9885 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9886 else
9887 config.uuidCurrentSnapshot.clear();
9888
9889 config.timeLastStateChange = mData->mLastStateChange;
9890 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9891 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9892
9893 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9894 if (FAILED(rc)) throw rc;
9895
9896 rc = i_saveStorageControllers(config.storageMachine);
9897 if (FAILED(rc)) throw rc;
9898
9899 // save machine's media registry if this is VirtualBox 4.0 or later
9900 if (config.canHaveOwnMediaRegistry())
9901 {
9902 // determine machine folder
9903 Utf8Str strMachineFolder = i_getSettingsFileFull();
9904 strMachineFolder.stripFilename();
9905 mParent->i_saveMediaRegistry(config.mediaRegistry,
9906 i_getId(), // only media with registry ID == machine UUID
9907 strMachineFolder);
9908 // this throws HRESULT
9909 }
9910
9911 // save snapshots
9912 rc = i_saveAllSnapshots(config);
9913 if (FAILED(rc)) throw rc;
9914}
9915
9916/**
9917 * Saves all snapshots of the machine into the given machine config file. Called
9918 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9919 * @param config
9920 * @return
9921 */
9922HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9923{
9924 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9925
9926 HRESULT rc = S_OK;
9927
9928 try
9929 {
9930 config.llFirstSnapshot.clear();
9931
9932 if (mData->mFirstSnapshot)
9933 {
9934 settings::Snapshot snapNew;
9935 config.llFirstSnapshot.push_back(snapNew);
9936
9937 // get reference to the fresh copy of the snapshot on the list and
9938 // work on that copy directly to avoid excessive copying later
9939 settings::Snapshot &snap = config.llFirstSnapshot.front();
9940
9941 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9942 if (FAILED(rc)) throw rc;
9943 }
9944
9945// if (mType == IsSessionMachine)
9946// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9947
9948 }
9949 catch (HRESULT err)
9950 {
9951 /* we assume that error info is set by the thrower */
9952 rc = err;
9953 }
9954 catch (...)
9955 {
9956 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9957 }
9958
9959 return rc;
9960}
9961
9962/**
9963 * Saves the VM hardware configuration. It is assumed that the
9964 * given node is empty.
9965 *
9966 * @param data Reference to the settings object for the hardware config.
9967 * @param pDbg Pointer to the settings object for the debugging config
9968 * which happens to live in mHWData.
9969 * @param pAutostart Pointer to the settings object for the autostart config
9970 * which happens to live in mHWData.
9971 */
9972HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9973 settings::Autostart *pAutostart)
9974{
9975 HRESULT rc = S_OK;
9976
9977 try
9978 {
9979 /* The hardware version attribute (optional).
9980 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9981 if ( mHWData->mHWVersion == "1"
9982 && mSSData->strStateFilePath.isEmpty()
9983 )
9984 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9985 other point needs to be found where this can be done. */
9986
9987 data.strVersion = mHWData->mHWVersion;
9988 data.uuid = mHWData->mHardwareUUID;
9989
9990 // CPU
9991 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9992 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9993 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9994 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9995 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9996 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9997 data.fPAE = !!mHWData->mPAEEnabled;
9998 data.enmLongMode = mHWData->mLongMode;
9999 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10000 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10001
10002 /* Standard and Extended CPUID leafs. */
10003 data.llCpuIdLeafs.clear();
10004 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10005 {
10006 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10007 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10008 }
10009 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10010 {
10011 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10012 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10013 }
10014
10015 data.cCPUs = mHWData->mCPUCount;
10016 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10017 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10018
10019 data.llCpus.clear();
10020 if (data.fCpuHotPlug)
10021 {
10022 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10023 {
10024 if (mHWData->mCPUAttached[idx])
10025 {
10026 settings::Cpu cpu;
10027 cpu.ulId = idx;
10028 data.llCpus.push_back(cpu);
10029 }
10030 }
10031 }
10032
10033 // memory
10034 data.ulMemorySizeMB = mHWData->mMemorySize;
10035 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10036
10037 // firmware
10038 data.firmwareType = mHWData->mFirmwareType;
10039
10040 // HID
10041 data.pointingHIDType = mHWData->mPointingHIDType;
10042 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10043
10044 // chipset
10045 data.chipsetType = mHWData->mChipsetType;
10046
10047 // paravirt
10048 data.paravirtProvider = mHWData->mParavirtProvider;
10049
10050
10051 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10052
10053 // HPET
10054 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10055
10056 // boot order
10057 data.mapBootOrder.clear();
10058 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10059 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10060
10061 // display
10062 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10063 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10064 data.cMonitors = mHWData->mMonitorCount;
10065 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10066 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10067 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10068 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10069 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10070 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10071 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10072 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10073 {
10074 if (mHWData->maVideoCaptureScreens[i])
10075 ASMBitSet(&data.u64VideoCaptureScreens, i);
10076 else
10077 ASMBitClear(&data.u64VideoCaptureScreens, i);
10078 }
10079 /* store relative video capture file if possible */
10080 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10081
10082 /* VRDEServer settings (optional) */
10083 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10084 if (FAILED(rc)) throw rc;
10085
10086 /* BIOS (required) */
10087 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10088 if (FAILED(rc)) throw rc;
10089
10090 /* USB Controller (required) */
10091 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10092 {
10093 ComObjPtr<USBController> ctrl = *it;
10094 settings::USBController settingsCtrl;
10095
10096 settingsCtrl.strName = ctrl->i_getName();
10097 settingsCtrl.enmType = ctrl->i_getControllerType();
10098
10099 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10100 }
10101
10102 /* USB device filters (required) */
10103 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10104 if (FAILED(rc)) throw rc;
10105
10106 /* Network adapters (required) */
10107 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10108 data.llNetworkAdapters.clear();
10109 /* Write out only the nominal number of network adapters for this
10110 * chipset type. Since Machine::commit() hasn't been called there
10111 * may be extra NIC settings in the vector. */
10112 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10113 {
10114 settings::NetworkAdapter nic;
10115 nic.ulSlot = (uint32_t)slot;
10116 /* paranoia check... must not be NULL, but must not crash either. */
10117 if (mNetworkAdapters[slot])
10118 {
10119 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10120 if (FAILED(rc)) throw rc;
10121
10122 data.llNetworkAdapters.push_back(nic);
10123 }
10124 }
10125
10126 /* Serial ports */
10127 data.llSerialPorts.clear();
10128 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10129 {
10130 settings::SerialPort s;
10131 s.ulSlot = slot;
10132 rc = mSerialPorts[slot]->i_saveSettings(s);
10133 if (FAILED(rc)) return rc;
10134
10135 data.llSerialPorts.push_back(s);
10136 }
10137
10138 /* Parallel ports */
10139 data.llParallelPorts.clear();
10140 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10141 {
10142 settings::ParallelPort p;
10143 p.ulSlot = slot;
10144 rc = mParallelPorts[slot]->i_saveSettings(p);
10145 if (FAILED(rc)) return rc;
10146
10147 data.llParallelPorts.push_back(p);
10148 }
10149
10150 /* Audio adapter */
10151 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10152 if (FAILED(rc)) return rc;
10153
10154 /* Shared folders */
10155 data.llSharedFolders.clear();
10156 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10157 it != mHWData->mSharedFolders.end();
10158 ++it)
10159 {
10160 SharedFolder *pSF = *it;
10161 AutoCaller sfCaller(pSF);
10162 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10163 settings::SharedFolder sf;
10164 sf.strName = pSF->i_getName();
10165 sf.strHostPath = pSF->i_getHostPath();
10166 sf.fWritable = !!pSF->i_isWritable();
10167 sf.fAutoMount = !!pSF->i_isAutoMounted();
10168
10169 data.llSharedFolders.push_back(sf);
10170 }
10171
10172 // clipboard
10173 data.clipboardMode = mHWData->mClipboardMode;
10174
10175 // drag'n'drop
10176 data.dndMode = mHWData->mDnDMode;
10177
10178 /* Guest */
10179 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10180
10181 // IO settings
10182 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10183 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10184
10185 /* BandwidthControl (required) */
10186 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10187 if (FAILED(rc)) throw rc;
10188
10189 /* Host PCI devices */
10190 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10191 it != mHWData->mPCIDeviceAssignments.end();
10192 ++it)
10193 {
10194 ComObjPtr<PCIDeviceAttachment> pda = *it;
10195 settings::HostPCIDeviceAttachment hpda;
10196
10197 rc = pda->i_saveSettings(hpda);
10198 if (FAILED(rc)) throw rc;
10199
10200 data.pciAttachments.push_back(hpda);
10201 }
10202
10203
10204 // guest properties
10205 data.llGuestProperties.clear();
10206#ifdef VBOX_WITH_GUEST_PROPS
10207 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10208 it != mHWData->mGuestProperties.end();
10209 ++it)
10210 {
10211 HWData::GuestProperty property = it->second;
10212
10213 /* Remove transient guest properties at shutdown unless we
10214 * are saving state */
10215 if ( ( mData->mMachineState == MachineState_PoweredOff
10216 || mData->mMachineState == MachineState_Aborted
10217 || mData->mMachineState == MachineState_Teleported)
10218 && ( property.mFlags & guestProp::TRANSIENT
10219 || property.mFlags & guestProp::TRANSRESET))
10220 continue;
10221 settings::GuestProperty prop;
10222 prop.strName = it->first;
10223 prop.strValue = property.strValue;
10224 prop.timestamp = property.mTimestamp;
10225 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10226 guestProp::writeFlags(property.mFlags, szFlags);
10227 prop.strFlags = szFlags;
10228
10229 data.llGuestProperties.push_back(prop);
10230 }
10231
10232 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10233 /* I presume this doesn't require a backup(). */
10234 mData->mGuestPropertiesModified = FALSE;
10235#endif /* VBOX_WITH_GUEST_PROPS defined */
10236
10237 *pDbg = mHWData->mDebugging;
10238 *pAutostart = mHWData->mAutostart;
10239
10240 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10241 }
10242 catch(std::bad_alloc &)
10243 {
10244 return E_OUTOFMEMORY;
10245 }
10246
10247 AssertComRC(rc);
10248 return rc;
10249}
10250
10251/**
10252 * Saves the storage controller configuration.
10253 *
10254 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10255 */
10256HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10257{
10258 data.llStorageControllers.clear();
10259
10260 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10261 it != mStorageControllers->end();
10262 ++it)
10263 {
10264 HRESULT rc;
10265 ComObjPtr<StorageController> pCtl = *it;
10266
10267 settings::StorageController ctl;
10268 ctl.strName = pCtl->i_getName();
10269 ctl.controllerType = pCtl->i_getControllerType();
10270 ctl.storageBus = pCtl->i_getStorageBus();
10271 ctl.ulInstance = pCtl->i_getInstance();
10272 ctl.fBootable = pCtl->i_getBootable();
10273
10274 /* Save the port count. */
10275 ULONG portCount;
10276 rc = pCtl->COMGETTER(PortCount)(&portCount);
10277 ComAssertComRCRet(rc, rc);
10278 ctl.ulPortCount = portCount;
10279
10280 /* Save fUseHostIOCache */
10281 BOOL fUseHostIOCache;
10282 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10283 ComAssertComRCRet(rc, rc);
10284 ctl.fUseHostIOCache = !!fUseHostIOCache;
10285
10286 /* Save IDE emulation settings. */
10287 if (ctl.controllerType == StorageControllerType_IntelAhci)
10288 {
10289 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10290 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10291 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10292 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10293 )
10294 ComAssertComRCRet(rc, rc);
10295 }
10296
10297 /* save the devices now. */
10298 rc = i_saveStorageDevices(pCtl, ctl);
10299 ComAssertComRCRet(rc, rc);
10300
10301 data.llStorageControllers.push_back(ctl);
10302 }
10303
10304 return S_OK;
10305}
10306
10307/**
10308 * Saves the hard disk configuration.
10309 */
10310HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10311 settings::StorageController &data)
10312{
10313 MediaData::AttachmentList atts;
10314
10315 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10316 if (FAILED(rc)) return rc;
10317
10318 data.llAttachedDevices.clear();
10319 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10320 it != atts.end();
10321 ++it)
10322 {
10323 settings::AttachedDevice dev;
10324 IMediumAttachment *iA = *it;
10325 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10326 Medium *pMedium = pAttach->i_getMedium();
10327
10328 dev.deviceType = pAttach->i_getType();
10329 dev.lPort = pAttach->i_getPort();
10330 dev.lDevice = pAttach->i_getDevice();
10331 dev.fPassThrough = pAttach->i_getPassthrough();
10332 dev.fHotPluggable = pAttach->i_getHotPluggable();
10333 if (pMedium)
10334 {
10335 if (pMedium->i_isHostDrive())
10336 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10337 else
10338 dev.uuid = pMedium->i_getId();
10339 dev.fTempEject = pAttach->i_getTempEject();
10340 dev.fNonRotational = pAttach->i_getNonRotational();
10341 dev.fDiscard = pAttach->i_getDiscard();
10342 }
10343
10344 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10345
10346 data.llAttachedDevices.push_back(dev);
10347 }
10348
10349 return S_OK;
10350}
10351
10352/**
10353 * Saves machine state settings as defined by aFlags
10354 * (SaveSTS_* values).
10355 *
10356 * @param aFlags Combination of SaveSTS_* flags.
10357 *
10358 * @note Locks objects for writing.
10359 */
10360HRESULT Machine::i_saveStateSettings(int aFlags)
10361{
10362 if (aFlags == 0)
10363 return S_OK;
10364
10365 AutoCaller autoCaller(this);
10366 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10367
10368 /* This object's write lock is also necessary to serialize file access
10369 * (prevent concurrent reads and writes) */
10370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10371
10372 HRESULT rc = S_OK;
10373
10374 Assert(mData->pMachineConfigFile);
10375
10376 try
10377 {
10378 if (aFlags & SaveSTS_CurStateModified)
10379 mData->pMachineConfigFile->fCurrentStateModified = true;
10380
10381 if (aFlags & SaveSTS_StateFilePath)
10382 {
10383 if (!mSSData->strStateFilePath.isEmpty())
10384 /* try to make the file name relative to the settings file dir */
10385 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10386 else
10387 mData->pMachineConfigFile->strStateFile.setNull();
10388 }
10389
10390 if (aFlags & SaveSTS_StateTimeStamp)
10391 {
10392 Assert( mData->mMachineState != MachineState_Aborted
10393 || mSSData->strStateFilePath.isEmpty());
10394
10395 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10396
10397 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10398//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10399 }
10400
10401 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10402 }
10403 catch (...)
10404 {
10405 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10406 }
10407
10408 return rc;
10409}
10410
10411/**
10412 * Ensures that the given medium is added to a media registry. If this machine
10413 * was created with 4.0 or later, then the machine registry is used. Otherwise
10414 * the global VirtualBox media registry is used.
10415 *
10416 * Caller must NOT hold machine lock, media tree or any medium locks!
10417 *
10418 * @param pMedium
10419 */
10420void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10421{
10422 /* Paranoia checks: do not hold machine or media tree locks. */
10423 AssertReturnVoid(!isWriteLockOnCurrentThread());
10424 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10425
10426 ComObjPtr<Medium> pBase;
10427 {
10428 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10429 pBase = pMedium->i_getBase();
10430 }
10431
10432 /* Paranoia checks: do not hold medium locks. */
10433 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10434 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10435
10436 // decide which medium registry to use now that the medium is attached:
10437 Guid uuid;
10438 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10439 // machine XML is VirtualBox 4.0 or higher:
10440 uuid = i_getId(); // machine UUID
10441 else
10442 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10443
10444 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10445 mParent->i_markRegistryModified(uuid);
10446
10447 /* For more complex hard disk structures it can happen that the base
10448 * medium isn't yet associated with any medium registry. Do that now. */
10449 if (pMedium != pBase)
10450 {
10451 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10452 mParent->i_markRegistryModified(uuid);
10453 }
10454}
10455
10456/**
10457 * Creates differencing hard disks for all normal hard disks attached to this
10458 * machine and a new set of attachments to refer to created disks.
10459 *
10460 * Used when taking a snapshot or when deleting the current state. Gets called
10461 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10462 *
10463 * This method assumes that mMediaData contains the original hard disk attachments
10464 * it needs to create diffs for. On success, these attachments will be replaced
10465 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10466 * called to delete created diffs which will also rollback mMediaData and restore
10467 * whatever was backed up before calling this method.
10468 *
10469 * Attachments with non-normal hard disks are left as is.
10470 *
10471 * If @a aOnline is @c false then the original hard disks that require implicit
10472 * diffs will be locked for reading. Otherwise it is assumed that they are
10473 * already locked for writing (when the VM was started). Note that in the latter
10474 * case it is responsibility of the caller to lock the newly created diffs for
10475 * writing if this method succeeds.
10476 *
10477 * @param aProgress Progress object to run (must contain at least as
10478 * many operations left as the number of hard disks
10479 * attached).
10480 * @param aOnline Whether the VM was online prior to this operation.
10481 *
10482 * @note The progress object is not marked as completed, neither on success nor
10483 * on failure. This is a responsibility of the caller.
10484 *
10485 * @note Locks this object and the media tree for writing.
10486 */
10487HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10488 ULONG aWeight,
10489 bool aOnline)
10490{
10491 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10492
10493 AutoCaller autoCaller(this);
10494 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10495
10496 AutoMultiWriteLock2 alock(this->lockHandle(),
10497 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10498
10499 /* must be in a protective state because we release the lock below */
10500 AssertReturn( mData->mMachineState == MachineState_Saving
10501 || mData->mMachineState == MachineState_LiveSnapshotting
10502 || mData->mMachineState == MachineState_RestoringSnapshot
10503 || mData->mMachineState == MachineState_DeletingSnapshot
10504 , E_FAIL);
10505
10506 HRESULT rc = S_OK;
10507
10508 // use appropriate locked media map (online or offline)
10509 MediumLockListMap lockedMediaOffline;
10510 MediumLockListMap *lockedMediaMap;
10511 if (aOnline)
10512 lockedMediaMap = &mData->mSession.mLockedMedia;
10513 else
10514 lockedMediaMap = &lockedMediaOffline;
10515
10516 try
10517 {
10518 if (!aOnline)
10519 {
10520 /* lock all attached hard disks early to detect "in use"
10521 * situations before creating actual diffs */
10522 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10523 it != mMediaData->mAttachments.end();
10524 ++it)
10525 {
10526 MediumAttachment* pAtt = *it;
10527 if (pAtt->i_getType() == DeviceType_HardDisk)
10528 {
10529 Medium* pMedium = pAtt->i_getMedium();
10530 Assert(pMedium);
10531
10532 MediumLockList *pMediumLockList(new MediumLockList());
10533 alock.release();
10534 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10535 false /* fMediumLockWrite */,
10536 false /* fMediumLockWriteAll */,
10537 NULL,
10538 *pMediumLockList);
10539 alock.acquire();
10540 if (FAILED(rc))
10541 {
10542 delete pMediumLockList;
10543 throw rc;
10544 }
10545 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10546 if (FAILED(rc))
10547 {
10548 throw setError(rc,
10549 tr("Collecting locking information for all attached media failed"));
10550 }
10551 }
10552 }
10553
10554 /* Now lock all media. If this fails, nothing is locked. */
10555 alock.release();
10556 rc = lockedMediaMap->Lock();
10557 alock.acquire();
10558 if (FAILED(rc))
10559 {
10560 throw setError(rc,
10561 tr("Locking of attached media failed"));
10562 }
10563 }
10564
10565 /* remember the current list (note that we don't use backup() since
10566 * mMediaData may be already backed up) */
10567 MediaData::AttachmentList atts = mMediaData->mAttachments;
10568
10569 /* start from scratch */
10570 mMediaData->mAttachments.clear();
10571
10572 /* go through remembered attachments and create diffs for normal hard
10573 * disks and attach them */
10574 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10575 it != atts.end();
10576 ++it)
10577 {
10578 MediumAttachment* pAtt = *it;
10579
10580 DeviceType_T devType = pAtt->i_getType();
10581 Medium* pMedium = pAtt->i_getMedium();
10582
10583 if ( devType != DeviceType_HardDisk
10584 || pMedium == NULL
10585 || pMedium->i_getType() != MediumType_Normal)
10586 {
10587 /* copy the attachment as is */
10588
10589 /** @todo the progress object created in Console::TakeSnaphot
10590 * only expects operations for hard disks. Later other
10591 * device types need to show up in the progress as well. */
10592 if (devType == DeviceType_HardDisk)
10593 {
10594 if (pMedium == NULL)
10595 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10596 aWeight); // weight
10597 else
10598 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10599 pMedium->i_getBase()->i_getName().c_str()).raw(),
10600 aWeight); // weight
10601 }
10602
10603 mMediaData->mAttachments.push_back(pAtt);
10604 continue;
10605 }
10606
10607 /* need a diff */
10608 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10609 pMedium->i_getBase()->i_getName().c_str()).raw(),
10610 aWeight); // weight
10611
10612 Utf8Str strFullSnapshotFolder;
10613 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10614
10615 ComObjPtr<Medium> diff;
10616 diff.createObject();
10617 // store the diff in the same registry as the parent
10618 // (this cannot fail here because we can't create implicit diffs for
10619 // unregistered images)
10620 Guid uuidRegistryParent;
10621 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10622 Assert(fInRegistry); NOREF(fInRegistry);
10623 rc = diff->init(mParent,
10624 pMedium->i_getPreferredDiffFormat(),
10625 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10626 uuidRegistryParent,
10627 DeviceType_HardDisk);
10628 if (FAILED(rc)) throw rc;
10629
10630 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10631 * the push_back? Looks like we're going to release medium with the
10632 * wrong kind of lock (general issue with if we fail anywhere at all)
10633 * and an orphaned VDI in the snapshots folder. */
10634
10635 /* update the appropriate lock list */
10636 MediumLockList *pMediumLockList;
10637 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10638 AssertComRCThrowRC(rc);
10639 if (aOnline)
10640 {
10641 alock.release();
10642 /* The currently attached medium will be read-only, change
10643 * the lock type to read. */
10644 rc = pMediumLockList->Update(pMedium, false);
10645 alock.acquire();
10646 AssertComRCThrowRC(rc);
10647 }
10648
10649 /* release the locks before the potentially lengthy operation */
10650 alock.release();
10651 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10652 pMediumLockList,
10653 NULL /* aProgress */,
10654 true /* aWait */);
10655 alock.acquire();
10656 if (FAILED(rc)) throw rc;
10657
10658 /* actual lock list update is done in Medium::commitMedia */
10659
10660 rc = diff->i_addBackReference(mData->mUuid);
10661 AssertComRCThrowRC(rc);
10662
10663 /* add a new attachment */
10664 ComObjPtr<MediumAttachment> attachment;
10665 attachment.createObject();
10666 rc = attachment->init(this,
10667 diff,
10668 pAtt->i_getControllerName(),
10669 pAtt->i_getPort(),
10670 pAtt->i_getDevice(),
10671 DeviceType_HardDisk,
10672 true /* aImplicit */,
10673 false /* aPassthrough */,
10674 false /* aTempEject */,
10675 pAtt->i_getNonRotational(),
10676 pAtt->i_getDiscard(),
10677 pAtt->i_getHotPluggable(),
10678 pAtt->i_getBandwidthGroup());
10679 if (FAILED(rc)) throw rc;
10680
10681 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10682 AssertComRCThrowRC(rc);
10683 mMediaData->mAttachments.push_back(attachment);
10684 }
10685 }
10686 catch (HRESULT aRC) { rc = aRC; }
10687
10688 /* unlock all hard disks we locked when there is no VM */
10689 if (!aOnline)
10690 {
10691 ErrorInfoKeeper eik;
10692
10693 HRESULT rc1 = lockedMediaMap->Clear();
10694 AssertComRC(rc1);
10695 }
10696
10697 return rc;
10698}
10699
10700/**
10701 * Deletes implicit differencing hard disks created either by
10702 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10703 *
10704 * Note that to delete hard disks created by #AttachDevice() this method is
10705 * called from #fixupMedia() when the changes are rolled back.
10706 *
10707 * @note Locks this object and the media tree for writing.
10708 */
10709HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10710{
10711 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10712
10713 AutoCaller autoCaller(this);
10714 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10715
10716 AutoMultiWriteLock2 alock(this->lockHandle(),
10717 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10718
10719 /* We absolutely must have backed up state. */
10720 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10721
10722 /* Check if there are any implicitly created diff images. */
10723 bool fImplicitDiffs = false;
10724 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10725 it != mMediaData->mAttachments.end();
10726 ++it)
10727 {
10728 const ComObjPtr<MediumAttachment> &pAtt = *it;
10729 if (pAtt->i_isImplicit())
10730 {
10731 fImplicitDiffs = true;
10732 break;
10733 }
10734 }
10735 /* If there is nothing to do, leave early. This saves lots of image locking
10736 * effort. It also avoids a MachineStateChanged event without real reason.
10737 * This is important e.g. when loading a VM config, because there should be
10738 * no events. Otherwise API clients can become thoroughly confused for
10739 * inaccessible VMs (the code for loading VM configs uses this method for
10740 * cleanup if the config makes no sense), as they take such events as an
10741 * indication that the VM is alive, and they would force the VM config to
10742 * be reread, leading to an endless loop. */
10743 if (!fImplicitDiffs)
10744 return S_OK;
10745
10746 HRESULT rc = S_OK;
10747 MachineState_T oldState = mData->mMachineState;
10748
10749 /* will release the lock before the potentially lengthy operation,
10750 * so protect with the special state (unless already protected) */
10751 if ( oldState != MachineState_Saving
10752 && oldState != MachineState_LiveSnapshotting
10753 && oldState != MachineState_RestoringSnapshot
10754 && oldState != MachineState_DeletingSnapshot
10755 && oldState != MachineState_DeletingSnapshotOnline
10756 && oldState != MachineState_DeletingSnapshotPaused
10757 )
10758 i_setMachineState(MachineState_SettingUp);
10759
10760 // use appropriate locked media map (online or offline)
10761 MediumLockListMap lockedMediaOffline;
10762 MediumLockListMap *lockedMediaMap;
10763 if (aOnline)
10764 lockedMediaMap = &mData->mSession.mLockedMedia;
10765 else
10766 lockedMediaMap = &lockedMediaOffline;
10767
10768 try
10769 {
10770 if (!aOnline)
10771 {
10772 /* lock all attached hard disks early to detect "in use"
10773 * situations before deleting actual diffs */
10774 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10775 it != mMediaData->mAttachments.end();
10776 ++it)
10777 {
10778 MediumAttachment* pAtt = *it;
10779 if (pAtt->i_getType() == DeviceType_HardDisk)
10780 {
10781 Medium* pMedium = pAtt->i_getMedium();
10782 Assert(pMedium);
10783
10784 MediumLockList *pMediumLockList(new MediumLockList());
10785 alock.release();
10786 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10787 false /* fMediumLockWrite */,
10788 false /* fMediumLockWriteAll */,
10789 NULL,
10790 *pMediumLockList);
10791 alock.acquire();
10792
10793 if (FAILED(rc))
10794 {
10795 delete pMediumLockList;
10796 throw rc;
10797 }
10798
10799 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10800 if (FAILED(rc))
10801 throw rc;
10802 }
10803 }
10804
10805 if (FAILED(rc))
10806 throw rc;
10807 } // end of offline
10808
10809 /* Lock lists are now up to date and include implicitly created media */
10810
10811 /* Go through remembered attachments and delete all implicitly created
10812 * diffs and fix up the attachment information */
10813 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10814 MediaData::AttachmentList implicitAtts;
10815 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10816 it != mMediaData->mAttachments.end();
10817 ++it)
10818 {
10819 ComObjPtr<MediumAttachment> pAtt = *it;
10820 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10821 if (pMedium.isNull())
10822 continue;
10823
10824 // Implicit attachments go on the list for deletion and back references are removed.
10825 if (pAtt->i_isImplicit())
10826 {
10827 /* Deassociate and mark for deletion */
10828 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10829 rc = pMedium->i_removeBackReference(mData->mUuid);
10830 if (FAILED(rc))
10831 throw rc;
10832 implicitAtts.push_back(pAtt);
10833 continue;
10834 }
10835
10836 /* Was this medium attached before? */
10837 if (!i_findAttachment(oldAtts, pMedium))
10838 {
10839 /* no: de-associate */
10840 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10841 rc = pMedium->i_removeBackReference(mData->mUuid);
10842 if (FAILED(rc))
10843 throw rc;
10844 continue;
10845 }
10846 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10847 }
10848
10849 /* If there are implicit attachments to delete, throw away the lock
10850 * map contents (which will unlock all media) since the medium
10851 * attachments will be rolled back. Below we need to completely
10852 * recreate the lock map anyway since it is infinitely complex to
10853 * do this incrementally (would need reconstructing each attachment
10854 * change, which would be extremely hairy). */
10855 if (implicitAtts.size() != 0)
10856 {
10857 ErrorInfoKeeper eik;
10858
10859 HRESULT rc1 = lockedMediaMap->Clear();
10860 AssertComRC(rc1);
10861 }
10862
10863 /* rollback hard disk changes */
10864 mMediaData.rollback();
10865
10866 MultiResult mrc(S_OK);
10867
10868 // Delete unused implicit diffs.
10869 if (implicitAtts.size() != 0)
10870 {
10871 alock.release();
10872
10873 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10874 {
10875 // Remove medium associated with this attachment.
10876 ComObjPtr<MediumAttachment> pAtt = *it;
10877 Assert(pAtt);
10878 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10879 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10880 Assert(pMedium);
10881
10882 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10883 // continue on delete failure, just collect error messages
10884 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10885 pMedium->i_getLocationFull().c_str() ));
10886 mrc = rc;
10887 }
10888
10889 alock.acquire();
10890
10891 /* if there is a VM recreate media lock map as mentioned above,
10892 * otherwise it is a waste of time and we leave things unlocked */
10893 if (aOnline)
10894 {
10895 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10896 /* must never be NULL, but better safe than sorry */
10897 if (!pMachine.isNull())
10898 {
10899 alock.release();
10900 rc = mData->mSession.mMachine->i_lockMedia();
10901 alock.acquire();
10902 if (FAILED(rc))
10903 throw rc;
10904 }
10905 }
10906 }
10907 }
10908 catch (HRESULT aRC) {rc = aRC;}
10909
10910 if (mData->mMachineState == MachineState_SettingUp)
10911 i_setMachineState(oldState);
10912
10913 /* unlock all hard disks we locked when there is no VM */
10914 if (!aOnline)
10915 {
10916 ErrorInfoKeeper eik;
10917
10918 HRESULT rc1 = lockedMediaMap->Clear();
10919 AssertComRC(rc1);
10920 }
10921
10922 return rc;
10923}
10924
10925
10926/**
10927 * Looks through the given list of media attachments for one with the given parameters
10928 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10929 * can be searched as well if needed.
10930 *
10931 * @param list
10932 * @param aControllerName
10933 * @param aControllerPort
10934 * @param aDevice
10935 * @return
10936 */
10937MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10938 IN_BSTR aControllerName,
10939 LONG aControllerPort,
10940 LONG aDevice)
10941{
10942 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10943 {
10944 MediumAttachment *pAttach = *it;
10945 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10946 return pAttach;
10947 }
10948
10949 return NULL;
10950}
10951
10952/**
10953 * Looks through the given list of media attachments for one with the given parameters
10954 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10955 * can be searched as well if needed.
10956 *
10957 * @param list
10958 * @param aControllerName
10959 * @param aControllerPort
10960 * @param aDevice
10961 * @return
10962 */
10963MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10964 ComObjPtr<Medium> pMedium)
10965{
10966 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10967 {
10968 MediumAttachment *pAttach = *it;
10969 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10970 if (pMediumThis == pMedium)
10971 return pAttach;
10972 }
10973
10974 return NULL;
10975}
10976
10977/**
10978 * Looks through the given list of media attachments for one with the given parameters
10979 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10980 * can be searched as well if needed.
10981 *
10982 * @param list
10983 * @param aControllerName
10984 * @param aControllerPort
10985 * @param aDevice
10986 * @return
10987 */
10988MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10989 Guid &id)
10990{
10991 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10992 {
10993 MediumAttachment *pAttach = *it;
10994 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10995 if (pMediumThis->i_getId() == id)
10996 return pAttach;
10997 }
10998
10999 return NULL;
11000}
11001
11002/**
11003 * Main implementation for Machine::DetachDevice. This also gets called
11004 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11005 *
11006 * @param pAttach Medium attachment to detach.
11007 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11008 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11009 * SnapshotMachine, and this must be its snapshot.
11010 * @return
11011 */
11012HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11013 AutoWriteLock &writeLock,
11014 Snapshot *pSnapshot)
11015{
11016 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11017 DeviceType_T mediumType = pAttach->i_getType();
11018
11019 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11020
11021 if (pAttach->i_isImplicit())
11022 {
11023 /* attempt to implicitly delete the implicitly created diff */
11024
11025 /// @todo move the implicit flag from MediumAttachment to Medium
11026 /// and forbid any hard disk operation when it is implicit. Or maybe
11027 /// a special media state for it to make it even more simple.
11028
11029 Assert(mMediaData.isBackedUp());
11030
11031 /* will release the lock before the potentially lengthy operation, so
11032 * protect with the special state */
11033 MachineState_T oldState = mData->mMachineState;
11034 i_setMachineState(MachineState_SettingUp);
11035
11036 writeLock.release();
11037
11038 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11039 true /*aWait*/);
11040
11041 writeLock.acquire();
11042
11043 i_setMachineState(oldState);
11044
11045 if (FAILED(rc)) return rc;
11046 }
11047
11048 i_setModified(IsModified_Storage);
11049 mMediaData.backup();
11050 mMediaData->mAttachments.remove(pAttach);
11051
11052 if (!oldmedium.isNull())
11053 {
11054 // if this is from a snapshot, do not defer detachment to commitMedia()
11055 if (pSnapshot)
11056 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11057 // else if non-hard disk media, do not defer detachment to commitMedia() either
11058 else if (mediumType != DeviceType_HardDisk)
11059 oldmedium->i_removeBackReference(mData->mUuid);
11060 }
11061
11062 return S_OK;
11063}
11064
11065/**
11066 * Goes thru all media of the given list and
11067 *
11068 * 1) calls i_detachDevice() on each of them for this machine and
11069 * 2) adds all Medium objects found in the process to the given list,
11070 * depending on cleanupMode.
11071 *
11072 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11073 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11074 * media to the list.
11075 *
11076 * This gets called from Machine::Unregister, both for the actual Machine and
11077 * the SnapshotMachine objects that might be found in the snapshots.
11078 *
11079 * Requires caller and locking. The machine lock must be passed in because it
11080 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11081 *
11082 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11083 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11084 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11085 * Full, then all media get added;
11086 * otherwise no media get added.
11087 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11088 * @return
11089 */
11090HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11091 Snapshot *pSnapshot,
11092 CleanupMode_T cleanupMode,
11093 MediaList &llMedia)
11094{
11095 Assert(isWriteLockOnCurrentThread());
11096
11097 HRESULT rc;
11098
11099 // make a temporary list because i_detachDevice invalidates iterators into
11100 // mMediaData->mAttachments
11101 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11102
11103 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11104 {
11105 ComObjPtr<MediumAttachment> &pAttach = *it;
11106 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11107
11108 if (!pMedium.isNull())
11109 {
11110 AutoCaller mac(pMedium);
11111 if (FAILED(mac.rc())) return mac.rc();
11112 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11113 DeviceType_T devType = pMedium->i_getDeviceType();
11114 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11115 && devType == DeviceType_HardDisk)
11116 || (cleanupMode == CleanupMode_Full)
11117 )
11118 {
11119 llMedia.push_back(pMedium);
11120 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11121 /* Not allowed to keep this lock as below we need the parent
11122 * medium lock, and the lock order is parent to child. */
11123 lock.release();
11124 /*
11125 * Search for medias which are not attached to any machine, but
11126 * in the chain to an attached disk. Mediums are only consided
11127 * if they are:
11128 * - have only one child
11129 * - no references to any machines
11130 * - are of normal medium type
11131 */
11132 while (!pParent.isNull())
11133 {
11134 AutoCaller mac1(pParent);
11135 if (FAILED(mac1.rc())) return mac1.rc();
11136 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11137 if (pParent->i_getChildren().size() == 1)
11138 {
11139 if ( pParent->i_getMachineBackRefCount() == 0
11140 && pParent->i_getType() == MediumType_Normal
11141 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11142 llMedia.push_back(pParent);
11143 }
11144 else
11145 break;
11146 pParent = pParent->i_getParent();
11147 }
11148 }
11149 }
11150
11151 // real machine: then we need to use the proper method
11152 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11153
11154 if (FAILED(rc))
11155 return rc;
11156 }
11157
11158 return S_OK;
11159}
11160
11161/**
11162 * Perform deferred hard disk detachments.
11163 *
11164 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11165 * backed up).
11166 *
11167 * If @a aOnline is @c true then this method will also unlock the old hard disks
11168 * for which the new implicit diffs were created and will lock these new diffs for
11169 * writing.
11170 *
11171 * @param aOnline Whether the VM was online prior to this operation.
11172 *
11173 * @note Locks this object for writing!
11174 */
11175void Machine::i_commitMedia(bool aOnline /*= false*/)
11176{
11177 AutoCaller autoCaller(this);
11178 AssertComRCReturnVoid(autoCaller.rc());
11179
11180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11181
11182 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11183
11184 HRESULT rc = S_OK;
11185
11186 /* no attach/detach operations -- nothing to do */
11187 if (!mMediaData.isBackedUp())
11188 return;
11189
11190 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11191 bool fMediaNeedsLocking = false;
11192
11193 /* enumerate new attachments */
11194 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11195 it != mMediaData->mAttachments.end();
11196 ++it)
11197 {
11198 MediumAttachment *pAttach = *it;
11199
11200 pAttach->i_commit();
11201
11202 Medium* pMedium = pAttach->i_getMedium();
11203 bool fImplicit = pAttach->i_isImplicit();
11204
11205 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11206 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11207 fImplicit));
11208
11209 /** @todo convert all this Machine-based voodoo to MediumAttachment
11210 * based commit logic. */
11211 if (fImplicit)
11212 {
11213 /* convert implicit attachment to normal */
11214 pAttach->i_setImplicit(false);
11215
11216 if ( aOnline
11217 && pMedium
11218 && pAttach->i_getType() == DeviceType_HardDisk
11219 )
11220 {
11221 /* update the appropriate lock list */
11222 MediumLockList *pMediumLockList;
11223 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11224 AssertComRC(rc);
11225 if (pMediumLockList)
11226 {
11227 /* unlock if there's a need to change the locking */
11228 if (!fMediaNeedsLocking)
11229 {
11230 rc = mData->mSession.mLockedMedia.Unlock();
11231 AssertComRC(rc);
11232 fMediaNeedsLocking = true;
11233 }
11234 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11235 AssertComRC(rc);
11236 rc = pMediumLockList->Append(pMedium, true);
11237 AssertComRC(rc);
11238 }
11239 }
11240
11241 continue;
11242 }
11243
11244 if (pMedium)
11245 {
11246 /* was this medium attached before? */
11247 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11248 {
11249 MediumAttachment *pOldAttach = *oldIt;
11250 if (pOldAttach->i_getMedium() == pMedium)
11251 {
11252 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11253
11254 /* yes: remove from old to avoid de-association */
11255 oldAtts.erase(oldIt);
11256 break;
11257 }
11258 }
11259 }
11260 }
11261
11262 /* enumerate remaining old attachments and de-associate from the
11263 * current machine state */
11264 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11265 {
11266 MediumAttachment *pAttach = *it;
11267 Medium* pMedium = pAttach->i_getMedium();
11268
11269 /* Detach only hard disks, since DVD/floppy media is detached
11270 * instantly in MountMedium. */
11271 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11272 {
11273 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11274
11275 /* now de-associate from the current machine state */
11276 rc = pMedium->i_removeBackReference(mData->mUuid);
11277 AssertComRC(rc);
11278
11279 if (aOnline)
11280 {
11281 /* unlock since medium is not used anymore */
11282 MediumLockList *pMediumLockList;
11283 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11284 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11285 {
11286 /* this happens for online snapshots, there the attachment
11287 * is changing, but only to a diff image created under
11288 * the old one, so there is no separate lock list */
11289 Assert(!pMediumLockList);
11290 }
11291 else
11292 {
11293 AssertComRC(rc);
11294 if (pMediumLockList)
11295 {
11296 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11297 AssertComRC(rc);
11298 }
11299 }
11300 }
11301 }
11302 }
11303
11304 /* take media locks again so that the locking state is consistent */
11305 if (fMediaNeedsLocking)
11306 {
11307 Assert(aOnline);
11308 rc = mData->mSession.mLockedMedia.Lock();
11309 AssertComRC(rc);
11310 }
11311
11312 /* commit the hard disk changes */
11313 mMediaData.commit();
11314
11315 if (i_isSessionMachine())
11316 {
11317 /*
11318 * Update the parent machine to point to the new owner.
11319 * This is necessary because the stored parent will point to the
11320 * session machine otherwise and cause crashes or errors later
11321 * when the session machine gets invalid.
11322 */
11323 /** @todo Change the MediumAttachment class to behave like any other
11324 * class in this regard by creating peer MediumAttachment
11325 * objects for session machines and share the data with the peer
11326 * machine.
11327 */
11328 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11329 it != mMediaData->mAttachments.end();
11330 ++it)
11331 (*it)->i_updateParentMachine(mPeer);
11332
11333 /* attach new data to the primary machine and reshare it */
11334 mPeer->mMediaData.attach(mMediaData);
11335 }
11336
11337 return;
11338}
11339
11340/**
11341 * Perform deferred deletion of implicitly created diffs.
11342 *
11343 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11344 * backed up).
11345 *
11346 * @note Locks this object for writing!
11347 */
11348void Machine::i_rollbackMedia()
11349{
11350 AutoCaller autoCaller(this);
11351 AssertComRCReturnVoid(autoCaller.rc());
11352
11353 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11354 LogFlowThisFunc(("Entering rollbackMedia\n"));
11355
11356 HRESULT rc = S_OK;
11357
11358 /* no attach/detach operations -- nothing to do */
11359 if (!mMediaData.isBackedUp())
11360 return;
11361
11362 /* enumerate new attachments */
11363 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11364 it != mMediaData->mAttachments.end();
11365 ++it)
11366 {
11367 MediumAttachment *pAttach = *it;
11368 /* Fix up the backrefs for DVD/floppy media. */
11369 if (pAttach->i_getType() != DeviceType_HardDisk)
11370 {
11371 Medium* pMedium = pAttach->i_getMedium();
11372 if (pMedium)
11373 {
11374 rc = pMedium->i_removeBackReference(mData->mUuid);
11375 AssertComRC(rc);
11376 }
11377 }
11378
11379 (*it)->i_rollback();
11380
11381 pAttach = *it;
11382 /* Fix up the backrefs for DVD/floppy media. */
11383 if (pAttach->i_getType() != DeviceType_HardDisk)
11384 {
11385 Medium* pMedium = pAttach->i_getMedium();
11386 if (pMedium)
11387 {
11388 rc = pMedium->i_addBackReference(mData->mUuid);
11389 AssertComRC(rc);
11390 }
11391 }
11392 }
11393
11394 /** @todo convert all this Machine-based voodoo to MediumAttachment
11395 * based rollback logic. */
11396 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11397
11398 return;
11399}
11400
11401/**
11402 * Returns true if the settings file is located in the directory named exactly
11403 * as the machine; this means, among other things, that the machine directory
11404 * should be auto-renamed.
11405 *
11406 * @param aSettingsDir if not NULL, the full machine settings file directory
11407 * name will be assigned there.
11408 *
11409 * @note Doesn't lock anything.
11410 * @note Not thread safe (must be called from this object's lock).
11411 */
11412bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11413{
11414 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11415 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11416 if (aSettingsDir)
11417 *aSettingsDir = strMachineDirName;
11418 strMachineDirName.stripPath(); // vmname
11419 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11420 strConfigFileOnly.stripPath() // vmname.vbox
11421 .stripSuffix(); // vmname
11422 /** @todo hack, make somehow use of ComposeMachineFilename */
11423 if (mUserData->s.fDirectoryIncludesUUID)
11424 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11425
11426 AssertReturn(!strMachineDirName.isEmpty(), false);
11427 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11428
11429 return strMachineDirName == strConfigFileOnly;
11430}
11431
11432/**
11433 * Discards all changes to machine settings.
11434 *
11435 * @param aNotify Whether to notify the direct session about changes or not.
11436 *
11437 * @note Locks objects for writing!
11438 */
11439void Machine::i_rollback(bool aNotify)
11440{
11441 AutoCaller autoCaller(this);
11442 AssertComRCReturn(autoCaller.rc(), (void)0);
11443
11444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11445
11446 if (!mStorageControllers.isNull())
11447 {
11448 if (mStorageControllers.isBackedUp())
11449 {
11450 /* unitialize all new devices (absent in the backed up list). */
11451 StorageControllerList::const_iterator it = mStorageControllers->begin();
11452 StorageControllerList *backedList = mStorageControllers.backedUpData();
11453 while (it != mStorageControllers->end())
11454 {
11455 if ( std::find(backedList->begin(), backedList->end(), *it)
11456 == backedList->end()
11457 )
11458 {
11459 (*it)->uninit();
11460 }
11461 ++it;
11462 }
11463
11464 /* restore the list */
11465 mStorageControllers.rollback();
11466 }
11467
11468 /* rollback any changes to devices after restoring the list */
11469 if (mData->flModifications & IsModified_Storage)
11470 {
11471 StorageControllerList::const_iterator it = mStorageControllers->begin();
11472 while (it != mStorageControllers->end())
11473 {
11474 (*it)->i_rollback();
11475 ++it;
11476 }
11477 }
11478 }
11479
11480 if (!mUSBControllers.isNull())
11481 {
11482 if (mUSBControllers.isBackedUp())
11483 {
11484 /* unitialize all new devices (absent in the backed up list). */
11485 USBControllerList::const_iterator it = mUSBControllers->begin();
11486 USBControllerList *backedList = mUSBControllers.backedUpData();
11487 while (it != mUSBControllers->end())
11488 {
11489 if ( std::find(backedList->begin(), backedList->end(), *it)
11490 == backedList->end()
11491 )
11492 {
11493 (*it)->uninit();
11494 }
11495 ++it;
11496 }
11497
11498 /* restore the list */
11499 mUSBControllers.rollback();
11500 }
11501
11502 /* rollback any changes to devices after restoring the list */
11503 if (mData->flModifications & IsModified_USB)
11504 {
11505 USBControllerList::const_iterator it = mUSBControllers->begin();
11506 while (it != mUSBControllers->end())
11507 {
11508 (*it)->i_rollback();
11509 ++it;
11510 }
11511 }
11512 }
11513
11514 mUserData.rollback();
11515
11516 mHWData.rollback();
11517
11518 if (mData->flModifications & IsModified_Storage)
11519 i_rollbackMedia();
11520
11521 if (mBIOSSettings)
11522 mBIOSSettings->i_rollback();
11523
11524 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11525 mVRDEServer->i_rollback();
11526
11527 if (mAudioAdapter)
11528 mAudioAdapter->i_rollback();
11529
11530 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11531 mUSBDeviceFilters->i_rollback();
11532
11533 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11534 mBandwidthControl->i_rollback();
11535
11536 if (!mHWData.isNull())
11537 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11538 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11539 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11540 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11541
11542 if (mData->flModifications & IsModified_NetworkAdapters)
11543 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11544 if ( mNetworkAdapters[slot]
11545 && mNetworkAdapters[slot]->i_isModified())
11546 {
11547 mNetworkAdapters[slot]->i_rollback();
11548 networkAdapters[slot] = mNetworkAdapters[slot];
11549 }
11550
11551 if (mData->flModifications & IsModified_SerialPorts)
11552 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11553 if ( mSerialPorts[slot]
11554 && mSerialPorts[slot]->i_isModified())
11555 {
11556 mSerialPorts[slot]->i_rollback();
11557 serialPorts[slot] = mSerialPorts[slot];
11558 }
11559
11560 if (mData->flModifications & IsModified_ParallelPorts)
11561 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11562 if ( mParallelPorts[slot]
11563 && mParallelPorts[slot]->i_isModified())
11564 {
11565 mParallelPorts[slot]->i_rollback();
11566 parallelPorts[slot] = mParallelPorts[slot];
11567 }
11568
11569 if (aNotify)
11570 {
11571 /* inform the direct session about changes */
11572
11573 ComObjPtr<Machine> that = this;
11574 uint32_t flModifications = mData->flModifications;
11575 alock.release();
11576
11577 if (flModifications & IsModified_SharedFolders)
11578 that->i_onSharedFolderChange();
11579
11580 if (flModifications & IsModified_VRDEServer)
11581 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11582 if (flModifications & IsModified_USB)
11583 that->i_onUSBControllerChange();
11584
11585 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11586 if (networkAdapters[slot])
11587 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11588 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11589 if (serialPorts[slot])
11590 that->i_onSerialPortChange(serialPorts[slot]);
11591 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11592 if (parallelPorts[slot])
11593 that->i_onParallelPortChange(parallelPorts[slot]);
11594
11595 if (flModifications & IsModified_Storage)
11596 that->i_onStorageControllerChange();
11597
11598#if 0
11599 if (flModifications & IsModified_BandwidthControl)
11600 that->onBandwidthControlChange();
11601#endif
11602 }
11603}
11604
11605/**
11606 * Commits all the changes to machine settings.
11607 *
11608 * Note that this operation is supposed to never fail.
11609 *
11610 * @note Locks this object and children for writing.
11611 */
11612void Machine::i_commit()
11613{
11614 AutoCaller autoCaller(this);
11615 AssertComRCReturnVoid(autoCaller.rc());
11616
11617 AutoCaller peerCaller(mPeer);
11618 AssertComRCReturnVoid(peerCaller.rc());
11619
11620 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11621
11622 /*
11623 * use safe commit to ensure Snapshot machines (that share mUserData)
11624 * will still refer to a valid memory location
11625 */
11626 mUserData.commitCopy();
11627
11628 mHWData.commit();
11629
11630 if (mMediaData.isBackedUp())
11631 i_commitMedia(Global::IsOnline(mData->mMachineState));
11632
11633 mBIOSSettings->i_commit();
11634 mVRDEServer->i_commit();
11635 mAudioAdapter->i_commit();
11636 mUSBDeviceFilters->i_commit();
11637 mBandwidthControl->i_commit();
11638
11639 /* Since mNetworkAdapters is a list which might have been changed (resized)
11640 * without using the Backupable<> template we need to handle the copying
11641 * of the list entries manually, including the creation of peers for the
11642 * new objects. */
11643 bool commitNetworkAdapters = false;
11644 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11645 if (mPeer)
11646 {
11647 /* commit everything, even the ones which will go away */
11648 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11649 mNetworkAdapters[slot]->i_commit();
11650 /* copy over the new entries, creating a peer and uninit the original */
11651 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11652 for (size_t slot = 0; slot < newSize; slot++)
11653 {
11654 /* look if this adapter has a peer device */
11655 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11656 if (!peer)
11657 {
11658 /* no peer means the adapter is a newly created one;
11659 * create a peer owning data this data share it with */
11660 peer.createObject();
11661 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11662 }
11663 mPeer->mNetworkAdapters[slot] = peer;
11664 }
11665 /* uninit any no longer needed network adapters */
11666 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11667 mNetworkAdapters[slot]->uninit();
11668 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11669 {
11670 if (mPeer->mNetworkAdapters[slot])
11671 mPeer->mNetworkAdapters[slot]->uninit();
11672 }
11673 /* Keep the original network adapter count until this point, so that
11674 * discarding a chipset type change will not lose settings. */
11675 mNetworkAdapters.resize(newSize);
11676 mPeer->mNetworkAdapters.resize(newSize);
11677 }
11678 else
11679 {
11680 /* we have no peer (our parent is the newly created machine);
11681 * just commit changes to the network adapters */
11682 commitNetworkAdapters = true;
11683 }
11684 if (commitNetworkAdapters)
11685 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11686 mNetworkAdapters[slot]->i_commit();
11687
11688 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11689 mSerialPorts[slot]->i_commit();
11690 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11691 mParallelPorts[slot]->i_commit();
11692
11693 bool commitStorageControllers = false;
11694
11695 if (mStorageControllers.isBackedUp())
11696 {
11697 mStorageControllers.commit();
11698
11699 if (mPeer)
11700 {
11701 /* Commit all changes to new controllers (this will reshare data with
11702 * peers for those who have peers) */
11703 StorageControllerList *newList = new StorageControllerList();
11704 StorageControllerList::const_iterator it = mStorageControllers->begin();
11705 while (it != mStorageControllers->end())
11706 {
11707 (*it)->i_commit();
11708
11709 /* look if this controller has a peer device */
11710 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11711 if (!peer)
11712 {
11713 /* no peer means the device is a newly created one;
11714 * create a peer owning data this device share it with */
11715 peer.createObject();
11716 peer->init(mPeer, *it, true /* aReshare */);
11717 }
11718 else
11719 {
11720 /* remove peer from the old list */
11721 mPeer->mStorageControllers->remove(peer);
11722 }
11723 /* and add it to the new list */
11724 newList->push_back(peer);
11725
11726 ++it;
11727 }
11728
11729 /* uninit old peer's controllers that are left */
11730 it = mPeer->mStorageControllers->begin();
11731 while (it != mPeer->mStorageControllers->end())
11732 {
11733 (*it)->uninit();
11734 ++it;
11735 }
11736
11737 /* attach new list of controllers to our peer */
11738 mPeer->mStorageControllers.attach(newList);
11739 }
11740 else
11741 {
11742 /* we have no peer (our parent is the newly created machine);
11743 * just commit changes to devices */
11744 commitStorageControllers = true;
11745 }
11746 }
11747 else
11748 {
11749 /* the list of controllers itself is not changed,
11750 * just commit changes to controllers themselves */
11751 commitStorageControllers = true;
11752 }
11753
11754 if (commitStorageControllers)
11755 {
11756 StorageControllerList::const_iterator it = mStorageControllers->begin();
11757 while (it != mStorageControllers->end())
11758 {
11759 (*it)->i_commit();
11760 ++it;
11761 }
11762 }
11763
11764 bool commitUSBControllers = false;
11765
11766 if (mUSBControllers.isBackedUp())
11767 {
11768 mUSBControllers.commit();
11769
11770 if (mPeer)
11771 {
11772 /* Commit all changes to new controllers (this will reshare data with
11773 * peers for those who have peers) */
11774 USBControllerList *newList = new USBControllerList();
11775 USBControllerList::const_iterator it = mUSBControllers->begin();
11776 while (it != mUSBControllers->end())
11777 {
11778 (*it)->i_commit();
11779
11780 /* look if this controller has a peer device */
11781 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11782 if (!peer)
11783 {
11784 /* no peer means the device is a newly created one;
11785 * create a peer owning data this device share it with */
11786 peer.createObject();
11787 peer->init(mPeer, *it, true /* aReshare */);
11788 }
11789 else
11790 {
11791 /* remove peer from the old list */
11792 mPeer->mUSBControllers->remove(peer);
11793 }
11794 /* and add it to the new list */
11795 newList->push_back(peer);
11796
11797 ++it;
11798 }
11799
11800 /* uninit old peer's controllers that are left */
11801 it = mPeer->mUSBControllers->begin();
11802 while (it != mPeer->mUSBControllers->end())
11803 {
11804 (*it)->uninit();
11805 ++it;
11806 }
11807
11808 /* attach new list of controllers to our peer */
11809 mPeer->mUSBControllers.attach(newList);
11810 }
11811 else
11812 {
11813 /* we have no peer (our parent is the newly created machine);
11814 * just commit changes to devices */
11815 commitUSBControllers = true;
11816 }
11817 }
11818 else
11819 {
11820 /* the list of controllers itself is not changed,
11821 * just commit changes to controllers themselves */
11822 commitUSBControllers = true;
11823 }
11824
11825 if (commitUSBControllers)
11826 {
11827 USBControllerList::const_iterator it = mUSBControllers->begin();
11828 while (it != mUSBControllers->end())
11829 {
11830 (*it)->i_commit();
11831 ++it;
11832 }
11833 }
11834
11835 if (i_isSessionMachine())
11836 {
11837 /* attach new data to the primary machine and reshare it */
11838 mPeer->mUserData.attach(mUserData);
11839 mPeer->mHWData.attach(mHWData);
11840 /* mMediaData is reshared by fixupMedia */
11841 // mPeer->mMediaData.attach(mMediaData);
11842 Assert(mPeer->mMediaData.data() == mMediaData.data());
11843 }
11844}
11845
11846/**
11847 * Copies all the hardware data from the given machine.
11848 *
11849 * Currently, only called when the VM is being restored from a snapshot. In
11850 * particular, this implies that the VM is not running during this method's
11851 * call.
11852 *
11853 * @note This method must be called from under this object's lock.
11854 *
11855 * @note This method doesn't call #commit(), so all data remains backed up and
11856 * unsaved.
11857 */
11858void Machine::i_copyFrom(Machine *aThat)
11859{
11860 AssertReturnVoid(!i_isSnapshotMachine());
11861 AssertReturnVoid(aThat->i_isSnapshotMachine());
11862
11863 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11864
11865 mHWData.assignCopy(aThat->mHWData);
11866
11867 // create copies of all shared folders (mHWData after attaching a copy
11868 // contains just references to original objects)
11869 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11870 it != mHWData->mSharedFolders.end();
11871 ++it)
11872 {
11873 ComObjPtr<SharedFolder> folder;
11874 folder.createObject();
11875 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11876 AssertComRC(rc);
11877 *it = folder;
11878 }
11879
11880 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11881 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11882 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11883 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11884 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11885
11886 /* create private copies of all controllers */
11887 mStorageControllers.backup();
11888 mStorageControllers->clear();
11889 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11890 it != aThat->mStorageControllers->end();
11891 ++it)
11892 {
11893 ComObjPtr<StorageController> ctrl;
11894 ctrl.createObject();
11895 ctrl->initCopy(this, *it);
11896 mStorageControllers->push_back(ctrl);
11897 }
11898
11899 /* create private copies of all USB controllers */
11900 mUSBControllers.backup();
11901 mUSBControllers->clear();
11902 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11903 it != aThat->mUSBControllers->end();
11904 ++it)
11905 {
11906 ComObjPtr<USBController> ctrl;
11907 ctrl.createObject();
11908 ctrl->initCopy(this, *it);
11909 mUSBControllers->push_back(ctrl);
11910 }
11911
11912 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11913 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11914 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11915 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11916 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11917 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11918 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11919}
11920
11921/**
11922 * Returns whether the given storage controller is hotplug capable.
11923 *
11924 * @returns true if the controller supports hotplugging
11925 * false otherwise.
11926 * @param enmCtrlType The controller type to check for.
11927 */
11928bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11929{
11930 ComPtr<ISystemProperties> systemProperties;
11931 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11932 if (FAILED(rc))
11933 return false;
11934
11935 BOOL aHotplugCapable = FALSE;
11936 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11937
11938 return RT_BOOL(aHotplugCapable);
11939}
11940
11941#ifdef VBOX_WITH_RESOURCE_USAGE_API
11942
11943void Machine::i_getDiskList(MediaList &list)
11944{
11945 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11946 it != mMediaData->mAttachments.end();
11947 ++it)
11948 {
11949 MediumAttachment* pAttach = *it;
11950 /* just in case */
11951 AssertStmt(pAttach, continue);
11952
11953 AutoCaller localAutoCallerA(pAttach);
11954 if (FAILED(localAutoCallerA.rc())) continue;
11955
11956 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11957
11958 if (pAttach->i_getType() == DeviceType_HardDisk)
11959 list.push_back(pAttach->i_getMedium());
11960 }
11961}
11962
11963void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11964{
11965 AssertReturnVoid(isWriteLockOnCurrentThread());
11966 AssertPtrReturnVoid(aCollector);
11967
11968 pm::CollectorHAL *hal = aCollector->getHAL();
11969 /* Create sub metrics */
11970 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11971 "Percentage of processor time spent in user mode by the VM process.");
11972 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11973 "Percentage of processor time spent in kernel mode by the VM process.");
11974 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11975 "Size of resident portion of VM process in memory.");
11976 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11977 "Actual size of all VM disks combined.");
11978 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11979 "Network receive rate.");
11980 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11981 "Network transmit rate.");
11982 /* Create and register base metrics */
11983 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11984 cpuLoadUser, cpuLoadKernel);
11985 aCollector->registerBaseMetric(cpuLoad);
11986 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11987 ramUsageUsed);
11988 aCollector->registerBaseMetric(ramUsage);
11989 MediaList disks;
11990 i_getDiskList(disks);
11991 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11992 diskUsageUsed);
11993 aCollector->registerBaseMetric(diskUsage);
11994
11995 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11996 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11997 new pm::AggregateAvg()));
11998 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11999 new pm::AggregateMin()));
12000 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12001 new pm::AggregateMax()));
12002 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12003 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12004 new pm::AggregateAvg()));
12005 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12006 new pm::AggregateMin()));
12007 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12008 new pm::AggregateMax()));
12009
12010 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12011 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12012 new pm::AggregateAvg()));
12013 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12014 new pm::AggregateMin()));
12015 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12016 new pm::AggregateMax()));
12017
12018 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12019 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12020 new pm::AggregateAvg()));
12021 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12022 new pm::AggregateMin()));
12023 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12024 new pm::AggregateMax()));
12025
12026
12027 /* Guest metrics collector */
12028 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12029 aCollector->registerGuest(mCollectorGuest);
12030 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12031 this, __PRETTY_FUNCTION__, mCollectorGuest));
12032
12033 /* Create sub metrics */
12034 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12035 "Percentage of processor time spent in user mode as seen by the guest.");
12036 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12037 "Percentage of processor time spent in kernel mode as seen by the guest.");
12038 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12039 "Percentage of processor time spent idling as seen by the guest.");
12040
12041 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12042 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12043 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12044 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12045 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12046 pm::SubMetric *guestMemCache = new pm::SubMetric(
12047 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12048
12049 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12050 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12051
12052 /* Create and register base metrics */
12053 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12054 machineNetRx, machineNetTx);
12055 aCollector->registerBaseMetric(machineNetRate);
12056
12057 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12058 guestLoadUser, guestLoadKernel, guestLoadIdle);
12059 aCollector->registerBaseMetric(guestCpuLoad);
12060
12061 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12062 guestMemTotal, guestMemFree,
12063 guestMemBalloon, guestMemShared,
12064 guestMemCache, guestPagedTotal);
12065 aCollector->registerBaseMetric(guestCpuMem);
12066
12067 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12068 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12069 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12070 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12071
12072 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12073 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12074 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12075 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12076
12077 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12078 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12079 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12080 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12081
12082 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12083 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12084 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12085 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12086
12087 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12088 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12089 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12090 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12091
12092 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12093 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12094 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12095 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12096
12097 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12098 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12099 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12100 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12101
12102 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12103 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12104 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12105 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12106
12107 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12108 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12109 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12110 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12111
12112 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12113 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12114 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12115 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12116
12117 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12118 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12119 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12120 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12121}
12122
12123void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12124{
12125 AssertReturnVoid(isWriteLockOnCurrentThread());
12126
12127 if (aCollector)
12128 {
12129 aCollector->unregisterMetricsFor(aMachine);
12130 aCollector->unregisterBaseMetricsFor(aMachine);
12131 }
12132}
12133
12134#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12135
12136
12137////////////////////////////////////////////////////////////////////////////////
12138
12139DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12140
12141HRESULT SessionMachine::FinalConstruct()
12142{
12143 LogFlowThisFunc(("\n"));
12144
12145 mClientToken = NULL;
12146
12147 return BaseFinalConstruct();
12148}
12149
12150void SessionMachine::FinalRelease()
12151{
12152 LogFlowThisFunc(("\n"));
12153
12154 Assert(!mClientToken);
12155 /* paranoia, should not hang around any more */
12156 if (mClientToken)
12157 {
12158 delete mClientToken;
12159 mClientToken = NULL;
12160 }
12161
12162 uninit(Uninit::Unexpected);
12163
12164 BaseFinalRelease();
12165}
12166
12167/**
12168 * @note Must be called only by Machine::LockMachine() from its own write lock.
12169 */
12170HRESULT SessionMachine::init(Machine *aMachine)
12171{
12172 LogFlowThisFuncEnter();
12173 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12174
12175 AssertReturn(aMachine, E_INVALIDARG);
12176
12177 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12178
12179 /* Enclose the state transition NotReady->InInit->Ready */
12180 AutoInitSpan autoInitSpan(this);
12181 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12182
12183 HRESULT rc = S_OK;
12184
12185 /* create the machine client token */
12186 try
12187 {
12188 mClientToken = new ClientToken(aMachine, this);
12189 if (!mClientToken->isReady())
12190 {
12191 delete mClientToken;
12192 mClientToken = NULL;
12193 rc = E_FAIL;
12194 }
12195 }
12196 catch (std::bad_alloc &)
12197 {
12198 rc = E_OUTOFMEMORY;
12199 }
12200 if (FAILED(rc))
12201 return rc;
12202
12203 /* memorize the peer Machine */
12204 unconst(mPeer) = aMachine;
12205 /* share the parent pointer */
12206 unconst(mParent) = aMachine->mParent;
12207
12208 /* take the pointers to data to share */
12209 mData.share(aMachine->mData);
12210 mSSData.share(aMachine->mSSData);
12211
12212 mUserData.share(aMachine->mUserData);
12213 mHWData.share(aMachine->mHWData);
12214 mMediaData.share(aMachine->mMediaData);
12215
12216 mStorageControllers.allocate();
12217 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12218 it != aMachine->mStorageControllers->end();
12219 ++it)
12220 {
12221 ComObjPtr<StorageController> ctl;
12222 ctl.createObject();
12223 ctl->init(this, *it);
12224 mStorageControllers->push_back(ctl);
12225 }
12226
12227 mUSBControllers.allocate();
12228 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12229 it != aMachine->mUSBControllers->end();
12230 ++it)
12231 {
12232 ComObjPtr<USBController> ctl;
12233 ctl.createObject();
12234 ctl->init(this, *it);
12235 mUSBControllers->push_back(ctl);
12236 }
12237
12238 unconst(mBIOSSettings).createObject();
12239 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12240 /* create another VRDEServer object that will be mutable */
12241 unconst(mVRDEServer).createObject();
12242 mVRDEServer->init(this, aMachine->mVRDEServer);
12243 /* create another audio adapter object that will be mutable */
12244 unconst(mAudioAdapter).createObject();
12245 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12246 /* create a list of serial ports that will be mutable */
12247 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12248 {
12249 unconst(mSerialPorts[slot]).createObject();
12250 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12251 }
12252 /* create a list of parallel ports that will be mutable */
12253 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12254 {
12255 unconst(mParallelPorts[slot]).createObject();
12256 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12257 }
12258
12259 /* create another USB device filters object that will be mutable */
12260 unconst(mUSBDeviceFilters).createObject();
12261 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12262
12263 /* create a list of network adapters that will be mutable */
12264 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12265 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12266 {
12267 unconst(mNetworkAdapters[slot]).createObject();
12268 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12269 }
12270
12271 /* create another bandwidth control object that will be mutable */
12272 unconst(mBandwidthControl).createObject();
12273 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12274
12275 /* default is to delete saved state on Saved -> PoweredOff transition */
12276 mRemoveSavedState = true;
12277
12278 /* Confirm a successful initialization when it's the case */
12279 autoInitSpan.setSucceeded();
12280
12281 miNATNetworksStarted = 0;
12282
12283 LogFlowThisFuncLeave();
12284 return rc;
12285}
12286
12287/**
12288 * Uninitializes this session object. If the reason is other than
12289 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12290 * or the client watcher code.
12291 *
12292 * @param aReason uninitialization reason
12293 *
12294 * @note Locks mParent + this object for writing.
12295 */
12296void SessionMachine::uninit(Uninit::Reason aReason)
12297{
12298 LogFlowThisFuncEnter();
12299 LogFlowThisFunc(("reason=%d\n", aReason));
12300
12301 /*
12302 * Strongly reference ourselves to prevent this object deletion after
12303 * mData->mSession.mMachine.setNull() below (which can release the last
12304 * reference and call the destructor). Important: this must be done before
12305 * accessing any members (and before AutoUninitSpan that does it as well).
12306 * This self reference will be released as the very last step on return.
12307 */
12308 ComObjPtr<SessionMachine> selfRef = this;
12309
12310 /* Enclose the state transition Ready->InUninit->NotReady */
12311 AutoUninitSpan autoUninitSpan(this);
12312 if (autoUninitSpan.uninitDone())
12313 {
12314 LogFlowThisFunc(("Already uninitialized\n"));
12315 LogFlowThisFuncLeave();
12316 return;
12317 }
12318
12319 if (autoUninitSpan.initFailed())
12320 {
12321 /* We've been called by init() because it's failed. It's not really
12322 * necessary (nor it's safe) to perform the regular uninit sequence
12323 * below, the following is enough.
12324 */
12325 LogFlowThisFunc(("Initialization failed.\n"));
12326 /* destroy the machine client token */
12327 if (mClientToken)
12328 {
12329 delete mClientToken;
12330 mClientToken = NULL;
12331 }
12332 uninitDataAndChildObjects();
12333 mData.free();
12334 unconst(mParent) = NULL;
12335 unconst(mPeer) = NULL;
12336 LogFlowThisFuncLeave();
12337 return;
12338 }
12339
12340 MachineState_T lastState;
12341 {
12342 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12343 lastState = mData->mMachineState;
12344 }
12345 NOREF(lastState);
12346
12347#ifdef VBOX_WITH_USB
12348 // release all captured USB devices, but do this before requesting the locks below
12349 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12350 {
12351 /* Console::captureUSBDevices() is called in the VM process only after
12352 * setting the machine state to Starting or Restoring.
12353 * Console::detachAllUSBDevices() will be called upon successful
12354 * termination. So, we need to release USB devices only if there was
12355 * an abnormal termination of a running VM.
12356 *
12357 * This is identical to SessionMachine::DetachAllUSBDevices except
12358 * for the aAbnormal argument. */
12359 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12360 AssertComRC(rc);
12361 NOREF(rc);
12362
12363 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12364 if (service)
12365 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12366 }
12367#endif /* VBOX_WITH_USB */
12368
12369 // we need to lock this object in uninit() because the lock is shared
12370 // with mPeer (as well as data we modify below). mParent lock is needed
12371 // by several calls to it, and USB needs host lock.
12372 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12373
12374#ifdef VBOX_WITH_RESOURCE_USAGE_API
12375 /*
12376 * It is safe to call Machine::i_unregisterMetrics() here because
12377 * PerformanceCollector::samplerCallback no longer accesses guest methods
12378 * holding the lock.
12379 */
12380 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12381 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12382 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12383 this, __PRETTY_FUNCTION__, mCollectorGuest));
12384 if (mCollectorGuest)
12385 {
12386 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12387 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12388 mCollectorGuest = NULL;
12389 }
12390#endif
12391
12392 if (aReason == Uninit::Abnormal)
12393 {
12394 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12395 Global::IsOnlineOrTransient(lastState)));
12396
12397 /* reset the state to Aborted */
12398 if (mData->mMachineState != MachineState_Aborted)
12399 i_setMachineState(MachineState_Aborted);
12400 }
12401
12402 // any machine settings modified?
12403 if (mData->flModifications)
12404 {
12405 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12406 i_rollback(false /* aNotify */);
12407 }
12408
12409 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12410 || !mConsoleTaskData.mSnapshot);
12411 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12412 {
12413 LogWarningThisFunc(("canceling failed save state request!\n"));
12414 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12415 }
12416 else if (!mConsoleTaskData.mSnapshot.isNull())
12417 {
12418 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12419
12420 /* delete all differencing hard disks created (this will also attach
12421 * their parents back by rolling back mMediaData) */
12422 i_rollbackMedia();
12423
12424 // delete the saved state file (it might have been already created)
12425 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12426 // think it's still in use
12427 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12428 mConsoleTaskData.mSnapshot->uninit();
12429 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12430 }
12431
12432 mData->mSession.mPID = NIL_RTPROCESS;
12433
12434 if (aReason == Uninit::Unexpected)
12435 {
12436 /* Uninitialization didn't come from #checkForDeath(), so tell the
12437 * client watcher thread to update the set of machines that have open
12438 * sessions. */
12439 mParent->i_updateClientWatcher();
12440 }
12441
12442 /* uninitialize all remote controls */
12443 if (mData->mSession.mRemoteControls.size())
12444 {
12445 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12446 mData->mSession.mRemoteControls.size()));
12447
12448 Data::Session::RemoteControlList::iterator it =
12449 mData->mSession.mRemoteControls.begin();
12450 while (it != mData->mSession.mRemoteControls.end())
12451 {
12452 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12453 HRESULT rc = (*it)->Uninitialize();
12454 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12455 if (FAILED(rc))
12456 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12457 ++it;
12458 }
12459 mData->mSession.mRemoteControls.clear();
12460 }
12461
12462 /* Remove all references to the NAT network service. The service will stop
12463 * if all references (also from other VMs) are removed. */
12464 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12465 {
12466 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12467 {
12468 NetworkAttachmentType_T type;
12469 HRESULT hrc;
12470
12471 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12472 if ( SUCCEEDED(hrc)
12473 && type == NetworkAttachmentType_NATNetwork)
12474 {
12475 Bstr name;
12476 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12477 if (SUCCEEDED(hrc))
12478 {
12479 multilock.release();
12480 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12481 mUserData->s.strName.c_str(), name.raw()));
12482 mParent->i_natNetworkRefDec(name.raw());
12483 multilock.acquire();
12484 }
12485 }
12486 }
12487 }
12488
12489 /*
12490 * An expected uninitialization can come only from #checkForDeath().
12491 * Otherwise it means that something's gone really wrong (for example,
12492 * the Session implementation has released the VirtualBox reference
12493 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12494 * etc). However, it's also possible, that the client releases the IPC
12495 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12496 * but the VirtualBox release event comes first to the server process.
12497 * This case is practically possible, so we should not assert on an
12498 * unexpected uninit, just log a warning.
12499 */
12500
12501 if ((aReason == Uninit::Unexpected))
12502 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12503
12504 if (aReason != Uninit::Normal)
12505 {
12506 mData->mSession.mDirectControl.setNull();
12507 }
12508 else
12509 {
12510 /* this must be null here (see #OnSessionEnd()) */
12511 Assert(mData->mSession.mDirectControl.isNull());
12512 Assert(mData->mSession.mState == SessionState_Unlocking);
12513 Assert(!mData->mSession.mProgress.isNull());
12514 }
12515 if (mData->mSession.mProgress)
12516 {
12517 if (aReason == Uninit::Normal)
12518 mData->mSession.mProgress->i_notifyComplete(S_OK);
12519 else
12520 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12521 COM_IIDOF(ISession),
12522 getComponentName(),
12523 tr("The VM session was aborted"));
12524 mData->mSession.mProgress.setNull();
12525 }
12526
12527 /* remove the association between the peer machine and this session machine */
12528 Assert( (SessionMachine*)mData->mSession.mMachine == this
12529 || aReason == Uninit::Unexpected);
12530
12531 /* reset the rest of session data */
12532 mData->mSession.mMachine.setNull();
12533 mData->mSession.mState = SessionState_Unlocked;
12534 mData->mSession.mType.setNull();
12535
12536 /* destroy the machine client token before leaving the exclusive lock */
12537 if (mClientToken)
12538 {
12539 delete mClientToken;
12540 mClientToken = NULL;
12541 }
12542
12543 /* fire an event */
12544 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12545
12546 uninitDataAndChildObjects();
12547
12548 /* free the essential data structure last */
12549 mData.free();
12550
12551 /* release the exclusive lock before setting the below two to NULL */
12552 multilock.release();
12553
12554 unconst(mParent) = NULL;
12555 unconst(mPeer) = NULL;
12556
12557 LogFlowThisFuncLeave();
12558}
12559
12560// util::Lockable interface
12561////////////////////////////////////////////////////////////////////////////////
12562
12563/**
12564 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12565 * with the primary Machine instance (mPeer).
12566 */
12567RWLockHandle *SessionMachine::lockHandle() const
12568{
12569 AssertReturn(mPeer != NULL, NULL);
12570 return mPeer->lockHandle();
12571}
12572
12573// IInternalMachineControl methods
12574////////////////////////////////////////////////////////////////////////////////
12575
12576/**
12577 * Passes collected guest statistics to performance collector object
12578 */
12579HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12580 ULONG aCpuKernel, ULONG aCpuIdle,
12581 ULONG aMemTotal, ULONG aMemFree,
12582 ULONG aMemBalloon, ULONG aMemShared,
12583 ULONG aMemCache, ULONG aPageTotal,
12584 ULONG aAllocVMM, ULONG aFreeVMM,
12585 ULONG aBalloonedVMM, ULONG aSharedVMM,
12586 ULONG aVmNetRx, ULONG aVmNetTx)
12587{
12588#ifdef VBOX_WITH_RESOURCE_USAGE_API
12589 if (mCollectorGuest)
12590 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12591 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12592 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12593 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12594
12595 return S_OK;
12596#else
12597 NOREF(aValidStats);
12598 NOREF(aCpuUser);
12599 NOREF(aCpuKernel);
12600 NOREF(aCpuIdle);
12601 NOREF(aMemTotal);
12602 NOREF(aMemFree);
12603 NOREF(aMemBalloon);
12604 NOREF(aMemShared);
12605 NOREF(aMemCache);
12606 NOREF(aPageTotal);
12607 NOREF(aAllocVMM);
12608 NOREF(aFreeVMM);
12609 NOREF(aBalloonedVMM);
12610 NOREF(aSharedVMM);
12611 NOREF(aVmNetRx);
12612 NOREF(aVmNetTx);
12613 return E_NOTIMPL;
12614#endif
12615}
12616
12617/**
12618 * @note Locks this object for writing.
12619 */
12620HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12621{
12622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12623
12624 mRemoveSavedState = RT_BOOL(aRemove);
12625
12626 return S_OK;
12627}
12628
12629/**
12630 * @note Locks the same as #i_setMachineState() does.
12631 */
12632HRESULT SessionMachine::updateState(MachineState_T aState)
12633{
12634 return i_setMachineState(aState);
12635}
12636
12637/**
12638 * @note Locks this object for writing.
12639 */
12640HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12641{
12642 IProgress* pProgress(aProgress);
12643
12644 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12645
12646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12647
12648 if (mData->mSession.mState != SessionState_Locked)
12649 return VBOX_E_INVALID_OBJECT_STATE;
12650
12651 if (!mData->mSession.mProgress.isNull())
12652 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12653
12654 /* If we didn't reference the NAT network service yet, add a reference to
12655 * force a start */
12656 if (miNATNetworksStarted < 1)
12657 {
12658 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12659 {
12660 NetworkAttachmentType_T type;
12661 HRESULT hrc;
12662 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12663 if ( SUCCEEDED(hrc)
12664 && type == NetworkAttachmentType_NATNetwork)
12665 {
12666 Bstr name;
12667 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12668 if (SUCCEEDED(hrc))
12669 {
12670 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12671 mUserData->s.strName.c_str(), name.raw()));
12672 mPeer->lockHandle()->unlockWrite();
12673 mParent->i_natNetworkRefInc(name.raw());
12674#ifdef RT_LOCK_STRICT
12675 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12676#else
12677 mPeer->lockHandle()->lockWrite();
12678#endif
12679 }
12680 }
12681 }
12682 miNATNetworksStarted++;
12683 }
12684
12685 LogFlowThisFunc(("returns S_OK.\n"));
12686 return S_OK;
12687}
12688
12689/**
12690 * @note Locks this object for writing.
12691 */
12692HRESULT SessionMachine::endPowerUp(LONG aResult)
12693{
12694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12695
12696 if (mData->mSession.mState != SessionState_Locked)
12697 return VBOX_E_INVALID_OBJECT_STATE;
12698
12699 /* Finalize the LaunchVMProcess progress object. */
12700 if (mData->mSession.mProgress)
12701 {
12702 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12703 mData->mSession.mProgress.setNull();
12704 }
12705
12706 if (SUCCEEDED((HRESULT)aResult))
12707 {
12708#ifdef VBOX_WITH_RESOURCE_USAGE_API
12709 /* The VM has been powered up successfully, so it makes sense
12710 * now to offer the performance metrics for a running machine
12711 * object. Doing it earlier wouldn't be safe. */
12712 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12713 mData->mSession.mPID);
12714#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12715 }
12716
12717 return S_OK;
12718}
12719
12720/**
12721 * @note Locks this object for writing.
12722 */
12723HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12724{
12725 LogFlowThisFuncEnter();
12726
12727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12728
12729 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12730 E_FAIL);
12731
12732 /* create a progress object to track operation completion */
12733 ComObjPtr<Progress> pProgress;
12734 pProgress.createObject();
12735 pProgress->init(i_getVirtualBox(),
12736 static_cast<IMachine *>(this) /* aInitiator */,
12737 Bstr(tr("Stopping the virtual machine")).raw(),
12738 FALSE /* aCancelable */);
12739
12740 /* fill in the console task data */
12741 mConsoleTaskData.mLastState = mData->mMachineState;
12742 mConsoleTaskData.mProgress = pProgress;
12743
12744 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12745 i_setMachineState(MachineState_Stopping);
12746
12747 pProgress.queryInterfaceTo(aProgress.asOutParam());
12748
12749 return S_OK;
12750}
12751
12752/**
12753 * @note Locks this object for writing.
12754 */
12755HRESULT SessionMachine::endPoweringDown(LONG aResult,
12756 const com::Utf8Str &aErrMsg)
12757{
12758 LogFlowThisFuncEnter();
12759
12760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12761
12762 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12763 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12764 && mConsoleTaskData.mLastState != MachineState_Null,
12765 E_FAIL);
12766
12767 /*
12768 * On failure, set the state to the state we had when BeginPoweringDown()
12769 * was called (this is expected by Console::PowerDown() and the associated
12770 * task). On success the VM process already changed the state to
12771 * MachineState_PoweredOff, so no need to do anything.
12772 */
12773 if (FAILED(aResult))
12774 i_setMachineState(mConsoleTaskData.mLastState);
12775
12776 /* notify the progress object about operation completion */
12777 Assert(mConsoleTaskData.mProgress);
12778 if (SUCCEEDED(aResult))
12779 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12780 else
12781 {
12782 if (aErrMsg.length())
12783 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12784 COM_IIDOF(ISession),
12785 getComponentName(),
12786 aErrMsg.c_str());
12787 else
12788 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12789 }
12790
12791 /* clear out the temporary saved state data */
12792 mConsoleTaskData.mLastState = MachineState_Null;
12793 mConsoleTaskData.mProgress.setNull();
12794
12795 LogFlowThisFuncLeave();
12796 return S_OK;
12797}
12798
12799
12800/**
12801 * Goes through the USB filters of the given machine to see if the given
12802 * device matches any filter or not.
12803 *
12804 * @note Locks the same as USBController::hasMatchingFilter() does.
12805 */
12806HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12807 BOOL *aMatched,
12808 ULONG *aMaskedInterfaces)
12809{
12810 LogFlowThisFunc(("\n"));
12811
12812#ifdef VBOX_WITH_USB
12813 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12814#else
12815 NOREF(aDevice);
12816 NOREF(aMaskedInterfaces);
12817 *aMatched = FALSE;
12818#endif
12819
12820 return S_OK;
12821}
12822
12823/**
12824 * @note Locks the same as Host::captureUSBDevice() does.
12825 */
12826HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
12827{
12828 LogFlowThisFunc(("\n"));
12829
12830#ifdef VBOX_WITH_USB
12831 /* if captureDeviceForVM() fails, it must have set extended error info */
12832 clearError();
12833 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12834 if (FAILED(rc)) return rc;
12835
12836 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12837 AssertReturn(service, E_FAIL);
12838 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
12839#else
12840 NOREF(aId);
12841 return E_NOTIMPL;
12842#endif
12843}
12844
12845/**
12846 * @note Locks the same as Host::detachUSBDevice() does.
12847 */
12848HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12849 BOOL aDone)
12850{
12851 LogFlowThisFunc(("\n"));
12852
12853#ifdef VBOX_WITH_USB
12854 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12855 AssertReturn(service, E_FAIL);
12856 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12857#else
12858 NOREF(aId);
12859 NOREF(aDone);
12860 return E_NOTIMPL;
12861#endif
12862}
12863
12864/**
12865 * Inserts all machine filters to the USB proxy service and then calls
12866 * Host::autoCaptureUSBDevices().
12867 *
12868 * Called by Console from the VM process upon VM startup.
12869 *
12870 * @note Locks what called methods lock.
12871 */
12872HRESULT SessionMachine::autoCaptureUSBDevices()
12873{
12874 LogFlowThisFunc(("\n"));
12875
12876#ifdef VBOX_WITH_USB
12877 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12878 AssertComRC(rc);
12879 NOREF(rc);
12880
12881 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12882 AssertReturn(service, E_FAIL);
12883 return service->autoCaptureDevicesForVM(this);
12884#else
12885 return S_OK;
12886#endif
12887}
12888
12889/**
12890 * Removes all machine filters from the USB proxy service and then calls
12891 * Host::detachAllUSBDevices().
12892 *
12893 * Called by Console from the VM process upon normal VM termination or by
12894 * SessionMachine::uninit() upon abnormal VM termination (from under the
12895 * Machine/SessionMachine lock).
12896 *
12897 * @note Locks what called methods lock.
12898 */
12899HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12900{
12901 LogFlowThisFunc(("\n"));
12902
12903#ifdef VBOX_WITH_USB
12904 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12905 AssertComRC(rc);
12906 NOREF(rc);
12907
12908 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12909 AssertReturn(service, E_FAIL);
12910 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12911#else
12912 NOREF(aDone);
12913 return S_OK;
12914#endif
12915}
12916
12917/**
12918 * @note Locks this object for writing.
12919 */
12920HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12921 ComPtr<IProgress> &aProgress)
12922{
12923 LogFlowThisFuncEnter();
12924
12925 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12926 /*
12927 * We don't assert below because it might happen that a non-direct session
12928 * informs us it is closed right after we've been uninitialized -- it's ok.
12929 */
12930
12931 /* get IInternalSessionControl interface */
12932 ComPtr<IInternalSessionControl> control(aSession);
12933
12934 ComAssertRet(!control.isNull(), E_INVALIDARG);
12935
12936 /* Creating a Progress object requires the VirtualBox lock, and
12937 * thus locking it here is required by the lock order rules. */
12938 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12939
12940 if (control == mData->mSession.mDirectControl)
12941 {
12942 /* The direct session is being normally closed by the client process
12943 * ----------------------------------------------------------------- */
12944
12945 /* go to the closing state (essential for all open*Session() calls and
12946 * for #checkForDeath()) */
12947 Assert(mData->mSession.mState == SessionState_Locked);
12948 mData->mSession.mState = SessionState_Unlocking;
12949
12950 /* set direct control to NULL to release the remote instance */
12951 mData->mSession.mDirectControl.setNull();
12952 LogFlowThisFunc(("Direct control is set to NULL\n"));
12953
12954 if (mData->mSession.mProgress)
12955 {
12956 /* finalize the progress, someone might wait if a frontend
12957 * closes the session before powering on the VM. */
12958 mData->mSession.mProgress->notifyComplete(E_FAIL,
12959 COM_IIDOF(ISession),
12960 getComponentName(),
12961 tr("The VM session was closed before any attempt to power it on"));
12962 mData->mSession.mProgress.setNull();
12963 }
12964
12965 /* Create the progress object the client will use to wait until
12966 * #checkForDeath() is called to uninitialize this session object after
12967 * it releases the IPC semaphore.
12968 * Note! Because we're "reusing" mProgress here, this must be a proxy
12969 * object just like for LaunchVMProcess. */
12970 Assert(mData->mSession.mProgress.isNull());
12971 ComObjPtr<ProgressProxy> progress;
12972 progress.createObject();
12973 ComPtr<IUnknown> pPeer(mPeer);
12974 progress->init(mParent, pPeer,
12975 Bstr(tr("Closing session")).raw(),
12976 FALSE /* aCancelable */);
12977 progress.queryInterfaceTo(aProgress.asOutParam());
12978 mData->mSession.mProgress = progress;
12979 }
12980 else
12981 {
12982 /* the remote session is being normally closed */
12983 Data::Session::RemoteControlList::iterator it =
12984 mData->mSession.mRemoteControls.begin();
12985 while (it != mData->mSession.mRemoteControls.end())
12986 {
12987 if (control == *it)
12988 break;
12989 ++it;
12990 }
12991 BOOL found = it != mData->mSession.mRemoteControls.end();
12992 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12993 E_INVALIDARG);
12994 // This MUST be erase(it), not remove(*it) as the latter triggers a
12995 // very nasty use after free due to the place where the value "lives".
12996 mData->mSession.mRemoteControls.erase(it);
12997 }
12998
12999 /* signal the client watcher thread, because the client is going away */
13000 mParent->i_updateClientWatcher();
13001
13002 LogFlowThisFuncLeave();
13003 return S_OK;
13004}
13005
13006/**
13007 * @note Locks this object for writing.
13008 */
13009HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
13010 com::Utf8Str &aStateFilePath)
13011{
13012 LogFlowThisFuncEnter();
13013
13014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13015
13016 AssertReturn( mData->mMachineState == MachineState_Paused
13017 && mConsoleTaskData.mLastState == MachineState_Null
13018 && mConsoleTaskData.strStateFilePath.isEmpty(),
13019 E_FAIL);
13020
13021 /* create a progress object to track operation completion */
13022 ComObjPtr<Progress> pProgress;
13023 pProgress.createObject();
13024 pProgress->init(i_getVirtualBox(),
13025 static_cast<IMachine *>(this) /* aInitiator */,
13026 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13027 FALSE /* aCancelable */);
13028
13029 /* stateFilePath is null when the machine is not running */
13030 if (mData->mMachineState == MachineState_Paused)
13031 i_composeSavedStateFilename(aStateFilePath);
13032
13033 /* fill in the console task data */
13034 mConsoleTaskData.mLastState = mData->mMachineState;
13035 mConsoleTaskData.strStateFilePath = aStateFilePath;
13036 mConsoleTaskData.mProgress = pProgress;
13037
13038 /* set the state to Saving (this is expected by Console::SaveState()) */
13039 i_setMachineState(MachineState_Saving);
13040
13041 pProgress.queryInterfaceTo(aProgress.asOutParam());
13042
13043 return S_OK;
13044}
13045
13046/**
13047 * @note Locks mParent + this object for writing.
13048 */
13049HRESULT SessionMachine::endSavingState(LONG aResult,
13050 const com::Utf8Str &aErrMsg)
13051{
13052 LogFlowThisFunc(("\n"));
13053
13054 /* endSavingState() need mParent lock */
13055 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13056
13057 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13058 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13059 && mConsoleTaskData.mLastState != MachineState_Null
13060 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13061 E_FAIL);
13062
13063 /*
13064 * On failure, set the state to the state we had when BeginSavingState()
13065 * was called (this is expected by Console::SaveState() and the associated
13066 * task). On success the VM process already changed the state to
13067 * MachineState_Saved, so no need to do anything.
13068 */
13069 if (FAILED(aResult))
13070 i_setMachineState(mConsoleTaskData.mLastState);
13071
13072 return i_endSavingState(aResult, aErrMsg);
13073}
13074
13075/**
13076 * @note Locks this object for writing.
13077 */
13078HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13079{
13080 LogFlowThisFunc(("\n"));
13081
13082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13083
13084 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13085 || mData->mMachineState == MachineState_Teleported
13086 || mData->mMachineState == MachineState_Aborted
13087 , E_FAIL); /** @todo setError. */
13088
13089 com::Utf8Str stateFilePathFull;
13090 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13091 if (RT_FAILURE(vrc))
13092 return setError(VBOX_E_FILE_ERROR,
13093 tr("Invalid saved state file path '%s' (%Rrc)"),
13094 aSavedStateFile.c_str(),
13095 vrc);
13096
13097 mSSData->strStateFilePath = stateFilePathFull;
13098
13099 /* The below i_setMachineState() will detect the state transition and will
13100 * update the settings file */
13101
13102 return i_setMachineState(MachineState_Saved);
13103}
13104
13105HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13106 std::vector<com::Utf8Str> &aValues,
13107 std::vector<LONG64> &aTimestamps,
13108 std::vector<com::Utf8Str> &aFlags)
13109{
13110 LogFlowThisFunc(("\n"));
13111
13112#ifdef VBOX_WITH_GUEST_PROPS
13113 using namespace guestProp;
13114
13115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13116
13117 size_t cEntries = mHWData->mGuestProperties.size();
13118 aNames.resize(cEntries);
13119 aValues.resize(cEntries);
13120 aTimestamps.resize(cEntries);
13121 aFlags.resize(cEntries);
13122
13123 size_t i = 0;
13124 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13125 it != mHWData->mGuestProperties.end();
13126 ++it, ++i)
13127 {
13128 char szFlags[MAX_FLAGS_LEN + 1];
13129 aNames[i] = it->first;
13130 aValues[i] = it->second.strValue;
13131 aTimestamps[i] = it->second.mTimestamp;
13132
13133 /* If it is NULL, keep it NULL. */
13134 if (it->second.mFlags)
13135 {
13136 writeFlags(it->second.mFlags, szFlags);
13137 aFlags[i] = szFlags;
13138 }
13139 else
13140 aFlags[i] = "";
13141 }
13142 return S_OK;
13143#else
13144 ReturnComNotImplemented();
13145#endif
13146}
13147
13148HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13149 const com::Utf8Str &aValue,
13150 LONG64 aTimestamp,
13151 const com::Utf8Str &aFlags)
13152{
13153 LogFlowThisFunc(("\n"));
13154
13155#ifdef VBOX_WITH_GUEST_PROPS
13156 using namespace guestProp;
13157
13158 try
13159 {
13160 /*
13161 * Convert input up front.
13162 */
13163 uint32_t fFlags = NILFLAG;
13164 if (aFlags.length())
13165 {
13166 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13167 AssertRCReturn(vrc, E_INVALIDARG);
13168 }
13169
13170 /*
13171 * Now grab the object lock, validate the state and do the update.
13172 */
13173
13174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13175
13176 switch (mData->mMachineState)
13177 {
13178 case MachineState_Paused:
13179 case MachineState_Running:
13180 case MachineState_Teleporting:
13181 case MachineState_TeleportingPausedVM:
13182 case MachineState_LiveSnapshotting:
13183 case MachineState_DeletingSnapshotOnline:
13184 case MachineState_DeletingSnapshotPaused:
13185 case MachineState_Saving:
13186 case MachineState_Stopping:
13187 break;
13188
13189 default:
13190 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13191 VBOX_E_INVALID_VM_STATE);
13192 }
13193
13194 i_setModified(IsModified_MachineData);
13195 mHWData.backup();
13196
13197 bool fDelete = !aValue.length();
13198 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13199 if (it != mHWData->mGuestProperties.end())
13200 {
13201 if (!fDelete)
13202 {
13203 it->second.strValue = aValue;
13204 it->second.mTimestamp = aTimestamp;
13205 it->second.mFlags = fFlags;
13206 }
13207 else
13208 mHWData->mGuestProperties.erase(it);
13209
13210 mData->mGuestPropertiesModified = TRUE;
13211 }
13212 else if (!fDelete)
13213 {
13214 HWData::GuestProperty prop;
13215 prop.strValue = aValue;
13216 prop.mTimestamp = aTimestamp;
13217 prop.mFlags = fFlags;
13218
13219 mHWData->mGuestProperties[aName] = prop;
13220 mData->mGuestPropertiesModified = TRUE;
13221 }
13222
13223 /*
13224 * Send a callback notification if appropriate
13225 */
13226 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13227 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13228 RTSTR_MAX,
13229 aName.c_str(),
13230 RTSTR_MAX, NULL)
13231 )
13232 {
13233 alock.release();
13234
13235 mParent->i_onGuestPropertyChange(mData->mUuid,
13236 Bstr(aName).raw(),
13237 Bstr(aValue).raw(),
13238 Bstr(aFlags).raw());
13239 }
13240 }
13241 catch (...)
13242 {
13243 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13244 }
13245 return S_OK;
13246#else
13247 ReturnComNotImplemented();
13248#endif
13249}
13250
13251
13252HRESULT SessionMachine::lockMedia()
13253{
13254 AutoMultiWriteLock2 alock(this->lockHandle(),
13255 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13256
13257 AssertReturn( mData->mMachineState == MachineState_Starting
13258 || mData->mMachineState == MachineState_Restoring
13259 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13260
13261 clearError();
13262 alock.release();
13263 return i_lockMedia();
13264}
13265
13266HRESULT SessionMachine::unlockMedia()
13267{
13268 HRESULT hrc = i_unlockMedia();
13269 return hrc;
13270}
13271
13272HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13273 ComPtr<IMediumAttachment> &aNewAttachment)
13274{
13275 // request the host lock first, since might be calling Host methods for getting host drives;
13276 // next, protect the media tree all the while we're in here, as well as our member variables
13277 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13278 this->lockHandle(),
13279 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13280
13281 IMediumAttachment *iAttach = aAttachment;
13282 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13283
13284 Bstr ctrlName;
13285 LONG lPort;
13286 LONG lDevice;
13287 bool fTempEject;
13288 {
13289 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13290
13291 /* Need to query the details first, as the IMediumAttachment reference
13292 * might be to the original settings, which we are going to change. */
13293 ctrlName = pAttach->i_getControllerName();
13294 lPort = pAttach->i_getPort();
13295 lDevice = pAttach->i_getDevice();
13296 fTempEject = pAttach->i_getTempEject();
13297 }
13298
13299 if (!fTempEject)
13300 {
13301 /* Remember previously mounted medium. The medium before taking the
13302 * backup is not necessarily the same thing. */
13303 ComObjPtr<Medium> oldmedium;
13304 oldmedium = pAttach->i_getMedium();
13305
13306 i_setModified(IsModified_Storage);
13307 mMediaData.backup();
13308
13309 // The backup operation makes the pAttach reference point to the
13310 // old settings. Re-get the correct reference.
13311 pAttach = i_findAttachment(mMediaData->mAttachments,
13312 ctrlName.raw(),
13313 lPort,
13314 lDevice);
13315
13316 {
13317 AutoCaller autoAttachCaller(this);
13318 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13319
13320 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13321 if (!oldmedium.isNull())
13322 oldmedium->i_removeBackReference(mData->mUuid);
13323
13324 pAttach->i_updateMedium(NULL);
13325 pAttach->i_updateEjected();
13326 }
13327
13328 i_setModified(IsModified_Storage);
13329 }
13330 else
13331 {
13332 {
13333 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13334 pAttach->i_updateEjected();
13335 }
13336 }
13337
13338 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13339
13340 return S_OK;
13341}
13342
13343// public methods only for internal purposes
13344/////////////////////////////////////////////////////////////////////////////
13345
13346#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13347/**
13348 * Called from the client watcher thread to check for expected or unexpected
13349 * death of the client process that has a direct session to this machine.
13350 *
13351 * On Win32 and on OS/2, this method is called only when we've got the
13352 * mutex (i.e. the client has either died or terminated normally) so it always
13353 * returns @c true (the client is terminated, the session machine is
13354 * uninitialized).
13355 *
13356 * On other platforms, the method returns @c true if the client process has
13357 * terminated normally or abnormally and the session machine was uninitialized,
13358 * and @c false if the client process is still alive.
13359 *
13360 * @note Locks this object for writing.
13361 */
13362bool SessionMachine::i_checkForDeath()
13363{
13364 Uninit::Reason reason;
13365 bool terminated = false;
13366
13367 /* Enclose autoCaller with a block because calling uninit() from under it
13368 * will deadlock. */
13369 {
13370 AutoCaller autoCaller(this);
13371 if (!autoCaller.isOk())
13372 {
13373 /* return true if not ready, to cause the client watcher to exclude
13374 * the corresponding session from watching */
13375 LogFlowThisFunc(("Already uninitialized!\n"));
13376 return true;
13377 }
13378
13379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13380
13381 /* Determine the reason of death: if the session state is Closing here,
13382 * everything is fine. Otherwise it means that the client did not call
13383 * OnSessionEnd() before it released the IPC semaphore. This may happen
13384 * either because the client process has abnormally terminated, or
13385 * because it simply forgot to call ISession::Close() before exiting. We
13386 * threat the latter also as an abnormal termination (see
13387 * Session::uninit() for details). */
13388 reason = mData->mSession.mState == SessionState_Unlocking ?
13389 Uninit::Normal :
13390 Uninit::Abnormal;
13391
13392 if (mClientToken)
13393 terminated = mClientToken->release();
13394 } /* AutoCaller block */
13395
13396 if (terminated)
13397 uninit(reason);
13398
13399 return terminated;
13400}
13401
13402void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13403{
13404 LogFlowThisFunc(("\n"));
13405
13406 strTokenId.setNull();
13407
13408 AutoCaller autoCaller(this);
13409 AssertComRCReturnVoid(autoCaller.rc());
13410
13411 Assert(mClientToken);
13412 if (mClientToken)
13413 mClientToken->getId(strTokenId);
13414}
13415#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13416IToken *SessionMachine::i_getToken()
13417{
13418 LogFlowThisFunc(("\n"));
13419
13420 AutoCaller autoCaller(this);
13421 AssertComRCReturn(autoCaller.rc(), NULL);
13422
13423 Assert(mClientToken);
13424 if (mClientToken)
13425 return mClientToken->getToken();
13426 else
13427 return NULL;
13428}
13429#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13430
13431Machine::ClientToken *SessionMachine::i_getClientToken()
13432{
13433 LogFlowThisFunc(("\n"));
13434
13435 AutoCaller autoCaller(this);
13436 AssertComRCReturn(autoCaller.rc(), NULL);
13437
13438 return mClientToken;
13439}
13440
13441
13442/**
13443 * @note Locks this object for reading.
13444 */
13445HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13446{
13447 LogFlowThisFunc(("\n"));
13448
13449 AutoCaller autoCaller(this);
13450 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13451
13452 ComPtr<IInternalSessionControl> directControl;
13453 {
13454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13455 directControl = mData->mSession.mDirectControl;
13456 }
13457
13458 /* ignore notifications sent after #OnSessionEnd() is called */
13459 if (!directControl)
13460 return S_OK;
13461
13462 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13463}
13464
13465/**
13466 * @note Locks this object for reading.
13467 */
13468HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13469 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13470 IN_BSTR aGuestIp, LONG aGuestPort)
13471{
13472 LogFlowThisFunc(("\n"));
13473
13474 AutoCaller autoCaller(this);
13475 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13476
13477 ComPtr<IInternalSessionControl> directControl;
13478 {
13479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13480 directControl = mData->mSession.mDirectControl;
13481 }
13482
13483 /* ignore notifications sent after #OnSessionEnd() is called */
13484 if (!directControl)
13485 return S_OK;
13486 /*
13487 * instead acting like callback we ask IVirtualBox deliver corresponding event
13488 */
13489
13490 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13491 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13492 return S_OK;
13493}
13494
13495/**
13496 * @note Locks this object for reading.
13497 */
13498HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13499{
13500 LogFlowThisFunc(("\n"));
13501
13502 AutoCaller autoCaller(this);
13503 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13504
13505 ComPtr<IInternalSessionControl> directControl;
13506 {
13507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13508 directControl = mData->mSession.mDirectControl;
13509 }
13510
13511 /* ignore notifications sent after #OnSessionEnd() is called */
13512 if (!directControl)
13513 return S_OK;
13514
13515 return directControl->OnSerialPortChange(serialPort);
13516}
13517
13518/**
13519 * @note Locks this object for reading.
13520 */
13521HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13522{
13523 LogFlowThisFunc(("\n"));
13524
13525 AutoCaller autoCaller(this);
13526 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13527
13528 ComPtr<IInternalSessionControl> directControl;
13529 {
13530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13531 directControl = mData->mSession.mDirectControl;
13532 }
13533
13534 /* ignore notifications sent after #OnSessionEnd() is called */
13535 if (!directControl)
13536 return S_OK;
13537
13538 return directControl->OnParallelPortChange(parallelPort);
13539}
13540
13541/**
13542 * @note Locks this object for reading.
13543 */
13544HRESULT SessionMachine::i_onStorageControllerChange()
13545{
13546 LogFlowThisFunc(("\n"));
13547
13548 AutoCaller autoCaller(this);
13549 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13550
13551 ComPtr<IInternalSessionControl> directControl;
13552 {
13553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13554 directControl = mData->mSession.mDirectControl;
13555 }
13556
13557 /* ignore notifications sent after #OnSessionEnd() is called */
13558 if (!directControl)
13559 return S_OK;
13560
13561 return directControl->OnStorageControllerChange();
13562}
13563
13564/**
13565 * @note Locks this object for reading.
13566 */
13567HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13568{
13569 LogFlowThisFunc(("\n"));
13570
13571 AutoCaller autoCaller(this);
13572 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13573
13574 ComPtr<IInternalSessionControl> directControl;
13575 {
13576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13577 directControl = mData->mSession.mDirectControl;
13578 }
13579
13580 /* ignore notifications sent after #OnSessionEnd() is called */
13581 if (!directControl)
13582 return S_OK;
13583
13584 return directControl->OnMediumChange(aAttachment, aForce);
13585}
13586
13587/**
13588 * @note Locks this object for reading.
13589 */
13590HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13591{
13592 LogFlowThisFunc(("\n"));
13593
13594 AutoCaller autoCaller(this);
13595 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13596
13597 ComPtr<IInternalSessionControl> directControl;
13598 {
13599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13600 directControl = mData->mSession.mDirectControl;
13601 }
13602
13603 /* ignore notifications sent after #OnSessionEnd() is called */
13604 if (!directControl)
13605 return S_OK;
13606
13607 return directControl->OnCPUChange(aCPU, aRemove);
13608}
13609
13610HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13611{
13612 LogFlowThisFunc(("\n"));
13613
13614 AutoCaller autoCaller(this);
13615 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13616
13617 ComPtr<IInternalSessionControl> directControl;
13618 {
13619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13620 directControl = mData->mSession.mDirectControl;
13621 }
13622
13623 /* ignore notifications sent after #OnSessionEnd() is called */
13624 if (!directControl)
13625 return S_OK;
13626
13627 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13628}
13629
13630/**
13631 * @note Locks this object for reading.
13632 */
13633HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13634{
13635 LogFlowThisFunc(("\n"));
13636
13637 AutoCaller autoCaller(this);
13638 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13639
13640 ComPtr<IInternalSessionControl> directControl;
13641 {
13642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13643 directControl = mData->mSession.mDirectControl;
13644 }
13645
13646 /* ignore notifications sent after #OnSessionEnd() is called */
13647 if (!directControl)
13648 return S_OK;
13649
13650 return directControl->OnVRDEServerChange(aRestart);
13651}
13652
13653/**
13654 * @note Locks this object for reading.
13655 */
13656HRESULT SessionMachine::i_onVideoCaptureChange()
13657{
13658 LogFlowThisFunc(("\n"));
13659
13660 AutoCaller autoCaller(this);
13661 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13662
13663 ComPtr<IInternalSessionControl> directControl;
13664 {
13665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13666 directControl = mData->mSession.mDirectControl;
13667 }
13668
13669 /* ignore notifications sent after #OnSessionEnd() is called */
13670 if (!directControl)
13671 return S_OK;
13672
13673 return directControl->OnVideoCaptureChange();
13674}
13675
13676/**
13677 * @note Locks this object for reading.
13678 */
13679HRESULT SessionMachine::i_onUSBControllerChange()
13680{
13681 LogFlowThisFunc(("\n"));
13682
13683 AutoCaller autoCaller(this);
13684 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13685
13686 ComPtr<IInternalSessionControl> directControl;
13687 {
13688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13689 directControl = mData->mSession.mDirectControl;
13690 }
13691
13692 /* ignore notifications sent after #OnSessionEnd() is called */
13693 if (!directControl)
13694 return S_OK;
13695
13696 return directControl->OnUSBControllerChange();
13697}
13698
13699/**
13700 * @note Locks this object for reading.
13701 */
13702HRESULT SessionMachine::i_onSharedFolderChange()
13703{
13704 LogFlowThisFunc(("\n"));
13705
13706 AutoCaller autoCaller(this);
13707 AssertComRCReturnRC(autoCaller.rc());
13708
13709 ComPtr<IInternalSessionControl> directControl;
13710 {
13711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13712 directControl = mData->mSession.mDirectControl;
13713 }
13714
13715 /* ignore notifications sent after #OnSessionEnd() is called */
13716 if (!directControl)
13717 return S_OK;
13718
13719 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13720}
13721
13722/**
13723 * @note Locks this object for reading.
13724 */
13725HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13726{
13727 LogFlowThisFunc(("\n"));
13728
13729 AutoCaller autoCaller(this);
13730 AssertComRCReturnRC(autoCaller.rc());
13731
13732 ComPtr<IInternalSessionControl> directControl;
13733 {
13734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13735 directControl = mData->mSession.mDirectControl;
13736 }
13737
13738 /* ignore notifications sent after #OnSessionEnd() is called */
13739 if (!directControl)
13740 return S_OK;
13741
13742 return directControl->OnClipboardModeChange(aClipboardMode);
13743}
13744
13745/**
13746 * @note Locks this object for reading.
13747 */
13748HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13749{
13750 LogFlowThisFunc(("\n"));
13751
13752 AutoCaller autoCaller(this);
13753 AssertComRCReturnRC(autoCaller.rc());
13754
13755 ComPtr<IInternalSessionControl> directControl;
13756 {
13757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13758 directControl = mData->mSession.mDirectControl;
13759 }
13760
13761 /* ignore notifications sent after #OnSessionEnd() is called */
13762 if (!directControl)
13763 return S_OK;
13764
13765 return directControl->OnDnDModeChange(aDnDMode);
13766}
13767
13768/**
13769 * @note Locks this object for reading.
13770 */
13771HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13772{
13773 LogFlowThisFunc(("\n"));
13774
13775 AutoCaller autoCaller(this);
13776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13777
13778 ComPtr<IInternalSessionControl> directControl;
13779 {
13780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13781 directControl = mData->mSession.mDirectControl;
13782 }
13783
13784 /* ignore notifications sent after #OnSessionEnd() is called */
13785 if (!directControl)
13786 return S_OK;
13787
13788 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13789}
13790
13791/**
13792 * @note Locks this object for reading.
13793 */
13794HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13795{
13796 LogFlowThisFunc(("\n"));
13797
13798 AutoCaller autoCaller(this);
13799 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13800
13801 ComPtr<IInternalSessionControl> directControl;
13802 {
13803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13804 directControl = mData->mSession.mDirectControl;
13805 }
13806
13807 /* ignore notifications sent after #OnSessionEnd() is called */
13808 if (!directControl)
13809 return S_OK;
13810
13811 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13812}
13813
13814/**
13815 * Returns @c true if this machine's USB controller reports it has a matching
13816 * filter for the given USB device and @c false otherwise.
13817 *
13818 * @note locks this object for reading.
13819 */
13820bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13821{
13822 AutoCaller autoCaller(this);
13823 /* silently return if not ready -- this method may be called after the
13824 * direct machine session has been called */
13825 if (!autoCaller.isOk())
13826 return false;
13827
13828#ifdef VBOX_WITH_USB
13829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13830
13831 switch (mData->mMachineState)
13832 {
13833 case MachineState_Starting:
13834 case MachineState_Restoring:
13835 case MachineState_TeleportingIn:
13836 case MachineState_Paused:
13837 case MachineState_Running:
13838 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13839 * elsewhere... */
13840 alock.release();
13841 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13842 default: break;
13843 }
13844#else
13845 NOREF(aDevice);
13846 NOREF(aMaskedIfs);
13847#endif
13848 return false;
13849}
13850
13851/**
13852 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13853 */
13854HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13855 IVirtualBoxErrorInfo *aError,
13856 ULONG aMaskedIfs,
13857 const com::Utf8Str &aCaptureFilename)
13858{
13859 LogFlowThisFunc(("\n"));
13860
13861 AutoCaller autoCaller(this);
13862
13863 /* This notification may happen after the machine object has been
13864 * uninitialized (the session was closed), so don't assert. */
13865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13866
13867 ComPtr<IInternalSessionControl> directControl;
13868 {
13869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13870 directControl = mData->mSession.mDirectControl;
13871 }
13872
13873 /* fail on notifications sent after #OnSessionEnd() is called, it is
13874 * expected by the caller */
13875 if (!directControl)
13876 return E_FAIL;
13877
13878 /* No locks should be held at this point. */
13879 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13880 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13881
13882 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
13883}
13884
13885/**
13886 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13887 */
13888HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13889 IVirtualBoxErrorInfo *aError)
13890{
13891 LogFlowThisFunc(("\n"));
13892
13893 AutoCaller autoCaller(this);
13894
13895 /* This notification may happen after the machine object has been
13896 * uninitialized (the session was closed), so don't assert. */
13897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13898
13899 ComPtr<IInternalSessionControl> directControl;
13900 {
13901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13902 directControl = mData->mSession.mDirectControl;
13903 }
13904
13905 /* fail on notifications sent after #OnSessionEnd() is called, it is
13906 * expected by the caller */
13907 if (!directControl)
13908 return E_FAIL;
13909
13910 /* No locks should be held at this point. */
13911 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13912 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13913
13914 return directControl->OnUSBDeviceDetach(aId, aError);
13915}
13916
13917// protected methods
13918/////////////////////////////////////////////////////////////////////////////
13919
13920/**
13921 * Helper method to finalize saving the state.
13922 *
13923 * @note Must be called from under this object's lock.
13924 *
13925 * @param aRc S_OK if the snapshot has been taken successfully
13926 * @param aErrMsg human readable error message for failure
13927 *
13928 * @note Locks mParent + this objects for writing.
13929 */
13930HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13931{
13932 LogFlowThisFuncEnter();
13933
13934 AutoCaller autoCaller(this);
13935 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13936
13937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13938
13939 HRESULT rc = S_OK;
13940
13941 if (SUCCEEDED(aRc))
13942 {
13943 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13944
13945 /* save all VM settings */
13946 rc = i_saveSettings(NULL);
13947 // no need to check whether VirtualBox.xml needs saving also since
13948 // we can't have a name change pending at this point
13949 }
13950 else
13951 {
13952 // delete the saved state file (it might have been already created);
13953 // we need not check whether this is shared with a snapshot here because
13954 // we certainly created this saved state file here anew
13955 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13956 }
13957
13958 /* notify the progress object about operation completion */
13959 Assert(mConsoleTaskData.mProgress);
13960 if (SUCCEEDED(aRc))
13961 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13962 else
13963 {
13964 if (aErrMsg.length())
13965 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13966 COM_IIDOF(ISession),
13967 getComponentName(),
13968 aErrMsg.c_str());
13969 else
13970 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13971 }
13972
13973 /* clear out the temporary saved state data */
13974 mConsoleTaskData.mLastState = MachineState_Null;
13975 mConsoleTaskData.strStateFilePath.setNull();
13976 mConsoleTaskData.mProgress.setNull();
13977
13978 LogFlowThisFuncLeave();
13979 return rc;
13980}
13981
13982/**
13983 * Deletes the given file if it is no longer in use by either the current machine state
13984 * (if the machine is "saved") or any of the machine's snapshots.
13985 *
13986 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13987 * but is different for each SnapshotMachine. When calling this, the order of calling this
13988 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13989 * is therefore critical. I know, it's all rather messy.
13990 *
13991 * @param strStateFile
13992 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13993 * the test for whether the saved state file is in use.
13994 */
13995void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13996 Snapshot *pSnapshotToIgnore)
13997{
13998 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13999 if ( (strStateFile.isNotEmpty())
14000 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14001 )
14002 // ... and it must also not be shared with other snapshots
14003 if ( !mData->mFirstSnapshot
14004 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14005 // this checks the SnapshotMachine's state file paths
14006 )
14007 RTFileDelete(strStateFile.c_str());
14008}
14009
14010/**
14011 * Locks the attached media.
14012 *
14013 * All attached hard disks are locked for writing and DVD/floppy are locked for
14014 * reading. Parents of attached hard disks (if any) are locked for reading.
14015 *
14016 * This method also performs accessibility check of all media it locks: if some
14017 * media is inaccessible, the method will return a failure and a bunch of
14018 * extended error info objects per each inaccessible medium.
14019 *
14020 * Note that this method is atomic: if it returns a success, all media are
14021 * locked as described above; on failure no media is locked at all (all
14022 * succeeded individual locks will be undone).
14023 *
14024 * The caller is responsible for doing the necessary state sanity checks.
14025 *
14026 * The locks made by this method must be undone by calling #unlockMedia() when
14027 * no more needed.
14028 */
14029HRESULT SessionMachine::i_lockMedia()
14030{
14031 AutoCaller autoCaller(this);
14032 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14033
14034 AutoMultiWriteLock2 alock(this->lockHandle(),
14035 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14036
14037 /* bail out if trying to lock things with already set up locking */
14038 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14039
14040 MultiResult mrc(S_OK);
14041
14042 /* Collect locking information for all medium objects attached to the VM. */
14043 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14044 it != mMediaData->mAttachments.end();
14045 ++it)
14046 {
14047 MediumAttachment* pAtt = *it;
14048 DeviceType_T devType = pAtt->i_getType();
14049 Medium *pMedium = pAtt->i_getMedium();
14050
14051 MediumLockList *pMediumLockList(new MediumLockList());
14052 // There can be attachments without a medium (floppy/dvd), and thus
14053 // it's impossible to create a medium lock list. It still makes sense
14054 // to have the empty medium lock list in the map in case a medium is
14055 // attached later.
14056 if (pMedium != NULL)
14057 {
14058 MediumType_T mediumType = pMedium->i_getType();
14059 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14060 || mediumType == MediumType_Shareable;
14061 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14062
14063 alock.release();
14064 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14065 !fIsReadOnlyLock /* fMediumLockWrite */,
14066 false /* fMediumLockWriteAll */,
14067 NULL,
14068 *pMediumLockList);
14069 alock.acquire();
14070 if (FAILED(mrc))
14071 {
14072 delete pMediumLockList;
14073 mData->mSession.mLockedMedia.Clear();
14074 break;
14075 }
14076 }
14077
14078 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14079 if (FAILED(rc))
14080 {
14081 mData->mSession.mLockedMedia.Clear();
14082 mrc = setError(rc,
14083 tr("Collecting locking information for all attached media failed"));
14084 break;
14085 }
14086 }
14087
14088 if (SUCCEEDED(mrc))
14089 {
14090 /* Now lock all media. If this fails, nothing is locked. */
14091 alock.release();
14092 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14093 alock.acquire();
14094 if (FAILED(rc))
14095 {
14096 mrc = setError(rc,
14097 tr("Locking of attached media failed"));
14098 }
14099 }
14100
14101 return mrc;
14102}
14103
14104/**
14105 * Undoes the locks made by by #lockMedia().
14106 */
14107HRESULT SessionMachine::i_unlockMedia()
14108{
14109 AutoCaller autoCaller(this);
14110 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14111
14112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14113
14114 /* we may be holding important error info on the current thread;
14115 * preserve it */
14116 ErrorInfoKeeper eik;
14117
14118 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14119 AssertComRC(rc);
14120 return rc;
14121}
14122
14123/**
14124 * Helper to change the machine state (reimplementation).
14125 *
14126 * @note Locks this object for writing.
14127 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14128 * it can cause crashes in random places due to unexpectedly committing
14129 * the current settings. The caller is responsible for that. The call
14130 * to saveStateSettings is fine, because this method does not commit.
14131 */
14132HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14133{
14134 LogFlowThisFuncEnter();
14135 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14136
14137 AutoCaller autoCaller(this);
14138 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14139
14140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14141
14142 MachineState_T oldMachineState = mData->mMachineState;
14143
14144 AssertMsgReturn(oldMachineState != aMachineState,
14145 ("oldMachineState=%s, aMachineState=%s\n",
14146 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14147 E_FAIL);
14148
14149 HRESULT rc = S_OK;
14150
14151 int stsFlags = 0;
14152 bool deleteSavedState = false;
14153
14154 /* detect some state transitions */
14155
14156 if ( ( oldMachineState == MachineState_Saved
14157 && aMachineState == MachineState_Restoring)
14158 || ( ( oldMachineState == MachineState_PoweredOff
14159 || oldMachineState == MachineState_Teleported
14160 || oldMachineState == MachineState_Aborted
14161 )
14162 && ( aMachineState == MachineState_TeleportingIn
14163 || aMachineState == MachineState_Starting
14164 )
14165 )
14166 )
14167 {
14168 /* The EMT thread is about to start */
14169
14170 /* Nothing to do here for now... */
14171
14172 /// @todo NEWMEDIA don't let mDVDDrive and other children
14173 /// change anything when in the Starting/Restoring state
14174 }
14175 else if ( ( oldMachineState == MachineState_Running
14176 || oldMachineState == MachineState_Paused
14177 || oldMachineState == MachineState_Teleporting
14178 || oldMachineState == MachineState_LiveSnapshotting
14179 || oldMachineState == MachineState_Stuck
14180 || oldMachineState == MachineState_Starting
14181 || oldMachineState == MachineState_Stopping
14182 || oldMachineState == MachineState_Saving
14183 || oldMachineState == MachineState_Restoring
14184 || oldMachineState == MachineState_TeleportingPausedVM
14185 || oldMachineState == MachineState_TeleportingIn
14186 )
14187 && ( aMachineState == MachineState_PoweredOff
14188 || aMachineState == MachineState_Saved
14189 || aMachineState == MachineState_Teleported
14190 || aMachineState == MachineState_Aborted
14191 )
14192 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14193 * snapshot */
14194 && ( mConsoleTaskData.mSnapshot.isNull()
14195 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14196 )
14197 )
14198 {
14199 /* The EMT thread has just stopped, unlock attached media. Note that as
14200 * opposed to locking that is done from Console, we do unlocking here
14201 * because the VM process may have aborted before having a chance to
14202 * properly unlock all media it locked. */
14203
14204 unlockMedia();
14205 }
14206
14207 if (oldMachineState == MachineState_Restoring)
14208 {
14209 if (aMachineState != MachineState_Saved)
14210 {
14211 /*
14212 * delete the saved state file once the machine has finished
14213 * restoring from it (note that Console sets the state from
14214 * Restoring to Saved if the VM couldn't restore successfully,
14215 * to give the user an ability to fix an error and retry --
14216 * we keep the saved state file in this case)
14217 */
14218 deleteSavedState = true;
14219 }
14220 }
14221 else if ( oldMachineState == MachineState_Saved
14222 && ( aMachineState == MachineState_PoweredOff
14223 || aMachineState == MachineState_Aborted
14224 || aMachineState == MachineState_Teleported
14225 )
14226 )
14227 {
14228 /*
14229 * delete the saved state after Console::ForgetSavedState() is called
14230 * or if the VM process (owning a direct VM session) crashed while the
14231 * VM was Saved
14232 */
14233
14234 /// @todo (dmik)
14235 // Not sure that deleting the saved state file just because of the
14236 // client death before it attempted to restore the VM is a good
14237 // thing. But when it crashes we need to go to the Aborted state
14238 // which cannot have the saved state file associated... The only
14239 // way to fix this is to make the Aborted condition not a VM state
14240 // but a bool flag: i.e., when a crash occurs, set it to true and
14241 // change the state to PoweredOff or Saved depending on the
14242 // saved state presence.
14243
14244 deleteSavedState = true;
14245 mData->mCurrentStateModified = TRUE;
14246 stsFlags |= SaveSTS_CurStateModified;
14247 }
14248
14249 if ( aMachineState == MachineState_Starting
14250 || aMachineState == MachineState_Restoring
14251 || aMachineState == MachineState_TeleportingIn
14252 )
14253 {
14254 /* set the current state modified flag to indicate that the current
14255 * state is no more identical to the state in the
14256 * current snapshot */
14257 if (!mData->mCurrentSnapshot.isNull())
14258 {
14259 mData->mCurrentStateModified = TRUE;
14260 stsFlags |= SaveSTS_CurStateModified;
14261 }
14262 }
14263
14264 if (deleteSavedState)
14265 {
14266 if (mRemoveSavedState)
14267 {
14268 Assert(!mSSData->strStateFilePath.isEmpty());
14269
14270 // it is safe to delete the saved state file if ...
14271 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14272 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14273 // ... none of the snapshots share the saved state file
14274 )
14275 RTFileDelete(mSSData->strStateFilePath.c_str());
14276 }
14277
14278 mSSData->strStateFilePath.setNull();
14279 stsFlags |= SaveSTS_StateFilePath;
14280 }
14281
14282 /* redirect to the underlying peer machine */
14283 mPeer->i_setMachineState(aMachineState);
14284
14285 if ( aMachineState == MachineState_PoweredOff
14286 || aMachineState == MachineState_Teleported
14287 || aMachineState == MachineState_Aborted
14288 || aMachineState == MachineState_Saved)
14289 {
14290 /* the machine has stopped execution
14291 * (or the saved state file was adopted) */
14292 stsFlags |= SaveSTS_StateTimeStamp;
14293 }
14294
14295 if ( ( oldMachineState == MachineState_PoweredOff
14296 || oldMachineState == MachineState_Aborted
14297 || oldMachineState == MachineState_Teleported
14298 )
14299 && aMachineState == MachineState_Saved)
14300 {
14301 /* the saved state file was adopted */
14302 Assert(!mSSData->strStateFilePath.isEmpty());
14303 stsFlags |= SaveSTS_StateFilePath;
14304 }
14305
14306#ifdef VBOX_WITH_GUEST_PROPS
14307 if ( aMachineState == MachineState_PoweredOff
14308 || aMachineState == MachineState_Aborted
14309 || aMachineState == MachineState_Teleported)
14310 {
14311 /* Make sure any transient guest properties get removed from the
14312 * property store on shutdown. */
14313 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14314
14315 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14316 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14317 while (it != llGuestProperties.end())
14318 {
14319 const settings::GuestProperty &prop = *it;
14320 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14321 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14322 {
14323 it = llGuestProperties.erase(it);
14324 fNeedsSaving = true;
14325 }
14326 else
14327 {
14328 ++it;
14329 }
14330 }
14331
14332 if (fNeedsSaving)
14333 {
14334 mData->mCurrentStateModified = TRUE;
14335 stsFlags |= SaveSTS_CurStateModified;
14336 }
14337 }
14338#endif /* VBOX_WITH_GUEST_PROPS */
14339
14340 rc = i_saveStateSettings(stsFlags);
14341
14342 if ( ( oldMachineState != MachineState_PoweredOff
14343 && oldMachineState != MachineState_Aborted
14344 && oldMachineState != MachineState_Teleported
14345 )
14346 && ( aMachineState == MachineState_PoweredOff
14347 || aMachineState == MachineState_Aborted
14348 || aMachineState == MachineState_Teleported
14349 )
14350 )
14351 {
14352 /* we've been shut down for any reason */
14353 /* no special action so far */
14354 }
14355
14356 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14357 LogFlowThisFuncLeave();
14358 return rc;
14359}
14360
14361/**
14362 * Sends the current machine state value to the VM process.
14363 *
14364 * @note Locks this object for reading, then calls a client process.
14365 */
14366HRESULT SessionMachine::i_updateMachineStateOnClient()
14367{
14368 AutoCaller autoCaller(this);
14369 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14370
14371 ComPtr<IInternalSessionControl> directControl;
14372 {
14373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14374 AssertReturn(!!mData, E_FAIL);
14375 directControl = mData->mSession.mDirectControl;
14376
14377 /* directControl may be already set to NULL here in #OnSessionEnd()
14378 * called too early by the direct session process while there is still
14379 * some operation (like deleting the snapshot) in progress. The client
14380 * process in this case is waiting inside Session::close() for the
14381 * "end session" process object to complete, while #uninit() called by
14382 * #checkForDeath() on the Watcher thread is waiting for the pending
14383 * operation to complete. For now, we accept this inconsistent behavior
14384 * and simply do nothing here. */
14385
14386 if (mData->mSession.mState == SessionState_Unlocking)
14387 return S_OK;
14388
14389 AssertReturn(!directControl.isNull(), E_FAIL);
14390 }
14391
14392 return directControl->UpdateMachineState(mData->mMachineState);
14393}
14394
14395HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14396{
14397 NOREF(aRemove);
14398 ReturnComNotImplemented();
14399}
14400
14401HRESULT Machine::updateState(MachineState_T aState)
14402{
14403 NOREF(aState);
14404 ReturnComNotImplemented();
14405}
14406
14407HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14408{
14409 NOREF(aProgress);
14410 ReturnComNotImplemented();
14411}
14412
14413HRESULT Machine::endPowerUp(LONG aResult)
14414{
14415 NOREF(aResult);
14416 ReturnComNotImplemented();
14417}
14418
14419HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14420{
14421 NOREF(aProgress);
14422 ReturnComNotImplemented();
14423}
14424
14425HRESULT Machine::endPoweringDown(LONG aResult,
14426 const com::Utf8Str &aErrMsg)
14427{
14428 NOREF(aResult);
14429 NOREF(aErrMsg);
14430 ReturnComNotImplemented();
14431}
14432
14433HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14434 BOOL *aMatched,
14435 ULONG *aMaskedInterfaces)
14436{
14437 NOREF(aDevice);
14438 NOREF(aMatched);
14439 NOREF(aMaskedInterfaces);
14440 ReturnComNotImplemented();
14441
14442}
14443
14444HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14445{
14446 NOREF(aId); NOREF(aCaptureFilename);
14447 ReturnComNotImplemented();
14448}
14449
14450HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14451 BOOL aDone)
14452{
14453 NOREF(aId);
14454 NOREF(aDone);
14455 ReturnComNotImplemented();
14456}
14457
14458HRESULT Machine::autoCaptureUSBDevices()
14459{
14460 ReturnComNotImplemented();
14461}
14462
14463HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14464{
14465 NOREF(aDone);
14466 ReturnComNotImplemented();
14467}
14468
14469HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14470 ComPtr<IProgress> &aProgress)
14471{
14472 NOREF(aSession);
14473 NOREF(aProgress);
14474 ReturnComNotImplemented();
14475}
14476
14477HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14478 com::Utf8Str &aStateFilePath)
14479{
14480 NOREF(aProgress);
14481 NOREF(aStateFilePath);
14482 ReturnComNotImplemented();
14483}
14484
14485HRESULT Machine::endSavingState(LONG aResult,
14486 const com::Utf8Str &aErrMsg)
14487{
14488 NOREF(aResult);
14489 NOREF(aErrMsg);
14490 ReturnComNotImplemented();
14491}
14492
14493HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14494{
14495 NOREF(aSavedStateFile);
14496 ReturnComNotImplemented();
14497}
14498
14499HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14500 const com::Utf8Str &aName,
14501 const com::Utf8Str &aDescription,
14502 const ComPtr<IProgress> &aConsoleProgress,
14503 BOOL aFTakingSnapshotOnline,
14504 com::Utf8Str &aStateFilePath)
14505{
14506 NOREF(aInitiator);
14507 NOREF(aName);
14508 NOREF(aDescription);
14509 NOREF(aConsoleProgress);
14510 NOREF(aFTakingSnapshotOnline);
14511 NOREF(aStateFilePath);
14512 ReturnComNotImplemented();
14513}
14514
14515HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14516{
14517 NOREF(aSuccess);
14518 ReturnComNotImplemented();
14519}
14520
14521HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14522 const com::Guid &aStartId,
14523 const com::Guid &aEndId,
14524 BOOL aDeleteAllChildren,
14525 MachineState_T *aMachineState,
14526 ComPtr<IProgress> &aProgress)
14527{
14528 NOREF(aInitiator);
14529 NOREF(aStartId);
14530 NOREF(aEndId);
14531 NOREF(aDeleteAllChildren);
14532 NOREF(aMachineState);
14533 NOREF(aProgress);
14534 ReturnComNotImplemented();
14535}
14536
14537HRESULT Machine::finishOnlineMergeMedium()
14538{
14539 ReturnComNotImplemented();
14540}
14541
14542HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14543 const ComPtr<ISnapshot> &aSnapshot,
14544 MachineState_T *aMachineState,
14545 ComPtr<IProgress> &aProgress)
14546{
14547 NOREF(aInitiator);
14548 NOREF(aSnapshot);
14549 NOREF(aMachineState);
14550 NOREF(aProgress);
14551 ReturnComNotImplemented();
14552}
14553
14554HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14555 std::vector<com::Utf8Str> &aValues,
14556 std::vector<LONG64> &aTimestamps,
14557 std::vector<com::Utf8Str> &aFlags)
14558{
14559 NOREF(aNames);
14560 NOREF(aValues);
14561 NOREF(aTimestamps);
14562 NOREF(aFlags);
14563 ReturnComNotImplemented();
14564}
14565
14566HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14567 const com::Utf8Str &aValue,
14568 LONG64 aTimestamp,
14569 const com::Utf8Str &aFlags)
14570{
14571 NOREF(aName);
14572 NOREF(aValue);
14573 NOREF(aTimestamp);
14574 NOREF(aFlags);
14575 ReturnComNotImplemented();
14576}
14577
14578HRESULT Machine::lockMedia()
14579{
14580 ReturnComNotImplemented();
14581}
14582
14583HRESULT Machine::unlockMedia()
14584{
14585 ReturnComNotImplemented();
14586}
14587
14588HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14589 ComPtr<IMediumAttachment> &aNewAttachment)
14590{
14591 NOREF(aAttachment);
14592 NOREF(aNewAttachment);
14593 ReturnComNotImplemented();
14594}
14595
14596HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14597 ULONG aCpuUser,
14598 ULONG aCpuKernel,
14599 ULONG aCpuIdle,
14600 ULONG aMemTotal,
14601 ULONG aMemFree,
14602 ULONG aMemBalloon,
14603 ULONG aMemShared,
14604 ULONG aMemCache,
14605 ULONG aPagedTotal,
14606 ULONG aMemAllocTotal,
14607 ULONG aMemFreeTotal,
14608 ULONG aMemBalloonTotal,
14609 ULONG aMemSharedTotal,
14610 ULONG aVmNetRx,
14611 ULONG aVmNetTx)
14612{
14613 NOREF(aValidStats);
14614 NOREF(aCpuUser);
14615 NOREF(aCpuKernel);
14616 NOREF(aCpuIdle);
14617 NOREF(aMemTotal);
14618 NOREF(aMemFree);
14619 NOREF(aMemBalloon);
14620 NOREF(aMemShared);
14621 NOREF(aMemCache);
14622 NOREF(aPagedTotal);
14623 NOREF(aMemAllocTotal);
14624 NOREF(aMemFreeTotal);
14625 NOREF(aMemBalloonTotal);
14626 NOREF(aMemSharedTotal);
14627 NOREF(aVmNetRx);
14628 NOREF(aVmNetTx);
14629 ReturnComNotImplemented();
14630}
Note: See TracBrowser for help on using the repository browser.

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