VirtualBox

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

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

MediumImpl: be a bit more verbose if locking of the media failed

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 503.7 KB
Line 
1/* $Id: MachineImpl.cpp 54966 2015-03-26 14:51:46Z 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_KVM:
1257 case ParavirtProvider_Minimal:
1258 break;
1259
1260 /* Resolve dynamic provider types to the effective types. */
1261 default:
1262 {
1263 ComPtr<IGuestOSType> ptrGuestOSType;
1264 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1266
1267 Bstr guestTypeFamilyId;
1268 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1269 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1270 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1271
1272 switch (mHWData->mParavirtProvider)
1273 {
1274 case ParavirtProvider_Legacy:
1275 {
1276 if (fOsXGuest)
1277 *aParavirtProvider = ParavirtProvider_Minimal;
1278 else
1279 *aParavirtProvider = ParavirtProvider_None;
1280 break;
1281 }
1282
1283 case ParavirtProvider_Default:
1284 {
1285 if (fOsXGuest)
1286 *aParavirtProvider = ParavirtProvider_Minimal;
1287 else if ( mUserData->s.strOsType == "Windows10"
1288 || mUserData->s.strOsType == "Windows10_64"
1289 || mUserData->s.strOsType == "Windows81"
1290 || mUserData->s.strOsType == "Windows81_64"
1291 || mUserData->s.strOsType == "Windows8"
1292 || mUserData->s.strOsType == "Windows8_64"
1293 || mUserData->s.strOsType == "Windows7"
1294 || mUserData->s.strOsType == "Windows7_64"
1295 || mUserData->s.strOsType == "WindowsVista"
1296 || mUserData->s.strOsType == "WindowsVista_64"
1297 || mUserData->s.strOsType == "Windows2012"
1298 || mUserData->s.strOsType == "Windows2012_64"
1299 || mUserData->s.strOsType == "Windows2008"
1300 || mUserData->s.strOsType == "Windows2008_64")
1301 {
1302 *aParavirtProvider = ParavirtProvider_HyperV;
1303 }
1304 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1305 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1306 || mUserData->s.strOsType == "Linux"
1307 || mUserData->s.strOsType == "Linux_64"
1308 || mUserData->s.strOsType == "ArchLinux"
1309 || mUserData->s.strOsType == "ArchLinux_64"
1310 || mUserData->s.strOsType == "Debian"
1311 || mUserData->s.strOsType == "Debian_64"
1312 || mUserData->s.strOsType == "Fedora"
1313 || mUserData->s.strOsType == "Fedora_64"
1314 || mUserData->s.strOsType == "Gentoo"
1315 || mUserData->s.strOsType == "Gentoo_64"
1316 || mUserData->s.strOsType == "Mandriva"
1317 || mUserData->s.strOsType == "Mandriva_64"
1318 || mUserData->s.strOsType == "OpenSUSE"
1319 || mUserData->s.strOsType == "OpenSUSE_64"
1320 || mUserData->s.strOsType == "Oracle"
1321 || mUserData->s.strOsType == "Oracle_64"
1322 || mUserData->s.strOsType == "RedHat"
1323 || mUserData->s.strOsType == "RedHat_64"
1324 || mUserData->s.strOsType == "Turbolinux"
1325 || mUserData->s.strOsType == "Turbolinux_64"
1326 || mUserData->s.strOsType == "Ubuntu"
1327 || mUserData->s.strOsType == "Ubuntu_64"
1328 || mUserData->s.strOsType == "Xandros"
1329 || mUserData->s.strOsType == "Xandros_64")
1330 {
1331 *aParavirtProvider = ParavirtProvider_KVM;
1332 }
1333 else
1334 *aParavirtProvider = ParavirtProvider_None;
1335 break;
1336 }
1337 }
1338 break;
1339 }
1340 }
1341
1342 Assert( *aParavirtProvider == ParavirtProvider_None
1343 || *aParavirtProvider == ParavirtProvider_Minimal
1344 || *aParavirtProvider == ParavirtProvider_HyperV
1345 || *aParavirtProvider == ParavirtProvider_KVM);
1346 return S_OK;
1347}
1348
1349HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1350{
1351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 aHardwareVersion = mHWData->mHWVersion;
1354
1355 return S_OK;
1356}
1357
1358HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1359{
1360 /* check known version */
1361 Utf8Str hwVersion = aHardwareVersion;
1362 if ( hwVersion.compare("1") != 0
1363 && hwVersion.compare("2") != 0)
1364 return setError(E_INVALIDARG,
1365 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1366
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 HRESULT rc = i_checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 i_setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 mHWData->mHWVersion = aHardwareVersion;
1375
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 if (!mHWData->mHardwareUUID.isZero())
1384 aHardwareUUID = mHWData->mHardwareUUID;
1385 else
1386 aHardwareUUID = mData->mUuid;
1387
1388 return S_OK;
1389}
1390
1391HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1392{
1393 if (!aHardwareUUID.isValid())
1394 return E_INVALIDARG;
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 HRESULT rc = i_checkStateDependency(MutableStateDep);
1399 if (FAILED(rc)) return rc;
1400
1401 i_setModified(IsModified_MachineData);
1402 mHWData.backup();
1403 if (aHardwareUUID == mData->mUuid)
1404 mHWData->mHardwareUUID.clear();
1405 else
1406 mHWData->mHardwareUUID = aHardwareUUID;
1407
1408 return S_OK;
1409}
1410
1411HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1412{
1413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 *aMemorySize = mHWData->mMemorySize;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::setMemorySize(ULONG aMemorySize)
1421{
1422 /* check RAM limits */
1423 if ( aMemorySize < MM_RAM_MIN_IN_MB
1424 || aMemorySize > MM_RAM_MAX_IN_MB
1425 )
1426 return setError(E_INVALIDARG,
1427 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1428 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1429
1430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 HRESULT rc = i_checkStateDependency(MutableStateDep);
1433 if (FAILED(rc)) return rc;
1434
1435 i_setModified(IsModified_MachineData);
1436 mHWData.backup();
1437 mHWData->mMemorySize = aMemorySize;
1438
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 *aCPUCount = mHWData->mCPUCount;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setCPUCount(ULONG aCPUCount)
1452{
1453 /* check CPU limits */
1454 if ( aCPUCount < SchemaDefs::MinCPUCount
1455 || aCPUCount > SchemaDefs::MaxCPUCount
1456 )
1457 return setError(E_INVALIDARG,
1458 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1459 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1460
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1464 if (mHWData->mCPUHotPlugEnabled)
1465 {
1466 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1467 {
1468 if (mHWData->mCPUAttached[idx])
1469 return setError(E_INVALIDARG,
1470 tr("There is still a CPU attached to socket %lu."
1471 "Detach the CPU before removing the socket"),
1472 aCPUCount, idx+1);
1473 }
1474 }
1475
1476 HRESULT rc = i_checkStateDependency(MutableStateDep);
1477 if (FAILED(rc)) return rc;
1478
1479 i_setModified(IsModified_MachineData);
1480 mHWData.backup();
1481 mHWData->mCPUCount = aCPUCount;
1482
1483 return S_OK;
1484}
1485
1486HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1487{
1488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1491
1492 return S_OK;
1493}
1494
1495HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1496{
1497 HRESULT rc = S_OK;
1498
1499 /* check throttle limits */
1500 if ( aCPUExecutionCap < 1
1501 || aCPUExecutionCap > 100
1502 )
1503 return setError(E_INVALIDARG,
1504 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1505 aCPUExecutionCap, 1, 100);
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 alock.release();
1510 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1511 alock.acquire();
1512 if (FAILED(rc)) return rc;
1513
1514 i_setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1517
1518 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1519 if (Global::IsOnline(mData->mMachineState))
1520 i_saveSettings(NULL);
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1539
1540 rc = i_checkStateDependency(MutableStateDep);
1541 if (FAILED(rc)) return rc;
1542
1543 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1544 {
1545 if (aCPUHotPlugEnabled)
1546 {
1547 i_setModified(IsModified_MachineData);
1548 mHWData.backup();
1549
1550 /* Add the amount of CPUs currently attached */
1551 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1552 mHWData->mCPUAttached[i] = true;
1553 }
1554 else
1555 {
1556 /*
1557 * We can disable hotplug only if the amount of maximum CPUs is equal
1558 * to the amount of attached CPUs
1559 */
1560 unsigned cCpusAttached = 0;
1561 unsigned iHighestId = 0;
1562
1563 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1564 {
1565 if (mHWData->mCPUAttached[i])
1566 {
1567 cCpusAttached++;
1568 iHighestId = i;
1569 }
1570 }
1571
1572 if ( (cCpusAttached != mHWData->mCPUCount)
1573 || (iHighestId >= mHWData->mCPUCount))
1574 return setError(E_INVALIDARG,
1575 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1576
1577 i_setModified(IsModified_MachineData);
1578 mHWData.backup();
1579 }
1580 }
1581
1582 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1583
1584 return rc;
1585}
1586
1587HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1588{
1589#ifdef VBOX_WITH_USB_CARDREADER
1590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1591
1592 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1593
1594 return S_OK;
1595#else
1596 NOREF(aEmulatedUSBCardReaderEnabled);
1597 return E_NOTIMPL;
1598#endif
1599}
1600
1601HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1602{
1603#ifdef VBOX_WITH_USB_CARDREADER
1604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1605
1606 HRESULT rc = i_checkStateDependency(MutableStateDep);
1607 if (FAILED(rc)) return rc;
1608
1609 i_setModified(IsModified_MachineData);
1610 mHWData.backup();
1611 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1612
1613 return S_OK;
1614#else
1615 NOREF(aEmulatedUSBCardReaderEnabled);
1616 return E_NOTIMPL;
1617#endif
1618}
1619
1620HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1621{
1622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 *aHPETEnabled = mHWData->mHPETEnabled;
1625
1626 return S_OK;
1627}
1628
1629HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1630{
1631 HRESULT rc = S_OK;
1632
1633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1634
1635 rc = i_checkStateDependency(MutableStateDep);
1636 if (FAILED(rc)) return rc;
1637
1638 i_setModified(IsModified_MachineData);
1639 mHWData.backup();
1640
1641 mHWData->mHPETEnabled = aHPETEnabled;
1642
1643 return rc;
1644}
1645
1646HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1647{
1648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1649
1650 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1651 return S_OK;
1652}
1653
1654HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1655{
1656 HRESULT rc = S_OK;
1657
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659
1660 i_setModified(IsModified_MachineData);
1661 mHWData.backup();
1662 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1663
1664 alock.release();
1665 rc = i_onVideoCaptureChange();
1666 alock.acquire();
1667 if (FAILED(rc))
1668 {
1669 /*
1670 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1671 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1672 * determine if it should start or stop capturing. Therefore we need to manually
1673 * undo change.
1674 */
1675 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1676 return rc;
1677 }
1678
1679 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1680 if (Global::IsOnline(mData->mMachineState))
1681 i_saveSettings(NULL);
1682
1683 return rc;
1684}
1685
1686HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1687{
1688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1689 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1690 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1691 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1692 return S_OK;
1693}
1694
1695HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1696{
1697 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1698 bool fChanged = false;
1699
1700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1701
1702 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1703 {
1704 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1705 {
1706 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1707 fChanged = true;
1708 }
1709 }
1710 if (fChanged)
1711 {
1712 alock.release();
1713 HRESULT rc = i_onVideoCaptureChange();
1714 alock.acquire();
1715 if (FAILED(rc)) return rc;
1716 i_setModified(IsModified_MachineData);
1717
1718 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1719 if (Global::IsOnline(mData->mMachineState))
1720 i_saveSettings(NULL);
1721 }
1722
1723 return S_OK;
1724}
1725
1726HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1727{
1728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1729 if (mHWData->mVideoCaptureFile.isEmpty())
1730 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1731 else
1732 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1733 return S_OK;
1734}
1735
1736HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1737{
1738 Utf8Str strFile(aVideoCaptureFile);
1739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1740
1741 if ( Global::IsOnline(mData->mMachineState)
1742 && mHWData->mVideoCaptureEnabled)
1743 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1744
1745 if (!RTPathStartsWithRoot(strFile.c_str()))
1746 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1747
1748 if (!strFile.isEmpty())
1749 {
1750 Utf8Str defaultFile;
1751 i_getDefaultVideoCaptureFile(defaultFile);
1752 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1753 strFile.setNull();
1754 }
1755
1756 i_setModified(IsModified_MachineData);
1757 mHWData.backup();
1758 mHWData->mVideoCaptureFile = strFile;
1759
1760 return S_OK;
1761}
1762
1763HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1767 return S_OK;
1768}
1769
1770HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1771{
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 if ( Global::IsOnline(mData->mMachineState)
1775 && mHWData->mVideoCaptureEnabled)
1776 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1786{
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1789 return S_OK;
1790}
1791
1792HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1793{
1794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 if ( Global::IsOnline(mData->mMachineState)
1797 && mHWData->mVideoCaptureEnabled)
1798 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1799
1800 i_setModified(IsModified_MachineData);
1801 mHWData.backup();
1802 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1808{
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1811 return S_OK;
1812}
1813
1814HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1815{
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 i_setModified(IsModified_MachineData);
1823 mHWData.backup();
1824 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1833 return S_OK;
1834}
1835
1836HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1837{
1838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 if ( Global::IsOnline(mData->mMachineState)
1841 && mHWData->mVideoCaptureEnabled)
1842 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1855 return S_OK;
1856}
1857
1858HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1859{
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 i_setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1877 return S_OK;
1878}
1879
1880HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1881{
1882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 if ( Global::IsOnline(mData->mMachineState)
1885 && mHWData->mVideoCaptureEnabled)
1886 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1887
1888 i_setModified(IsModified_MachineData);
1889 mHWData.backup();
1890 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1896{
1897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1900 return S_OK;
1901}
1902
1903HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1904{
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 if ( Global::IsOnline(mData->mMachineState)
1908 && mHWData->mVideoCaptureEnabled)
1909 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1910
1911 i_setModified(IsModified_MachineData);
1912 mHWData.backup();
1913 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1914
1915 return S_OK;
1916}
1917
1918HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1919{
1920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1921
1922 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1923
1924 return S_OK;
1925}
1926
1927HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1928{
1929 switch (aGraphicsControllerType)
1930 {
1931 case GraphicsControllerType_Null:
1932 case GraphicsControllerType_VBoxVGA:
1933#ifdef VBOX_WITH_VMSVGA
1934 case GraphicsControllerType_VMSVGA:
1935#endif
1936 break;
1937 default:
1938 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1939 }
1940
1941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1942
1943 HRESULT rc = i_checkStateDependency(MutableStateDep);
1944 if (FAILED(rc)) return rc;
1945
1946 i_setModified(IsModified_MachineData);
1947 mHWData.backup();
1948 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1949
1950 return S_OK;
1951}
1952
1953HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1954{
1955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 *aVRAMSize = mHWData->mVRAMSize;
1958
1959 return S_OK;
1960}
1961
1962HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1963{
1964 /* check VRAM limits */
1965 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1966 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1967 return setError(E_INVALIDARG,
1968 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1969 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1970
1971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 HRESULT rc = i_checkStateDependency(MutableStateDep);
1974 if (FAILED(rc)) return rc;
1975
1976 i_setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mVRAMSize = aVRAMSize;
1979
1980 return S_OK;
1981}
1982
1983/** @todo this method should not be public */
1984HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1985{
1986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1989
1990 return S_OK;
1991}
1992
1993/**
1994 * Set the memory balloon size.
1995 *
1996 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1997 * we have to make sure that we never call IGuest from here.
1998 */
1999HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2000{
2001 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2002#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2003 /* check limits */
2004 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2005 return setError(E_INVALIDARG,
2006 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2007 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2008
2009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2014
2015 return S_OK;
2016#else
2017 NOREF(aMemoryBalloonSize);
2018 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2019#endif
2020}
2021
2022HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2023{
2024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2027 return S_OK;
2028}
2029
2030HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2031{
2032#ifdef VBOX_WITH_PAGE_SHARING
2033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2039 return S_OK;
2040#else
2041 NOREF(aPageFusionEnabled);
2042 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2043#endif
2044}
2045
2046HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2047{
2048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2051
2052 return S_OK;
2053}
2054
2055HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2056{
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 HRESULT rc = i_checkStateDependency(MutableStateDep);
2060 if (FAILED(rc)) return rc;
2061
2062 /** @todo check validity! */
2063
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2067
2068 return S_OK;
2069}
2070
2071
2072HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2073{
2074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2077
2078 return S_OK;
2079}
2080
2081HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2082{
2083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 HRESULT rc = i_checkStateDependency(MutableStateDep);
2086 if (FAILED(rc)) return rc;
2087
2088 /** @todo check validity! */
2089 i_setModified(IsModified_MachineData);
2090 mHWData.backup();
2091 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2092
2093 return S_OK;
2094}
2095
2096HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2097{
2098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2099
2100 *aMonitorCount = mHWData->mMonitorCount;
2101
2102 return S_OK;
2103}
2104
2105HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2106{
2107 /* make sure monitor count is a sensible number */
2108 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2109 return setError(E_INVALIDARG,
2110 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2111 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2112
2113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 HRESULT rc = i_checkStateDependency(MutableStateDep);
2116 if (FAILED(rc)) return rc;
2117
2118 i_setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mMonitorCount = aMonitorCount;
2121
2122 return S_OK;
2123}
2124
2125HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2126{
2127 /* mBIOSSettings is constant during life time, no need to lock */
2128 aBIOSSettings = mBIOSSettings;
2129
2130 return S_OK;
2131}
2132
2133HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2134{
2135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 switch (aProperty)
2138 {
2139 case CPUPropertyType_PAE:
2140 *aValue = mHWData->mPAEEnabled;
2141 break;
2142
2143 case CPUPropertyType_Synthetic:
2144 *aValue = mHWData->mSyntheticCpu;
2145 break;
2146
2147 case CPUPropertyType_LongMode:
2148 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2149 *aValue = TRUE;
2150 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2151 *aValue = FALSE;
2152#if HC_ARCH_BITS == 64
2153 else
2154 *aValue = TRUE;
2155#else
2156 else
2157 {
2158 *aValue = FALSE;
2159
2160 ComPtr<IGuestOSType> ptrGuestOSType;
2161 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2162 if (SUCCEEDED(hrc2))
2163 {
2164 BOOL fIs64Bit = FALSE;
2165 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2166 if (SUCCEEDED(hrc2) && fIs64Bit)
2167 {
2168 ComObjPtr<Host> ptrHost = mParent->i_host();
2169 alock.release();
2170
2171 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2172 if (FAILED(hrc2))
2173 *aValue = FALSE;
2174 }
2175 }
2176 }
2177#endif
2178 break;
2179
2180 case CPUPropertyType_TripleFaultReset:
2181 *aValue = mHWData->mTripleFaultReset;
2182 break;
2183
2184 default:
2185 return E_INVALIDARG;
2186 }
2187 return S_OK;
2188}
2189
2190HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2191{
2192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2193
2194 HRESULT rc = i_checkStateDependency(MutableStateDep);
2195 if (FAILED(rc)) return rc;
2196
2197 switch (aProperty)
2198 {
2199 case CPUPropertyType_PAE:
2200 i_setModified(IsModified_MachineData);
2201 mHWData.backup();
2202 mHWData->mPAEEnabled = !!aValue;
2203 break;
2204
2205 case CPUPropertyType_Synthetic:
2206 i_setModified(IsModified_MachineData);
2207 mHWData.backup();
2208 mHWData->mSyntheticCpu = !!aValue;
2209 break;
2210
2211 case CPUPropertyType_LongMode:
2212 i_setModified(IsModified_MachineData);
2213 mHWData.backup();
2214 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2215 break;
2216
2217 case CPUPropertyType_TripleFaultReset:
2218 i_setModified(IsModified_MachineData);
2219 mHWData.backup();
2220 mHWData->mTripleFaultReset = !!aValue;
2221 break;
2222
2223 default:
2224 return E_INVALIDARG;
2225 }
2226 return S_OK;
2227}
2228
2229HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2230{
2231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
2233 switch(aId)
2234 {
2235 case 0x0:
2236 case 0x1:
2237 case 0x2:
2238 case 0x3:
2239 case 0x4:
2240 case 0x5:
2241 case 0x6:
2242 case 0x7:
2243 case 0x8:
2244 case 0x9:
2245 case 0xA:
2246 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2247 return E_INVALIDARG;
2248
2249 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2250 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2251 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2252 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2253 break;
2254
2255 case 0x80000000:
2256 case 0x80000001:
2257 case 0x80000002:
2258 case 0x80000003:
2259 case 0x80000004:
2260 case 0x80000005:
2261 case 0x80000006:
2262 case 0x80000007:
2263 case 0x80000008:
2264 case 0x80000009:
2265 case 0x8000000A:
2266 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2267 return E_INVALIDARG;
2268
2269 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2270 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2271 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2272 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2273 break;
2274
2275 default:
2276 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2277 }
2278 return S_OK;
2279}
2280
2281
2282HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2283{
2284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2285
2286 HRESULT rc = i_checkStateDependency(MutableStateDep);
2287 if (FAILED(rc)) return rc;
2288
2289 switch(aId)
2290 {
2291 case 0x0:
2292 case 0x1:
2293 case 0x2:
2294 case 0x3:
2295 case 0x4:
2296 case 0x5:
2297 case 0x6:
2298 case 0x7:
2299 case 0x8:
2300 case 0x9:
2301 case 0xA:
2302 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2303 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2304 i_setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2307 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2308 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2309 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2310 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2311 break;
2312
2313 case 0x80000000:
2314 case 0x80000001:
2315 case 0x80000002:
2316 case 0x80000003:
2317 case 0x80000004:
2318 case 0x80000005:
2319 case 0x80000006:
2320 case 0x80000007:
2321 case 0x80000008:
2322 case 0x80000009:
2323 case 0x8000000A:
2324 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2325 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2329 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2330 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2331 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2332 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2333 break;
2334
2335 default:
2336 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2337 }
2338 return S_OK;
2339}
2340
2341HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2342{
2343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2344
2345 HRESULT rc = i_checkStateDependency(MutableStateDep);
2346 if (FAILED(rc)) return rc;
2347
2348 switch(aId)
2349 {
2350 case 0x0:
2351 case 0x1:
2352 case 0x2:
2353 case 0x3:
2354 case 0x4:
2355 case 0x5:
2356 case 0x6:
2357 case 0x7:
2358 case 0x8:
2359 case 0x9:
2360 case 0xA:
2361 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2362 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2363 i_setModified(IsModified_MachineData);
2364 mHWData.backup();
2365 /* Invalidate leaf. */
2366 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2367 break;
2368
2369 case 0x80000000:
2370 case 0x80000001:
2371 case 0x80000002:
2372 case 0x80000003:
2373 case 0x80000004:
2374 case 0x80000005:
2375 case 0x80000006:
2376 case 0x80000007:
2377 case 0x80000008:
2378 case 0x80000009:
2379 case 0x8000000A:
2380 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2381 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2382 i_setModified(IsModified_MachineData);
2383 mHWData.backup();
2384 /* Invalidate leaf. */
2385 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2386 break;
2387
2388 default:
2389 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2390 }
2391 return S_OK;
2392}
2393
2394HRESULT Machine::removeAllCPUIDLeaves()
2395{
2396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 HRESULT rc = i_checkStateDependency(MutableStateDep);
2399 if (FAILED(rc)) return rc;
2400
2401 i_setModified(IsModified_MachineData);
2402 mHWData.backup();
2403
2404 /* Invalidate all standard leafs. */
2405 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2406 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2407
2408 /* Invalidate all extended leafs. */
2409 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2410 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2411
2412 return S_OK;
2413}
2414HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2415{
2416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2417
2418 switch(aProperty)
2419 {
2420 case HWVirtExPropertyType_Enabled:
2421 *aValue = mHWData->mHWVirtExEnabled;
2422 break;
2423
2424 case HWVirtExPropertyType_VPID:
2425 *aValue = mHWData->mHWVirtExVPIDEnabled;
2426 break;
2427
2428 case HWVirtExPropertyType_NestedPaging:
2429 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2430 break;
2431
2432 case HWVirtExPropertyType_UnrestrictedExecution:
2433 *aValue = mHWData->mHWVirtExUXEnabled;
2434 break;
2435
2436 case HWVirtExPropertyType_LargePages:
2437 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2438#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2439 *aValue = FALSE;
2440#endif
2441 break;
2442
2443 case HWVirtExPropertyType_Force:
2444 *aValue = mHWData->mHWVirtExForceEnabled;
2445 break;
2446
2447 default:
2448 return E_INVALIDARG;
2449 }
2450 return S_OK;
2451}
2452
2453HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2454{
2455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2456
2457 HRESULT rc = i_checkStateDependency(MutableStateDep);
2458 if (FAILED(rc)) return rc;
2459
2460 switch(aProperty)
2461 {
2462 case HWVirtExPropertyType_Enabled:
2463 i_setModified(IsModified_MachineData);
2464 mHWData.backup();
2465 mHWData->mHWVirtExEnabled = !!aValue;
2466 break;
2467
2468 case HWVirtExPropertyType_VPID:
2469 i_setModified(IsModified_MachineData);
2470 mHWData.backup();
2471 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2472 break;
2473
2474 case HWVirtExPropertyType_NestedPaging:
2475 i_setModified(IsModified_MachineData);
2476 mHWData.backup();
2477 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2478 break;
2479
2480 case HWVirtExPropertyType_UnrestrictedExecution:
2481 i_setModified(IsModified_MachineData);
2482 mHWData.backup();
2483 mHWData->mHWVirtExUXEnabled = !!aValue;
2484 break;
2485
2486 case HWVirtExPropertyType_LargePages:
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2490 break;
2491
2492 case HWVirtExPropertyType_Force:
2493 i_setModified(IsModified_MachineData);
2494 mHWData.backup();
2495 mHWData->mHWVirtExForceEnabled = !!aValue;
2496 break;
2497
2498 default:
2499 return E_INVALIDARG;
2500 }
2501
2502 return S_OK;
2503}
2504
2505HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2506{
2507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2515{
2516 /* @todo (r=dmik):
2517 * 1. Allow to change the name of the snapshot folder containing snapshots
2518 * 2. Rename the folder on disk instead of just changing the property
2519 * value (to be smart and not to leave garbage). Note that it cannot be
2520 * done here because the change may be rolled back. Thus, the right
2521 * place is #saveSettings().
2522 */
2523
2524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2525
2526 HRESULT rc = i_checkStateDependency(MutableStateDep);
2527 if (FAILED(rc)) return rc;
2528
2529 if (!mData->mCurrentSnapshot.isNull())
2530 return setError(E_FAIL,
2531 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2532
2533 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2534
2535 if (strSnapshotFolder.isEmpty())
2536 strSnapshotFolder = "Snapshots";
2537 int vrc = i_calculateFullPath(strSnapshotFolder,
2538 strSnapshotFolder);
2539 if (RT_FAILURE(vrc))
2540 return setError(E_FAIL,
2541 tr("Invalid snapshot folder '%s' (%Rrc)"),
2542 strSnapshotFolder.c_str(), vrc);
2543
2544 i_setModified(IsModified_MachineData);
2545 mUserData.backup();
2546
2547 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2548
2549 return S_OK;
2550}
2551
2552HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2553{
2554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2555
2556 aMediumAttachments.resize(mMediaData->mAttachments.size());
2557 size_t i = 0;
2558 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2559 it != mMediaData->mAttachments.end(); ++it, ++i)
2560 aMediumAttachments[i] = *it;
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 Assert(!!mVRDEServer);
2570
2571 aVRDEServer = mVRDEServer;
2572
2573 return S_OK;
2574}
2575
2576HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2577{
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 aAudioAdapter = mAudioAdapter;
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2586{
2587#ifdef VBOX_WITH_VUSB
2588 clearError();
2589 MultiResult rc(S_OK);
2590
2591# ifdef VBOX_WITH_USB
2592 rc = mParent->i_host()->i_checkUSBProxyService();
2593 if (FAILED(rc)) return rc;
2594# endif
2595
2596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2597
2598 USBControllerList data = *mUSBControllers.data();
2599 aUSBControllers.resize(data.size());
2600 size_t i = 0;
2601 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2602 aUSBControllers[i] = *it;
2603
2604 return S_OK;
2605#else
2606 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2607 * extended error info to indicate that USB is simply not available
2608 * (w/o treating it as a failure), for example, as in OSE */
2609 NOREF(aUSBControllers);
2610 ReturnComNotImplemented();
2611#endif /* VBOX_WITH_VUSB */
2612}
2613
2614HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2615{
2616#ifdef VBOX_WITH_VUSB
2617 clearError();
2618 MultiResult rc(S_OK);
2619
2620# ifdef VBOX_WITH_USB
2621 rc = mParent->i_host()->i_checkUSBProxyService();
2622 if (FAILED(rc)) return rc;
2623# endif
2624
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 aUSBDeviceFilters = mUSBDeviceFilters;
2628 return rc;
2629#else
2630 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2631 * extended error info to indicate that USB is simply not available
2632 * (w/o treating it as a failure), for example, as in OSE */
2633 NOREF(aUSBDeviceFilters);
2634 ReturnComNotImplemented();
2635#endif /* VBOX_WITH_VUSB */
2636}
2637
2638HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 aSettingsFilePath = mData->m_strConfigFileFull;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2652 if (FAILED(rc)) return rc;
2653
2654 if (!mData->pMachineConfigFile->fileExists())
2655 // this is a new machine, and no config file exists yet:
2656 *aSettingsModified = TRUE;
2657 else
2658 *aSettingsModified = (mData->flModifications != 0);
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2664{
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 *aSessionState = mData->mSession.mState;
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2674{
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 aSessionType = mData->mSession.mType;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 *aSessionPID = mData->mSession.mPID;
2687
2688 return S_OK;
2689}
2690
2691HRESULT Machine::getState(MachineState_T *aState)
2692{
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 *aState = mData->mMachineState;
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2710{
2711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2712
2713 aStateFilePath = mSSData->strStateFilePath;
2714
2715 return S_OK;
2716}
2717
2718HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2719{
2720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 i_getLogFolder(aLogFolder);
2723
2724 return S_OK;
2725}
2726
2727HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2728{
2729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2730
2731 aCurrentSnapshot = mData->mCurrentSnapshot;
2732
2733 return S_OK;
2734}
2735
2736HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2737{
2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2741 ? 0
2742 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 /* Note: for machines with no snapshots, we always return FALSE
2752 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2753 * reasons :) */
2754
2755 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2756 ? FALSE
2757 : mData->mCurrentStateModified;
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 aSharedFolders.resize(mHWData->mSharedFolders.size());
2767 size_t i = 0;
2768 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2769 it != mHWData->mSharedFolders.end(); ++i, ++it)
2770 aSharedFolders[i] = *it;
2771
2772 return S_OK;
2773}
2774
2775HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 *aClipboardMode = mHWData->mClipboardMode;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2785{
2786 HRESULT rc = S_OK;
2787
2788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 alock.release();
2791 rc = i_onClipboardModeChange(aClipboardMode);
2792 alock.acquire();
2793 if (FAILED(rc)) return rc;
2794
2795 i_setModified(IsModified_MachineData);
2796 mHWData.backup();
2797 mHWData->mClipboardMode = aClipboardMode;
2798
2799 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2800 if (Global::IsOnline(mData->mMachineState))
2801 i_saveSettings(NULL);
2802
2803 return S_OK;
2804}
2805
2806HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2807{
2808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2809
2810 *aDnDMode = mHWData->mDnDMode;
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2816{
2817 HRESULT rc = S_OK;
2818
2819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 alock.release();
2822 rc = i_onDnDModeChange(aDnDMode);
2823
2824 alock.acquire();
2825 if (FAILED(rc)) return rc;
2826
2827 i_setModified(IsModified_MachineData);
2828 mHWData.backup();
2829 mHWData->mDnDMode = aDnDMode;
2830
2831 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2832 if (Global::IsOnline(mData->mMachineState))
2833 i_saveSettings(NULL);
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 try
2843 {
2844 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2845 }
2846 catch (...)
2847 {
2848 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2849 }
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2855{
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2859 if (FAILED(rc)) return rc;
2860
2861 i_setModified(IsModified_MachineData);
2862 mHWData.backup();
2863 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2864 return rc;
2865}
2866
2867HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870 StorageControllerList data = *mStorageControllers.data();
2871 size_t i = 0;
2872 aStorageControllers.resize(data.size());
2873 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2874 aStorageControllers[i] = *it;
2875 return S_OK;
2876}
2877
2878HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2879{
2880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2881
2882 *aEnabled = mUserData->s.fTeleporterEnabled;
2883
2884 return S_OK;
2885}
2886
2887HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2888{
2889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 /* Only allow it to be set to true when PoweredOff or Aborted.
2892 (Clearing it is always permitted.) */
2893 if ( aTeleporterEnabled
2894 && mData->mRegistered
2895 && ( !i_isSessionMachine()
2896 || ( mData->mMachineState != MachineState_PoweredOff
2897 && mData->mMachineState != MachineState_Teleported
2898 && mData->mMachineState != MachineState_Aborted
2899 )
2900 )
2901 )
2902 return setError(VBOX_E_INVALID_VM_STATE,
2903 tr("The machine is not powered off (state is %s)"),
2904 Global::stringifyMachineState(mData->mMachineState));
2905
2906 i_setModified(IsModified_MachineData);
2907 mUserData.backup();
2908 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2914{
2915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2923{
2924 if (aTeleporterPort >= _64K)
2925 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2926
2927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 HRESULT rc = i_checkStateDependency(MutableStateDep);
2930 if (FAILED(rc)) return rc;
2931
2932 i_setModified(IsModified_MachineData);
2933 mUserData.backup();
2934 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2935
2936 return S_OK;
2937}
2938
2939HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2940{
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 HRESULT rc = i_checkStateDependency(MutableStateDep);
2953 if (FAILED(rc)) return rc;
2954
2955 i_setModified(IsModified_MachineData);
2956 mUserData.backup();
2957 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2958
2959 return S_OK;
2960}
2961
2962HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2963{
2964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2965 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2966
2967 return S_OK;
2968}
2969
2970HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2971{
2972 /*
2973 * Hash the password first.
2974 */
2975 com::Utf8Str aT = aTeleporterPassword;
2976
2977 if (!aT.isEmpty())
2978 {
2979 if (VBoxIsPasswordHashed(&aT))
2980 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2981 VBoxHashPassword(&aT);
2982 }
2983
2984 /*
2985 * Do the update.
2986 */
2987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2988 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2989 if (SUCCEEDED(hrc))
2990 {
2991 i_setModified(IsModified_MachineData);
2992 mUserData.backup();
2993 mUserData->s.strTeleporterPassword = aT;
2994 }
2995
2996 return hrc;
2997}
2998
2999HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3000{
3001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3004 return S_OK;
3005}
3006
3007HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3008{
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 /* @todo deal with running state change. */
3012 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3013 if (FAILED(rc)) return rc;
3014
3015 i_setModified(IsModified_MachineData);
3016 mUserData.backup();
3017 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3018 return S_OK;
3019}
3020
3021HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3022{
3023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3026 return S_OK;
3027}
3028
3029HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3030{
3031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 /* @todo deal with running state change. */
3034 HRESULT rc = i_checkStateDependency(MutableStateDep);
3035 if (FAILED(rc)) return rc;
3036
3037 i_setModified(IsModified_MachineData);
3038 mUserData.backup();
3039 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3040 return S_OK;
3041}
3042
3043HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3044{
3045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3048 return S_OK;
3049}
3050
3051HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3052{
3053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3054
3055 /* @todo deal with running state change. */
3056 HRESULT rc = i_checkStateDependency(MutableStateDep);
3057 if (FAILED(rc)) return rc;
3058
3059 i_setModified(IsModified_MachineData);
3060 mUserData.backup();
3061 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3062 return S_OK;
3063}
3064
3065HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3066{
3067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3068
3069 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3070
3071 return S_OK;
3072}
3073
3074HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3075{
3076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3077
3078 /* @todo deal with running state change. */
3079 HRESULT rc = i_checkStateDependency(MutableStateDep);
3080 if (FAILED(rc)) return rc;
3081
3082 i_setModified(IsModified_MachineData);
3083 mUserData.backup();
3084 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3085
3086 return S_OK;
3087}
3088
3089HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3090{
3091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3094 return S_OK;
3095}
3096
3097HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3098{
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 /* @todo deal with running state change. */
3102 HRESULT rc = i_checkStateDependency(MutableStateDep);
3103 if (FAILED(rc)) return rc;
3104
3105 i_setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3108 return S_OK;
3109}
3110
3111HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3112{
3113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3114
3115 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3116
3117 return S_OK;
3118}
3119
3120HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3121{
3122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 /* Only allow it to be set to true when PoweredOff or Aborted.
3125 (Clearing it is always permitted.) */
3126 if ( aRTCUseUTC
3127 && mData->mRegistered
3128 && ( !i_isSessionMachine()
3129 || ( mData->mMachineState != MachineState_PoweredOff
3130 && mData->mMachineState != MachineState_Teleported
3131 && mData->mMachineState != MachineState_Aborted
3132 )
3133 )
3134 )
3135 return setError(VBOX_E_INVALID_VM_STATE,
3136 tr("The machine is not powered off (state is %s)"),
3137 Global::stringifyMachineState(mData->mMachineState));
3138
3139 i_setModified(IsModified_MachineData);
3140 mUserData.backup();
3141 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3142
3143 return S_OK;
3144}
3145
3146HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3147{
3148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3151
3152 return S_OK;
3153}
3154
3155HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3156{
3157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3158
3159 HRESULT rc = i_checkStateDependency(MutableStateDep);
3160 if (FAILED(rc)) return rc;
3161
3162 i_setModified(IsModified_MachineData);
3163 mHWData.backup();
3164 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3165
3166 return S_OK;
3167}
3168
3169HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3170{
3171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3172
3173 *aIOCacheSize = mHWData->mIOCacheSize;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3179{
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 HRESULT rc = i_checkStateDependency(MutableStateDep);
3183 if (FAILED(rc)) return rc;
3184
3185 i_setModified(IsModified_MachineData);
3186 mHWData.backup();
3187 mHWData->mIOCacheSize = aIOCacheSize;
3188
3189 return S_OK;
3190}
3191
3192
3193/**
3194 * @note Locks objects!
3195 */
3196HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3197 LockType_T aLockType)
3198
3199{
3200 /* check the session state */
3201 SessionState_T state;
3202 HRESULT rc = aSession->COMGETTER(State)(&state);
3203 if (FAILED(rc)) return rc;
3204
3205 if (state != SessionState_Unlocked)
3206 return setError(VBOX_E_INVALID_OBJECT_STATE,
3207 tr("The given session is busy"));
3208
3209 // get the client's IInternalSessionControl interface
3210 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3211 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3212 E_INVALIDARG);
3213
3214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3215
3216 if (!mData->mRegistered)
3217 return setError(E_UNEXPECTED,
3218 tr("The machine '%s' is not registered"),
3219 mUserData->s.strName.c_str());
3220
3221 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3222
3223 SessionState_T oldState = mData->mSession.mState;
3224 /* Hack: in case the session is closing and there is a progress object
3225 * which allows waiting for the session to be closed, take the opportunity
3226 * and do a limited wait (max. 1 second). This helps a lot when the system
3227 * is busy and thus session closing can take a little while. */
3228 if ( mData->mSession.mState == SessionState_Unlocking
3229 && mData->mSession.mProgress)
3230 {
3231 alock.release();
3232 mData->mSession.mProgress->WaitForCompletion(1000);
3233 alock.acquire();
3234 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3235 }
3236
3237 // try again now
3238 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3239 // (i.e. session machine exists)
3240 && (aLockType == LockType_Shared) // caller wants a shared link to the
3241 // existing session that holds the write lock:
3242 )
3243 {
3244 // OK, share the session... we are now dealing with three processes:
3245 // 1) VBoxSVC (where this code runs);
3246 // 2) process C: the caller's client process (who wants a shared session);
3247 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3248
3249 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3250 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3251 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3252 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3253 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3254
3255 /*
3256 * Release the lock before calling the client process. It's safe here
3257 * since the only thing to do after we get the lock again is to add
3258 * the remote control to the list (which doesn't directly influence
3259 * anything).
3260 */
3261 alock.release();
3262
3263 // get the console of the session holding the write lock (this is a remote call)
3264 ComPtr<IConsole> pConsoleW;
3265 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3266 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3267 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3268 if (FAILED(rc))
3269 // the failure may occur w/o any error info (from RPC), so provide one
3270 return setError(VBOX_E_VM_ERROR,
3271 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3272
3273 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3274
3275 // share the session machine and W's console with the caller's session
3276 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3277 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3278 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3279
3280 if (FAILED(rc))
3281 // the failure may occur w/o any error info (from RPC), so provide one
3282 return setError(VBOX_E_VM_ERROR,
3283 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3284 alock.acquire();
3285
3286 // need to revalidate the state after acquiring the lock again
3287 if (mData->mSession.mState != SessionState_Locked)
3288 {
3289 pSessionControl->Uninitialize();
3290 return setError(VBOX_E_INVALID_SESSION_STATE,
3291 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3292 mUserData->s.strName.c_str());
3293 }
3294
3295 // add the caller's session to the list
3296 mData->mSession.mRemoteControls.push_back(pSessionControl);
3297 }
3298 else if ( mData->mSession.mState == SessionState_Locked
3299 || mData->mSession.mState == SessionState_Unlocking
3300 )
3301 {
3302 // sharing not permitted, or machine still unlocking:
3303 return setError(VBOX_E_INVALID_OBJECT_STATE,
3304 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3305 mUserData->s.strName.c_str());
3306 }
3307 else
3308 {
3309 // machine is not locked: then write-lock the machine (create the session machine)
3310
3311 // must not be busy
3312 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3313
3314 // get the caller's session PID
3315 RTPROCESS pid = NIL_RTPROCESS;
3316 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3317 pSessionControl->GetPID((ULONG*)&pid);
3318 Assert(pid != NIL_RTPROCESS);
3319
3320 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3321
3322 if (fLaunchingVMProcess)
3323 {
3324 if (mData->mSession.mPID == NIL_RTPROCESS)
3325 {
3326 // two or more clients racing for a lock, the one which set the
3327 // session state to Spawning will win, the others will get an
3328 // error as we can't decide here if waiting a little would help
3329 // (only for shared locks this would avoid an error)
3330 return setError(VBOX_E_INVALID_OBJECT_STATE,
3331 tr("The machine '%s' already has a lock request pending"),
3332 mUserData->s.strName.c_str());
3333 }
3334
3335 // this machine is awaiting for a spawning session to be opened:
3336 // then the calling process must be the one that got started by
3337 // LaunchVMProcess()
3338
3339 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3340 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3341
3342#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3343 /* Hardened windows builds spawns three processes when a VM is
3344 launched, the 3rd one is the one that will end up here. */
3345 RTPROCESS ppid;
3346 int rc = RTProcQueryParent(pid, &ppid);
3347 if (RT_SUCCESS(rc))
3348 rc = RTProcQueryParent(ppid, &ppid);
3349 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3350 || rc == VERR_ACCESS_DENIED)
3351 {
3352 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3353 mData->mSession.mPID = pid;
3354 }
3355#endif
3356
3357 if (mData->mSession.mPID != pid)
3358 return setError(E_ACCESSDENIED,
3359 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3360 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3361 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3362 }
3363
3364 // create the mutable SessionMachine from the current machine
3365 ComObjPtr<SessionMachine> sessionMachine;
3366 sessionMachine.createObject();
3367 rc = sessionMachine->init(this);
3368 AssertComRC(rc);
3369
3370 /* NOTE: doing return from this function after this point but
3371 * before the end is forbidden since it may call SessionMachine::uninit()
3372 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3373 * lock while still holding the Machine lock in alock so that a deadlock
3374 * is possible due to the wrong lock order. */
3375
3376 if (SUCCEEDED(rc))
3377 {
3378 /*
3379 * Set the session state to Spawning to protect against subsequent
3380 * attempts to open a session and to unregister the machine after
3381 * we release the lock.
3382 */
3383 SessionState_T origState = mData->mSession.mState;
3384 mData->mSession.mState = SessionState_Spawning;
3385
3386#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3387 /* Get the client token ID to be passed to the client process */
3388 Utf8Str strTokenId;
3389 sessionMachine->i_getTokenId(strTokenId);
3390 Assert(!strTokenId.isEmpty());
3391#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3392 /* Get the client token to be passed to the client process */
3393 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3394 /* The token is now "owned" by pToken, fix refcount */
3395 if (!pToken.isNull())
3396 pToken->Release();
3397#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3398
3399 /*
3400 * Release the lock before calling the client process -- it will call
3401 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3402 * because the state is Spawning, so that LaunchVMProcess() and
3403 * LockMachine() calls will fail. This method, called before we
3404 * acquire the lock again, will fail because of the wrong PID.
3405 *
3406 * Note that mData->mSession.mRemoteControls accessed outside
3407 * the lock may not be modified when state is Spawning, so it's safe.
3408 */
3409 alock.release();
3410
3411 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3412#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3413 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3414#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3415 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3416 /* Now the token is owned by the client process. */
3417 pToken.setNull();
3418#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3419 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3420
3421 /* The failure may occur w/o any error info (from RPC), so provide one */
3422 if (FAILED(rc))
3423 setError(VBOX_E_VM_ERROR,
3424 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3425
3426 if ( SUCCEEDED(rc)
3427 && fLaunchingVMProcess
3428 )
3429 {
3430 /* complete the remote session initialization */
3431
3432 /* get the console from the direct session */
3433 ComPtr<IConsole> console;
3434 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3435 ComAssertComRC(rc);
3436
3437 if (SUCCEEDED(rc) && !console)
3438 {
3439 ComAssert(!!console);
3440 rc = E_FAIL;
3441 }
3442
3443 /* assign machine & console to the remote session */
3444 if (SUCCEEDED(rc))
3445 {
3446 /*
3447 * after LaunchVMProcess(), the first and the only
3448 * entry in remoteControls is that remote session
3449 */
3450 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3451 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3452 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3453
3454 /* The failure may occur w/o any error info (from RPC), so provide one */
3455 if (FAILED(rc))
3456 setError(VBOX_E_VM_ERROR,
3457 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3458 }
3459
3460 if (FAILED(rc))
3461 pSessionControl->Uninitialize();
3462 }
3463
3464 /* acquire the lock again */
3465 alock.acquire();
3466
3467 /* Restore the session state */
3468 mData->mSession.mState = origState;
3469 }
3470
3471 // finalize spawning anyway (this is why we don't return on errors above)
3472 if (fLaunchingVMProcess)
3473 {
3474 /* Note that the progress object is finalized later */
3475 /** @todo Consider checking mData->mSession.mProgress for cancellation
3476 * around here. */
3477
3478 /* We don't reset mSession.mPID here because it is necessary for
3479 * SessionMachine::uninit() to reap the child process later. */
3480
3481 if (FAILED(rc))
3482 {
3483 /* Close the remote session, remove the remote control from the list
3484 * and reset session state to Closed (@note keep the code in sync
3485 * with the relevant part in checkForSpawnFailure()). */
3486
3487 Assert(mData->mSession.mRemoteControls.size() == 1);
3488 if (mData->mSession.mRemoteControls.size() == 1)
3489 {
3490 ErrorInfoKeeper eik;
3491 mData->mSession.mRemoteControls.front()->Uninitialize();
3492 }
3493
3494 mData->mSession.mRemoteControls.clear();
3495 mData->mSession.mState = SessionState_Unlocked;
3496 }
3497 }
3498 else
3499 {
3500 /* memorize PID of the directly opened session */
3501 if (SUCCEEDED(rc))
3502 mData->mSession.mPID = pid;
3503 }
3504
3505 if (SUCCEEDED(rc))
3506 {
3507 /* memorize the direct session control and cache IUnknown for it */
3508 mData->mSession.mDirectControl = pSessionControl;
3509 mData->mSession.mState = SessionState_Locked;
3510 /* associate the SessionMachine with this Machine */
3511 mData->mSession.mMachine = sessionMachine;
3512
3513 /* request an IUnknown pointer early from the remote party for later
3514 * identity checks (it will be internally cached within mDirectControl
3515 * at least on XPCOM) */
3516 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3517 NOREF(unk);
3518 }
3519
3520 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3521 * would break the lock order */
3522 alock.release();
3523
3524 /* uninitialize the created session machine on failure */
3525 if (FAILED(rc))
3526 sessionMachine->uninit();
3527
3528 }
3529
3530 if (SUCCEEDED(rc))
3531 {
3532 /*
3533 * tell the client watcher thread to update the set of
3534 * machines that have open sessions
3535 */
3536 mParent->i_updateClientWatcher();
3537
3538 if (oldState != SessionState_Locked)
3539 /* fire an event */
3540 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3541 }
3542
3543 return rc;
3544}
3545
3546/**
3547 * @note Locks objects!
3548 */
3549HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3550 const com::Utf8Str &aType,
3551 const com::Utf8Str &aEnvironment,
3552 ComPtr<IProgress> &aProgress)
3553{
3554 Utf8Str strFrontend(aType);
3555 /* "emergencystop" doesn't need the session, so skip the checks/interface
3556 * retrieval. This code doesn't quite fit in here, but introducing a
3557 * special API method would be even more effort, and would require explicit
3558 * support by every API client. It's better to hide the feature a bit. */
3559 if (strFrontend != "emergencystop")
3560 CheckComArgNotNull(aSession);
3561
3562 HRESULT rc = S_OK;
3563 if (strFrontend.isEmpty())
3564 {
3565 Bstr bstrFrontend;
3566 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3567 if (FAILED(rc))
3568 return rc;
3569 strFrontend = bstrFrontend;
3570 if (strFrontend.isEmpty())
3571 {
3572 ComPtr<ISystemProperties> systemProperties;
3573 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3574 if (FAILED(rc))
3575 return rc;
3576 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3577 if (FAILED(rc))
3578 return rc;
3579 strFrontend = bstrFrontend;
3580 }
3581 /* paranoia - emergencystop is not a valid default */
3582 if (strFrontend == "emergencystop")
3583 strFrontend = Utf8Str::Empty;
3584 }
3585 /* default frontend: Qt GUI */
3586 if (strFrontend.isEmpty())
3587 strFrontend = "GUI/Qt";
3588
3589 if (strFrontend != "emergencystop")
3590 {
3591 /* check the session state */
3592 SessionState_T state;
3593 rc = aSession->COMGETTER(State)(&state);
3594 if (FAILED(rc))
3595 return rc;
3596
3597 if (state != SessionState_Unlocked)
3598 return setError(VBOX_E_INVALID_OBJECT_STATE,
3599 tr("The given session is busy"));
3600
3601 /* get the IInternalSessionControl interface */
3602 ComPtr<IInternalSessionControl> control(aSession);
3603 ComAssertMsgRet(!control.isNull(),
3604 ("No IInternalSessionControl interface"),
3605 E_INVALIDARG);
3606
3607 /* get the teleporter enable state for the progress object init. */
3608 BOOL fTeleporterEnabled;
3609 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3610 if (FAILED(rc))
3611 return rc;
3612
3613 /* create a progress object */
3614 ComObjPtr<ProgressProxy> progress;
3615 progress.createObject();
3616 rc = progress->init(mParent,
3617 static_cast<IMachine*>(this),
3618 Bstr(tr("Starting VM")).raw(),
3619 TRUE /* aCancelable */,
3620 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3621 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3622 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3623 2 /* uFirstOperationWeight */,
3624 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3625
3626 if (SUCCEEDED(rc))
3627 {
3628 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3629 if (SUCCEEDED(rc))
3630 {
3631 aProgress = progress;
3632
3633 /* signal the client watcher thread */
3634 mParent->i_updateClientWatcher();
3635
3636 /* fire an event */
3637 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3638 }
3639 }
3640 }
3641 else
3642 {
3643 /* no progress object - either instant success or failure */
3644 aProgress = NULL;
3645
3646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3647
3648 if (mData->mSession.mState != SessionState_Locked)
3649 return setError(VBOX_E_INVALID_OBJECT_STATE,
3650 tr("The machine '%s' is not locked by a session"),
3651 mUserData->s.strName.c_str());
3652
3653 /* must have a VM process associated - do not kill normal API clients
3654 * with an open session */
3655 if (!Global::IsOnline(mData->mMachineState))
3656 return setError(VBOX_E_INVALID_OBJECT_STATE,
3657 tr("The machine '%s' does not have a VM process"),
3658 mUserData->s.strName.c_str());
3659
3660 /* forcibly terminate the VM process */
3661 if (mData->mSession.mPID != NIL_RTPROCESS)
3662 RTProcTerminate(mData->mSession.mPID);
3663
3664 /* signal the client watcher thread, as most likely the client has
3665 * been terminated */
3666 mParent->i_updateClientWatcher();
3667 }
3668
3669 return rc;
3670}
3671
3672HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3673{
3674 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3675 return setError(E_INVALIDARG,
3676 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3677 aPosition, SchemaDefs::MaxBootPosition);
3678
3679 if (aDevice == DeviceType_USB)
3680 return setError(E_NOTIMPL,
3681 tr("Booting from USB device is currently not supported"));
3682
3683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3684
3685 HRESULT rc = i_checkStateDependency(MutableStateDep);
3686 if (FAILED(rc)) return rc;
3687
3688 i_setModified(IsModified_MachineData);
3689 mHWData.backup();
3690 mHWData->mBootOrder[aPosition - 1] = aDevice;
3691
3692 return S_OK;
3693}
3694
3695HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3696{
3697 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3698 return setError(E_INVALIDARG,
3699 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3700 aPosition, SchemaDefs::MaxBootPosition);
3701
3702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3703
3704 *aDevice = mHWData->mBootOrder[aPosition - 1];
3705
3706 return S_OK;
3707}
3708
3709HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3710 LONG aControllerPort,
3711 LONG aDevice,
3712 DeviceType_T aType,
3713 const ComPtr<IMedium> &aMedium)
3714{
3715 IMedium *aM = aMedium;
3716 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3717 aName.c_str(), aControllerPort, aDevice, aType, aM));
3718
3719 // request the host lock first, since might be calling Host methods for getting host drives;
3720 // next, protect the media tree all the while we're in here, as well as our member variables
3721 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3722 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3723
3724 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3725 if (FAILED(rc)) return rc;
3726
3727 /// @todo NEWMEDIA implicit machine registration
3728 if (!mData->mRegistered)
3729 return setError(VBOX_E_INVALID_OBJECT_STATE,
3730 tr("Cannot attach storage devices to an unregistered machine"));
3731
3732 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3733
3734 /* Check for an existing controller. */
3735 ComObjPtr<StorageController> ctl;
3736 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3737 if (FAILED(rc)) return rc;
3738
3739 StorageControllerType_T ctrlType;
3740 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3741 if (FAILED(rc))
3742 return setError(E_FAIL,
3743 tr("Could not get type of controller '%s'"),
3744 aName.c_str());
3745
3746 bool fSilent = false;
3747 Utf8Str strReconfig;
3748
3749 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3750 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3751 if ( mData->mMachineState == MachineState_Paused
3752 && strReconfig == "1")
3753 fSilent = true;
3754
3755 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3756 bool fHotplug = false;
3757 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3758 fHotplug = true;
3759
3760 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3761 return setError(VBOX_E_INVALID_VM_STATE,
3762 tr("Controller '%s' does not support hotplugging"),
3763 aName.c_str());
3764
3765 // check that the port and device are not out of range
3766 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3767 if (FAILED(rc)) return rc;
3768
3769 /* check if the device slot is already busy */
3770 MediumAttachment *pAttachTemp;
3771 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3772 Bstr(aName).raw(),
3773 aControllerPort,
3774 aDevice)))
3775 {
3776 Medium *pMedium = pAttachTemp->i_getMedium();
3777 if (pMedium)
3778 {
3779 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3780 return setError(VBOX_E_OBJECT_IN_USE,
3781 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3782 pMedium->i_getLocationFull().c_str(),
3783 aControllerPort,
3784 aDevice,
3785 aName.c_str());
3786 }
3787 else
3788 return setError(VBOX_E_OBJECT_IN_USE,
3789 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3790 aControllerPort, aDevice, aName.c_str());
3791 }
3792
3793 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3794 if (aMedium && medium.isNull())
3795 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3796
3797 AutoCaller mediumCaller(medium);
3798 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3799
3800 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3801
3802 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3803 && !medium.isNull()
3804 )
3805 return setError(VBOX_E_OBJECT_IN_USE,
3806 tr("Medium '%s' is already attached to this virtual machine"),
3807 medium->i_getLocationFull().c_str());
3808
3809 if (!medium.isNull())
3810 {
3811 MediumType_T mtype = medium->i_getType();
3812 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3813 // For DVDs it's not written to the config file, so needs no global config
3814 // version bump. For floppies it's a new attribute "type", which is ignored
3815 // by older VirtualBox version, so needs no global config version bump either.
3816 // For hard disks this type is not accepted.
3817 if (mtype == MediumType_MultiAttach)
3818 {
3819 // This type is new with VirtualBox 4.0 and therefore requires settings
3820 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3821 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3822 // two reasons: The medium type is a property of the media registry tree, which
3823 // can reside in the global config file (for pre-4.0 media); we would therefore
3824 // possibly need to bump the global config version. We don't want to do that though
3825 // because that might make downgrading to pre-4.0 impossible.
3826 // As a result, we can only use these two new types if the medium is NOT in the
3827 // global registry:
3828 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3829 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3830 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3831 )
3832 return setError(VBOX_E_INVALID_OBJECT_STATE,
3833 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3834 "to machines that were created with VirtualBox 4.0 or later"),
3835 medium->i_getLocationFull().c_str());
3836 }
3837 }
3838
3839 bool fIndirect = false;
3840 if (!medium.isNull())
3841 fIndirect = medium->i_isReadOnly();
3842 bool associate = true;
3843
3844 do
3845 {
3846 if ( aType == DeviceType_HardDisk
3847 && mMediaData.isBackedUp())
3848 {
3849 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3850
3851 /* check if the medium was attached to the VM before we started
3852 * changing attachments in which case the attachment just needs to
3853 * be restored */
3854 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3855 {
3856 AssertReturn(!fIndirect, E_FAIL);
3857
3858 /* see if it's the same bus/channel/device */
3859 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3860 {
3861 /* the simplest case: restore the whole attachment
3862 * and return, nothing else to do */
3863 mMediaData->mAttachments.push_back(pAttachTemp);
3864
3865 /* Reattach the medium to the VM. */
3866 if (fHotplug || fSilent)
3867 {
3868 mediumLock.release();
3869 treeLock.release();
3870 alock.release();
3871
3872 MediumLockList *pMediumLockList(new MediumLockList());
3873
3874 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3875 true /* fMediumLockWrite */,
3876 false /* fMediumLockWriteAll */,
3877 NULL,
3878 *pMediumLockList);
3879 alock.acquire();
3880 if (FAILED(rc))
3881 delete pMediumLockList;
3882 else
3883 {
3884 mData->mSession.mLockedMedia.Unlock();
3885 alock.release();
3886 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3887 mData->mSession.mLockedMedia.Lock();
3888 alock.acquire();
3889 }
3890 alock.release();
3891
3892 if (SUCCEEDED(rc))
3893 {
3894 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3895 /* Remove lock list in case of error. */
3896 if (FAILED(rc))
3897 {
3898 mData->mSession.mLockedMedia.Unlock();
3899 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3900 mData->mSession.mLockedMedia.Lock();
3901 }
3902 }
3903 }
3904
3905 return S_OK;
3906 }
3907
3908 /* bus/channel/device differ; we need a new attachment object,
3909 * but don't try to associate it again */
3910 associate = false;
3911 break;
3912 }
3913 }
3914
3915 /* go further only if the attachment is to be indirect */
3916 if (!fIndirect)
3917 break;
3918
3919 /* perform the so called smart attachment logic for indirect
3920 * attachments. Note that smart attachment is only applicable to base
3921 * hard disks. */
3922
3923 if (medium->i_getParent().isNull())
3924 {
3925 /* first, investigate the backup copy of the current hard disk
3926 * attachments to make it possible to re-attach existing diffs to
3927 * another device slot w/o losing their contents */
3928 if (mMediaData.isBackedUp())
3929 {
3930 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3931
3932 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3933 uint32_t foundLevel = 0;
3934
3935 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3936 {
3937 uint32_t level = 0;
3938 MediumAttachment *pAttach = *it;
3939 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3940 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3941 if (pMedium.isNull())
3942 continue;
3943
3944 if (pMedium->i_getBase(&level) == medium)
3945 {
3946 /* skip the hard disk if its currently attached (we
3947 * cannot attach the same hard disk twice) */
3948 if (i_findAttachment(mMediaData->mAttachments,
3949 pMedium))
3950 continue;
3951
3952 /* matched device, channel and bus (i.e. attached to the
3953 * same place) will win and immediately stop the search;
3954 * otherwise the attachment that has the youngest
3955 * descendant of medium will be used
3956 */
3957 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3958 {
3959 /* the simplest case: restore the whole attachment
3960 * and return, nothing else to do */
3961 mMediaData->mAttachments.push_back(*it);
3962
3963 /* Reattach the medium to the VM. */
3964 if (fHotplug || fSilent)
3965 {
3966 mediumLock.release();
3967 treeLock.release();
3968 alock.release();
3969
3970 MediumLockList *pMediumLockList(new MediumLockList());
3971
3972 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3973 true /* fMediumLockWrite */,
3974 false /* fMediumLockWriteAll */,
3975 NULL,
3976 *pMediumLockList);
3977 alock.acquire();
3978 if (FAILED(rc))
3979 delete pMediumLockList;
3980 else
3981 {
3982 mData->mSession.mLockedMedia.Unlock();
3983 alock.release();
3984 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3985 mData->mSession.mLockedMedia.Lock();
3986 alock.acquire();
3987 }
3988 alock.release();
3989
3990 if (SUCCEEDED(rc))
3991 {
3992 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3993 /* Remove lock list in case of error. */
3994 if (FAILED(rc))
3995 {
3996 mData->mSession.mLockedMedia.Unlock();
3997 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3998 mData->mSession.mLockedMedia.Lock();
3999 }
4000 }
4001 }
4002
4003 return S_OK;
4004 }
4005 else if ( foundIt == oldAtts.end()
4006 || level > foundLevel /* prefer younger */
4007 )
4008 {
4009 foundIt = it;
4010 foundLevel = level;
4011 }
4012 }
4013 }
4014
4015 if (foundIt != oldAtts.end())
4016 {
4017 /* use the previously attached hard disk */
4018 medium = (*foundIt)->i_getMedium();
4019 mediumCaller.attach(medium);
4020 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4021 mediumLock.attach(medium);
4022 /* not implicit, doesn't require association with this VM */
4023 fIndirect = false;
4024 associate = false;
4025 /* go right to the MediumAttachment creation */
4026 break;
4027 }
4028 }
4029
4030 /* must give up the medium lock and medium tree lock as below we
4031 * go over snapshots, which needs a lock with higher lock order. */
4032 mediumLock.release();
4033 treeLock.release();
4034
4035 /* then, search through snapshots for the best diff in the given
4036 * hard disk's chain to base the new diff on */
4037
4038 ComObjPtr<Medium> base;
4039 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4040 while (snap)
4041 {
4042 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4043
4044 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4045
4046 MediumAttachment *pAttachFound = NULL;
4047 uint32_t foundLevel = 0;
4048
4049 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4050 {
4051 MediumAttachment *pAttach = *it;
4052 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4053 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4054 if (pMedium.isNull())
4055 continue;
4056
4057 uint32_t level = 0;
4058 if (pMedium->i_getBase(&level) == medium)
4059 {
4060 /* matched device, channel and bus (i.e. attached to the
4061 * same place) will win and immediately stop the search;
4062 * otherwise the attachment that has the youngest
4063 * descendant of medium will be used
4064 */
4065 if ( pAttach->i_getDevice() == aDevice
4066 && pAttach->i_getPort() == aControllerPort
4067 && pAttach->i_getControllerName() == aName
4068 )
4069 {
4070 pAttachFound = pAttach;
4071 break;
4072 }
4073 else if ( !pAttachFound
4074 || level > foundLevel /* prefer younger */
4075 )
4076 {
4077 pAttachFound = pAttach;
4078 foundLevel = level;
4079 }
4080 }
4081 }
4082
4083 if (pAttachFound)
4084 {
4085 base = pAttachFound->i_getMedium();
4086 break;
4087 }
4088
4089 snap = snap->i_getParent();
4090 }
4091
4092 /* re-lock medium tree and the medium, as we need it below */
4093 treeLock.acquire();
4094 mediumLock.acquire();
4095
4096 /* found a suitable diff, use it as a base */
4097 if (!base.isNull())
4098 {
4099 medium = base;
4100 mediumCaller.attach(medium);
4101 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4102 mediumLock.attach(medium);
4103 }
4104 }
4105
4106 Utf8Str strFullSnapshotFolder;
4107 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4108
4109 ComObjPtr<Medium> diff;
4110 diff.createObject();
4111 // store this diff in the same registry as the parent
4112 Guid uuidRegistryParent;
4113 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4114 {
4115 // parent image has no registry: this can happen if we're attaching a new immutable
4116 // image that has not yet been attached (medium then points to the base and we're
4117 // creating the diff image for the immutable, and the parent is not yet registered);
4118 // put the parent in the machine registry then
4119 mediumLock.release();
4120 treeLock.release();
4121 alock.release();
4122 i_addMediumToRegistry(medium);
4123 alock.acquire();
4124 treeLock.acquire();
4125 mediumLock.acquire();
4126 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4127 }
4128 rc = diff->init(mParent,
4129 medium->i_getPreferredDiffFormat(),
4130 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4131 uuidRegistryParent,
4132 DeviceType_HardDisk);
4133 if (FAILED(rc)) return rc;
4134
4135 /* Apply the normal locking logic to the entire chain. */
4136 MediumLockList *pMediumLockList(new MediumLockList());
4137 mediumLock.release();
4138 treeLock.release();
4139 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4140 true /* fMediumLockWrite */,
4141 false /* fMediumLockWriteAll */,
4142 medium,
4143 *pMediumLockList);
4144 treeLock.acquire();
4145 mediumLock.acquire();
4146 if (SUCCEEDED(rc))
4147 {
4148 mediumLock.release();
4149 treeLock.release();
4150 rc = pMediumLockList->Lock();
4151 treeLock.acquire();
4152 mediumLock.acquire();
4153 if (FAILED(rc))
4154 setError(rc,
4155 tr("Could not lock medium when creating diff '%s'"),
4156 diff->i_getLocationFull().c_str());
4157 else
4158 {
4159 /* will release the lock before the potentially lengthy
4160 * operation, so protect with the special state */
4161 MachineState_T oldState = mData->mMachineState;
4162 i_setMachineState(MachineState_SettingUp);
4163
4164 mediumLock.release();
4165 treeLock.release();
4166 alock.release();
4167
4168 rc = medium->i_createDiffStorage(diff,
4169 MediumVariant_Standard,
4170 pMediumLockList,
4171 NULL /* aProgress */,
4172 true /* aWait */);
4173
4174 alock.acquire();
4175 treeLock.acquire();
4176 mediumLock.acquire();
4177
4178 i_setMachineState(oldState);
4179 }
4180 }
4181
4182 /* Unlock the media and free the associated memory. */
4183 delete pMediumLockList;
4184
4185 if (FAILED(rc)) return rc;
4186
4187 /* use the created diff for the actual attachment */
4188 medium = diff;
4189 mediumCaller.attach(medium);
4190 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4191 mediumLock.attach(medium);
4192 }
4193 while (0);
4194
4195 ComObjPtr<MediumAttachment> attachment;
4196 attachment.createObject();
4197 rc = attachment->init(this,
4198 medium,
4199 aName,
4200 aControllerPort,
4201 aDevice,
4202 aType,
4203 fIndirect,
4204 false /* fPassthrough */,
4205 false /* fTempEject */,
4206 false /* fNonRotational */,
4207 false /* fDiscard */,
4208 fHotplug /* fHotPluggable */,
4209 Utf8Str::Empty);
4210 if (FAILED(rc)) return rc;
4211
4212 if (associate && !medium.isNull())
4213 {
4214 // as the last step, associate the medium to the VM
4215 rc = medium->i_addBackReference(mData->mUuid);
4216 // here we can fail because of Deleting, or being in process of creating a Diff
4217 if (FAILED(rc)) return rc;
4218
4219 mediumLock.release();
4220 treeLock.release();
4221 alock.release();
4222 i_addMediumToRegistry(medium);
4223 alock.acquire();
4224 treeLock.acquire();
4225 mediumLock.acquire();
4226 }
4227
4228 /* success: finally remember the attachment */
4229 i_setModified(IsModified_Storage);
4230 mMediaData.backup();
4231 mMediaData->mAttachments.push_back(attachment);
4232
4233 mediumLock.release();
4234 treeLock.release();
4235 alock.release();
4236
4237 if (fHotplug || fSilent)
4238 {
4239 if (!medium.isNull())
4240 {
4241 MediumLockList *pMediumLockList(new MediumLockList());
4242
4243 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4244 true /* fMediumLockWrite */,
4245 false /* fMediumLockWriteAll */,
4246 NULL,
4247 *pMediumLockList);
4248 alock.acquire();
4249 if (FAILED(rc))
4250 delete pMediumLockList;
4251 else
4252 {
4253 mData->mSession.mLockedMedia.Unlock();
4254 alock.release();
4255 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4256 mData->mSession.mLockedMedia.Lock();
4257 alock.acquire();
4258 }
4259 alock.release();
4260 }
4261
4262 if (SUCCEEDED(rc))
4263 {
4264 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4265 /* Remove lock list in case of error. */
4266 if (FAILED(rc))
4267 {
4268 mData->mSession.mLockedMedia.Unlock();
4269 mData->mSession.mLockedMedia.Remove(attachment);
4270 mData->mSession.mLockedMedia.Lock();
4271 }
4272 }
4273 }
4274
4275 /* Save modified registries, but skip this machine as it's the caller's
4276 * job to save its settings like all other settings changes. */
4277 mParent->i_unmarkRegistryModified(i_getId());
4278 mParent->i_saveModifiedRegistries();
4279
4280 return rc;
4281}
4282
4283HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4284 LONG aDevice)
4285{
4286 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4287 aName.c_str(), aControllerPort, aDevice));
4288
4289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4290
4291 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4292 if (FAILED(rc)) return rc;
4293
4294 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4295
4296 /* Check for an existing controller. */
4297 ComObjPtr<StorageController> ctl;
4298 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4299 if (FAILED(rc)) return rc;
4300
4301 StorageControllerType_T ctrlType;
4302 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4303 if (FAILED(rc))
4304 return setError(E_FAIL,
4305 tr("Could not get type of controller '%s'"),
4306 aName.c_str());
4307
4308 bool fSilent = false;
4309 Utf8Str strReconfig;
4310
4311 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4312 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4313 if ( mData->mMachineState == MachineState_Paused
4314 && strReconfig == "1")
4315 fSilent = true;
4316
4317 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4318 bool fHotplug = false;
4319 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4320 fHotplug = true;
4321
4322 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4323 return setError(VBOX_E_INVALID_VM_STATE,
4324 tr("Controller '%s' does not support hotplugging"),
4325 aName.c_str());
4326
4327 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4328 Bstr(aName).raw(),
4329 aControllerPort,
4330 aDevice);
4331 if (!pAttach)
4332 return setError(VBOX_E_OBJECT_NOT_FOUND,
4333 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4334 aDevice, aControllerPort, aName.c_str());
4335
4336 if (fHotplug && !pAttach->i_getHotPluggable())
4337 return setError(VBOX_E_NOT_SUPPORTED,
4338 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4339 aDevice, aControllerPort, aName.c_str());
4340
4341 /*
4342 * The VM has to detach the device before we delete any implicit diffs.
4343 * If this fails we can roll back without loosing data.
4344 */
4345 if (fHotplug || fSilent)
4346 {
4347 alock.release();
4348 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4349 alock.acquire();
4350 }
4351 if (FAILED(rc)) return rc;
4352
4353 /* If we are here everything went well and we can delete the implicit now. */
4354 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4355
4356 alock.release();
4357
4358 /* Save modified registries, but skip this machine as it's the caller's
4359 * job to save its settings like all other settings changes. */
4360 mParent->i_unmarkRegistryModified(i_getId());
4361 mParent->i_saveModifiedRegistries();
4362
4363 return rc;
4364}
4365
4366HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4367 LONG aDevice, BOOL aPassthrough)
4368{
4369 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4370 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4371
4372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4373
4374 HRESULT rc = i_checkStateDependency(MutableStateDep);
4375 if (FAILED(rc)) return rc;
4376
4377 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4378
4379 if (Global::IsOnlineOrTransient(mData->mMachineState))
4380 return setError(VBOX_E_INVALID_VM_STATE,
4381 tr("Invalid machine state: %s"),
4382 Global::stringifyMachineState(mData->mMachineState));
4383
4384 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4385 Bstr(aName).raw(),
4386 aControllerPort,
4387 aDevice);
4388 if (!pAttach)
4389 return setError(VBOX_E_OBJECT_NOT_FOUND,
4390 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4391 aDevice, aControllerPort, aName.c_str());
4392
4393
4394 i_setModified(IsModified_Storage);
4395 mMediaData.backup();
4396
4397 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4398
4399 if (pAttach->i_getType() != DeviceType_DVD)
4400 return setError(E_INVALIDARG,
4401 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4402 aDevice, aControllerPort, aName.c_str());
4403 pAttach->i_updatePassthrough(!!aPassthrough);
4404
4405 return S_OK;
4406}
4407
4408HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4409 LONG aDevice, BOOL aTemporaryEject)
4410{
4411
4412 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4413 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4414
4415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4416
4417 HRESULT rc = i_checkStateDependency(MutableStateDep);
4418 if (FAILED(rc)) return rc;
4419
4420 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4421 Bstr(aName).raw(),
4422 aControllerPort,
4423 aDevice);
4424 if (!pAttach)
4425 return setError(VBOX_E_OBJECT_NOT_FOUND,
4426 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4427 aDevice, aControllerPort, aName.c_str());
4428
4429
4430 i_setModified(IsModified_Storage);
4431 mMediaData.backup();
4432
4433 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4434
4435 if (pAttach->i_getType() != DeviceType_DVD)
4436 return setError(E_INVALIDARG,
4437 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4438 aDevice, aControllerPort, aName.c_str());
4439 pAttach->i_updateTempEject(!!aTemporaryEject);
4440
4441 return S_OK;
4442}
4443
4444HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4445 LONG aDevice, BOOL aNonRotational)
4446{
4447
4448 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4449 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4450
4451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4452
4453 HRESULT rc = i_checkStateDependency(MutableStateDep);
4454 if (FAILED(rc)) return rc;
4455
4456 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4457
4458 if (Global::IsOnlineOrTransient(mData->mMachineState))
4459 return setError(VBOX_E_INVALID_VM_STATE,
4460 tr("Invalid machine state: %s"),
4461 Global::stringifyMachineState(mData->mMachineState));
4462
4463 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4464 Bstr(aName).raw(),
4465 aControllerPort,
4466 aDevice);
4467 if (!pAttach)
4468 return setError(VBOX_E_OBJECT_NOT_FOUND,
4469 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4470 aDevice, aControllerPort, aName.c_str());
4471
4472
4473 i_setModified(IsModified_Storage);
4474 mMediaData.backup();
4475
4476 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4477
4478 if (pAttach->i_getType() != DeviceType_HardDisk)
4479 return setError(E_INVALIDARG,
4480 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4481 aDevice, aControllerPort, aName.c_str());
4482 pAttach->i_updateNonRotational(!!aNonRotational);
4483
4484 return S_OK;
4485}
4486
4487HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4488 LONG aDevice, BOOL aDiscard)
4489{
4490
4491 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4492 aName.c_str(), aControllerPort, aDevice, aDiscard));
4493
4494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4495
4496 HRESULT rc = i_checkStateDependency(MutableStateDep);
4497 if (FAILED(rc)) return rc;
4498
4499 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4500
4501 if (Global::IsOnlineOrTransient(mData->mMachineState))
4502 return setError(VBOX_E_INVALID_VM_STATE,
4503 tr("Invalid machine state: %s"),
4504 Global::stringifyMachineState(mData->mMachineState));
4505
4506 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4507 Bstr(aName).raw(),
4508 aControllerPort,
4509 aDevice);
4510 if (!pAttach)
4511 return setError(VBOX_E_OBJECT_NOT_FOUND,
4512 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4513 aDevice, aControllerPort, aName.c_str());
4514
4515
4516 i_setModified(IsModified_Storage);
4517 mMediaData.backup();
4518
4519 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4520
4521 if (pAttach->i_getType() != DeviceType_HardDisk)
4522 return setError(E_INVALIDARG,
4523 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4524 aDevice, aControllerPort, aName.c_str());
4525 pAttach->i_updateDiscard(!!aDiscard);
4526
4527 return S_OK;
4528}
4529
4530HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4531 LONG aDevice, BOOL aHotPluggable)
4532{
4533 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4534 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4535
4536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4537
4538 HRESULT rc = i_checkStateDependency(MutableStateDep);
4539 if (FAILED(rc)) return rc;
4540
4541 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4542
4543 if (Global::IsOnlineOrTransient(mData->mMachineState))
4544 return setError(VBOX_E_INVALID_VM_STATE,
4545 tr("Invalid machine state: %s"),
4546 Global::stringifyMachineState(mData->mMachineState));
4547
4548 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4549 Bstr(aName).raw(),
4550 aControllerPort,
4551 aDevice);
4552 if (!pAttach)
4553 return setError(VBOX_E_OBJECT_NOT_FOUND,
4554 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4555 aDevice, aControllerPort, aName.c_str());
4556
4557 /* Check for an existing controller. */
4558 ComObjPtr<StorageController> ctl;
4559 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4560 if (FAILED(rc)) return rc;
4561
4562 StorageControllerType_T ctrlType;
4563 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4564 if (FAILED(rc))
4565 return setError(E_FAIL,
4566 tr("Could not get type of controller '%s'"),
4567 aName.c_str());
4568
4569 if (!i_isControllerHotplugCapable(ctrlType))
4570 return setError(VBOX_E_NOT_SUPPORTED,
4571 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4572 aName.c_str());
4573
4574 i_setModified(IsModified_Storage);
4575 mMediaData.backup();
4576
4577 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4578
4579 if (pAttach->i_getType() == DeviceType_Floppy)
4580 return setError(E_INVALIDARG,
4581 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4582 aDevice, aControllerPort, aName.c_str());
4583 pAttach->i_updateHotPluggable(!!aHotPluggable);
4584
4585 return S_OK;
4586}
4587
4588HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4589 LONG aDevice)
4590{
4591 int rc = S_OK;
4592 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4593 aName.c_str(), aControllerPort, aDevice));
4594
4595 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4596
4597 return rc;
4598}
4599
4600HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4601 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4602{
4603 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4604 aName.c_str(), aControllerPort, aDevice));
4605
4606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4607
4608 HRESULT rc = i_checkStateDependency(MutableStateDep);
4609 if (FAILED(rc)) return rc;
4610
4611 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4612
4613 if (Global::IsOnlineOrTransient(mData->mMachineState))
4614 return setError(VBOX_E_INVALID_VM_STATE,
4615 tr("Invalid machine state: %s"),
4616 Global::stringifyMachineState(mData->mMachineState));
4617
4618 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4619 Bstr(aName).raw(),
4620 aControllerPort,
4621 aDevice);
4622 if (!pAttach)
4623 return setError(VBOX_E_OBJECT_NOT_FOUND,
4624 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4625 aDevice, aControllerPort, aName.c_str());
4626
4627
4628 i_setModified(IsModified_Storage);
4629 mMediaData.backup();
4630
4631 IBandwidthGroup *iB = aBandwidthGroup;
4632 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4633 if (aBandwidthGroup && group.isNull())
4634 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4635
4636 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4637
4638 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4639 if (strBandwidthGroupOld.isNotEmpty())
4640 {
4641 /* Get the bandwidth group object and release it - this must not fail. */
4642 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4643 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4644 Assert(SUCCEEDED(rc));
4645
4646 pBandwidthGroupOld->i_release();
4647 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4648 }
4649
4650 if (!group.isNull())
4651 {
4652 group->i_reference();
4653 pAttach->i_updateBandwidthGroup(group->i_getName());
4654 }
4655
4656 return S_OK;
4657}
4658
4659HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4660 LONG aControllerPort,
4661 LONG aDevice,
4662 DeviceType_T aType)
4663{
4664 HRESULT rc = S_OK;
4665
4666 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4667 aName.c_str(), aControllerPort, aDevice, aType));
4668
4669 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4670
4671 return rc;
4672}
4673
4674
4675HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4676 LONG aControllerPort,
4677 LONG aDevice,
4678 BOOL aForce)
4679{
4680 int rc = S_OK;
4681 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4682 aName.c_str(), aControllerPort, aForce));
4683
4684 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4685
4686 return rc;
4687}
4688
4689HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4690 LONG aControllerPort,
4691 LONG aDevice,
4692 const ComPtr<IMedium> &aMedium,
4693 BOOL aForce)
4694{
4695 int rc = S_OK;
4696 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4697 aName.c_str(), aControllerPort, aDevice, aForce));
4698
4699 // request the host lock first, since might be calling Host methods for getting host drives;
4700 // next, protect the media tree all the while we're in here, as well as our member variables
4701 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4702 this->lockHandle(),
4703 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4704
4705 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4706 Bstr(aName).raw(),
4707 aControllerPort,
4708 aDevice);
4709 if (pAttach.isNull())
4710 return setError(VBOX_E_OBJECT_NOT_FOUND,
4711 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4712 aDevice, aControllerPort, aName.c_str());
4713
4714 /* Remember previously mounted medium. The medium before taking the
4715 * backup is not necessarily the same thing. */
4716 ComObjPtr<Medium> oldmedium;
4717 oldmedium = pAttach->i_getMedium();
4718
4719 IMedium *iM = aMedium;
4720 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4721 if (aMedium && pMedium.isNull())
4722 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4723
4724 AutoCaller mediumCaller(pMedium);
4725 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4726
4727 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4728 if (pMedium)
4729 {
4730 DeviceType_T mediumType = pAttach->i_getType();
4731 switch (mediumType)
4732 {
4733 case DeviceType_DVD:
4734 case DeviceType_Floppy:
4735 break;
4736
4737 default:
4738 return setError(VBOX_E_INVALID_OBJECT_STATE,
4739 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4740 aControllerPort,
4741 aDevice,
4742 aName.c_str());
4743 }
4744 }
4745
4746 i_setModified(IsModified_Storage);
4747 mMediaData.backup();
4748
4749 {
4750 // The backup operation makes the pAttach reference point to the
4751 // old settings. Re-get the correct reference.
4752 pAttach = i_findAttachment(mMediaData->mAttachments,
4753 Bstr(aName).raw(),
4754 aControllerPort,
4755 aDevice);
4756 if (!oldmedium.isNull())
4757 oldmedium->i_removeBackReference(mData->mUuid);
4758 if (!pMedium.isNull())
4759 {
4760 pMedium->i_addBackReference(mData->mUuid);
4761
4762 mediumLock.release();
4763 multiLock.release();
4764 i_addMediumToRegistry(pMedium);
4765 multiLock.acquire();
4766 mediumLock.acquire();
4767 }
4768
4769 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4770 pAttach->i_updateMedium(pMedium);
4771 }
4772
4773 i_setModified(IsModified_Storage);
4774
4775 mediumLock.release();
4776 multiLock.release();
4777 rc = i_onMediumChange(pAttach, aForce);
4778 multiLock.acquire();
4779 mediumLock.acquire();
4780
4781 /* On error roll back this change only. */
4782 if (FAILED(rc))
4783 {
4784 if (!pMedium.isNull())
4785 pMedium->i_removeBackReference(mData->mUuid);
4786 pAttach = i_findAttachment(mMediaData->mAttachments,
4787 Bstr(aName).raw(),
4788 aControllerPort,
4789 aDevice);
4790 /* If the attachment is gone in the meantime, bail out. */
4791 if (pAttach.isNull())
4792 return rc;
4793 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4794 if (!oldmedium.isNull())
4795 oldmedium->i_addBackReference(mData->mUuid);
4796 pAttach->i_updateMedium(oldmedium);
4797 }
4798
4799 mediumLock.release();
4800 multiLock.release();
4801
4802 /* Save modified registries, but skip this machine as it's the caller's
4803 * job to save its settings like all other settings changes. */
4804 mParent->i_unmarkRegistryModified(i_getId());
4805 mParent->i_saveModifiedRegistries();
4806
4807 return rc;
4808}
4809HRESULT Machine::getMedium(const com::Utf8Str &aName,
4810 LONG aControllerPort,
4811 LONG aDevice,
4812 ComPtr<IMedium> &aMedium)
4813{
4814 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4815 aName.c_str(), aControllerPort, aDevice));
4816
4817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4818
4819 aMedium = NULL;
4820
4821 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4822 Bstr(aName).raw(),
4823 aControllerPort,
4824 aDevice);
4825 if (pAttach.isNull())
4826 return setError(VBOX_E_OBJECT_NOT_FOUND,
4827 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4828 aDevice, aControllerPort, aName.c_str());
4829
4830 aMedium = pAttach->i_getMedium();
4831
4832 return S_OK;
4833}
4834
4835HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4836{
4837
4838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4839
4840 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4841
4842 return S_OK;
4843}
4844
4845HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4846{
4847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4848
4849 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4850
4851 return S_OK;
4852}
4853
4854HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4855{
4856 /* Do not assert if slot is out of range, just return the advertised
4857 status. testdriver/vbox.py triggers this in logVmInfo. */
4858 if (aSlot >= mNetworkAdapters.size())
4859 return setError(E_INVALIDARG,
4860 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4861 aSlot, mNetworkAdapters.size());
4862
4863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4864
4865 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4866
4867 return S_OK;
4868}
4869
4870HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4871{
4872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4873
4874 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4875 size_t i = 0;
4876 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4877 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4878 ++it, ++i)
4879 aKeys[i] = it->first;
4880
4881 return S_OK;
4882}
4883
4884 /**
4885 * @note Locks this object for reading.
4886 */
4887HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4888 com::Utf8Str &aValue)
4889{
4890 /* start with nothing found */
4891 aValue = "";
4892
4893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4894
4895 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4896 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4897 // found:
4898 aValue = it->second; // source is a Utf8Str
4899
4900 /* return the result to caller (may be empty) */
4901 return S_OK;
4902}
4903
4904 /**
4905 * @note Locks mParent for writing + this object for writing.
4906 */
4907HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4908{
4909 Utf8Str strOldValue; // empty
4910
4911 // locking note: we only hold the read lock briefly to look up the old value,
4912 // then release it and call the onExtraCanChange callbacks. There is a small
4913 // chance of a race insofar as the callback might be called twice if two callers
4914 // change the same key at the same time, but that's a much better solution
4915 // than the deadlock we had here before. The actual changing of the extradata
4916 // is then performed under the write lock and race-free.
4917
4918 // look up the old value first; if nothing has changed then we need not do anything
4919 {
4920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4921
4922 // For snapshots don't even think about allowing changes, extradata
4923 // is global for a machine, so there is nothing snapshot specific.
4924 if (i_isSnapshotMachine())
4925 return setError(VBOX_E_INVALID_VM_STATE,
4926 tr("Cannot set extradata for a snapshot"));
4927
4928 // check if the right IMachine instance is used
4929 if (!i_isSessionMachine())
4930 return setError(VBOX_E_INVALID_VM_STATE,
4931 tr("Cannot set extradata for an immutable machine"));
4932
4933 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4934 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4935 strOldValue = it->second;
4936 }
4937
4938 bool fChanged;
4939 if ((fChanged = (strOldValue != aValue)))
4940 {
4941 // ask for permission from all listeners outside the locks;
4942 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4943 // lock to copy the list of callbacks to invoke
4944 Bstr error;
4945 Bstr bstrValue(aValue);
4946
4947 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4948 {
4949 const char *sep = error.isEmpty() ? "" : ": ";
4950 CBSTR err = error.raw();
4951 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4952 sep, err));
4953 return setError(E_ACCESSDENIED,
4954 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4955 aKey.c_str(),
4956 aValue.c_str(),
4957 sep,
4958 err);
4959 }
4960
4961 // data is changing and change not vetoed: then write it out under the lock
4962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4963
4964 if (aValue.isEmpty())
4965 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4966 else
4967 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4968 // creates a new key if needed
4969
4970 bool fNeedsGlobalSaveSettings = false;
4971 // This saving of settings is tricky: there is no "old state" for the
4972 // extradata items at all (unlike all other settings), so the old/new
4973 // settings comparison would give a wrong result!
4974 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4975
4976 if (fNeedsGlobalSaveSettings)
4977 {
4978 // save the global settings; for that we should hold only the VirtualBox lock
4979 alock.release();
4980 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4981 mParent->i_saveSettings();
4982 }
4983 }
4984
4985 // fire notification outside the lock
4986 if (fChanged)
4987 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4988
4989 return S_OK;
4990}
4991
4992HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4993{
4994 aProgress = NULL;
4995 NOREF(aSettingsFilePath);
4996 ReturnComNotImplemented();
4997}
4998
4999HRESULT Machine::saveSettings()
5000{
5001 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5002
5003 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5004 if (FAILED(rc)) return rc;
5005
5006 /* the settings file path may never be null */
5007 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5008
5009 /* save all VM data excluding snapshots */
5010 bool fNeedsGlobalSaveSettings = false;
5011 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5012 mlock.release();
5013
5014 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5015 {
5016 // save the global settings; for that we should hold only the VirtualBox lock
5017 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5018 rc = mParent->i_saveSettings();
5019 }
5020
5021 return rc;
5022}
5023
5024
5025HRESULT Machine::discardSettings()
5026{
5027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5028
5029 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5030 if (FAILED(rc)) return rc;
5031
5032 /*
5033 * during this rollback, the session will be notified if data has
5034 * been actually changed
5035 */
5036 i_rollback(true /* aNotify */);
5037
5038 return S_OK;
5039}
5040
5041/** @note Locks objects! */
5042HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5043 std::vector<ComPtr<IMedium> > &aMedia)
5044{
5045 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5046 AutoLimitedCaller autoCaller(this);
5047 AssertComRCReturnRC(autoCaller.rc());
5048
5049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5050
5051 Guid id(i_getId());
5052
5053 if (mData->mSession.mState != SessionState_Unlocked)
5054 return setError(VBOX_E_INVALID_OBJECT_STATE,
5055 tr("Cannot unregister the machine '%s' while it is locked"),
5056 mUserData->s.strName.c_str());
5057
5058 // wait for state dependents to drop to zero
5059 i_ensureNoStateDependencies();
5060
5061 if (!mData->mAccessible)
5062 {
5063 // inaccessible maschines can only be unregistered; uninitialize ourselves
5064 // here because currently there may be no unregistered that are inaccessible
5065 // (this state combination is not supported). Note releasing the caller and
5066 // leaving the lock before calling uninit()
5067 alock.release();
5068 autoCaller.release();
5069
5070 uninit();
5071
5072 mParent->i_unregisterMachine(this, id);
5073 // calls VirtualBox::i_saveSettings()
5074
5075 return S_OK;
5076 }
5077
5078 HRESULT rc = S_OK;
5079
5080 // discard saved state
5081 if (mData->mMachineState == MachineState_Saved)
5082 {
5083 // add the saved state file to the list of files the caller should delete
5084 Assert(!mSSData->strStateFilePath.isEmpty());
5085 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5086
5087 mSSData->strStateFilePath.setNull();
5088
5089 // unconditionally set the machine state to powered off, we now
5090 // know no session has locked the machine
5091 mData->mMachineState = MachineState_PoweredOff;
5092 }
5093
5094 size_t cSnapshots = 0;
5095 if (mData->mFirstSnapshot)
5096 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5097 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5098 // fail now before we start detaching media
5099 return setError(VBOX_E_INVALID_OBJECT_STATE,
5100 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5101 mUserData->s.strName.c_str(), cSnapshots);
5102
5103 // This list collects the medium objects from all medium attachments
5104 // which we will detach from the machine and its snapshots, in a specific
5105 // order which allows for closing all media without getting "media in use"
5106 // errors, simply by going through the list from the front to the back:
5107 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5108 // and must be closed before the parent media from the snapshots, or closing the parents
5109 // will fail because they still have children);
5110 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5111 // the root ("first") snapshot of the machine.
5112 MediaList llMedia;
5113
5114 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5115 && mMediaData->mAttachments.size()
5116 )
5117 {
5118 // we have media attachments: detach them all and add the Medium objects to our list
5119 if (aCleanupMode != CleanupMode_UnregisterOnly)
5120 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5121 else
5122 return setError(VBOX_E_INVALID_OBJECT_STATE,
5123 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5124 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5125 }
5126
5127 if (cSnapshots)
5128 {
5129 // add the media from the medium attachments of the snapshots to llMedia
5130 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5131 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5132 // into the children first
5133
5134 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5135 MachineState_T oldState = mData->mMachineState;
5136 mData->mMachineState = MachineState_DeletingSnapshot;
5137
5138 // make a copy of the first snapshot so the refcount does not drop to 0
5139 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5140 // because of the AutoCaller voodoo)
5141 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5142
5143 // GO!
5144 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5145
5146 mData->mMachineState = oldState;
5147 }
5148
5149 if (FAILED(rc))
5150 {
5151 i_rollbackMedia();
5152 return rc;
5153 }
5154
5155 // commit all the media changes made above
5156 i_commitMedia();
5157
5158 mData->mRegistered = false;
5159
5160 // machine lock no longer needed
5161 alock.release();
5162
5163 // return media to caller
5164 size_t i = 0;
5165 aMedia.resize(llMedia.size());
5166 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5167 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5168
5169 mParent->i_unregisterMachine(this, id);
5170 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5171
5172 return S_OK;
5173}
5174
5175struct Machine::DeleteTask
5176{
5177 ComObjPtr<Machine> pMachine;
5178 RTCList<ComPtr<IMedium> > llMediums;
5179 StringsList llFilesToDelete;
5180 ComObjPtr<Progress> pProgress;
5181};
5182
5183HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5184{
5185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5186
5187 HRESULT rc = i_checkStateDependency(MutableStateDep);
5188 if (FAILED(rc)) return rc;
5189
5190 if (mData->mRegistered)
5191 return setError(VBOX_E_INVALID_VM_STATE,
5192 tr("Cannot delete settings of a registered machine"));
5193
5194 DeleteTask *pTask = new DeleteTask;
5195 pTask->pMachine = this;
5196
5197 // collect files to delete
5198 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5199
5200 for (size_t i = 0; i < aMedia.size(); ++i)
5201 {
5202 IMedium *pIMedium(aMedia[i]);
5203 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5204 if (pMedium.isNull())
5205 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5206 SafeArray<BSTR> ids;
5207 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5208 if (FAILED(rc)) return rc;
5209 /* At this point the medium should not have any back references
5210 * anymore. If it has it is attached to another VM and *must* not
5211 * deleted. */
5212 if (ids.size() < 1)
5213 pTask->llMediums.append(pMedium);
5214 }
5215 if (mData->pMachineConfigFile->fileExists())
5216 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5217
5218 pTask->pProgress.createObject();
5219 pTask->pProgress->init(i_getVirtualBox(),
5220 static_cast<IMachine*>(this) /* aInitiator */,
5221 Bstr(tr("Deleting files")).raw(),
5222 true /* fCancellable */,
5223 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5224 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5225
5226 int vrc = RTThreadCreate(NULL,
5227 Machine::deleteThread,
5228 (void*)pTask,
5229 0,
5230 RTTHREADTYPE_MAIN_WORKER,
5231 0,
5232 "MachineDelete");
5233
5234 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5235
5236 if (RT_FAILURE(vrc))
5237 {
5238 delete pTask;
5239 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5240 }
5241
5242 LogFlowFuncLeave();
5243
5244 return S_OK;
5245}
5246
5247/**
5248 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5249 * calls Machine::deleteTaskWorker() on the actual machine object.
5250 * @param Thread
5251 * @param pvUser
5252 * @return
5253 */
5254/*static*/
5255DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5256{
5257 LogFlowFuncEnter();
5258
5259 DeleteTask *pTask = (DeleteTask*)pvUser;
5260 Assert(pTask);
5261 Assert(pTask->pMachine);
5262 Assert(pTask->pProgress);
5263
5264 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5265 pTask->pProgress->i_notifyComplete(rc);
5266
5267 delete pTask;
5268
5269 LogFlowFuncLeave();
5270
5271 NOREF(Thread);
5272
5273 return VINF_SUCCESS;
5274}
5275
5276/**
5277 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5278 * @param task
5279 * @return
5280 */
5281HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5282{
5283 AutoCaller autoCaller(this);
5284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5285
5286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5287
5288 HRESULT rc = S_OK;
5289
5290 try
5291 {
5292 ULONG uLogHistoryCount = 3;
5293 ComPtr<ISystemProperties> systemProperties;
5294 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5295 if (FAILED(rc)) throw rc;
5296
5297 if (!systemProperties.isNull())
5298 {
5299 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5300 if (FAILED(rc)) throw rc;
5301 }
5302
5303 MachineState_T oldState = mData->mMachineState;
5304 i_setMachineState(MachineState_SettingUp);
5305 alock.release();
5306 for (size_t i = 0; i < task.llMediums.size(); ++i)
5307 {
5308 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5309 {
5310 AutoCaller mac(pMedium);
5311 if (FAILED(mac.rc())) throw mac.rc();
5312 Utf8Str strLocation = pMedium->i_getLocationFull();
5313 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5314 if (FAILED(rc)) throw rc;
5315 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5316 }
5317 if (pMedium->i_isMediumFormatFile())
5318 {
5319 ComPtr<IProgress> pProgress2;
5320 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5321 if (FAILED(rc)) throw rc;
5322 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5323 if (FAILED(rc)) throw rc;
5324 /* Check the result of the asynchronous process. */
5325 LONG iRc;
5326 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5327 if (FAILED(rc)) throw rc;
5328 /* If the thread of the progress object has an error, then
5329 * retrieve the error info from there, or it'll be lost. */
5330 if (FAILED(iRc))
5331 throw setError(ProgressErrorInfo(pProgress2));
5332 }
5333
5334 /* Close the medium, deliberately without checking the return
5335 * code, and without leaving any trace in the error info, as
5336 * a failure here is a very minor issue, which shouldn't happen
5337 * as above we even managed to delete the medium. */
5338 {
5339 ErrorInfoKeeper eik;
5340 pMedium->Close();
5341 }
5342 }
5343 i_setMachineState(oldState);
5344 alock.acquire();
5345
5346 // delete the files pushed on the task list by Machine::Delete()
5347 // (this includes saved states of the machine and snapshots and
5348 // medium storage files from the IMedium list passed in, and the
5349 // machine XML file)
5350 StringsList::const_iterator it = task.llFilesToDelete.begin();
5351 while (it != task.llFilesToDelete.end())
5352 {
5353 const Utf8Str &strFile = *it;
5354 LogFunc(("Deleting file %s\n", strFile.c_str()));
5355 int vrc = RTFileDelete(strFile.c_str());
5356 if (RT_FAILURE(vrc))
5357 throw setError(VBOX_E_IPRT_ERROR,
5358 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5359
5360 ++it;
5361 if (it == task.llFilesToDelete.end())
5362 {
5363 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5364 if (FAILED(rc)) throw rc;
5365 break;
5366 }
5367
5368 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5369 if (FAILED(rc)) throw rc;
5370 }
5371
5372 /* delete the settings only when the file actually exists */
5373 if (mData->pMachineConfigFile->fileExists())
5374 {
5375 /* Delete any backup or uncommitted XML files. Ignore failures.
5376 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5377 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5378 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5379 RTFileDelete(otherXml.c_str());
5380 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5381 RTFileDelete(otherXml.c_str());
5382
5383 /* delete the Logs folder, nothing important should be left
5384 * there (we don't check for errors because the user might have
5385 * some private files there that we don't want to delete) */
5386 Utf8Str logFolder;
5387 getLogFolder(logFolder);
5388 Assert(logFolder.length());
5389 if (RTDirExists(logFolder.c_str()))
5390 {
5391 /* Delete all VBox.log[.N] files from the Logs folder
5392 * (this must be in sync with the rotation logic in
5393 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5394 * files that may have been created by the GUI. */
5395 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5396 logFolder.c_str(), RTPATH_DELIMITER);
5397 RTFileDelete(log.c_str());
5398 log = Utf8StrFmt("%s%cVBox.png",
5399 logFolder.c_str(), RTPATH_DELIMITER);
5400 RTFileDelete(log.c_str());
5401 for (int i = uLogHistoryCount; i > 0; i--)
5402 {
5403 log = Utf8StrFmt("%s%cVBox.log.%d",
5404 logFolder.c_str(), RTPATH_DELIMITER, i);
5405 RTFileDelete(log.c_str());
5406 log = Utf8StrFmt("%s%cVBox.png.%d",
5407 logFolder.c_str(), RTPATH_DELIMITER, i);
5408 RTFileDelete(log.c_str());
5409 }
5410#if defined(RT_OS_WINDOWS)
5411 log = Utf8StrFmt("%s%cVBoxStartup.log",
5412 logFolder.c_str(), RTPATH_DELIMITER);
5413 RTFileDelete(log.c_str());
5414#endif
5415
5416 RTDirRemove(logFolder.c_str());
5417 }
5418
5419 /* delete the Snapshots folder, nothing important should be left
5420 * there (we don't check for errors because the user might have
5421 * some private files there that we don't want to delete) */
5422 Utf8Str strFullSnapshotFolder;
5423 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5424 Assert(!strFullSnapshotFolder.isEmpty());
5425 if (RTDirExists(strFullSnapshotFolder.c_str()))
5426 RTDirRemove(strFullSnapshotFolder.c_str());
5427
5428 // delete the directory that contains the settings file, but only
5429 // if it matches the VM name
5430 Utf8Str settingsDir;
5431 if (i_isInOwnDir(&settingsDir))
5432 RTDirRemove(settingsDir.c_str());
5433 }
5434
5435 alock.release();
5436
5437 mParent->i_saveModifiedRegistries();
5438 }
5439 catch (HRESULT aRC) { rc = aRC; }
5440
5441 return rc;
5442}
5443
5444HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5445{
5446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5447
5448 ComObjPtr<Snapshot> pSnapshot;
5449 HRESULT rc;
5450
5451 if (aNameOrId.isEmpty())
5452 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5453 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5454 else
5455 {
5456 Guid uuid(aNameOrId);
5457 if (uuid.isValid())
5458 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5459 else
5460 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5461 }
5462 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5463
5464 return rc;
5465}
5466
5467HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5468{
5469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5470
5471 HRESULT rc = i_checkStateDependency(MutableStateDep);
5472 if (FAILED(rc)) return rc;
5473
5474 ComObjPtr<SharedFolder> sharedFolder;
5475 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5476 if (SUCCEEDED(rc))
5477 return setError(VBOX_E_OBJECT_IN_USE,
5478 tr("Shared folder named '%s' already exists"),
5479 aName.c_str());
5480
5481 sharedFolder.createObject();
5482 rc = sharedFolder->init(i_getMachine(),
5483 aName,
5484 aHostPath,
5485 !!aWritable,
5486 !!aAutomount,
5487 true /* fFailOnError */);
5488 if (FAILED(rc)) return rc;
5489
5490 i_setModified(IsModified_SharedFolders);
5491 mHWData.backup();
5492 mHWData->mSharedFolders.push_back(sharedFolder);
5493
5494 /* inform the direct session if any */
5495 alock.release();
5496 i_onSharedFolderChange();
5497
5498 return S_OK;
5499}
5500
5501HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5502{
5503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5504
5505 HRESULT rc = i_checkStateDependency(MutableStateDep);
5506 if (FAILED(rc)) return rc;
5507
5508 ComObjPtr<SharedFolder> sharedFolder;
5509 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5510 if (FAILED(rc)) return rc;
5511
5512 i_setModified(IsModified_SharedFolders);
5513 mHWData.backup();
5514 mHWData->mSharedFolders.remove(sharedFolder);
5515
5516 /* inform the direct session if any */
5517 alock.release();
5518 i_onSharedFolderChange();
5519
5520 return S_OK;
5521}
5522
5523HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5524{
5525 /* start with No */
5526 *aCanShow = FALSE;
5527
5528 ComPtr<IInternalSessionControl> directControl;
5529 {
5530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5531
5532 if (mData->mSession.mState != SessionState_Locked)
5533 return setError(VBOX_E_INVALID_VM_STATE,
5534 tr("Machine is not locked for session (session state: %s)"),
5535 Global::stringifySessionState(mData->mSession.mState));
5536
5537 directControl = mData->mSession.mDirectControl;
5538 }
5539
5540 /* ignore calls made after #OnSessionEnd() is called */
5541 if (!directControl)
5542 return S_OK;
5543
5544 LONG64 dummy;
5545 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5546}
5547
5548HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5549{
5550 ComPtr<IInternalSessionControl> directControl;
5551 {
5552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5553
5554 if (mData->mSession.mState != SessionState_Locked)
5555 return setError(E_FAIL,
5556 tr("Machine is not locked for session (session state: %s)"),
5557 Global::stringifySessionState(mData->mSession.mState));
5558
5559 directControl = mData->mSession.mDirectControl;
5560 }
5561
5562 /* ignore calls made after #OnSessionEnd() is called */
5563 if (!directControl)
5564 return S_OK;
5565
5566 BOOL dummy;
5567 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5568}
5569
5570#ifdef VBOX_WITH_GUEST_PROPS
5571/**
5572 * Look up a guest property in VBoxSVC's internal structures.
5573 */
5574HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5575 com::Utf8Str &aValue,
5576 LONG64 *aTimestamp,
5577 com::Utf8Str &aFlags) const
5578{
5579 using namespace guestProp;
5580
5581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5582 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5583
5584 if (it != mHWData->mGuestProperties.end())
5585 {
5586 char szFlags[MAX_FLAGS_LEN + 1];
5587 aValue = it->second.strValue;
5588 *aTimestamp = it->second.mTimestamp;
5589 writeFlags(it->second.mFlags, szFlags);
5590 aFlags = Utf8Str(szFlags);
5591 }
5592
5593 return S_OK;
5594}
5595
5596/**
5597 * Query the VM that a guest property belongs to for the property.
5598 * @returns E_ACCESSDENIED if the VM process is not available or not
5599 * currently handling queries and the lookup should then be done in
5600 * VBoxSVC.
5601 */
5602HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5603 com::Utf8Str &aValue,
5604 LONG64 *aTimestamp,
5605 com::Utf8Str &aFlags) const
5606{
5607 HRESULT rc = S_OK;
5608 BSTR bValue = NULL;
5609 BSTR bFlags = NULL;
5610
5611 ComPtr<IInternalSessionControl> directControl;
5612 directControl = mData->mSession.mDirectControl;
5613
5614 /* fail if we were called after #OnSessionEnd() is called. This is a
5615 * silly race condition. */
5616
5617 /** @todo This code is bothering API clients (like python script clients) with
5618 * the AccessGuestProperty call, creating unncessary IPC. Need to
5619 * have a way of figuring out which kind of direct session it is... */
5620 if (!directControl)
5621 rc = E_ACCESSDENIED;
5622 else
5623 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5624 0 /* accessMode */,
5625 &bValue, aTimestamp, &bFlags);
5626
5627 aValue = bValue;
5628 aFlags = bFlags;
5629
5630 return rc;
5631}
5632#endif // VBOX_WITH_GUEST_PROPS
5633
5634HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5635 com::Utf8Str &aValue,
5636 LONG64 *aTimestamp,
5637 com::Utf8Str &aFlags)
5638{
5639#ifndef VBOX_WITH_GUEST_PROPS
5640 ReturnComNotImplemented();
5641#else // VBOX_WITH_GUEST_PROPS
5642
5643 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5644
5645 if (rc == E_ACCESSDENIED)
5646 /* The VM is not running or the service is not (yet) accessible */
5647 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5648 return rc;
5649#endif // VBOX_WITH_GUEST_PROPS
5650}
5651
5652HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5653{
5654 LONG64 dummyTimestamp;
5655 com::Utf8Str dummyFlags;
5656 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5657 return rc;
5658
5659}
5660HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5661{
5662 com::Utf8Str dummyFlags;
5663 com::Utf8Str dummyValue;
5664 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5665 return rc;
5666}
5667
5668#ifdef VBOX_WITH_GUEST_PROPS
5669/**
5670 * Set a guest property in VBoxSVC's internal structures.
5671 */
5672HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5673 const com::Utf8Str &aFlags, bool fDelete)
5674{
5675 using namespace guestProp;
5676
5677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5678 HRESULT rc = S_OK;
5679
5680 rc = i_checkStateDependency(MutableOrSavedStateDep);
5681 if (FAILED(rc)) return rc;
5682
5683 try
5684 {
5685 uint32_t fFlags = NILFLAG;
5686 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5687 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5688
5689 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5690 if (it == mHWData->mGuestProperties.end())
5691 {
5692 if (!fDelete)
5693 {
5694 i_setModified(IsModified_MachineData);
5695 mHWData.backupEx();
5696
5697 RTTIMESPEC time;
5698 HWData::GuestProperty prop;
5699 prop.strValue = Bstr(aValue).raw();
5700 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5701 prop.mFlags = fFlags;
5702 mHWData->mGuestProperties[aName] = prop;
5703 }
5704 }
5705 else
5706 {
5707 if (it->second.mFlags & (RDONLYHOST))
5708 {
5709 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5710 }
5711 else
5712 {
5713 i_setModified(IsModified_MachineData);
5714 mHWData.backupEx();
5715
5716 /* The backupEx() operation invalidates our iterator,
5717 * so get a new one. */
5718 it = mHWData->mGuestProperties.find(aName);
5719 Assert(it != mHWData->mGuestProperties.end());
5720
5721 if (!fDelete)
5722 {
5723 RTTIMESPEC time;
5724 it->second.strValue = aValue;
5725 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5726 it->second.mFlags = fFlags;
5727 }
5728 else
5729 mHWData->mGuestProperties.erase(it);
5730 }
5731 }
5732
5733 if ( SUCCEEDED(rc)
5734 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5735 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5736 RTSTR_MAX,
5737 aName.c_str(),
5738 RTSTR_MAX,
5739 NULL)
5740 )
5741 )
5742 {
5743 alock.release();
5744
5745 mParent->i_onGuestPropertyChange(mData->mUuid,
5746 Bstr(aName).raw(),
5747 Bstr(aValue).raw(),
5748 Bstr(aFlags).raw());
5749 }
5750 }
5751 catch (std::bad_alloc &)
5752 {
5753 rc = E_OUTOFMEMORY;
5754 }
5755
5756 return rc;
5757}
5758
5759/**
5760 * Set a property on the VM that that property belongs to.
5761 * @returns E_ACCESSDENIED if the VM process is not available or not
5762 * currently handling queries and the setting should then be done in
5763 * VBoxSVC.
5764 */
5765HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5766 const com::Utf8Str &aFlags, bool fDelete)
5767{
5768 HRESULT rc;
5769
5770 try
5771 {
5772 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5773
5774 BSTR dummy = NULL; /* will not be changed (setter) */
5775 LONG64 dummy64;
5776 if (!directControl)
5777 rc = E_ACCESSDENIED;
5778 else
5779 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5780 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5781 fDelete? 2: 1 /* accessMode */,
5782 &dummy, &dummy64, &dummy);
5783 }
5784 catch (std::bad_alloc &)
5785 {
5786 rc = E_OUTOFMEMORY;
5787 }
5788
5789 return rc;
5790}
5791#endif // VBOX_WITH_GUEST_PROPS
5792
5793HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5794 const com::Utf8Str &aFlags)
5795{
5796#ifndef VBOX_WITH_GUEST_PROPS
5797 ReturnComNotImplemented();
5798#else // VBOX_WITH_GUEST_PROPS
5799 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5800 if (rc == E_ACCESSDENIED)
5801 /* The VM is not running or the service is not (yet) accessible */
5802 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5803 return rc;
5804#endif // VBOX_WITH_GUEST_PROPS
5805}
5806
5807HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5808{
5809 return setGuestProperty(aProperty, aValue, "");
5810}
5811
5812HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5813{
5814#ifndef VBOX_WITH_GUEST_PROPS
5815 ReturnComNotImplemented();
5816#else // VBOX_WITH_GUEST_PROPS
5817 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5818 if (rc == E_ACCESSDENIED)
5819 /* The VM is not running or the service is not (yet) accessible */
5820 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5821 return rc;
5822#endif // VBOX_WITH_GUEST_PROPS
5823}
5824
5825#ifdef VBOX_WITH_GUEST_PROPS
5826/**
5827 * Enumerate the guest properties in VBoxSVC's internal structures.
5828 */
5829HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5830 std::vector<com::Utf8Str> &aNames,
5831 std::vector<com::Utf8Str> &aValues,
5832 std::vector<LONG64> &aTimestamps,
5833 std::vector<com::Utf8Str> &aFlags)
5834{
5835 using namespace guestProp;
5836
5837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5838 Utf8Str strPatterns(aPatterns);
5839
5840 HWData::GuestPropertyMap propMap;
5841
5842 /*
5843 * Look for matching patterns and build up a list.
5844 */
5845 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5846 while (it != mHWData->mGuestProperties.end())
5847 {
5848 if ( strPatterns.isEmpty()
5849 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5850 RTSTR_MAX,
5851 it->first.c_str(),
5852 RTSTR_MAX,
5853 NULL)
5854 )
5855 propMap.insert(*it);
5856 it++;
5857 }
5858
5859 alock.release();
5860
5861 /*
5862 * And build up the arrays for returning the property information.
5863 */
5864 size_t cEntries = propMap.size();
5865
5866 aNames.resize(cEntries);
5867 aValues.resize(cEntries);
5868 aTimestamps.resize(cEntries);
5869 aFlags.resize(cEntries);
5870
5871 char szFlags[MAX_FLAGS_LEN + 1];
5872 size_t i= 0;
5873 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5874 {
5875 aNames[i] = it->first;
5876 aValues[i] = it->second.strValue;
5877 aTimestamps[i] = it->second.mTimestamp;
5878 writeFlags(it->second.mFlags, szFlags);
5879 aFlags[i] = Utf8Str(szFlags);
5880 }
5881
5882 return S_OK;
5883}
5884
5885/**
5886 * Enumerate the properties managed by a VM.
5887 * @returns E_ACCESSDENIED if the VM process is not available or not
5888 * currently handling queries and the setting should then be done in
5889 * VBoxSVC.
5890 */
5891HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5892 std::vector<com::Utf8Str> &aNames,
5893 std::vector<com::Utf8Str> &aValues,
5894 std::vector<LONG64> &aTimestamps,
5895 std::vector<com::Utf8Str> &aFlags)
5896{
5897 HRESULT rc;
5898 ComPtr<IInternalSessionControl> directControl;
5899 directControl = mData->mSession.mDirectControl;
5900
5901
5902 com::SafeArray<BSTR> bNames;
5903 com::SafeArray<BSTR> bValues;
5904 com::SafeArray<LONG64> bTimestamps;
5905 com::SafeArray<BSTR> bFlags;
5906
5907 if (!directControl)
5908 rc = E_ACCESSDENIED;
5909 else
5910 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5911 ComSafeArrayAsOutParam(bNames),
5912 ComSafeArrayAsOutParam(bValues),
5913 ComSafeArrayAsOutParam(bTimestamps),
5914 ComSafeArrayAsOutParam(bFlags));
5915 size_t i;
5916 aNames.resize(bNames.size());
5917 for (i = 0; i < bNames.size(); ++i)
5918 aNames[i] = Utf8Str(bNames[i]);
5919 aValues.resize(bValues.size());
5920 for (i = 0; i < bValues.size(); ++i)
5921 aValues[i] = Utf8Str(bValues[i]);
5922 aTimestamps.resize(bTimestamps.size());
5923 for (i = 0; i < bTimestamps.size(); ++i)
5924 aTimestamps[i] = bTimestamps[i];
5925 aFlags.resize(bFlags.size());
5926 for (i = 0; i < bFlags.size(); ++i)
5927 aFlags[i] = Utf8Str(bFlags[i]);
5928
5929 return rc;
5930}
5931#endif // VBOX_WITH_GUEST_PROPS
5932HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5933 std::vector<com::Utf8Str> &aNames,
5934 std::vector<com::Utf8Str> &aValues,
5935 std::vector<LONG64> &aTimestamps,
5936 std::vector<com::Utf8Str> &aFlags)
5937{
5938#ifndef VBOX_WITH_GUEST_PROPS
5939 ReturnComNotImplemented();
5940#else // VBOX_WITH_GUEST_PROPS
5941
5942 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5943
5944 if (rc == E_ACCESSDENIED)
5945 /* The VM is not running or the service is not (yet) accessible */
5946 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5947 return rc;
5948#endif // VBOX_WITH_GUEST_PROPS
5949}
5950
5951HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5952 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5953{
5954 MediaData::AttachmentList atts;
5955
5956 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5957 if (FAILED(rc)) return rc;
5958
5959 size_t i = 0;
5960 aMediumAttachments.resize(atts.size());
5961 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5962 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5963
5964 return S_OK;
5965}
5966
5967HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5968 LONG aControllerPort,
5969 LONG aDevice,
5970 ComPtr<IMediumAttachment> &aAttachment)
5971{
5972 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5973 aName.c_str(), aControllerPort, aDevice));
5974
5975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5976
5977 aAttachment = NULL;
5978
5979 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5980 Bstr(aName).raw(),
5981 aControllerPort,
5982 aDevice);
5983 if (pAttach.isNull())
5984 return setError(VBOX_E_OBJECT_NOT_FOUND,
5985 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5986 aDevice, aControllerPort, aName.c_str());
5987
5988 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5989
5990 return S_OK;
5991}
5992
5993
5994HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5995 StorageBus_T aConnectionType,
5996 ComPtr<IStorageController> &aController)
5997{
5998 if ( (aConnectionType <= StorageBus_Null)
5999 || (aConnectionType > StorageBus_USB))
6000 return setError(E_INVALIDARG,
6001 tr("Invalid connection type: %d"),
6002 aConnectionType);
6003
6004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6005
6006 HRESULT rc = i_checkStateDependency(MutableStateDep);
6007 if (FAILED(rc)) return rc;
6008
6009 /* try to find one with the name first. */
6010 ComObjPtr<StorageController> ctrl;
6011
6012 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6013 if (SUCCEEDED(rc))
6014 return setError(VBOX_E_OBJECT_IN_USE,
6015 tr("Storage controller named '%s' already exists"),
6016 aName.c_str());
6017
6018 ctrl.createObject();
6019
6020 /* get a new instance number for the storage controller */
6021 ULONG ulInstance = 0;
6022 bool fBootable = true;
6023 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6024 it != mStorageControllers->end();
6025 ++it)
6026 {
6027 if ((*it)->i_getStorageBus() == aConnectionType)
6028 {
6029 ULONG ulCurInst = (*it)->i_getInstance();
6030
6031 if (ulCurInst >= ulInstance)
6032 ulInstance = ulCurInst + 1;
6033
6034 /* Only one controller of each type can be marked as bootable. */
6035 if ((*it)->i_getBootable())
6036 fBootable = false;
6037 }
6038 }
6039
6040 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6041 if (FAILED(rc)) return rc;
6042
6043 i_setModified(IsModified_Storage);
6044 mStorageControllers.backup();
6045 mStorageControllers->push_back(ctrl);
6046
6047 ctrl.queryInterfaceTo(aController.asOutParam());
6048
6049 /* inform the direct session if any */
6050 alock.release();
6051 i_onStorageControllerChange();
6052
6053 return S_OK;
6054}
6055
6056HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6057 ComPtr<IStorageController> &aStorageController)
6058{
6059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6060
6061 ComObjPtr<StorageController> ctrl;
6062
6063 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6064 if (SUCCEEDED(rc))
6065 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6066
6067 return rc;
6068}
6069
6070HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6071 ComPtr<IStorageController> &aStorageController)
6072{
6073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6074
6075 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6076 it != mStorageControllers->end();
6077 ++it)
6078 {
6079 if ((*it)->i_getInstance() == aInstance)
6080 {
6081 (*it).queryInterfaceTo(aStorageController.asOutParam());
6082 return S_OK;
6083 }
6084 }
6085
6086 return setError(VBOX_E_OBJECT_NOT_FOUND,
6087 tr("Could not find a storage controller with instance number '%lu'"),
6088 aInstance);
6089}
6090
6091HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6092{
6093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6094
6095 HRESULT rc = i_checkStateDependency(MutableStateDep);
6096 if (FAILED(rc)) return rc;
6097
6098 ComObjPtr<StorageController> ctrl;
6099
6100 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6101 if (SUCCEEDED(rc))
6102 {
6103 /* Ensure that only one controller of each type is marked as bootable. */
6104 if (aBootable == TRUE)
6105 {
6106 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6107 it != mStorageControllers->end();
6108 ++it)
6109 {
6110 ComObjPtr<StorageController> aCtrl = (*it);
6111
6112 if ( (aCtrl->i_getName() != aName)
6113 && aCtrl->i_getBootable() == TRUE
6114 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6115 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6116 {
6117 aCtrl->i_setBootable(FALSE);
6118 break;
6119 }
6120 }
6121 }
6122
6123 if (SUCCEEDED(rc))
6124 {
6125 ctrl->i_setBootable(aBootable);
6126 i_setModified(IsModified_Storage);
6127 }
6128 }
6129
6130 if (SUCCEEDED(rc))
6131 {
6132 /* inform the direct session if any */
6133 alock.release();
6134 i_onStorageControllerChange();
6135 }
6136
6137 return rc;
6138}
6139
6140HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6141{
6142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6143
6144 HRESULT rc = i_checkStateDependency(MutableStateDep);
6145 if (FAILED(rc)) return rc;
6146
6147 ComObjPtr<StorageController> ctrl;
6148 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6149 if (FAILED(rc)) return rc;
6150
6151 {
6152 /* find all attached devices to the appropriate storage controller and detach them all */
6153 // make a temporary list because detachDevice invalidates iterators into
6154 // mMediaData->mAttachments
6155 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6156
6157 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6158 it != llAttachments2.end();
6159 ++it)
6160 {
6161 MediumAttachment *pAttachTemp = *it;
6162
6163 AutoCaller localAutoCaller(pAttachTemp);
6164 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6165
6166 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6167
6168 if (pAttachTemp->i_getControllerName() == aName)
6169 {
6170 rc = i_detachDevice(pAttachTemp, alock, NULL);
6171 if (FAILED(rc)) return rc;
6172 }
6173 }
6174 }
6175
6176 /* We can remove it now. */
6177 i_setModified(IsModified_Storage);
6178 mStorageControllers.backup();
6179
6180 ctrl->i_unshare();
6181
6182 mStorageControllers->remove(ctrl);
6183
6184 /* inform the direct session if any */
6185 alock.release();
6186 i_onStorageControllerChange();
6187
6188 return S_OK;
6189}
6190
6191HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6192 ComPtr<IUSBController> &aController)
6193{
6194 if ( (aType <= USBControllerType_Null)
6195 || (aType >= USBControllerType_Last))
6196 return setError(E_INVALIDARG,
6197 tr("Invalid USB controller type: %d"),
6198 aType);
6199
6200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6201
6202 HRESULT rc = i_checkStateDependency(MutableStateDep);
6203 if (FAILED(rc)) return rc;
6204
6205 /* try to find one with the same type first. */
6206 ComObjPtr<USBController> ctrl;
6207
6208 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6209 if (SUCCEEDED(rc))
6210 return setError(VBOX_E_OBJECT_IN_USE,
6211 tr("USB controller named '%s' already exists"),
6212 aName.c_str());
6213
6214 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6215 ULONG maxInstances;
6216 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6217 if (FAILED(rc))
6218 return rc;
6219
6220 ULONG cInstances = i_getUSBControllerCountByType(aType);
6221 if (cInstances >= maxInstances)
6222 return setError(E_INVALIDARG,
6223 tr("Too many USB controllers of this type"));
6224
6225 ctrl.createObject();
6226
6227 rc = ctrl->init(this, aName, aType);
6228 if (FAILED(rc)) return rc;
6229
6230 i_setModified(IsModified_USB);
6231 mUSBControllers.backup();
6232 mUSBControllers->push_back(ctrl);
6233
6234 ctrl.queryInterfaceTo(aController.asOutParam());
6235
6236 /* inform the direct session if any */
6237 alock.release();
6238 i_onUSBControllerChange();
6239
6240 return S_OK;
6241}
6242
6243HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6244{
6245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6246
6247 ComObjPtr<USBController> ctrl;
6248
6249 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6250 if (SUCCEEDED(rc))
6251 ctrl.queryInterfaceTo(aController.asOutParam());
6252
6253 return rc;
6254}
6255
6256HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6257 ULONG *aControllers)
6258{
6259 if ( (aType <= USBControllerType_Null)
6260 || (aType >= USBControllerType_Last))
6261 return setError(E_INVALIDARG,
6262 tr("Invalid USB controller type: %d"),
6263 aType);
6264
6265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6266
6267 ComObjPtr<USBController> ctrl;
6268
6269 *aControllers = i_getUSBControllerCountByType(aType);
6270
6271 return S_OK;
6272}
6273
6274HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6275{
6276
6277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6278
6279 HRESULT rc = i_checkStateDependency(MutableStateDep);
6280 if (FAILED(rc)) return rc;
6281
6282 ComObjPtr<USBController> ctrl;
6283 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6284 if (FAILED(rc)) return rc;
6285
6286 i_setModified(IsModified_USB);
6287 mUSBControllers.backup();
6288
6289 ctrl->i_unshare();
6290
6291 mUSBControllers->remove(ctrl);
6292
6293 /* inform the direct session if any */
6294 alock.release();
6295 i_onUSBControllerChange();
6296
6297 return S_OK;
6298}
6299
6300HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6301 ULONG *aOriginX,
6302 ULONG *aOriginY,
6303 ULONG *aWidth,
6304 ULONG *aHeight,
6305 BOOL *aEnabled)
6306{
6307 uint32_t u32OriginX= 0;
6308 uint32_t u32OriginY= 0;
6309 uint32_t u32Width = 0;
6310 uint32_t u32Height = 0;
6311 uint16_t u16Flags = 0;
6312
6313 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6314 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6315 if (RT_FAILURE(vrc))
6316 {
6317#ifdef RT_OS_WINDOWS
6318 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6319 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6320 * So just assign fEnable to TRUE again.
6321 * The right fix would be to change GUI API wrappers to make sure that parameters
6322 * are changed only if API succeeds.
6323 */
6324 *aEnabled = TRUE;
6325#endif
6326 return setError(VBOX_E_IPRT_ERROR,
6327 tr("Saved guest size is not available (%Rrc)"),
6328 vrc);
6329 }
6330
6331 *aOriginX = u32OriginX;
6332 *aOriginY = u32OriginY;
6333 *aWidth = u32Width;
6334 *aHeight = u32Height;
6335 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6336
6337 return S_OK;
6338}
6339
6340HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6341{
6342 if (aScreenId != 0)
6343 return E_NOTIMPL;
6344
6345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6346
6347 uint8_t *pu8Data = NULL;
6348 uint32_t cbData = 0;
6349 uint32_t u32Width = 0;
6350 uint32_t u32Height = 0;
6351
6352 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6353
6354 if (RT_FAILURE(vrc))
6355 return setError(VBOX_E_IPRT_ERROR,
6356 tr("Saved screenshot data is not available (%Rrc)"),
6357 vrc);
6358
6359 *aSize = cbData;
6360 *aWidth = u32Width;
6361 *aHeight = u32Height;
6362
6363 freeSavedDisplayScreenshot(pu8Data);
6364
6365 return S_OK;
6366}
6367
6368HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6369 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6370{
6371 if (aScreenId != 0)
6372 return E_NOTIMPL;
6373
6374 if ( aBitmapFormat != BitmapFormat_BGR0
6375 && aBitmapFormat != BitmapFormat_BGRA
6376 && aBitmapFormat != BitmapFormat_RGBA
6377 && aBitmapFormat != BitmapFormat_PNG)
6378 return setError(E_NOTIMPL,
6379 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6380
6381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6382
6383 uint8_t *pu8Data = NULL;
6384 uint32_t cbData = 0;
6385 uint32_t u32Width = 0;
6386 uint32_t u32Height = 0;
6387
6388 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6389
6390 if (RT_FAILURE(vrc))
6391 return setError(VBOX_E_IPRT_ERROR,
6392 tr("Saved thumbnail data is not available (%Rrc)"),
6393 vrc);
6394
6395 HRESULT hr = S_OK;
6396
6397 *aWidth = u32Width;
6398 *aHeight = u32Height;
6399
6400 if (cbData > 0)
6401 {
6402 /* Convert pixels to the format expected by the API caller. */
6403 if (aBitmapFormat == BitmapFormat_BGR0)
6404 {
6405 /* [0] B, [1] G, [2] R, [3] 0. */
6406 aData.resize(cbData);
6407 memcpy(&aData.front(), pu8Data, cbData);
6408 }
6409 else if (aBitmapFormat == BitmapFormat_BGRA)
6410 {
6411 /* [0] B, [1] G, [2] R, [3] A. */
6412 aData.resize(cbData);
6413 for (uint32_t i = 0; i < cbData; i += 4)
6414 {
6415 aData[i] = pu8Data[i];
6416 aData[i + 1] = pu8Data[i + 1];
6417 aData[i + 2] = pu8Data[i + 2];
6418 aData[i + 3] = 0xff;
6419 }
6420 }
6421 else if (aBitmapFormat == BitmapFormat_RGBA)
6422 {
6423 /* [0] R, [1] G, [2] B, [3] A. */
6424 aData.resize(cbData);
6425 for (uint32_t i = 0; i < cbData; i += 4)
6426 {
6427 aData[i] = pu8Data[i + 2];
6428 aData[i + 1] = pu8Data[i + 1];
6429 aData[i + 2] = pu8Data[i];
6430 aData[i + 3] = 0xff;
6431 }
6432 }
6433 else if (aBitmapFormat == BitmapFormat_PNG)
6434 {
6435 uint8_t *pu8PNG = NULL;
6436 uint32_t cbPNG = 0;
6437 uint32_t cxPNG = 0;
6438 uint32_t cyPNG = 0;
6439
6440 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6441
6442 if (RT_SUCCESS(vrc))
6443 {
6444 aData.resize(cbPNG);
6445 if (cbPNG)
6446 memcpy(&aData.front(), pu8PNG, cbPNG);
6447 }
6448 else
6449 hr = setError(VBOX_E_IPRT_ERROR,
6450 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6451 vrc);
6452
6453 RTMemFree(pu8PNG);
6454 }
6455 }
6456
6457 freeSavedDisplayScreenshot(pu8Data);
6458
6459 return hr;
6460}
6461
6462HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6463{
6464 if (aScreenId != 0)
6465 return E_NOTIMPL;
6466
6467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6468
6469 uint8_t *pu8Data = NULL;
6470 uint32_t cbData = 0;
6471 uint32_t u32Width = 0;
6472 uint32_t u32Height = 0;
6473
6474 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6475
6476 if (RT_FAILURE(vrc))
6477 return setError(VBOX_E_IPRT_ERROR,
6478 tr("Saved screenshot data is not available (%Rrc)"),
6479 vrc);
6480
6481 *aSize = cbData;
6482 *aWidth = u32Width;
6483 *aHeight = u32Height;
6484
6485 freeSavedDisplayScreenshot(pu8Data);
6486
6487 return S_OK;
6488}
6489
6490HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6491{
6492 if (aScreenId != 0)
6493 return E_NOTIMPL;
6494
6495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6496
6497 uint8_t *pu8Data = NULL;
6498 uint32_t cbData = 0;
6499 uint32_t u32Width = 0;
6500 uint32_t u32Height = 0;
6501
6502 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6503
6504 if (RT_FAILURE(vrc))
6505 return setError(VBOX_E_IPRT_ERROR,
6506 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6507 vrc);
6508
6509 *aWidth = u32Width;
6510 *aHeight = u32Height;
6511
6512 aData.resize(cbData);
6513 if (cbData)
6514 memcpy(&aData.front(), pu8Data, cbData);
6515
6516 freeSavedDisplayScreenshot(pu8Data);
6517
6518 return S_OK;
6519}
6520
6521HRESULT Machine::hotPlugCPU(ULONG aCpu)
6522{
6523 HRESULT rc = S_OK;
6524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6525
6526 if (!mHWData->mCPUHotPlugEnabled)
6527 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6528
6529 if (aCpu >= mHWData->mCPUCount)
6530 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6531
6532 if (mHWData->mCPUAttached[aCpu])
6533 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6534
6535 alock.release();
6536 rc = i_onCPUChange(aCpu, false);
6537 alock.acquire();
6538 if (FAILED(rc)) return rc;
6539
6540 i_setModified(IsModified_MachineData);
6541 mHWData.backup();
6542 mHWData->mCPUAttached[aCpu] = true;
6543
6544 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6545 if (Global::IsOnline(mData->mMachineState))
6546 i_saveSettings(NULL);
6547
6548 return S_OK;
6549}
6550
6551HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6552{
6553 HRESULT rc = S_OK;
6554
6555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6556
6557 if (!mHWData->mCPUHotPlugEnabled)
6558 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6559
6560 if (aCpu >= SchemaDefs::MaxCPUCount)
6561 return setError(E_INVALIDARG,
6562 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6563 SchemaDefs::MaxCPUCount);
6564
6565 if (!mHWData->mCPUAttached[aCpu])
6566 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6567
6568 /* CPU 0 can't be detached */
6569 if (aCpu == 0)
6570 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6571
6572 alock.release();
6573 rc = i_onCPUChange(aCpu, true);
6574 alock.acquire();
6575 if (FAILED(rc)) return rc;
6576
6577 i_setModified(IsModified_MachineData);
6578 mHWData.backup();
6579 mHWData->mCPUAttached[aCpu] = false;
6580
6581 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6582 if (Global::IsOnline(mData->mMachineState))
6583 i_saveSettings(NULL);
6584
6585 return S_OK;
6586}
6587
6588HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6589{
6590 *aAttached = false;
6591
6592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6593
6594 /* If hotplug is enabled the CPU is always enabled. */
6595 if (!mHWData->mCPUHotPlugEnabled)
6596 {
6597 if (aCpu < mHWData->mCPUCount)
6598 *aAttached = true;
6599 }
6600 else
6601 {
6602 if (aCpu < SchemaDefs::MaxCPUCount)
6603 *aAttached = mHWData->mCPUAttached[aCpu];
6604 }
6605
6606 return S_OK;
6607}
6608
6609HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6610{
6611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6612
6613 Utf8Str log = i_queryLogFilename(aIdx);
6614 if (!RTFileExists(log.c_str()))
6615 log.setNull();
6616 aFilename = log;
6617
6618 return S_OK;
6619}
6620
6621HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6622{
6623 if (aSize < 0)
6624 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6625
6626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6627
6628 HRESULT rc = S_OK;
6629 Utf8Str log = i_queryLogFilename(aIdx);
6630
6631 /* do not unnecessarily hold the lock while doing something which does
6632 * not need the lock and potentially takes a long time. */
6633 alock.release();
6634
6635 /* Limit the chunk size to 32K for now, as that gives better performance
6636 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6637 * One byte expands to approx. 25 bytes of breathtaking XML. */
6638 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6639 aData.resize(cbData);
6640
6641 RTFILE LogFile;
6642 int vrc = RTFileOpen(&LogFile, log.c_str(),
6643 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6644 if (RT_SUCCESS(vrc))
6645 {
6646 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6647 if (RT_SUCCESS(vrc))
6648 aData.resize(cbData);
6649 else
6650 rc = setError(VBOX_E_IPRT_ERROR,
6651 tr("Could not read log file '%s' (%Rrc)"),
6652 log.c_str(), vrc);
6653 RTFileClose(LogFile);
6654 }
6655 else
6656 rc = setError(VBOX_E_IPRT_ERROR,
6657 tr("Could not open log file '%s' (%Rrc)"),
6658 log.c_str(), vrc);
6659
6660 if (FAILED(rc))
6661 aData.resize(0);
6662
6663 return rc;
6664}
6665
6666
6667/**
6668 * Currently this method doesn't attach device to the running VM,
6669 * just makes sure it's plugged on next VM start.
6670 */
6671HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6672{
6673 // lock scope
6674 {
6675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6676
6677 HRESULT rc = i_checkStateDependency(MutableStateDep);
6678 if (FAILED(rc)) return rc;
6679
6680 ChipsetType_T aChipset = ChipsetType_PIIX3;
6681 COMGETTER(ChipsetType)(&aChipset);
6682
6683 if (aChipset != ChipsetType_ICH9)
6684 {
6685 return setError(E_INVALIDARG,
6686 tr("Host PCI attachment only supported with ICH9 chipset"));
6687 }
6688
6689 // check if device with this host PCI address already attached
6690 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6691 it != mHWData->mPCIDeviceAssignments.end();
6692 ++it)
6693 {
6694 LONG iHostAddress = -1;
6695 ComPtr<PCIDeviceAttachment> pAttach;
6696 pAttach = *it;
6697 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6698 if (iHostAddress == aHostAddress)
6699 return setError(E_INVALIDARG,
6700 tr("Device with host PCI address already attached to this VM"));
6701 }
6702
6703 ComObjPtr<PCIDeviceAttachment> pda;
6704 char name[32];
6705
6706 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6707 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6708 Bstr bname(name);
6709 pda.createObject();
6710 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6711 i_setModified(IsModified_MachineData);
6712 mHWData.backup();
6713 mHWData->mPCIDeviceAssignments.push_back(pda);
6714 }
6715
6716 return S_OK;
6717}
6718
6719/**
6720 * Currently this method doesn't detach device from the running VM,
6721 * just makes sure it's not plugged on next VM start.
6722 */
6723HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6724{
6725 ComObjPtr<PCIDeviceAttachment> pAttach;
6726 bool fRemoved = false;
6727 HRESULT rc;
6728
6729 // lock scope
6730 {
6731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6732
6733 rc = i_checkStateDependency(MutableStateDep);
6734 if (FAILED(rc)) return rc;
6735
6736 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6737 it != mHWData->mPCIDeviceAssignments.end();
6738 ++it)
6739 {
6740 LONG iHostAddress = -1;
6741 pAttach = *it;
6742 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6743 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6744 {
6745 i_setModified(IsModified_MachineData);
6746 mHWData.backup();
6747 mHWData->mPCIDeviceAssignments.remove(pAttach);
6748 fRemoved = true;
6749 break;
6750 }
6751 }
6752 }
6753
6754
6755 /* Fire event outside of the lock */
6756 if (fRemoved)
6757 {
6758 Assert(!pAttach.isNull());
6759 ComPtr<IEventSource> es;
6760 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6761 Assert(SUCCEEDED(rc));
6762 Bstr mid;
6763 rc = this->COMGETTER(Id)(mid.asOutParam());
6764 Assert(SUCCEEDED(rc));
6765 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6766 }
6767
6768 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6769 tr("No host PCI device %08x attached"),
6770 aHostAddress
6771 );
6772}
6773
6774HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6775{
6776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6777
6778 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6779
6780 size_t i = 0;
6781 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6782 it != mHWData->mPCIDeviceAssignments.end();
6783 ++i, ++it)
6784 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6785
6786 return S_OK;
6787}
6788
6789HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6790{
6791 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6792
6793 return S_OK;
6794}
6795
6796HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6797{
6798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6799
6800 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6801
6802 return S_OK;
6803}
6804
6805HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6806{
6807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6808 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6809 if (SUCCEEDED(hrc))
6810 {
6811 hrc = mHWData.backupEx();
6812 if (SUCCEEDED(hrc))
6813 {
6814 i_setModified(IsModified_MachineData);
6815 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6816 }
6817 }
6818 return hrc;
6819}
6820
6821HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6822{
6823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6824 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6825 return S_OK;
6826}
6827
6828HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6829{
6830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6831 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6832 if (SUCCEEDED(hrc))
6833 {
6834 hrc = mHWData.backupEx();
6835 if (SUCCEEDED(hrc))
6836 {
6837 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6838 if (SUCCEEDED(hrc))
6839 i_setModified(IsModified_MachineData);
6840 }
6841 }
6842 return hrc;
6843}
6844
6845HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6846{
6847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6848
6849 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6850
6851 return S_OK;
6852}
6853
6854HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6855{
6856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6857 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6858 if (SUCCEEDED(hrc))
6859 {
6860 hrc = mHWData.backupEx();
6861 if (SUCCEEDED(hrc))
6862 {
6863 i_setModified(IsModified_MachineData);
6864 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6865 }
6866 }
6867 return hrc;
6868}
6869
6870HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6871{
6872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6873
6874 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6875
6876 return S_OK;
6877}
6878
6879HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6880{
6881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6882
6883 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6884 if ( SUCCEEDED(hrc)
6885 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6886 {
6887 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6888 int vrc;
6889
6890 if (aAutostartEnabled)
6891 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6892 else
6893 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6894
6895 if (RT_SUCCESS(vrc))
6896 {
6897 hrc = mHWData.backupEx();
6898 if (SUCCEEDED(hrc))
6899 {
6900 i_setModified(IsModified_MachineData);
6901 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6902 }
6903 }
6904 else if (vrc == VERR_NOT_SUPPORTED)
6905 hrc = setError(VBOX_E_NOT_SUPPORTED,
6906 tr("The VM autostart feature is not supported on this platform"));
6907 else if (vrc == VERR_PATH_NOT_FOUND)
6908 hrc = setError(E_FAIL,
6909 tr("The path to the autostart database is not set"));
6910 else
6911 hrc = setError(E_UNEXPECTED,
6912 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6913 aAutostartEnabled ? "Adding" : "Removing",
6914 mUserData->s.strName.c_str(), vrc);
6915 }
6916 return hrc;
6917}
6918
6919HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6920{
6921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6922
6923 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6924
6925 return S_OK;
6926}
6927
6928HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6929{
6930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6931 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6932 if (SUCCEEDED(hrc))
6933 {
6934 hrc = mHWData.backupEx();
6935 if (SUCCEEDED(hrc))
6936 {
6937 i_setModified(IsModified_MachineData);
6938 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6939 }
6940 }
6941 return hrc;
6942}
6943
6944HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6945{
6946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6947
6948 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6949
6950 return S_OK;
6951}
6952
6953HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6954{
6955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6956 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6957 if ( SUCCEEDED(hrc)
6958 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6959 {
6960 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6961 int vrc;
6962
6963 if (aAutostopType != AutostopType_Disabled)
6964 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6965 else
6966 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6967
6968 if (RT_SUCCESS(vrc))
6969 {
6970 hrc = mHWData.backupEx();
6971 if (SUCCEEDED(hrc))
6972 {
6973 i_setModified(IsModified_MachineData);
6974 mHWData->mAutostart.enmAutostopType = aAutostopType;
6975 }
6976 }
6977 else if (vrc == VERR_NOT_SUPPORTED)
6978 hrc = setError(VBOX_E_NOT_SUPPORTED,
6979 tr("The VM autostop feature is not supported on this platform"));
6980 else if (vrc == VERR_PATH_NOT_FOUND)
6981 hrc = setError(E_FAIL,
6982 tr("The path to the autostart database is not set"));
6983 else
6984 hrc = setError(E_UNEXPECTED,
6985 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6986 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6987 mUserData->s.strName.c_str(), vrc);
6988 }
6989 return hrc;
6990}
6991
6992HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6993{
6994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6995
6996 aDefaultFrontend = mHWData->mDefaultFrontend;
6997
6998 return S_OK;
6999}
7000
7001HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7002{
7003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7004 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7005 if (SUCCEEDED(hrc))
7006 {
7007 hrc = mHWData.backupEx();
7008 if (SUCCEEDED(hrc))
7009 {
7010 i_setModified(IsModified_MachineData);
7011 mHWData->mDefaultFrontend = aDefaultFrontend;
7012 }
7013 }
7014 return hrc;
7015}
7016
7017HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7018{
7019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7020 size_t cbIcon = mUserData->mIcon.size();
7021 aIcon.resize(cbIcon);
7022 if (cbIcon)
7023 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7024 return S_OK;
7025}
7026
7027HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7028{
7029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7030 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7031 if (SUCCEEDED(hrc))
7032 {
7033 i_setModified(IsModified_MachineData);
7034 mUserData.backup();
7035 size_t cbIcon = aIcon.size();
7036 mUserData->mIcon.resize(cbIcon);
7037 if (cbIcon)
7038 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7039 }
7040 return hrc;
7041}
7042
7043HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7044{
7045#ifdef VBOX_WITH_USB
7046 *aUSBProxyAvailable = true;
7047#else
7048 *aUSBProxyAvailable = false;
7049#endif
7050 return S_OK;
7051}
7052
7053HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7054 ComPtr<IProgress> &aProgress)
7055{
7056 ComObjPtr<Progress> pP;
7057 Progress *ppP = pP;
7058 IProgress *iP = static_cast<IProgress *>(ppP);
7059 IProgress **pProgress = &iP;
7060
7061 IMachine *pTarget = aTarget;
7062
7063 /* Convert the options. */
7064 RTCList<CloneOptions_T> optList;
7065 if (aOptions.size())
7066 for (size_t i = 0; i < aOptions.size(); ++i)
7067 optList.append(aOptions[i]);
7068
7069 if (optList.contains(CloneOptions_Link))
7070 {
7071 if (!i_isSnapshotMachine())
7072 return setError(E_INVALIDARG,
7073 tr("Linked clone can only be created from a snapshot"));
7074 if (aMode != CloneMode_MachineState)
7075 return setError(E_INVALIDARG,
7076 tr("Linked clone can only be created for a single machine state"));
7077 }
7078 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7079
7080 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7081
7082 HRESULT rc = pWorker->start(pProgress);
7083
7084 pP = static_cast<Progress *>(*pProgress);
7085 pP.queryInterfaceTo(aProgress.asOutParam());
7086
7087 return rc;
7088
7089}
7090
7091// public methods for internal purposes
7092/////////////////////////////////////////////////////////////////////////////
7093
7094/**
7095 * Adds the given IsModified_* flag to the dirty flags of the machine.
7096 * This must be called either during i_loadSettings or under the machine write lock.
7097 * @param fl
7098 */
7099void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7100{
7101 mData->flModifications |= fl;
7102 if (fAllowStateModification && i_isStateModificationAllowed())
7103 mData->mCurrentStateModified = true;
7104}
7105
7106/**
7107 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7108 * care of the write locking.
7109 *
7110 * @param fModifications The flag to add.
7111 */
7112void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7113{
7114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7115 i_setModified(fModification, fAllowStateModification);
7116}
7117
7118/**
7119 * Saves the registry entry of this machine to the given configuration node.
7120 *
7121 * @param aEntryNode Node to save the registry entry to.
7122 *
7123 * @note locks this object for reading.
7124 */
7125HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7126{
7127 AutoLimitedCaller autoCaller(this);
7128 AssertComRCReturnRC(autoCaller.rc());
7129
7130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7131
7132 data.uuid = mData->mUuid;
7133 data.strSettingsFile = mData->m_strConfigFile;
7134
7135 return S_OK;
7136}
7137
7138/**
7139 * Calculates the absolute path of the given path taking the directory of the
7140 * machine settings file as the current directory.
7141 *
7142 * @param aPath Path to calculate the absolute path for.
7143 * @param aResult Where to put the result (used only on success, can be the
7144 * same Utf8Str instance as passed in @a aPath).
7145 * @return IPRT result.
7146 *
7147 * @note Locks this object for reading.
7148 */
7149int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7150{
7151 AutoCaller autoCaller(this);
7152 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7153
7154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7155
7156 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7157
7158 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7159
7160 strSettingsDir.stripFilename();
7161 char folder[RTPATH_MAX];
7162 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7163 if (RT_SUCCESS(vrc))
7164 aResult = folder;
7165
7166 return vrc;
7167}
7168
7169/**
7170 * Copies strSource to strTarget, making it relative to the machine folder
7171 * if it is a subdirectory thereof, or simply copying it otherwise.
7172 *
7173 * @param strSource Path to evaluate and copy.
7174 * @param strTarget Buffer to receive target path.
7175 *
7176 * @note Locks this object for reading.
7177 */
7178void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7179 Utf8Str &strTarget)
7180{
7181 AutoCaller autoCaller(this);
7182 AssertComRCReturn(autoCaller.rc(), (void)0);
7183
7184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7185
7186 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7187 // use strTarget as a temporary buffer to hold the machine settings dir
7188 strTarget = mData->m_strConfigFileFull;
7189 strTarget.stripFilename();
7190 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7191 {
7192 // is relative: then append what's left
7193 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7194 // for empty paths (only possible for subdirs) use "." to avoid
7195 // triggering default settings for not present config attributes.
7196 if (strTarget.isEmpty())
7197 strTarget = ".";
7198 }
7199 else
7200 // is not relative: then overwrite
7201 strTarget = strSource;
7202}
7203
7204/**
7205 * Returns the full path to the machine's log folder in the
7206 * \a aLogFolder argument.
7207 */
7208void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7209{
7210 AutoCaller autoCaller(this);
7211 AssertComRCReturnVoid(autoCaller.rc());
7212
7213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7214
7215 char szTmp[RTPATH_MAX];
7216 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7217 if (RT_SUCCESS(vrc))
7218 {
7219 if (szTmp[0] && !mUserData.isNull())
7220 {
7221 char szTmp2[RTPATH_MAX];
7222 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7223 if (RT_SUCCESS(vrc))
7224 aLogFolder = BstrFmt("%s%c%s",
7225 szTmp2,
7226 RTPATH_DELIMITER,
7227 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7228 }
7229 else
7230 vrc = VERR_PATH_IS_RELATIVE;
7231 }
7232
7233 if (RT_FAILURE(vrc))
7234 {
7235 // fallback if VBOX_USER_LOGHOME is not set or invalid
7236 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7237 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7238 aLogFolder.append(RTPATH_DELIMITER);
7239 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7240 }
7241}
7242
7243/**
7244 * Returns the full path to the machine's log file for an given index.
7245 */
7246Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7247 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7248{
7249 Utf8Str logFolder;
7250 getLogFolder(logFolder);
7251 Assert(logFolder.length());
7252 Utf8Str log;
7253 if (idx == 0)
7254 log = Utf8StrFmt("%s%cVBox.log",
7255 logFolder.c_str(), RTPATH_DELIMITER);
7256 else
7257 log = Utf8StrFmt("%s%cVBox.log.%d",
7258 logFolder.c_str(), RTPATH_DELIMITER, idx);
7259 return log;
7260}
7261
7262/**
7263 * Returns the full path to the machine's (hardened) startup log file.
7264 */
7265Utf8Str Machine::i_getStartupLogFilename(void)
7266{
7267 Utf8Str strFilename;
7268 getLogFolder(strFilename);
7269 Assert(strFilename.length());
7270 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7271 return strFilename;
7272}
7273
7274
7275/**
7276 * Composes a unique saved state filename based on the current system time. The filename is
7277 * granular to the second so this will work so long as no more than one snapshot is taken on
7278 * a machine per second.
7279 *
7280 * Before version 4.1, we used this formula for saved state files:
7281 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7282 * which no longer works because saved state files can now be shared between the saved state of the
7283 * "saved" machine and an online snapshot, and the following would cause problems:
7284 * 1) save machine
7285 * 2) create online snapshot from that machine state --> reusing saved state file
7286 * 3) save machine again --> filename would be reused, breaking the online snapshot
7287 *
7288 * So instead we now use a timestamp.
7289 *
7290 * @param str
7291 */
7292
7293void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7294{
7295 AutoCaller autoCaller(this);
7296 AssertComRCReturnVoid(autoCaller.rc());
7297
7298 {
7299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7300 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7301 }
7302
7303 RTTIMESPEC ts;
7304 RTTimeNow(&ts);
7305 RTTIME time;
7306 RTTimeExplode(&time, &ts);
7307
7308 strStateFilePath += RTPATH_DELIMITER;
7309 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7310 time.i32Year, time.u8Month, time.u8MonthDay,
7311 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7312}
7313
7314/**
7315 * Returns the full path to the default video capture file.
7316 */
7317void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7318{
7319 AutoCaller autoCaller(this);
7320 AssertComRCReturnVoid(autoCaller.rc());
7321
7322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7323
7324 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7325 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7326 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7327}
7328
7329/**
7330 * Returns whether at least one USB controller is present for the VM.
7331 */
7332bool Machine::i_isUSBControllerPresent()
7333{
7334 AutoCaller autoCaller(this);
7335 AssertComRCReturn(autoCaller.rc(), false);
7336
7337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7338
7339 return (mUSBControllers->size() > 0);
7340}
7341
7342/**
7343 * @note Locks this object for writing, calls the client process
7344 * (inside the lock).
7345 */
7346HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7347 const Utf8Str &strFrontend,
7348 const Utf8Str &strEnvironment,
7349 ProgressProxy *aProgress)
7350{
7351 LogFlowThisFuncEnter();
7352
7353 AssertReturn(aControl, E_FAIL);
7354 AssertReturn(aProgress, E_FAIL);
7355 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7356
7357 AutoCaller autoCaller(this);
7358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7359
7360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7361
7362 if (!mData->mRegistered)
7363 return setError(E_UNEXPECTED,
7364 tr("The machine '%s' is not registered"),
7365 mUserData->s.strName.c_str());
7366
7367 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7368
7369 if ( mData->mSession.mState == SessionState_Locked
7370 || mData->mSession.mState == SessionState_Spawning
7371 || mData->mSession.mState == SessionState_Unlocking)
7372 return setError(VBOX_E_INVALID_OBJECT_STATE,
7373 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7374 mUserData->s.strName.c_str());
7375
7376 /* may not be busy */
7377 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7378
7379 /* get the path to the executable */
7380 char szPath[RTPATH_MAX];
7381 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7382 size_t cchBufLeft = strlen(szPath);
7383 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7384 szPath[cchBufLeft] = 0;
7385 char *pszNamePart = szPath + cchBufLeft;
7386 cchBufLeft = sizeof(szPath) - cchBufLeft;
7387
7388 int vrc = VINF_SUCCESS;
7389 RTPROCESS pid = NIL_RTPROCESS;
7390
7391 RTENV env = RTENV_DEFAULT;
7392
7393 if (!strEnvironment.isEmpty())
7394 {
7395 char *newEnvStr = NULL;
7396
7397 do
7398 {
7399 /* clone the current environment */
7400 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7401 AssertRCBreakStmt(vrc2, vrc = vrc2);
7402
7403 newEnvStr = RTStrDup(strEnvironment.c_str());
7404 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7405
7406 /* put new variables to the environment
7407 * (ignore empty variable names here since RTEnv API
7408 * intentionally doesn't do that) */
7409 char *var = newEnvStr;
7410 for (char *p = newEnvStr; *p; ++p)
7411 {
7412 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7413 {
7414 *p = '\0';
7415 if (*var)
7416 {
7417 char *val = strchr(var, '=');
7418 if (val)
7419 {
7420 *val++ = '\0';
7421 vrc2 = RTEnvSetEx(env, var, val);
7422 }
7423 else
7424 vrc2 = RTEnvUnsetEx(env, var);
7425 if (RT_FAILURE(vrc2))
7426 break;
7427 }
7428 var = p + 1;
7429 }
7430 }
7431 if (RT_SUCCESS(vrc2) && *var)
7432 vrc2 = RTEnvPutEx(env, var);
7433
7434 AssertRCBreakStmt(vrc2, vrc = vrc2);
7435 }
7436 while (0);
7437
7438 if (newEnvStr != NULL)
7439 RTStrFree(newEnvStr);
7440 }
7441
7442 /* Hardened startup logging */
7443#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7444 Utf8Str strSupStartLogArg("--sup-startup-log=");
7445 {
7446 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7447 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7448 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7449 {
7450 Utf8Str strStartupLogDir = strStartupLogFile;
7451 strStartupLogDir.stripFilename();
7452 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7453 file without stripping the file. */
7454 }
7455 strSupStartLogArg.append(strStartupLogFile);
7456 }
7457 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7458#else
7459 const char *pszSupStartupLogArg = NULL;
7460#endif
7461
7462
7463#ifdef VBOX_WITH_QTGUI
7464 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7465 {
7466# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7467 /* Modify the base path so that we don't need to use ".." below. */
7468 RTPathStripTrailingSlash(szPath);
7469 RTPathStripFilename(szPath);
7470 cchBufLeft = strlen(szPath);
7471 pszNamePart = szPath + cchBufLeft;
7472 cchBufLeft = sizeof(szPath) - cchBufLeft;
7473
7474# define OSX_APP_NAME "VirtualBoxVM"
7475# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7476
7477 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7478 if ( strAppOverride.contains(".")
7479 || strAppOverride.contains("/")
7480 || strAppOverride.contains("\\")
7481 || strAppOverride.contains(":"))
7482 strAppOverride.setNull();
7483 Utf8Str strAppPath;
7484 if (!strAppOverride.isEmpty())
7485 {
7486 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7487 Utf8Str strFullPath(szPath);
7488 strFullPath.append(strAppPath);
7489 /* there is a race, but people using this deserve the failure */
7490 if (!RTFileExists(strFullPath.c_str()))
7491 strAppOverride.setNull();
7492 }
7493 if (strAppOverride.isEmpty())
7494 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7495 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7496 strcpy(pszNamePart, strAppPath.c_str());
7497# else
7498 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7499 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7500 strcpy(pszNamePart, s_szVirtualBox_exe);
7501# endif
7502
7503 Utf8Str idStr = mData->mUuid.toString();
7504 const char *apszArgs[] =
7505 {
7506 szPath,
7507 "--comment", mUserData->s.strName.c_str(),
7508 "--startvm", idStr.c_str(),
7509 "--no-startvm-errormsgbox",
7510 pszSupStartupLogArg,
7511 NULL
7512 };
7513 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7514 }
7515#else /* !VBOX_WITH_QTGUI */
7516 if (0)
7517 ;
7518#endif /* VBOX_WITH_QTGUI */
7519
7520 else
7521
7522#ifdef VBOX_WITH_VBOXSDL
7523 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7524 {
7525 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7526 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7527 strcpy(pszNamePart, s_szVBoxSDL_exe);
7528
7529 Utf8Str idStr = mData->mUuid.toString();
7530 const char *apszArgs[] =
7531 {
7532 szPath,
7533 "--comment", mUserData->s.strName.c_str(),
7534 "--startvm", idStr.c_str(),
7535 pszSupStartupLogArg,
7536 NULL
7537 };
7538 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7539 }
7540#else /* !VBOX_WITH_VBOXSDL */
7541 if (0)
7542 ;
7543#endif /* !VBOX_WITH_VBOXSDL */
7544
7545 else
7546
7547#ifdef VBOX_WITH_HEADLESS
7548 if ( strFrontend == "headless"
7549 || strFrontend == "capture"
7550 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7551 )
7552 {
7553 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7554 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7555 * and a VM works even if the server has not been installed.
7556 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7557 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7558 * differently in 4.0 and 3.x.
7559 */
7560 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7561 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7562 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7563
7564 Utf8Str idStr = mData->mUuid.toString();
7565 const char *apszArgs[] =
7566 {
7567 szPath,
7568 "--comment", mUserData->s.strName.c_str(),
7569 "--startvm", idStr.c_str(),
7570 "--vrde", "config",
7571 0, /* For "--capture". */
7572 0, /* For "--sup-startup-log". */
7573 0
7574 };
7575 unsigned iArg = 7;
7576 if (strFrontend == "capture")
7577 apszArgs[iArg++] = "--capture";
7578 apszArgs[iArg++] = pszSupStartupLogArg;
7579
7580# ifdef RT_OS_WINDOWS
7581 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7582# else
7583 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7584# endif
7585 }
7586#else /* !VBOX_WITH_HEADLESS */
7587 if (0)
7588 ;
7589#endif /* !VBOX_WITH_HEADLESS */
7590 else
7591 {
7592 RTEnvDestroy(env);
7593 return setError(E_INVALIDARG,
7594 tr("Invalid frontend name: '%s'"),
7595 strFrontend.c_str());
7596 }
7597
7598 RTEnvDestroy(env);
7599
7600 if (RT_FAILURE(vrc))
7601 return setError(VBOX_E_IPRT_ERROR,
7602 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7603 mUserData->s.strName.c_str(), vrc);
7604
7605 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7606
7607 /*
7608 * Note that we don't release the lock here before calling the client,
7609 * because it doesn't need to call us back if called with a NULL argument.
7610 * Releasing the lock here is dangerous because we didn't prepare the
7611 * launch data yet, but the client we've just started may happen to be
7612 * too fast and call LockMachine() that will fail (because of PID, etc.),
7613 * so that the Machine will never get out of the Spawning session state.
7614 */
7615
7616 /* inform the session that it will be a remote one */
7617 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7618#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7619 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7620#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7621 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7622#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7623 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7624
7625 if (FAILED(rc))
7626 {
7627 /* restore the session state */
7628 mData->mSession.mState = SessionState_Unlocked;
7629 alock.release();
7630 mParent->i_addProcessToReap(pid);
7631 /* The failure may occur w/o any error info (from RPC), so provide one */
7632 return setError(VBOX_E_VM_ERROR,
7633 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7634 }
7635
7636 /* attach launch data to the machine */
7637 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7638 mData->mSession.mRemoteControls.push_back(aControl);
7639 mData->mSession.mProgress = aProgress;
7640 mData->mSession.mPID = pid;
7641 mData->mSession.mState = SessionState_Spawning;
7642 mData->mSession.mType = strFrontend;
7643
7644 alock.release();
7645 mParent->i_addProcessToReap(pid);
7646
7647 LogFlowThisFuncLeave();
7648 return S_OK;
7649}
7650
7651/**
7652 * Returns @c true if the given session machine instance has an open direct
7653 * session (and optionally also for direct sessions which are closing) and
7654 * returns the session control machine instance if so.
7655 *
7656 * Note that when the method returns @c false, the arguments remain unchanged.
7657 *
7658 * @param aMachine Session machine object.
7659 * @param aControl Direct session control object (optional).
7660 * @param aAllowClosing If true then additionally a session which is currently
7661 * being closed will also be allowed.
7662 *
7663 * @note locks this object for reading.
7664 */
7665bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7666 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7667 bool aAllowClosing /*= false*/)
7668{
7669 AutoLimitedCaller autoCaller(this);
7670 AssertComRCReturn(autoCaller.rc(), false);
7671
7672 /* just return false for inaccessible machines */
7673 if (getObjectState().getState() != ObjectState::Ready)
7674 return false;
7675
7676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7677
7678 if ( mData->mSession.mState == SessionState_Locked
7679 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7680 )
7681 {
7682 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7683
7684 aMachine = mData->mSession.mMachine;
7685
7686 if (aControl != NULL)
7687 *aControl = mData->mSession.mDirectControl;
7688
7689 return true;
7690 }
7691
7692 return false;
7693}
7694
7695/**
7696 * Returns @c true if the given machine has an spawning direct session.
7697 *
7698 * @note locks this object for reading.
7699 */
7700bool Machine::i_isSessionSpawning()
7701{
7702 AutoLimitedCaller autoCaller(this);
7703 AssertComRCReturn(autoCaller.rc(), false);
7704
7705 /* just return false for inaccessible machines */
7706 if (getObjectState().getState() != ObjectState::Ready)
7707 return false;
7708
7709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7710
7711 if (mData->mSession.mState == SessionState_Spawning)
7712 return true;
7713
7714 return false;
7715}
7716
7717/**
7718 * Called from the client watcher thread to check for unexpected client process
7719 * death during Session_Spawning state (e.g. before it successfully opened a
7720 * direct session).
7721 *
7722 * On Win32 and on OS/2, this method is called only when we've got the
7723 * direct client's process termination notification, so it always returns @c
7724 * true.
7725 *
7726 * On other platforms, this method returns @c true if the client process is
7727 * terminated and @c false if it's still alive.
7728 *
7729 * @note Locks this object for writing.
7730 */
7731bool Machine::i_checkForSpawnFailure()
7732{
7733 AutoCaller autoCaller(this);
7734 if (!autoCaller.isOk())
7735 {
7736 /* nothing to do */
7737 LogFlowThisFunc(("Already uninitialized!\n"));
7738 return true;
7739 }
7740
7741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7742
7743 if (mData->mSession.mState != SessionState_Spawning)
7744 {
7745 /* nothing to do */
7746 LogFlowThisFunc(("Not spawning any more!\n"));
7747 return true;
7748 }
7749
7750 HRESULT rc = S_OK;
7751
7752 /* PID not yet initialized, skip check. */
7753 if (mData->mSession.mPID == NIL_RTPROCESS)
7754 return false;
7755
7756 RTPROCSTATUS status;
7757 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7758
7759 if (vrc != VERR_PROCESS_RUNNING)
7760 {
7761 Utf8Str strExtraInfo;
7762
7763#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7764 /* If the startup logfile exists and is of non-zero length, tell the
7765 user to look there for more details to encourage them to attach it
7766 when reporting startup issues. */
7767 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7768 uint64_t cbStartupLogFile = 0;
7769 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7770 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7771 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7772#endif
7773
7774 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7775 rc = setError(E_FAIL,
7776 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7777 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7778 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7779 rc = setError(E_FAIL,
7780 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7781 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7782 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7783 rc = setError(E_FAIL,
7784 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7785 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7786 else
7787 rc = setError(E_FAIL,
7788 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7789 i_getName().c_str(), vrc, strExtraInfo.c_str());
7790 }
7791
7792 if (FAILED(rc))
7793 {
7794 /* Close the remote session, remove the remote control from the list
7795 * and reset session state to Closed (@note keep the code in sync with
7796 * the relevant part in LockMachine()). */
7797
7798 Assert(mData->mSession.mRemoteControls.size() == 1);
7799 if (mData->mSession.mRemoteControls.size() == 1)
7800 {
7801 ErrorInfoKeeper eik;
7802 mData->mSession.mRemoteControls.front()->Uninitialize();
7803 }
7804
7805 mData->mSession.mRemoteControls.clear();
7806 mData->mSession.mState = SessionState_Unlocked;
7807
7808 /* finalize the progress after setting the state */
7809 if (!mData->mSession.mProgress.isNull())
7810 {
7811 mData->mSession.mProgress->notifyComplete(rc);
7812 mData->mSession.mProgress.setNull();
7813 }
7814
7815 mData->mSession.mPID = NIL_RTPROCESS;
7816
7817 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7818 return true;
7819 }
7820
7821 return false;
7822}
7823
7824/**
7825 * Checks whether the machine can be registered. If so, commits and saves
7826 * all settings.
7827 *
7828 * @note Must be called from mParent's write lock. Locks this object and
7829 * children for writing.
7830 */
7831HRESULT Machine::i_prepareRegister()
7832{
7833 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7834
7835 AutoLimitedCaller autoCaller(this);
7836 AssertComRCReturnRC(autoCaller.rc());
7837
7838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7839
7840 /* wait for state dependents to drop to zero */
7841 i_ensureNoStateDependencies();
7842
7843 if (!mData->mAccessible)
7844 return setError(VBOX_E_INVALID_OBJECT_STATE,
7845 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7846 mUserData->s.strName.c_str(),
7847 mData->mUuid.toString().c_str());
7848
7849 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7850
7851 if (mData->mRegistered)
7852 return setError(VBOX_E_INVALID_OBJECT_STATE,
7853 tr("The machine '%s' with UUID {%s} is already registered"),
7854 mUserData->s.strName.c_str(),
7855 mData->mUuid.toString().c_str());
7856
7857 HRESULT rc = S_OK;
7858
7859 // Ensure the settings are saved. If we are going to be registered and
7860 // no config file exists yet, create it by calling i_saveSettings() too.
7861 if ( (mData->flModifications)
7862 || (!mData->pMachineConfigFile->fileExists())
7863 )
7864 {
7865 rc = i_saveSettings(NULL);
7866 // no need to check whether VirtualBox.xml needs saving too since
7867 // we can't have a machine XML file rename pending
7868 if (FAILED(rc)) return rc;
7869 }
7870
7871 /* more config checking goes here */
7872
7873 if (SUCCEEDED(rc))
7874 {
7875 /* we may have had implicit modifications we want to fix on success */
7876 i_commit();
7877
7878 mData->mRegistered = true;
7879 }
7880 else
7881 {
7882 /* we may have had implicit modifications we want to cancel on failure*/
7883 i_rollback(false /* aNotify */);
7884 }
7885
7886 return rc;
7887}
7888
7889/**
7890 * Increases the number of objects dependent on the machine state or on the
7891 * registered state. Guarantees that these two states will not change at least
7892 * until #releaseStateDependency() is called.
7893 *
7894 * Depending on the @a aDepType value, additional state checks may be made.
7895 * These checks will set extended error info on failure. See
7896 * #checkStateDependency() for more info.
7897 *
7898 * If this method returns a failure, the dependency is not added and the caller
7899 * is not allowed to rely on any particular machine state or registration state
7900 * value and may return the failed result code to the upper level.
7901 *
7902 * @param aDepType Dependency type to add.
7903 * @param aState Current machine state (NULL if not interested).
7904 * @param aRegistered Current registered state (NULL if not interested).
7905 *
7906 * @note Locks this object for writing.
7907 */
7908HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7909 MachineState_T *aState /* = NULL */,
7910 BOOL *aRegistered /* = NULL */)
7911{
7912 AutoCaller autoCaller(this);
7913 AssertComRCReturnRC(autoCaller.rc());
7914
7915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7916
7917 HRESULT rc = i_checkStateDependency(aDepType);
7918 if (FAILED(rc)) return rc;
7919
7920 {
7921 if (mData->mMachineStateChangePending != 0)
7922 {
7923 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7924 * drop to zero so don't add more. It may make sense to wait a bit
7925 * and retry before reporting an error (since the pending state
7926 * transition should be really quick) but let's just assert for
7927 * now to see if it ever happens on practice. */
7928
7929 AssertFailed();
7930
7931 return setError(E_ACCESSDENIED,
7932 tr("Machine state change is in progress. Please retry the operation later."));
7933 }
7934
7935 ++mData->mMachineStateDeps;
7936 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7937 }
7938
7939 if (aState)
7940 *aState = mData->mMachineState;
7941 if (aRegistered)
7942 *aRegistered = mData->mRegistered;
7943
7944 return S_OK;
7945}
7946
7947/**
7948 * Decreases the number of objects dependent on the machine state.
7949 * Must always complete the #addStateDependency() call after the state
7950 * dependency is no more necessary.
7951 */
7952void Machine::i_releaseStateDependency()
7953{
7954 AutoCaller autoCaller(this);
7955 AssertComRCReturnVoid(autoCaller.rc());
7956
7957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7958
7959 /* releaseStateDependency() w/o addStateDependency()? */
7960 AssertReturnVoid(mData->mMachineStateDeps != 0);
7961 -- mData->mMachineStateDeps;
7962
7963 if (mData->mMachineStateDeps == 0)
7964 {
7965 /* inform i_ensureNoStateDependencies() that there are no more deps */
7966 if (mData->mMachineStateChangePending != 0)
7967 {
7968 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7969 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7970 }
7971 }
7972}
7973
7974Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7975{
7976 /* start with nothing found */
7977 Utf8Str strResult("");
7978
7979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7980
7981 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7982 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7983 // found:
7984 strResult = it->second; // source is a Utf8Str
7985
7986 return strResult;
7987}
7988
7989// protected methods
7990/////////////////////////////////////////////////////////////////////////////
7991
7992/**
7993 * Performs machine state checks based on the @a aDepType value. If a check
7994 * fails, this method will set extended error info, otherwise it will return
7995 * S_OK. It is supposed, that on failure, the caller will immediately return
7996 * the return value of this method to the upper level.
7997 *
7998 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7999 *
8000 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8001 * current state of this machine object allows to change settings of the
8002 * machine (i.e. the machine is not registered, or registered but not running
8003 * and not saved). It is useful to call this method from Machine setters
8004 * before performing any change.
8005 *
8006 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8007 * as for MutableStateDep except that if the machine is saved, S_OK is also
8008 * returned. This is useful in setters which allow changing machine
8009 * properties when it is in the saved state.
8010 *
8011 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8012 * if the current state of this machine object allows to change runtime
8013 * changeable settings of the machine (i.e. the machine is not registered, or
8014 * registered but either running or not running and not saved). It is useful
8015 * to call this method from Machine setters before performing any changes to
8016 * runtime changeable settings.
8017 *
8018 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8019 * the same as for MutableOrRunningStateDep except that if the machine is
8020 * saved, S_OK is also returned. This is useful in setters which allow
8021 * changing runtime and saved state changeable machine properties.
8022 *
8023 * @param aDepType Dependency type to check.
8024 *
8025 * @note Non Machine based classes should use #addStateDependency() and
8026 * #releaseStateDependency() methods or the smart AutoStateDependency
8027 * template.
8028 *
8029 * @note This method must be called from under this object's read or write
8030 * lock.
8031 */
8032HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8033{
8034 switch (aDepType)
8035 {
8036 case AnyStateDep:
8037 {
8038 break;
8039 }
8040 case MutableStateDep:
8041 {
8042 if ( mData->mRegistered
8043 && ( !i_isSessionMachine()
8044 || ( mData->mMachineState != MachineState_Aborted
8045 && mData->mMachineState != MachineState_Teleported
8046 && mData->mMachineState != MachineState_PoweredOff
8047 )
8048 )
8049 )
8050 return setError(VBOX_E_INVALID_VM_STATE,
8051 tr("The machine is not mutable (state is %s)"),
8052 Global::stringifyMachineState(mData->mMachineState));
8053 break;
8054 }
8055 case MutableOrSavedStateDep:
8056 {
8057 if ( mData->mRegistered
8058 && ( !i_isSessionMachine()
8059 || ( mData->mMachineState != MachineState_Aborted
8060 && mData->mMachineState != MachineState_Teleported
8061 && mData->mMachineState != MachineState_Saved
8062 && mData->mMachineState != MachineState_PoweredOff
8063 )
8064 )
8065 )
8066 return setError(VBOX_E_INVALID_VM_STATE,
8067 tr("The machine is not mutable (state is %s)"),
8068 Global::stringifyMachineState(mData->mMachineState));
8069 break;
8070 }
8071 case MutableOrRunningStateDep:
8072 {
8073 if ( mData->mRegistered
8074 && ( !i_isSessionMachine()
8075 || ( mData->mMachineState != MachineState_Aborted
8076 && mData->mMachineState != MachineState_Teleported
8077 && mData->mMachineState != MachineState_PoweredOff
8078 && !Global::IsOnline(mData->mMachineState)
8079 )
8080 )
8081 )
8082 return setError(VBOX_E_INVALID_VM_STATE,
8083 tr("The machine is not mutable (state is %s)"),
8084 Global::stringifyMachineState(mData->mMachineState));
8085 break;
8086 }
8087 case MutableOrSavedOrRunningStateDep:
8088 {
8089 if ( mData->mRegistered
8090 && ( !i_isSessionMachine()
8091 || ( mData->mMachineState != MachineState_Aborted
8092 && mData->mMachineState != MachineState_Teleported
8093 && mData->mMachineState != MachineState_Saved
8094 && mData->mMachineState != MachineState_PoweredOff
8095 && !Global::IsOnline(mData->mMachineState)
8096 )
8097 )
8098 )
8099 return setError(VBOX_E_INVALID_VM_STATE,
8100 tr("The machine is not mutable (state is %s)"),
8101 Global::stringifyMachineState(mData->mMachineState));
8102 break;
8103 }
8104 }
8105
8106 return S_OK;
8107}
8108
8109/**
8110 * Helper to initialize all associated child objects and allocate data
8111 * structures.
8112 *
8113 * This method must be called as a part of the object's initialization procedure
8114 * (usually done in the #init() method).
8115 *
8116 * @note Must be called only from #init() or from #registeredInit().
8117 */
8118HRESULT Machine::initDataAndChildObjects()
8119{
8120 AutoCaller autoCaller(this);
8121 AssertComRCReturnRC(autoCaller.rc());
8122 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8123 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8124
8125 AssertReturn(!mData->mAccessible, E_FAIL);
8126
8127 /* allocate data structures */
8128 mSSData.allocate();
8129 mUserData.allocate();
8130 mHWData.allocate();
8131 mMediaData.allocate();
8132 mStorageControllers.allocate();
8133 mUSBControllers.allocate();
8134
8135 /* initialize mOSTypeId */
8136 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8137
8138 /* create associated BIOS settings object */
8139 unconst(mBIOSSettings).createObject();
8140 mBIOSSettings->init(this);
8141
8142 /* create an associated VRDE object (default is disabled) */
8143 unconst(mVRDEServer).createObject();
8144 mVRDEServer->init(this);
8145
8146 /* create associated serial port objects */
8147 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8148 {
8149 unconst(mSerialPorts[slot]).createObject();
8150 mSerialPorts[slot]->init(this, slot);
8151 }
8152
8153 /* create associated parallel port objects */
8154 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8155 {
8156 unconst(mParallelPorts[slot]).createObject();
8157 mParallelPorts[slot]->init(this, slot);
8158 }
8159
8160 /* create the audio adapter object (always present, default is disabled) */
8161 unconst(mAudioAdapter).createObject();
8162 mAudioAdapter->init(this);
8163
8164 /* create the USB device filters object (always present) */
8165 unconst(mUSBDeviceFilters).createObject();
8166 mUSBDeviceFilters->init(this);
8167
8168 /* create associated network adapter objects */
8169 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8170 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8171 {
8172 unconst(mNetworkAdapters[slot]).createObject();
8173 mNetworkAdapters[slot]->init(this, slot);
8174 }
8175
8176 /* create the bandwidth control */
8177 unconst(mBandwidthControl).createObject();
8178 mBandwidthControl->init(this);
8179
8180 return S_OK;
8181}
8182
8183/**
8184 * Helper to uninitialize all associated child objects and to free all data
8185 * structures.
8186 *
8187 * This method must be called as a part of the object's uninitialization
8188 * procedure (usually done in the #uninit() method).
8189 *
8190 * @note Must be called only from #uninit() or from #registeredInit().
8191 */
8192void Machine::uninitDataAndChildObjects()
8193{
8194 AutoCaller autoCaller(this);
8195 AssertComRCReturnVoid(autoCaller.rc());
8196 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8197 || getObjectState().getState() == ObjectState::Limited);
8198
8199 /* tell all our other child objects we've been uninitialized */
8200 if (mBandwidthControl)
8201 {
8202 mBandwidthControl->uninit();
8203 unconst(mBandwidthControl).setNull();
8204 }
8205
8206 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8207 {
8208 if (mNetworkAdapters[slot])
8209 {
8210 mNetworkAdapters[slot]->uninit();
8211 unconst(mNetworkAdapters[slot]).setNull();
8212 }
8213 }
8214
8215 if (mUSBDeviceFilters)
8216 {
8217 mUSBDeviceFilters->uninit();
8218 unconst(mUSBDeviceFilters).setNull();
8219 }
8220
8221 if (mAudioAdapter)
8222 {
8223 mAudioAdapter->uninit();
8224 unconst(mAudioAdapter).setNull();
8225 }
8226
8227 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8228 {
8229 if (mParallelPorts[slot])
8230 {
8231 mParallelPorts[slot]->uninit();
8232 unconst(mParallelPorts[slot]).setNull();
8233 }
8234 }
8235
8236 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8237 {
8238 if (mSerialPorts[slot])
8239 {
8240 mSerialPorts[slot]->uninit();
8241 unconst(mSerialPorts[slot]).setNull();
8242 }
8243 }
8244
8245 if (mVRDEServer)
8246 {
8247 mVRDEServer->uninit();
8248 unconst(mVRDEServer).setNull();
8249 }
8250
8251 if (mBIOSSettings)
8252 {
8253 mBIOSSettings->uninit();
8254 unconst(mBIOSSettings).setNull();
8255 }
8256
8257 /* Deassociate media (only when a real Machine or a SnapshotMachine
8258 * instance is uninitialized; SessionMachine instances refer to real
8259 * Machine media). This is necessary for a clean re-initialization of
8260 * the VM after successfully re-checking the accessibility state. Note
8261 * that in case of normal Machine or SnapshotMachine uninitialization (as
8262 * a result of unregistering or deleting the snapshot), outdated media
8263 * attachments will already be uninitialized and deleted, so this
8264 * code will not affect them. */
8265 if ( !!mMediaData
8266 && (!i_isSessionMachine())
8267 )
8268 {
8269 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8270 it != mMediaData->mAttachments.end();
8271 ++it)
8272 {
8273 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8274 if (pMedium.isNull())
8275 continue;
8276 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8277 AssertComRC(rc);
8278 }
8279 }
8280
8281 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8282 {
8283 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8284 if (mData->mFirstSnapshot)
8285 {
8286 // snapshots tree is protected by machine write lock; strictly
8287 // this isn't necessary here since we're deleting the entire
8288 // machine, but otherwise we assert in Snapshot::uninit()
8289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8290 mData->mFirstSnapshot->uninit();
8291 mData->mFirstSnapshot.setNull();
8292 }
8293
8294 mData->mCurrentSnapshot.setNull();
8295 }
8296
8297 /* free data structures (the essential mData structure is not freed here
8298 * since it may be still in use) */
8299 mMediaData.free();
8300 mStorageControllers.free();
8301 mUSBControllers.free();
8302 mHWData.free();
8303 mUserData.free();
8304 mSSData.free();
8305}
8306
8307/**
8308 * Returns a pointer to the Machine object for this machine that acts like a
8309 * parent for complex machine data objects such as shared folders, etc.
8310 *
8311 * For primary Machine objects and for SnapshotMachine objects, returns this
8312 * object's pointer itself. For SessionMachine objects, returns the peer
8313 * (primary) machine pointer.
8314 */
8315Machine* Machine::i_getMachine()
8316{
8317 if (i_isSessionMachine())
8318 return (Machine*)mPeer;
8319 return this;
8320}
8321
8322/**
8323 * Makes sure that there are no machine state dependents. If necessary, waits
8324 * for the number of dependents to drop to zero.
8325 *
8326 * Make sure this method is called from under this object's write lock to
8327 * guarantee that no new dependents may be added when this method returns
8328 * control to the caller.
8329 *
8330 * @note Locks this object for writing. The lock will be released while waiting
8331 * (if necessary).
8332 *
8333 * @warning To be used only in methods that change the machine state!
8334 */
8335void Machine::i_ensureNoStateDependencies()
8336{
8337 AssertReturnVoid(isWriteLockOnCurrentThread());
8338
8339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8340
8341 /* Wait for all state dependents if necessary */
8342 if (mData->mMachineStateDeps != 0)
8343 {
8344 /* lazy semaphore creation */
8345 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8346 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8347
8348 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8349 mData->mMachineStateDeps));
8350
8351 ++mData->mMachineStateChangePending;
8352
8353 /* reset the semaphore before waiting, the last dependent will signal
8354 * it */
8355 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8356
8357 alock.release();
8358
8359 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8360
8361 alock.acquire();
8362
8363 -- mData->mMachineStateChangePending;
8364 }
8365}
8366
8367/**
8368 * Changes the machine state and informs callbacks.
8369 *
8370 * This method is not intended to fail so it either returns S_OK or asserts (and
8371 * returns a failure).
8372 *
8373 * @note Locks this object for writing.
8374 */
8375HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8376{
8377 LogFlowThisFuncEnter();
8378 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8379
8380 AutoCaller autoCaller(this);
8381 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8382
8383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8384
8385 /* wait for state dependents to drop to zero */
8386 i_ensureNoStateDependencies();
8387
8388 MachineState_T const enmOldState = mData->mMachineState;
8389 if (enmOldState != aMachineState)
8390 {
8391 mData->mMachineState = aMachineState;
8392 RTTimeNow(&mData->mLastStateChange);
8393
8394#ifdef VBOX_WITH_DTRACE_R3_MAIN
8395 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8396#endif
8397 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8398 }
8399
8400 LogFlowThisFuncLeave();
8401 return S_OK;
8402}
8403
8404/**
8405 * Searches for a shared folder with the given logical name
8406 * in the collection of shared folders.
8407 *
8408 * @param aName logical name of the shared folder
8409 * @param aSharedFolder where to return the found object
8410 * @param aSetError whether to set the error info if the folder is
8411 * not found
8412 * @return
8413 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8414 *
8415 * @note
8416 * must be called from under the object's lock!
8417 */
8418HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8419 ComObjPtr<SharedFolder> &aSharedFolder,
8420 bool aSetError /* = false */)
8421{
8422 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8423 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8424 it != mHWData->mSharedFolders.end();
8425 ++it)
8426 {
8427 SharedFolder *pSF = *it;
8428 AutoCaller autoCaller(pSF);
8429 if (pSF->i_getName() == aName)
8430 {
8431 aSharedFolder = pSF;
8432 rc = S_OK;
8433 break;
8434 }
8435 }
8436
8437 if (aSetError && FAILED(rc))
8438 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8439
8440 return rc;
8441}
8442
8443/**
8444 * Initializes all machine instance data from the given settings structures
8445 * from XML. The exception is the machine UUID which needs special handling
8446 * depending on the caller's use case, so the caller needs to set that herself.
8447 *
8448 * This gets called in several contexts during machine initialization:
8449 *
8450 * -- When machine XML exists on disk already and needs to be loaded into memory,
8451 * for example, from registeredInit() to load all registered machines on
8452 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8453 * attached to the machine should be part of some media registry already.
8454 *
8455 * -- During OVF import, when a machine config has been constructed from an
8456 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8457 * ensure that the media listed as attachments in the config (which have
8458 * been imported from the OVF) receive the correct registry ID.
8459 *
8460 * -- During VM cloning.
8461 *
8462 * @param config Machine settings from XML.
8463 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8464 * for each attached medium in the config.
8465 * @return
8466 */
8467HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8468 const Guid *puuidRegistry)
8469{
8470 // copy name, description, OS type, teleporter, UTC etc.
8471 mUserData->s = config.machineUserData;
8472
8473 // Decode the Icon overide data from config userdata and set onto Machine.
8474 #define DECODE_STR_MAX _1M
8475 const char* pszStr = config.machineUserData.ovIcon.c_str();
8476 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8477 if (cbOut > DECODE_STR_MAX)
8478 return setError(E_FAIL,
8479 tr("Icon Data too long.'%d' > '%d'"),
8480 cbOut,
8481 DECODE_STR_MAX);
8482 mUserData->mIcon.resize(cbOut);
8483 int vrc = VINF_SUCCESS;
8484 if (cbOut)
8485 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8486 if (RT_FAILURE(vrc))
8487 {
8488 mUserData->mIcon.resize(0);
8489 return setError(E_FAIL,
8490 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8491 pszStr,
8492 vrc);
8493 }
8494
8495 // look up the object by Id to check it is valid
8496 ComPtr<IGuestOSType> guestOSType;
8497 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8498 guestOSType.asOutParam());
8499 if (FAILED(rc)) return rc;
8500
8501 // stateFile (optional)
8502 if (config.strStateFile.isEmpty())
8503 mSSData->strStateFilePath.setNull();
8504 else
8505 {
8506 Utf8Str stateFilePathFull(config.strStateFile);
8507 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8508 if (RT_FAILURE(vrc))
8509 return setError(E_FAIL,
8510 tr("Invalid saved state file path '%s' (%Rrc)"),
8511 config.strStateFile.c_str(),
8512 vrc);
8513 mSSData->strStateFilePath = stateFilePathFull;
8514 }
8515
8516 // snapshot folder needs special processing so set it again
8517 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8518 if (FAILED(rc)) return rc;
8519
8520 /* Copy the extra data items (Not in any case config is already the same as
8521 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8522 * make sure the extra data map is copied). */
8523 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8524
8525 /* currentStateModified (optional, default is true) */
8526 mData->mCurrentStateModified = config.fCurrentStateModified;
8527
8528 mData->mLastStateChange = config.timeLastStateChange;
8529
8530 /*
8531 * note: all mUserData members must be assigned prior this point because
8532 * we need to commit changes in order to let mUserData be shared by all
8533 * snapshot machine instances.
8534 */
8535 mUserData.commitCopy();
8536
8537 // machine registry, if present (must be loaded before snapshots)
8538 if (config.canHaveOwnMediaRegistry())
8539 {
8540 // determine machine folder
8541 Utf8Str strMachineFolder = i_getSettingsFileFull();
8542 strMachineFolder.stripFilename();
8543 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8544 config.mediaRegistry,
8545 strMachineFolder);
8546 if (FAILED(rc)) return rc;
8547 }
8548
8549 /* Snapshot node (optional) */
8550 size_t cRootSnapshots;
8551 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8552 {
8553 // there must be only one root snapshot
8554 Assert(cRootSnapshots == 1);
8555
8556 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8557
8558 rc = i_loadSnapshot(snap,
8559 config.uuidCurrentSnapshot,
8560 NULL); // no parent == first snapshot
8561 if (FAILED(rc)) return rc;
8562 }
8563
8564 // hardware data
8565 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8566 if (FAILED(rc)) return rc;
8567
8568 // load storage controllers
8569 rc = i_loadStorageControllers(config.storageMachine,
8570 puuidRegistry,
8571 NULL /* puuidSnapshot */);
8572 if (FAILED(rc)) return rc;
8573
8574 /*
8575 * NOTE: the assignment below must be the last thing to do,
8576 * otherwise it will be not possible to change the settings
8577 * somewhere in the code above because all setters will be
8578 * blocked by i_checkStateDependency(MutableStateDep).
8579 */
8580
8581 /* set the machine state to Aborted or Saved when appropriate */
8582 if (config.fAborted)
8583 {
8584 mSSData->strStateFilePath.setNull();
8585
8586 /* no need to use i_setMachineState() during init() */
8587 mData->mMachineState = MachineState_Aborted;
8588 }
8589 else if (!mSSData->strStateFilePath.isEmpty())
8590 {
8591 /* no need to use i_setMachineState() during init() */
8592 mData->mMachineState = MachineState_Saved;
8593 }
8594
8595 // after loading settings, we are no longer different from the XML on disk
8596 mData->flModifications = 0;
8597
8598 return S_OK;
8599}
8600
8601/**
8602 * Recursively loads all snapshots starting from the given.
8603 *
8604 * @param aNode <Snapshot> node.
8605 * @param aCurSnapshotId Current snapshot ID from the settings file.
8606 * @param aParentSnapshot Parent snapshot.
8607 */
8608HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8609 const Guid &aCurSnapshotId,
8610 Snapshot *aParentSnapshot)
8611{
8612 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8613 AssertReturn(!i_isSessionMachine(), E_FAIL);
8614
8615 HRESULT rc = S_OK;
8616
8617 Utf8Str strStateFile;
8618 if (!data.strStateFile.isEmpty())
8619 {
8620 /* optional */
8621 strStateFile = data.strStateFile;
8622 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8623 if (RT_FAILURE(vrc))
8624 return setError(E_FAIL,
8625 tr("Invalid saved state file path '%s' (%Rrc)"),
8626 strStateFile.c_str(),
8627 vrc);
8628 }
8629
8630 /* create a snapshot machine object */
8631 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8632 pSnapshotMachine.createObject();
8633 rc = pSnapshotMachine->initFromSettings(this,
8634 data.hardware,
8635 &data.debugging,
8636 &data.autostart,
8637 data.storage,
8638 data.uuid.ref(),
8639 strStateFile);
8640 if (FAILED(rc)) return rc;
8641
8642 /* create a snapshot object */
8643 ComObjPtr<Snapshot> pSnapshot;
8644 pSnapshot.createObject();
8645 /* initialize the snapshot */
8646 rc = pSnapshot->init(mParent, // VirtualBox object
8647 data.uuid,
8648 data.strName,
8649 data.strDescription,
8650 data.timestamp,
8651 pSnapshotMachine,
8652 aParentSnapshot);
8653 if (FAILED(rc)) return rc;
8654
8655 /* memorize the first snapshot if necessary */
8656 if (!mData->mFirstSnapshot)
8657 mData->mFirstSnapshot = pSnapshot;
8658
8659 /* memorize the current snapshot when appropriate */
8660 if ( !mData->mCurrentSnapshot
8661 && pSnapshot->i_getId() == aCurSnapshotId
8662 )
8663 mData->mCurrentSnapshot = pSnapshot;
8664
8665 // now create the children
8666 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8667 it != data.llChildSnapshots.end();
8668 ++it)
8669 {
8670 const settings::Snapshot &childData = *it;
8671 // recurse
8672 rc = i_loadSnapshot(childData,
8673 aCurSnapshotId,
8674 pSnapshot); // parent = the one we created above
8675 if (FAILED(rc)) return rc;
8676 }
8677
8678 return rc;
8679}
8680
8681/**
8682 * Loads settings into mHWData.
8683 *
8684 * @param data Reference to the hardware settings.
8685 * @param pDbg Pointer to the debugging settings.
8686 * @param pAutostart Pointer to the autostart settings.
8687 */
8688HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8689 const settings::Autostart *pAutostart)
8690{
8691 AssertReturn(!i_isSessionMachine(), E_FAIL);
8692
8693 HRESULT rc = S_OK;
8694
8695 try
8696 {
8697 /* The hardware version attribute (optional). */
8698 mHWData->mHWVersion = data.strVersion;
8699 mHWData->mHardwareUUID = data.uuid;
8700
8701 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8702 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8703 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8704 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8705 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8706 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8707 mHWData->mPAEEnabled = data.fPAE;
8708 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8709 mHWData->mLongMode = data.enmLongMode;
8710 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8711 mHWData->mCPUCount = data.cCPUs;
8712 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8713 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8714
8715 // cpu
8716 if (mHWData->mCPUHotPlugEnabled)
8717 {
8718 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8719 it != data.llCpus.end();
8720 ++it)
8721 {
8722 const settings::Cpu &cpu = *it;
8723
8724 mHWData->mCPUAttached[cpu.ulId] = true;
8725 }
8726 }
8727
8728 // cpuid leafs
8729 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8730 it != data.llCpuIdLeafs.end();
8731 ++it)
8732 {
8733 const settings::CpuIdLeaf &leaf = *it;
8734
8735 switch (leaf.ulId)
8736 {
8737 case 0x0:
8738 case 0x1:
8739 case 0x2:
8740 case 0x3:
8741 case 0x4:
8742 case 0x5:
8743 case 0x6:
8744 case 0x7:
8745 case 0x8:
8746 case 0x9:
8747 case 0xA:
8748 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8749 break;
8750
8751 case 0x80000000:
8752 case 0x80000001:
8753 case 0x80000002:
8754 case 0x80000003:
8755 case 0x80000004:
8756 case 0x80000005:
8757 case 0x80000006:
8758 case 0x80000007:
8759 case 0x80000008:
8760 case 0x80000009:
8761 case 0x8000000A:
8762 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8763 break;
8764
8765 default:
8766 /* just ignore */
8767 break;
8768 }
8769 }
8770
8771 mHWData->mMemorySize = data.ulMemorySizeMB;
8772 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8773
8774 // boot order
8775 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8776 {
8777 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8778 if (it == data.mapBootOrder.end())
8779 mHWData->mBootOrder[i] = DeviceType_Null;
8780 else
8781 mHWData->mBootOrder[i] = it->second;
8782 }
8783
8784 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8785 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8786 mHWData->mMonitorCount = data.cMonitors;
8787 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8788 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8789 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8790 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8791 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8792 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8793 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8794 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8795 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8796 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8797 if (!data.strVideoCaptureFile.isEmpty())
8798 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8799 else
8800 mHWData->mVideoCaptureFile.setNull();
8801 mHWData->mFirmwareType = data.firmwareType;
8802 mHWData->mPointingHIDType = data.pointingHIDType;
8803 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8804 mHWData->mChipsetType = data.chipsetType;
8805 mHWData->mParavirtProvider = data.paravirtProvider;
8806 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8807 mHWData->mHPETEnabled = data.fHPETEnabled;
8808
8809 /* VRDEServer */
8810 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8811 if (FAILED(rc)) return rc;
8812
8813 /* BIOS */
8814 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8815 if (FAILED(rc)) return rc;
8816
8817 // Bandwidth control (must come before network adapters)
8818 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8819 if (FAILED(rc)) return rc;
8820
8821 /* Shared folders */
8822 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8823 it != data.usbSettings.llUSBControllers.end();
8824 ++it)
8825 {
8826 const settings::USBController &settingsCtrl = *it;
8827 ComObjPtr<USBController> newCtrl;
8828
8829 newCtrl.createObject();
8830 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8831 mUSBControllers->push_back(newCtrl);
8832 }
8833
8834 /* USB device filters */
8835 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8836 if (FAILED(rc)) return rc;
8837
8838 // network adapters
8839 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8840 size_t oldCount = mNetworkAdapters.size();
8841 if (newCount > oldCount)
8842 {
8843 mNetworkAdapters.resize(newCount);
8844 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8845 {
8846 unconst(mNetworkAdapters[slot]).createObject();
8847 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8848 }
8849 }
8850 else if (newCount < oldCount)
8851 mNetworkAdapters.resize(newCount);
8852 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8853 it != data.llNetworkAdapters.end();
8854 ++it)
8855 {
8856 const settings::NetworkAdapter &nic = *it;
8857
8858 /* slot unicity is guaranteed by XML Schema */
8859 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8860 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8861 if (FAILED(rc)) return rc;
8862 }
8863
8864 // serial ports
8865 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8866 it != data.llSerialPorts.end();
8867 ++it)
8868 {
8869 const settings::SerialPort &s = *it;
8870
8871 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8872 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8873 if (FAILED(rc)) return rc;
8874 }
8875
8876 // parallel ports (optional)
8877 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8878 it != data.llParallelPorts.end();
8879 ++it)
8880 {
8881 const settings::ParallelPort &p = *it;
8882
8883 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8884 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8885 if (FAILED(rc)) return rc;
8886 }
8887
8888 /* AudioAdapter */
8889 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8890 if (FAILED(rc)) return rc;
8891
8892 /* Shared folders */
8893 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8894 it != data.llSharedFolders.end();
8895 ++it)
8896 {
8897 const settings::SharedFolder &sf = *it;
8898
8899 ComObjPtr<SharedFolder> sharedFolder;
8900 /* Check for double entries. Not allowed! */
8901 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8902 if (SUCCEEDED(rc))
8903 return setError(VBOX_E_OBJECT_IN_USE,
8904 tr("Shared folder named '%s' already exists"),
8905 sf.strName.c_str());
8906
8907 /* Create the new shared folder. Don't break on error. This will be
8908 * reported when the machine starts. */
8909 sharedFolder.createObject();
8910 rc = sharedFolder->init(i_getMachine(),
8911 sf.strName,
8912 sf.strHostPath,
8913 RT_BOOL(sf.fWritable),
8914 RT_BOOL(sf.fAutoMount),
8915 false /* fFailOnError */);
8916 if (FAILED(rc)) return rc;
8917 mHWData->mSharedFolders.push_back(sharedFolder);
8918 }
8919
8920 // Clipboard
8921 mHWData->mClipboardMode = data.clipboardMode;
8922
8923 // drag'n'drop
8924 mHWData->mDnDMode = data.dndMode;
8925
8926 // guest settings
8927 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8928
8929 // IO settings
8930 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8931 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8932
8933 // Host PCI devices
8934 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8935 it != data.pciAttachments.end();
8936 ++it)
8937 {
8938 const settings::HostPCIDeviceAttachment &hpda = *it;
8939 ComObjPtr<PCIDeviceAttachment> pda;
8940
8941 pda.createObject();
8942 pda->i_loadSettings(this, hpda);
8943 mHWData->mPCIDeviceAssignments.push_back(pda);
8944 }
8945
8946 /*
8947 * (The following isn't really real hardware, but it lives in HWData
8948 * for reasons of convenience.)
8949 */
8950
8951#ifdef VBOX_WITH_GUEST_PROPS
8952 /* Guest properties (optional) */
8953 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8954 it != data.llGuestProperties.end();
8955 ++it)
8956 {
8957 const settings::GuestProperty &prop = *it;
8958 uint32_t fFlags = guestProp::NILFLAG;
8959 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8960 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8961 mHWData->mGuestProperties[prop.strName] = property;
8962 }
8963
8964 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8965#endif /* VBOX_WITH_GUEST_PROPS defined */
8966
8967 rc = i_loadDebugging(pDbg);
8968 if (FAILED(rc))
8969 return rc;
8970
8971 mHWData->mAutostart = *pAutostart;
8972
8973 /* default frontend */
8974 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8975 }
8976 catch(std::bad_alloc &)
8977 {
8978 return E_OUTOFMEMORY;
8979 }
8980
8981 AssertComRC(rc);
8982 return rc;
8983}
8984
8985/**
8986 * Called from Machine::loadHardware() to load the debugging settings of the
8987 * machine.
8988 *
8989 * @param pDbg Pointer to the settings.
8990 */
8991HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8992{
8993 mHWData->mDebugging = *pDbg;
8994 /* no more processing currently required, this will probably change. */
8995 return S_OK;
8996}
8997
8998/**
8999 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9000 *
9001 * @param data
9002 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9003 * @param puuidSnapshot
9004 * @return
9005 */
9006HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9007 const Guid *puuidRegistry,
9008 const Guid *puuidSnapshot)
9009{
9010 AssertReturn(!i_isSessionMachine(), E_FAIL);
9011
9012 HRESULT rc = S_OK;
9013
9014 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9015 it != data.llStorageControllers.end();
9016 ++it)
9017 {
9018 const settings::StorageController &ctlData = *it;
9019
9020 ComObjPtr<StorageController> pCtl;
9021 /* Try to find one with the name first. */
9022 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9023 if (SUCCEEDED(rc))
9024 return setError(VBOX_E_OBJECT_IN_USE,
9025 tr("Storage controller named '%s' already exists"),
9026 ctlData.strName.c_str());
9027
9028 pCtl.createObject();
9029 rc = pCtl->init(this,
9030 ctlData.strName,
9031 ctlData.storageBus,
9032 ctlData.ulInstance,
9033 ctlData.fBootable);
9034 if (FAILED(rc)) return rc;
9035
9036 mStorageControllers->push_back(pCtl);
9037
9038 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9039 if (FAILED(rc)) return rc;
9040
9041 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9042 if (FAILED(rc)) return rc;
9043
9044 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9045 if (FAILED(rc)) return rc;
9046
9047 /* Set IDE emulation settings (only for AHCI controller). */
9048 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9049 {
9050 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9051 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9052 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9053 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9054 )
9055 return rc;
9056 }
9057
9058 /* Load the attached devices now. */
9059 rc = i_loadStorageDevices(pCtl,
9060 ctlData,
9061 puuidRegistry,
9062 puuidSnapshot);
9063 if (FAILED(rc)) return rc;
9064 }
9065
9066 return S_OK;
9067}
9068
9069/**
9070 * Called from i_loadStorageControllers for a controller's devices.
9071 *
9072 * @param aStorageController
9073 * @param data
9074 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9075 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9076 * @return
9077 */
9078HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9079 const settings::StorageController &data,
9080 const Guid *puuidRegistry,
9081 const Guid *puuidSnapshot)
9082{
9083 HRESULT rc = S_OK;
9084
9085 /* paranoia: detect duplicate attachments */
9086 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9087 it != data.llAttachedDevices.end();
9088 ++it)
9089 {
9090 const settings::AttachedDevice &ad = *it;
9091
9092 for (settings::AttachedDevicesList::const_iterator it2 = it;
9093 it2 != data.llAttachedDevices.end();
9094 ++it2)
9095 {
9096 if (it == it2)
9097 continue;
9098
9099 const settings::AttachedDevice &ad2 = *it2;
9100
9101 if ( ad.lPort == ad2.lPort
9102 && ad.lDevice == ad2.lDevice)
9103 {
9104 return setError(E_FAIL,
9105 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9106 aStorageController->i_getName().c_str(),
9107 ad.lPort,
9108 ad.lDevice,
9109 mUserData->s.strName.c_str());
9110 }
9111 }
9112 }
9113
9114 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9115 it != data.llAttachedDevices.end();
9116 ++it)
9117 {
9118 const settings::AttachedDevice &dev = *it;
9119 ComObjPtr<Medium> medium;
9120
9121 switch (dev.deviceType)
9122 {
9123 case DeviceType_Floppy:
9124 case DeviceType_DVD:
9125 if (dev.strHostDriveSrc.isNotEmpty())
9126 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9127 false /* fRefresh */, medium);
9128 else
9129 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9130 dev.uuid,
9131 false /* fRefresh */,
9132 false /* aSetError */,
9133 medium);
9134 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9135 // This is not an error. The host drive or UUID might have vanished, so just go
9136 // ahead without this removeable medium attachment
9137 rc = S_OK;
9138 break;
9139
9140 case DeviceType_HardDisk:
9141 {
9142 /* find a hard disk by UUID */
9143 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9144 if (FAILED(rc))
9145 {
9146 if (i_isSnapshotMachine())
9147 {
9148 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9149 // so the user knows that the bad disk is in a snapshot somewhere
9150 com::ErrorInfo info;
9151 return setError(E_FAIL,
9152 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9153 puuidSnapshot->raw(),
9154 info.getText().raw());
9155 }
9156 else
9157 return rc;
9158 }
9159
9160 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9161
9162 if (medium->i_getType() == MediumType_Immutable)
9163 {
9164 if (i_isSnapshotMachine())
9165 return setError(E_FAIL,
9166 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9167 "of the virtual machine '%s' ('%s')"),
9168 medium->i_getLocationFull().c_str(),
9169 dev.uuid.raw(),
9170 puuidSnapshot->raw(),
9171 mUserData->s.strName.c_str(),
9172 mData->m_strConfigFileFull.c_str());
9173
9174 return setError(E_FAIL,
9175 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9176 medium->i_getLocationFull().c_str(),
9177 dev.uuid.raw(),
9178 mUserData->s.strName.c_str(),
9179 mData->m_strConfigFileFull.c_str());
9180 }
9181
9182 if (medium->i_getType() == MediumType_MultiAttach)
9183 {
9184 if (i_isSnapshotMachine())
9185 return setError(E_FAIL,
9186 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9187 "of the virtual machine '%s' ('%s')"),
9188 medium->i_getLocationFull().c_str(),
9189 dev.uuid.raw(),
9190 puuidSnapshot->raw(),
9191 mUserData->s.strName.c_str(),
9192 mData->m_strConfigFileFull.c_str());
9193
9194 return setError(E_FAIL,
9195 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9196 medium->i_getLocationFull().c_str(),
9197 dev.uuid.raw(),
9198 mUserData->s.strName.c_str(),
9199 mData->m_strConfigFileFull.c_str());
9200 }
9201
9202 if ( !i_isSnapshotMachine()
9203 && medium->i_getChildren().size() != 0
9204 )
9205 return setError(E_FAIL,
9206 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9207 "because it has %d differencing child hard disks"),
9208 medium->i_getLocationFull().c_str(),
9209 dev.uuid.raw(),
9210 mUserData->s.strName.c_str(),
9211 mData->m_strConfigFileFull.c_str(),
9212 medium->i_getChildren().size());
9213
9214 if (i_findAttachment(mMediaData->mAttachments,
9215 medium))
9216 return setError(E_FAIL,
9217 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9218 medium->i_getLocationFull().c_str(),
9219 dev.uuid.raw(),
9220 mUserData->s.strName.c_str(),
9221 mData->m_strConfigFileFull.c_str());
9222
9223 break;
9224 }
9225
9226 default:
9227 return setError(E_FAIL,
9228 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9229 medium->i_getLocationFull().c_str(),
9230 mUserData->s.strName.c_str(),
9231 mData->m_strConfigFileFull.c_str());
9232 }
9233
9234 if (FAILED(rc))
9235 break;
9236
9237 /* Bandwidth groups are loaded at this point. */
9238 ComObjPtr<BandwidthGroup> pBwGroup;
9239
9240 if (!dev.strBwGroup.isEmpty())
9241 {
9242 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9243 if (FAILED(rc))
9244 return setError(E_FAIL,
9245 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9246 medium->i_getLocationFull().c_str(),
9247 dev.strBwGroup.c_str(),
9248 mUserData->s.strName.c_str(),
9249 mData->m_strConfigFileFull.c_str());
9250 pBwGroup->i_reference();
9251 }
9252
9253 const Bstr controllerName = aStorageController->i_getName();
9254 ComObjPtr<MediumAttachment> pAttachment;
9255 pAttachment.createObject();
9256 rc = pAttachment->init(this,
9257 medium,
9258 controllerName,
9259 dev.lPort,
9260 dev.lDevice,
9261 dev.deviceType,
9262 false,
9263 dev.fPassThrough,
9264 dev.fTempEject,
9265 dev.fNonRotational,
9266 dev.fDiscard,
9267 dev.fHotPluggable,
9268 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9269 if (FAILED(rc)) break;
9270
9271 /* associate the medium with this machine and snapshot */
9272 if (!medium.isNull())
9273 {
9274 AutoCaller medCaller(medium);
9275 if (FAILED(medCaller.rc())) return medCaller.rc();
9276 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9277
9278 if (i_isSnapshotMachine())
9279 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9280 else
9281 rc = medium->i_addBackReference(mData->mUuid);
9282 /* If the medium->addBackReference fails it sets an appropriate
9283 * error message, so no need to do any guesswork here. */
9284
9285 if (puuidRegistry)
9286 // caller wants registry ID to be set on all attached media (OVF import case)
9287 medium->i_addRegistry(*puuidRegistry);
9288 }
9289
9290 if (FAILED(rc))
9291 break;
9292
9293 /* back up mMediaData to let registeredInit() properly rollback on failure
9294 * (= limited accessibility) */
9295 i_setModified(IsModified_Storage);
9296 mMediaData.backup();
9297 mMediaData->mAttachments.push_back(pAttachment);
9298 }
9299
9300 return rc;
9301}
9302
9303/**
9304 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9305 *
9306 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9307 * @param aSnapshot where to return the found snapshot
9308 * @param aSetError true to set extended error info on failure
9309 */
9310HRESULT Machine::i_findSnapshotById(const Guid &aId,
9311 ComObjPtr<Snapshot> &aSnapshot,
9312 bool aSetError /* = false */)
9313{
9314 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9315
9316 if (!mData->mFirstSnapshot)
9317 {
9318 if (aSetError)
9319 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9320 return E_FAIL;
9321 }
9322
9323 if (aId.isZero())
9324 aSnapshot = mData->mFirstSnapshot;
9325 else
9326 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9327
9328 if (!aSnapshot)
9329 {
9330 if (aSetError)
9331 return setError(E_FAIL,
9332 tr("Could not find a snapshot with UUID {%s}"),
9333 aId.toString().c_str());
9334 return E_FAIL;
9335 }
9336
9337 return S_OK;
9338}
9339
9340/**
9341 * Returns the snapshot with the given name or fails of no such snapshot.
9342 *
9343 * @param aName snapshot name to find
9344 * @param aSnapshot where to return the found snapshot
9345 * @param aSetError true to set extended error info on failure
9346 */
9347HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9348 ComObjPtr<Snapshot> &aSnapshot,
9349 bool aSetError /* = false */)
9350{
9351 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9352
9353 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9354
9355 if (!mData->mFirstSnapshot)
9356 {
9357 if (aSetError)
9358 return setError(VBOX_E_OBJECT_NOT_FOUND,
9359 tr("This machine does not have any snapshots"));
9360 return VBOX_E_OBJECT_NOT_FOUND;
9361 }
9362
9363 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9364
9365 if (!aSnapshot)
9366 {
9367 if (aSetError)
9368 return setError(VBOX_E_OBJECT_NOT_FOUND,
9369 tr("Could not find a snapshot named '%s'"), strName.c_str());
9370 return VBOX_E_OBJECT_NOT_FOUND;
9371 }
9372
9373 return S_OK;
9374}
9375
9376/**
9377 * Returns a storage controller object with the given name.
9378 *
9379 * @param aName storage controller name to find
9380 * @param aStorageController where to return the found storage controller
9381 * @param aSetError true to set extended error info on failure
9382 */
9383HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9384 ComObjPtr<StorageController> &aStorageController,
9385 bool aSetError /* = false */)
9386{
9387 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9388
9389 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9390 it != mStorageControllers->end();
9391 ++it)
9392 {
9393 if ((*it)->i_getName() == aName)
9394 {
9395 aStorageController = (*it);
9396 return S_OK;
9397 }
9398 }
9399
9400 if (aSetError)
9401 return setError(VBOX_E_OBJECT_NOT_FOUND,
9402 tr("Could not find a storage controller named '%s'"),
9403 aName.c_str());
9404 return VBOX_E_OBJECT_NOT_FOUND;
9405}
9406
9407/**
9408 * Returns a USB controller object with the given name.
9409 *
9410 * @param aName USB controller name to find
9411 * @param aUSBController where to return the found USB controller
9412 * @param aSetError true to set extended error info on failure
9413 */
9414HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9415 ComObjPtr<USBController> &aUSBController,
9416 bool aSetError /* = false */)
9417{
9418 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9419
9420 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9421 it != mUSBControllers->end();
9422 ++it)
9423 {
9424 if ((*it)->i_getName() == aName)
9425 {
9426 aUSBController = (*it);
9427 return S_OK;
9428 }
9429 }
9430
9431 if (aSetError)
9432 return setError(VBOX_E_OBJECT_NOT_FOUND,
9433 tr("Could not find a storage controller named '%s'"),
9434 aName.c_str());
9435 return VBOX_E_OBJECT_NOT_FOUND;
9436}
9437
9438/**
9439 * Returns the number of USB controller instance of the given type.
9440 *
9441 * @param enmType USB controller type.
9442 */
9443ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9444{
9445 ULONG cCtrls = 0;
9446
9447 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9448 it != mUSBControllers->end();
9449 ++it)
9450 {
9451 if ((*it)->i_getControllerType() == enmType)
9452 cCtrls++;
9453 }
9454
9455 return cCtrls;
9456}
9457
9458HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9459 MediaData::AttachmentList &atts)
9460{
9461 AutoCaller autoCaller(this);
9462 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9463
9464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9465
9466 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9467 it != mMediaData->mAttachments.end();
9468 ++it)
9469 {
9470 const ComObjPtr<MediumAttachment> &pAtt = *it;
9471 // should never happen, but deal with NULL pointers in the list.
9472 AssertStmt(!pAtt.isNull(), continue);
9473
9474 // getControllerName() needs caller+read lock
9475 AutoCaller autoAttCaller(pAtt);
9476 if (FAILED(autoAttCaller.rc()))
9477 {
9478 atts.clear();
9479 return autoAttCaller.rc();
9480 }
9481 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9482
9483 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9484 atts.push_back(pAtt);
9485 }
9486
9487 return S_OK;
9488}
9489
9490
9491/**
9492 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9493 * file if the machine name was changed and about creating a new settings file
9494 * if this is a new machine.
9495 *
9496 * @note Must be never called directly but only from #saveSettings().
9497 */
9498HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9499{
9500 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9501
9502 HRESULT rc = S_OK;
9503
9504 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9505
9506 /// @todo need to handle primary group change, too
9507
9508 /* attempt to rename the settings file if machine name is changed */
9509 if ( mUserData->s.fNameSync
9510 && mUserData.isBackedUp()
9511 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9512 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9513 )
9514 {
9515 bool dirRenamed = false;
9516 bool fileRenamed = false;
9517
9518 Utf8Str configFile, newConfigFile;
9519 Utf8Str configFilePrev, newConfigFilePrev;
9520 Utf8Str configDir, newConfigDir;
9521
9522 do
9523 {
9524 int vrc = VINF_SUCCESS;
9525
9526 Utf8Str name = mUserData.backedUpData()->s.strName;
9527 Utf8Str newName = mUserData->s.strName;
9528 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9529 if (group == "/")
9530 group.setNull();
9531 Utf8Str newGroup = mUserData->s.llGroups.front();
9532 if (newGroup == "/")
9533 newGroup.setNull();
9534
9535 configFile = mData->m_strConfigFileFull;
9536
9537 /* first, rename the directory if it matches the group and machine name */
9538 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9539 group.c_str(), RTPATH_DELIMITER, name.c_str());
9540 /** @todo hack, make somehow use of ComposeMachineFilename */
9541 if (mUserData->s.fDirectoryIncludesUUID)
9542 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9543 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9544 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9545 /** @todo hack, make somehow use of ComposeMachineFilename */
9546 if (mUserData->s.fDirectoryIncludesUUID)
9547 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9548 configDir = configFile;
9549 configDir.stripFilename();
9550 newConfigDir = configDir;
9551 if ( configDir.length() >= groupPlusName.length()
9552 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9553 groupPlusName.c_str()))
9554 {
9555 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9556 Utf8Str newConfigBaseDir(newConfigDir);
9557 newConfigDir.append(newGroupPlusName);
9558 /* consistency: use \ if appropriate on the platform */
9559 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9560 /* new dir and old dir cannot be equal here because of 'if'
9561 * above and because name != newName */
9562 Assert(configDir != newConfigDir);
9563 if (!fSettingsFileIsNew)
9564 {
9565 /* perform real rename only if the machine is not new */
9566 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9567 if ( vrc == VERR_FILE_NOT_FOUND
9568 || vrc == VERR_PATH_NOT_FOUND)
9569 {
9570 /* create the parent directory, then retry renaming */
9571 Utf8Str parent(newConfigDir);
9572 parent.stripFilename();
9573 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9574 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9575 }
9576 if (RT_FAILURE(vrc))
9577 {
9578 rc = setError(E_FAIL,
9579 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9580 configDir.c_str(),
9581 newConfigDir.c_str(),
9582 vrc);
9583 break;
9584 }
9585 /* delete subdirectories which are no longer needed */
9586 Utf8Str dir(configDir);
9587 dir.stripFilename();
9588 while (dir != newConfigBaseDir && dir != ".")
9589 {
9590 vrc = RTDirRemove(dir.c_str());
9591 if (RT_FAILURE(vrc))
9592 break;
9593 dir.stripFilename();
9594 }
9595 dirRenamed = true;
9596 }
9597 }
9598
9599 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9600 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9601
9602 /* then try to rename the settings file itself */
9603 if (newConfigFile != configFile)
9604 {
9605 /* get the path to old settings file in renamed directory */
9606 configFile = Utf8StrFmt("%s%c%s",
9607 newConfigDir.c_str(),
9608 RTPATH_DELIMITER,
9609 RTPathFilename(configFile.c_str()));
9610 if (!fSettingsFileIsNew)
9611 {
9612 /* perform real rename only if the machine is not new */
9613 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9614 if (RT_FAILURE(vrc))
9615 {
9616 rc = setError(E_FAIL,
9617 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9618 configFile.c_str(),
9619 newConfigFile.c_str(),
9620 vrc);
9621 break;
9622 }
9623 fileRenamed = true;
9624 configFilePrev = configFile;
9625 configFilePrev += "-prev";
9626 newConfigFilePrev = newConfigFile;
9627 newConfigFilePrev += "-prev";
9628 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9629 }
9630 }
9631
9632 // update m_strConfigFileFull amd mConfigFile
9633 mData->m_strConfigFileFull = newConfigFile;
9634 // compute the relative path too
9635 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9636
9637 // store the old and new so that VirtualBox::i_saveSettings() can update
9638 // the media registry
9639 if ( mData->mRegistered
9640 && (configDir != newConfigDir || configFile != newConfigFile))
9641 {
9642 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9643
9644 if (pfNeedsGlobalSaveSettings)
9645 *pfNeedsGlobalSaveSettings = true;
9646 }
9647
9648 // in the saved state file path, replace the old directory with the new directory
9649 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9650 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9651
9652 // and do the same thing for the saved state file paths of all the online snapshots
9653 if (mData->mFirstSnapshot)
9654 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9655 newConfigDir.c_str());
9656 }
9657 while (0);
9658
9659 if (FAILED(rc))
9660 {
9661 /* silently try to rename everything back */
9662 if (fileRenamed)
9663 {
9664 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9665 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9666 }
9667 if (dirRenamed)
9668 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9669 }
9670
9671 if (FAILED(rc)) return rc;
9672 }
9673
9674 if (fSettingsFileIsNew)
9675 {
9676 /* create a virgin config file */
9677 int vrc = VINF_SUCCESS;
9678
9679 /* ensure the settings directory exists */
9680 Utf8Str path(mData->m_strConfigFileFull);
9681 path.stripFilename();
9682 if (!RTDirExists(path.c_str()))
9683 {
9684 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9685 if (RT_FAILURE(vrc))
9686 {
9687 return setError(E_FAIL,
9688 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9689 path.c_str(),
9690 vrc);
9691 }
9692 }
9693
9694 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9695 path = Utf8Str(mData->m_strConfigFileFull);
9696 RTFILE f = NIL_RTFILE;
9697 vrc = RTFileOpen(&f, path.c_str(),
9698 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9699 if (RT_FAILURE(vrc))
9700 return setError(E_FAIL,
9701 tr("Could not create the settings file '%s' (%Rrc)"),
9702 path.c_str(),
9703 vrc);
9704 RTFileClose(f);
9705 }
9706
9707 return rc;
9708}
9709
9710/**
9711 * Saves and commits machine data, user data and hardware data.
9712 *
9713 * Note that on failure, the data remains uncommitted.
9714 *
9715 * @a aFlags may combine the following flags:
9716 *
9717 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9718 * Used when saving settings after an operation that makes them 100%
9719 * correspond to the settings from the current snapshot.
9720 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9721 * #isReallyModified() returns false. This is necessary for cases when we
9722 * change machine data directly, not through the backup()/commit() mechanism.
9723 * - SaveS_Force: settings will be saved without doing a deep compare of the
9724 * settings structures. This is used when this is called because snapshots
9725 * have changed to avoid the overhead of the deep compare.
9726 *
9727 * @note Must be called from under this object's write lock. Locks children for
9728 * writing.
9729 *
9730 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9731 * initialized to false and that will be set to true by this function if
9732 * the caller must invoke VirtualBox::i_saveSettings() because the global
9733 * settings have changed. This will happen if a machine rename has been
9734 * saved and the global machine and media registries will therefore need
9735 * updating.
9736 */
9737HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9738 int aFlags /*= 0*/)
9739{
9740 LogFlowThisFuncEnter();
9741
9742 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9743
9744 /* make sure child objects are unable to modify the settings while we are
9745 * saving them */
9746 i_ensureNoStateDependencies();
9747
9748 AssertReturn(!i_isSnapshotMachine(),
9749 E_FAIL);
9750
9751 HRESULT rc = S_OK;
9752 bool fNeedsWrite = false;
9753
9754 /* First, prepare to save settings. It will care about renaming the
9755 * settings directory and file if the machine name was changed and about
9756 * creating a new settings file if this is a new machine. */
9757 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9758 if (FAILED(rc)) return rc;
9759
9760 // keep a pointer to the current settings structures
9761 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9762 settings::MachineConfigFile *pNewConfig = NULL;
9763
9764 try
9765 {
9766 // make a fresh one to have everyone write stuff into
9767 pNewConfig = new settings::MachineConfigFile(NULL);
9768 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9769
9770 // now go and copy all the settings data from COM to the settings structures
9771 // (this calles i_saveSettings() on all the COM objects in the machine)
9772 i_copyMachineDataToSettings(*pNewConfig);
9773
9774 if (aFlags & SaveS_ResetCurStateModified)
9775 {
9776 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9777 mData->mCurrentStateModified = FALSE;
9778 fNeedsWrite = true; // always, no need to compare
9779 }
9780 else if (aFlags & SaveS_Force)
9781 {
9782 fNeedsWrite = true; // always, no need to compare
9783 }
9784 else
9785 {
9786 if (!mData->mCurrentStateModified)
9787 {
9788 // do a deep compare of the settings that we just saved with the settings
9789 // previously stored in the config file; this invokes MachineConfigFile::operator==
9790 // which does a deep compare of all the settings, which is expensive but less expensive
9791 // than writing out XML in vain
9792 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9793
9794 // could still be modified if any settings changed
9795 mData->mCurrentStateModified = fAnySettingsChanged;
9796
9797 fNeedsWrite = fAnySettingsChanged;
9798 }
9799 else
9800 fNeedsWrite = true;
9801 }
9802
9803 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9804
9805 if (fNeedsWrite)
9806 // now spit it all out!
9807 pNewConfig->write(mData->m_strConfigFileFull);
9808
9809 mData->pMachineConfigFile = pNewConfig;
9810 delete pOldConfig;
9811 i_commit();
9812
9813 // after saving settings, we are no longer different from the XML on disk
9814 mData->flModifications = 0;
9815 }
9816 catch (HRESULT err)
9817 {
9818 // we assume that error info is set by the thrower
9819 rc = err;
9820
9821 // restore old config
9822 delete pNewConfig;
9823 mData->pMachineConfigFile = pOldConfig;
9824 }
9825 catch (...)
9826 {
9827 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9828 }
9829
9830 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9831 {
9832 /* Fire the data change event, even on failure (since we've already
9833 * committed all data). This is done only for SessionMachines because
9834 * mutable Machine instances are always not registered (i.e. private
9835 * to the client process that creates them) and thus don't need to
9836 * inform callbacks. */
9837 if (i_isSessionMachine())
9838 mParent->i_onMachineDataChange(mData->mUuid);
9839 }
9840
9841 LogFlowThisFunc(("rc=%08X\n", rc));
9842 LogFlowThisFuncLeave();
9843 return rc;
9844}
9845
9846/**
9847 * Implementation for saving the machine settings into the given
9848 * settings::MachineConfigFile instance. This copies machine extradata
9849 * from the previous machine config file in the instance data, if any.
9850 *
9851 * This gets called from two locations:
9852 *
9853 * -- Machine::i_saveSettings(), during the regular XML writing;
9854 *
9855 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9856 * exported to OVF and we write the VirtualBox proprietary XML
9857 * into a <vbox:Machine> tag.
9858 *
9859 * This routine fills all the fields in there, including snapshots, *except*
9860 * for the following:
9861 *
9862 * -- fCurrentStateModified. There is some special logic associated with that.
9863 *
9864 * The caller can then call MachineConfigFile::write() or do something else
9865 * with it.
9866 *
9867 * Caller must hold the machine lock!
9868 *
9869 * This throws XML errors and HRESULT, so the caller must have a catch block!
9870 */
9871void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9872{
9873 // deep copy extradata
9874 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9875
9876 config.uuid = mData->mUuid;
9877
9878 // copy name, description, OS type, teleport, UTC etc.
9879 config.machineUserData = mUserData->s;
9880
9881 // Encode the Icon Override data from Machine and store on config userdata.
9882 std::vector<BYTE> iconByte;
9883 getIcon(iconByte);
9884 ssize_t cbData = iconByte.size();
9885 if (cbData > 0)
9886 {
9887 ssize_t cchOut = RTBase64EncodedLength(cbData);
9888 Utf8Str strIconData;
9889 strIconData.reserve(cchOut+1);
9890 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9891 strIconData.mutableRaw(), strIconData.capacity(),
9892 NULL);
9893 if (RT_FAILURE(vrc))
9894 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9895 strIconData.jolt();
9896 config.machineUserData.ovIcon = strIconData;
9897 }
9898 else
9899 config.machineUserData.ovIcon.setNull();
9900
9901 if ( mData->mMachineState == MachineState_Saved
9902 || mData->mMachineState == MachineState_Restoring
9903 // when deleting a snapshot we may or may not have a saved state in the current state,
9904 // so let's not assert here please
9905 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9906 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9907 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9908 && (!mSSData->strStateFilePath.isEmpty())
9909 )
9910 )
9911 {
9912 Assert(!mSSData->strStateFilePath.isEmpty());
9913 /* try to make the file name relative to the settings file dir */
9914 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9915 }
9916 else
9917 {
9918 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9919 config.strStateFile.setNull();
9920 }
9921
9922 if (mData->mCurrentSnapshot)
9923 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9924 else
9925 config.uuidCurrentSnapshot.clear();
9926
9927 config.timeLastStateChange = mData->mLastStateChange;
9928 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9929 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9930
9931 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9932 if (FAILED(rc)) throw rc;
9933
9934 rc = i_saveStorageControllers(config.storageMachine);
9935 if (FAILED(rc)) throw rc;
9936
9937 // save machine's media registry if this is VirtualBox 4.0 or later
9938 if (config.canHaveOwnMediaRegistry())
9939 {
9940 // determine machine folder
9941 Utf8Str strMachineFolder = i_getSettingsFileFull();
9942 strMachineFolder.stripFilename();
9943 mParent->i_saveMediaRegistry(config.mediaRegistry,
9944 i_getId(), // only media with registry ID == machine UUID
9945 strMachineFolder);
9946 // this throws HRESULT
9947 }
9948
9949 // save snapshots
9950 rc = i_saveAllSnapshots(config);
9951 if (FAILED(rc)) throw rc;
9952}
9953
9954/**
9955 * Saves all snapshots of the machine into the given machine config file. Called
9956 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9957 * @param config
9958 * @return
9959 */
9960HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9961{
9962 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9963
9964 HRESULT rc = S_OK;
9965
9966 try
9967 {
9968 config.llFirstSnapshot.clear();
9969
9970 if (mData->mFirstSnapshot)
9971 {
9972 // the settings use a list for "the first snapshot"
9973 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
9974
9975 // get reference to the snapshot on the list and work on that
9976 // element straight in the list to avoid excessive copying later
9977 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9978 if (FAILED(rc)) throw rc;
9979 }
9980
9981// if (mType == IsSessionMachine)
9982// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9983
9984 }
9985 catch (HRESULT err)
9986 {
9987 /* we assume that error info is set by the thrower */
9988 rc = err;
9989 }
9990 catch (...)
9991 {
9992 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9993 }
9994
9995 return rc;
9996}
9997
9998/**
9999 * Saves the VM hardware configuration. It is assumed that the
10000 * given node is empty.
10001 *
10002 * @param data Reference to the settings object for the hardware config.
10003 * @param pDbg Pointer to the settings object for the debugging config
10004 * which happens to live in mHWData.
10005 * @param pAutostart Pointer to the settings object for the autostart config
10006 * which happens to live in mHWData.
10007 */
10008HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10009 settings::Autostart *pAutostart)
10010{
10011 HRESULT rc = S_OK;
10012
10013 try
10014 {
10015 /* The hardware version attribute (optional).
10016 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10017 if ( mHWData->mHWVersion == "1"
10018 && mSSData->strStateFilePath.isEmpty()
10019 )
10020 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10021 other point needs to be found where this can be done. */
10022
10023 data.strVersion = mHWData->mHWVersion;
10024 data.uuid = mHWData->mHardwareUUID;
10025
10026 // CPU
10027 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10028 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10029 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10030 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10031 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10032 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10033 data.fPAE = !!mHWData->mPAEEnabled;
10034 data.enmLongMode = mHWData->mLongMode;
10035 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10036 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10037
10038 /* Standard and Extended CPUID leafs. */
10039 data.llCpuIdLeafs.clear();
10040 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10041 {
10042 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10043 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10044 }
10045 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10046 {
10047 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10048 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10049 }
10050
10051 data.cCPUs = mHWData->mCPUCount;
10052 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10053 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10054
10055 data.llCpus.clear();
10056 if (data.fCpuHotPlug)
10057 {
10058 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10059 {
10060 if (mHWData->mCPUAttached[idx])
10061 {
10062 settings::Cpu cpu;
10063 cpu.ulId = idx;
10064 data.llCpus.push_back(cpu);
10065 }
10066 }
10067 }
10068
10069 // memory
10070 data.ulMemorySizeMB = mHWData->mMemorySize;
10071 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10072
10073 // firmware
10074 data.firmwareType = mHWData->mFirmwareType;
10075
10076 // HID
10077 data.pointingHIDType = mHWData->mPointingHIDType;
10078 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10079
10080 // chipset
10081 data.chipsetType = mHWData->mChipsetType;
10082
10083 // paravirt
10084 data.paravirtProvider = mHWData->mParavirtProvider;
10085
10086
10087 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10088
10089 // HPET
10090 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10091
10092 // boot order
10093 data.mapBootOrder.clear();
10094 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10095 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10096
10097 // display
10098 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10099 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10100 data.cMonitors = mHWData->mMonitorCount;
10101 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10102 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10103 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10104 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10105 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10106 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10107 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10108 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10109 {
10110 if (mHWData->maVideoCaptureScreens[i])
10111 ASMBitSet(&data.u64VideoCaptureScreens, i);
10112 else
10113 ASMBitClear(&data.u64VideoCaptureScreens, i);
10114 }
10115 /* store relative video capture file if possible */
10116 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10117
10118 /* VRDEServer settings (optional) */
10119 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10120 if (FAILED(rc)) throw rc;
10121
10122 /* BIOS (required) */
10123 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10124 if (FAILED(rc)) throw rc;
10125
10126 /* USB Controller (required) */
10127 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10128 {
10129 ComObjPtr<USBController> ctrl = *it;
10130 settings::USBController settingsCtrl;
10131
10132 settingsCtrl.strName = ctrl->i_getName();
10133 settingsCtrl.enmType = ctrl->i_getControllerType();
10134
10135 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10136 }
10137
10138 /* USB device filters (required) */
10139 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10140 if (FAILED(rc)) throw rc;
10141
10142 /* Network adapters (required) */
10143 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10144 data.llNetworkAdapters.clear();
10145 /* Write out only the nominal number of network adapters for this
10146 * chipset type. Since Machine::commit() hasn't been called there
10147 * may be extra NIC settings in the vector. */
10148 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10149 {
10150 settings::NetworkAdapter nic;
10151 nic.ulSlot = (uint32_t)slot;
10152 /* paranoia check... must not be NULL, but must not crash either. */
10153 if (mNetworkAdapters[slot])
10154 {
10155 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10156 if (FAILED(rc)) throw rc;
10157
10158 data.llNetworkAdapters.push_back(nic);
10159 }
10160 }
10161
10162 /* Serial ports */
10163 data.llSerialPorts.clear();
10164 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10165 {
10166 settings::SerialPort s;
10167 s.ulSlot = slot;
10168 rc = mSerialPorts[slot]->i_saveSettings(s);
10169 if (FAILED(rc)) return rc;
10170
10171 data.llSerialPorts.push_back(s);
10172 }
10173
10174 /* Parallel ports */
10175 data.llParallelPorts.clear();
10176 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10177 {
10178 settings::ParallelPort p;
10179 p.ulSlot = slot;
10180 rc = mParallelPorts[slot]->i_saveSettings(p);
10181 if (FAILED(rc)) return rc;
10182
10183 data.llParallelPorts.push_back(p);
10184 }
10185
10186 /* Audio adapter */
10187 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10188 if (FAILED(rc)) return rc;
10189
10190 /* Shared folders */
10191 data.llSharedFolders.clear();
10192 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10193 it != mHWData->mSharedFolders.end();
10194 ++it)
10195 {
10196 SharedFolder *pSF = *it;
10197 AutoCaller sfCaller(pSF);
10198 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10199 settings::SharedFolder sf;
10200 sf.strName = pSF->i_getName();
10201 sf.strHostPath = pSF->i_getHostPath();
10202 sf.fWritable = !!pSF->i_isWritable();
10203 sf.fAutoMount = !!pSF->i_isAutoMounted();
10204
10205 data.llSharedFolders.push_back(sf);
10206 }
10207
10208 // clipboard
10209 data.clipboardMode = mHWData->mClipboardMode;
10210
10211 // drag'n'drop
10212 data.dndMode = mHWData->mDnDMode;
10213
10214 /* Guest */
10215 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10216
10217 // IO settings
10218 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10219 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10220
10221 /* BandwidthControl (required) */
10222 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10223 if (FAILED(rc)) throw rc;
10224
10225 /* Host PCI devices */
10226 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10227 it != mHWData->mPCIDeviceAssignments.end();
10228 ++it)
10229 {
10230 ComObjPtr<PCIDeviceAttachment> pda = *it;
10231 settings::HostPCIDeviceAttachment hpda;
10232
10233 rc = pda->i_saveSettings(hpda);
10234 if (FAILED(rc)) throw rc;
10235
10236 data.pciAttachments.push_back(hpda);
10237 }
10238
10239
10240 // guest properties
10241 data.llGuestProperties.clear();
10242#ifdef VBOX_WITH_GUEST_PROPS
10243 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10244 it != mHWData->mGuestProperties.end();
10245 ++it)
10246 {
10247 HWData::GuestProperty property = it->second;
10248
10249 /* Remove transient guest properties at shutdown unless we
10250 * are saving state */
10251 if ( ( mData->mMachineState == MachineState_PoweredOff
10252 || mData->mMachineState == MachineState_Aborted
10253 || mData->mMachineState == MachineState_Teleported)
10254 && ( property.mFlags & guestProp::TRANSIENT
10255 || property.mFlags & guestProp::TRANSRESET))
10256 continue;
10257 settings::GuestProperty prop;
10258 prop.strName = it->first;
10259 prop.strValue = property.strValue;
10260 prop.timestamp = property.mTimestamp;
10261 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10262 guestProp::writeFlags(property.mFlags, szFlags);
10263 prop.strFlags = szFlags;
10264
10265 data.llGuestProperties.push_back(prop);
10266 }
10267
10268 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10269 /* I presume this doesn't require a backup(). */
10270 mData->mGuestPropertiesModified = FALSE;
10271#endif /* VBOX_WITH_GUEST_PROPS defined */
10272
10273 *pDbg = mHWData->mDebugging;
10274 *pAutostart = mHWData->mAutostart;
10275
10276 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10277 }
10278 catch(std::bad_alloc &)
10279 {
10280 return E_OUTOFMEMORY;
10281 }
10282
10283 AssertComRC(rc);
10284 return rc;
10285}
10286
10287/**
10288 * Saves the storage controller configuration.
10289 *
10290 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10291 */
10292HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10293{
10294 data.llStorageControllers.clear();
10295
10296 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10297 it != mStorageControllers->end();
10298 ++it)
10299 {
10300 HRESULT rc;
10301 ComObjPtr<StorageController> pCtl = *it;
10302
10303 settings::StorageController ctl;
10304 ctl.strName = pCtl->i_getName();
10305 ctl.controllerType = pCtl->i_getControllerType();
10306 ctl.storageBus = pCtl->i_getStorageBus();
10307 ctl.ulInstance = pCtl->i_getInstance();
10308 ctl.fBootable = pCtl->i_getBootable();
10309
10310 /* Save the port count. */
10311 ULONG portCount;
10312 rc = pCtl->COMGETTER(PortCount)(&portCount);
10313 ComAssertComRCRet(rc, rc);
10314 ctl.ulPortCount = portCount;
10315
10316 /* Save fUseHostIOCache */
10317 BOOL fUseHostIOCache;
10318 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10319 ComAssertComRCRet(rc, rc);
10320 ctl.fUseHostIOCache = !!fUseHostIOCache;
10321
10322 /* Save IDE emulation settings. */
10323 if (ctl.controllerType == StorageControllerType_IntelAhci)
10324 {
10325 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10326 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10327 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10328 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10329 )
10330 ComAssertComRCRet(rc, rc);
10331 }
10332
10333 /* save the devices now. */
10334 rc = i_saveStorageDevices(pCtl, ctl);
10335 ComAssertComRCRet(rc, rc);
10336
10337 data.llStorageControllers.push_back(ctl);
10338 }
10339
10340 return S_OK;
10341}
10342
10343/**
10344 * Saves the hard disk configuration.
10345 */
10346HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10347 settings::StorageController &data)
10348{
10349 MediaData::AttachmentList atts;
10350
10351 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10352 if (FAILED(rc)) return rc;
10353
10354 data.llAttachedDevices.clear();
10355 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10356 it != atts.end();
10357 ++it)
10358 {
10359 settings::AttachedDevice dev;
10360 IMediumAttachment *iA = *it;
10361 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10362 Medium *pMedium = pAttach->i_getMedium();
10363
10364 dev.deviceType = pAttach->i_getType();
10365 dev.lPort = pAttach->i_getPort();
10366 dev.lDevice = pAttach->i_getDevice();
10367 dev.fPassThrough = pAttach->i_getPassthrough();
10368 dev.fHotPluggable = pAttach->i_getHotPluggable();
10369 if (pMedium)
10370 {
10371 if (pMedium->i_isHostDrive())
10372 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10373 else
10374 dev.uuid = pMedium->i_getId();
10375 dev.fTempEject = pAttach->i_getTempEject();
10376 dev.fNonRotational = pAttach->i_getNonRotational();
10377 dev.fDiscard = pAttach->i_getDiscard();
10378 }
10379
10380 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10381
10382 data.llAttachedDevices.push_back(dev);
10383 }
10384
10385 return S_OK;
10386}
10387
10388/**
10389 * Saves machine state settings as defined by aFlags
10390 * (SaveSTS_* values).
10391 *
10392 * @param aFlags Combination of SaveSTS_* flags.
10393 *
10394 * @note Locks objects for writing.
10395 */
10396HRESULT Machine::i_saveStateSettings(int aFlags)
10397{
10398 if (aFlags == 0)
10399 return S_OK;
10400
10401 AutoCaller autoCaller(this);
10402 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10403
10404 /* This object's write lock is also necessary to serialize file access
10405 * (prevent concurrent reads and writes) */
10406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10407
10408 HRESULT rc = S_OK;
10409
10410 Assert(mData->pMachineConfigFile);
10411
10412 try
10413 {
10414 if (aFlags & SaveSTS_CurStateModified)
10415 mData->pMachineConfigFile->fCurrentStateModified = true;
10416
10417 if (aFlags & SaveSTS_StateFilePath)
10418 {
10419 if (!mSSData->strStateFilePath.isEmpty())
10420 /* try to make the file name relative to the settings file dir */
10421 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10422 else
10423 mData->pMachineConfigFile->strStateFile.setNull();
10424 }
10425
10426 if (aFlags & SaveSTS_StateTimeStamp)
10427 {
10428 Assert( mData->mMachineState != MachineState_Aborted
10429 || mSSData->strStateFilePath.isEmpty());
10430
10431 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10432
10433 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10434//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10435 }
10436
10437 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10438 }
10439 catch (...)
10440 {
10441 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10442 }
10443
10444 return rc;
10445}
10446
10447/**
10448 * Ensures that the given medium is added to a media registry. If this machine
10449 * was created with 4.0 or later, then the machine registry is used. Otherwise
10450 * the global VirtualBox media registry is used.
10451 *
10452 * Caller must NOT hold machine lock, media tree or any medium locks!
10453 *
10454 * @param pMedium
10455 */
10456void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10457{
10458 /* Paranoia checks: do not hold machine or media tree locks. */
10459 AssertReturnVoid(!isWriteLockOnCurrentThread());
10460 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10461
10462 ComObjPtr<Medium> pBase;
10463 {
10464 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10465 pBase = pMedium->i_getBase();
10466 }
10467
10468 /* Paranoia checks: do not hold medium locks. */
10469 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10470 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10471
10472 // decide which medium registry to use now that the medium is attached:
10473 Guid uuid;
10474 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10475 // machine XML is VirtualBox 4.0 or higher:
10476 uuid = i_getId(); // machine UUID
10477 else
10478 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10479
10480 if (pMedium->i_addRegistry(uuid))
10481 mParent->i_markRegistryModified(uuid);
10482
10483 /* For more complex hard disk structures it can happen that the base
10484 * medium isn't yet associated with any medium registry. Do that now. */
10485 if (pMedium != pBase)
10486 {
10487 /* Tree lock needed by Medium::addRegistry when recursing. */
10488 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10489 if (pBase->i_addRegistryRecursive(uuid))
10490 {
10491 treeLock.release();
10492 mParent->i_markRegistryModified(uuid);
10493 }
10494 }
10495}
10496
10497/**
10498 * Creates differencing hard disks for all normal hard disks attached to this
10499 * machine and a new set of attachments to refer to created disks.
10500 *
10501 * Used when taking a snapshot or when deleting the current state. Gets called
10502 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10503 *
10504 * This method assumes that mMediaData contains the original hard disk attachments
10505 * it needs to create diffs for. On success, these attachments will be replaced
10506 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10507 * called to delete created diffs which will also rollback mMediaData and restore
10508 * whatever was backed up before calling this method.
10509 *
10510 * Attachments with non-normal hard disks are left as is.
10511 *
10512 * If @a aOnline is @c false then the original hard disks that require implicit
10513 * diffs will be locked for reading. Otherwise it is assumed that they are
10514 * already locked for writing (when the VM was started). Note that in the latter
10515 * case it is responsibility of the caller to lock the newly created diffs for
10516 * writing if this method succeeds.
10517 *
10518 * @param aProgress Progress object to run (must contain at least as
10519 * many operations left as the number of hard disks
10520 * attached).
10521 * @param aOnline Whether the VM was online prior to this operation.
10522 *
10523 * @note The progress object is not marked as completed, neither on success nor
10524 * on failure. This is a responsibility of the caller.
10525 *
10526 * @note Locks this object and the media tree for writing.
10527 */
10528HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10529 ULONG aWeight,
10530 bool aOnline)
10531{
10532 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10533
10534 AutoCaller autoCaller(this);
10535 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10536
10537 AutoMultiWriteLock2 alock(this->lockHandle(),
10538 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10539
10540 /* must be in a protective state because we release the lock below */
10541 AssertReturn( mData->mMachineState == MachineState_Saving
10542 || mData->mMachineState == MachineState_LiveSnapshotting
10543 || mData->mMachineState == MachineState_RestoringSnapshot
10544 || mData->mMachineState == MachineState_DeletingSnapshot
10545 , E_FAIL);
10546
10547 HRESULT rc = S_OK;
10548
10549 // use appropriate locked media map (online or offline)
10550 MediumLockListMap lockedMediaOffline;
10551 MediumLockListMap *lockedMediaMap;
10552 if (aOnline)
10553 lockedMediaMap = &mData->mSession.mLockedMedia;
10554 else
10555 lockedMediaMap = &lockedMediaOffline;
10556
10557 try
10558 {
10559 if (!aOnline)
10560 {
10561 /* lock all attached hard disks early to detect "in use"
10562 * situations before creating actual diffs */
10563 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10564 it != mMediaData->mAttachments.end();
10565 ++it)
10566 {
10567 MediumAttachment* pAtt = *it;
10568 if (pAtt->i_getType() == DeviceType_HardDisk)
10569 {
10570 Medium* pMedium = pAtt->i_getMedium();
10571 Assert(pMedium);
10572
10573 MediumLockList *pMediumLockList(new MediumLockList());
10574 alock.release();
10575 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10576 false /* fMediumLockWrite */,
10577 false /* fMediumLockWriteAll */,
10578 NULL,
10579 *pMediumLockList);
10580 alock.acquire();
10581 if (FAILED(rc))
10582 {
10583 delete pMediumLockList;
10584 throw rc;
10585 }
10586 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10587 if (FAILED(rc))
10588 {
10589 throw setError(rc,
10590 tr("Collecting locking information for all attached media failed"));
10591 }
10592 }
10593 }
10594
10595 /* Now lock all media. If this fails, nothing is locked. */
10596 alock.release();
10597 rc = lockedMediaMap->Lock();
10598 alock.acquire();
10599 if (FAILED(rc))
10600 {
10601 throw setError(rc,
10602 tr("Locking of attached media failed"));
10603 }
10604 }
10605
10606 /* remember the current list (note that we don't use backup() since
10607 * mMediaData may be already backed up) */
10608 MediaData::AttachmentList atts = mMediaData->mAttachments;
10609
10610 /* start from scratch */
10611 mMediaData->mAttachments.clear();
10612
10613 /* go through remembered attachments and create diffs for normal hard
10614 * disks and attach them */
10615 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10616 it != atts.end();
10617 ++it)
10618 {
10619 MediumAttachment* pAtt = *it;
10620
10621 DeviceType_T devType = pAtt->i_getType();
10622 Medium* pMedium = pAtt->i_getMedium();
10623
10624 if ( devType != DeviceType_HardDisk
10625 || pMedium == NULL
10626 || pMedium->i_getType() != MediumType_Normal)
10627 {
10628 /* copy the attachment as is */
10629
10630 /** @todo the progress object created in Console::TakeSnaphot
10631 * only expects operations for hard disks. Later other
10632 * device types need to show up in the progress as well. */
10633 if (devType == DeviceType_HardDisk)
10634 {
10635 if (pMedium == NULL)
10636 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10637 aWeight); // weight
10638 else
10639 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10640 pMedium->i_getBase()->i_getName().c_str()).raw(),
10641 aWeight); // weight
10642 }
10643
10644 mMediaData->mAttachments.push_back(pAtt);
10645 continue;
10646 }
10647
10648 /* need a diff */
10649 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10650 pMedium->i_getBase()->i_getName().c_str()).raw(),
10651 aWeight); // weight
10652
10653 Utf8Str strFullSnapshotFolder;
10654 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10655
10656 ComObjPtr<Medium> diff;
10657 diff.createObject();
10658 // store the diff in the same registry as the parent
10659 // (this cannot fail here because we can't create implicit diffs for
10660 // unregistered images)
10661 Guid uuidRegistryParent;
10662 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10663 Assert(fInRegistry); NOREF(fInRegistry);
10664 rc = diff->init(mParent,
10665 pMedium->i_getPreferredDiffFormat(),
10666 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10667 uuidRegistryParent,
10668 DeviceType_HardDisk);
10669 if (FAILED(rc)) throw rc;
10670
10671 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10672 * the push_back? Looks like we're going to release medium with the
10673 * wrong kind of lock (general issue with if we fail anywhere at all)
10674 * and an orphaned VDI in the snapshots folder. */
10675
10676 /* update the appropriate lock list */
10677 MediumLockList *pMediumLockList;
10678 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10679 AssertComRCThrowRC(rc);
10680 if (aOnline)
10681 {
10682 alock.release();
10683 /* The currently attached medium will be read-only, change
10684 * the lock type to read. */
10685 rc = pMediumLockList->Update(pMedium, false);
10686 alock.acquire();
10687 AssertComRCThrowRC(rc);
10688 }
10689
10690 /* release the locks before the potentially lengthy operation */
10691 alock.release();
10692 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10693 pMediumLockList,
10694 NULL /* aProgress */,
10695 true /* aWait */);
10696 alock.acquire();
10697 if (FAILED(rc)) throw rc;
10698
10699 /* actual lock list update is done in Medium::commitMedia */
10700
10701 rc = diff->i_addBackReference(mData->mUuid);
10702 AssertComRCThrowRC(rc);
10703
10704 /* add a new attachment */
10705 ComObjPtr<MediumAttachment> attachment;
10706 attachment.createObject();
10707 rc = attachment->init(this,
10708 diff,
10709 pAtt->i_getControllerName(),
10710 pAtt->i_getPort(),
10711 pAtt->i_getDevice(),
10712 DeviceType_HardDisk,
10713 true /* aImplicit */,
10714 false /* aPassthrough */,
10715 false /* aTempEject */,
10716 pAtt->i_getNonRotational(),
10717 pAtt->i_getDiscard(),
10718 pAtt->i_getHotPluggable(),
10719 pAtt->i_getBandwidthGroup());
10720 if (FAILED(rc)) throw rc;
10721
10722 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10723 AssertComRCThrowRC(rc);
10724 mMediaData->mAttachments.push_back(attachment);
10725 }
10726 }
10727 catch (HRESULT aRC) { rc = aRC; }
10728
10729 /* unlock all hard disks we locked when there is no VM */
10730 if (!aOnline)
10731 {
10732 ErrorInfoKeeper eik;
10733
10734 HRESULT rc1 = lockedMediaMap->Clear();
10735 AssertComRC(rc1);
10736 }
10737
10738 return rc;
10739}
10740
10741/**
10742 * Deletes implicit differencing hard disks created either by
10743 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10744 *
10745 * Note that to delete hard disks created by #AttachDevice() this method is
10746 * called from #fixupMedia() when the changes are rolled back.
10747 *
10748 * @note Locks this object and the media tree for writing.
10749 */
10750HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10751{
10752 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10753
10754 AutoCaller autoCaller(this);
10755 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10756
10757 AutoMultiWriteLock2 alock(this->lockHandle(),
10758 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10759
10760 /* We absolutely must have backed up state. */
10761 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10762
10763 /* Check if there are any implicitly created diff images. */
10764 bool fImplicitDiffs = false;
10765 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10766 it != mMediaData->mAttachments.end();
10767 ++it)
10768 {
10769 const ComObjPtr<MediumAttachment> &pAtt = *it;
10770 if (pAtt->i_isImplicit())
10771 {
10772 fImplicitDiffs = true;
10773 break;
10774 }
10775 }
10776 /* If there is nothing to do, leave early. This saves lots of image locking
10777 * effort. It also avoids a MachineStateChanged event without real reason.
10778 * This is important e.g. when loading a VM config, because there should be
10779 * no events. Otherwise API clients can become thoroughly confused for
10780 * inaccessible VMs (the code for loading VM configs uses this method for
10781 * cleanup if the config makes no sense), as they take such events as an
10782 * indication that the VM is alive, and they would force the VM config to
10783 * be reread, leading to an endless loop. */
10784 if (!fImplicitDiffs)
10785 return S_OK;
10786
10787 HRESULT rc = S_OK;
10788 MachineState_T oldState = mData->mMachineState;
10789
10790 /* will release the lock before the potentially lengthy operation,
10791 * so protect with the special state (unless already protected) */
10792 if ( oldState != MachineState_Saving
10793 && oldState != MachineState_LiveSnapshotting
10794 && oldState != MachineState_RestoringSnapshot
10795 && oldState != MachineState_DeletingSnapshot
10796 && oldState != MachineState_DeletingSnapshotOnline
10797 && oldState != MachineState_DeletingSnapshotPaused
10798 )
10799 i_setMachineState(MachineState_SettingUp);
10800
10801 // use appropriate locked media map (online or offline)
10802 MediumLockListMap lockedMediaOffline;
10803 MediumLockListMap *lockedMediaMap;
10804 if (aOnline)
10805 lockedMediaMap = &mData->mSession.mLockedMedia;
10806 else
10807 lockedMediaMap = &lockedMediaOffline;
10808
10809 try
10810 {
10811 if (!aOnline)
10812 {
10813 /* lock all attached hard disks early to detect "in use"
10814 * situations before deleting actual diffs */
10815 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10816 it != mMediaData->mAttachments.end();
10817 ++it)
10818 {
10819 MediumAttachment* pAtt = *it;
10820 if (pAtt->i_getType() == DeviceType_HardDisk)
10821 {
10822 Medium* pMedium = pAtt->i_getMedium();
10823 Assert(pMedium);
10824
10825 MediumLockList *pMediumLockList(new MediumLockList());
10826 alock.release();
10827 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10828 false /* fMediumLockWrite */,
10829 false /* fMediumLockWriteAll */,
10830 NULL,
10831 *pMediumLockList);
10832 alock.acquire();
10833
10834 if (FAILED(rc))
10835 {
10836 delete pMediumLockList;
10837 throw rc;
10838 }
10839
10840 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10841 if (FAILED(rc))
10842 throw rc;
10843 }
10844 }
10845
10846 if (FAILED(rc))
10847 throw rc;
10848 } // end of offline
10849
10850 /* Lock lists are now up to date and include implicitly created media */
10851
10852 /* Go through remembered attachments and delete all implicitly created
10853 * diffs and fix up the attachment information */
10854 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10855 MediaData::AttachmentList implicitAtts;
10856 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10857 it != mMediaData->mAttachments.end();
10858 ++it)
10859 {
10860 ComObjPtr<MediumAttachment> pAtt = *it;
10861 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10862 if (pMedium.isNull())
10863 continue;
10864
10865 // Implicit attachments go on the list for deletion and back references are removed.
10866 if (pAtt->i_isImplicit())
10867 {
10868 /* Deassociate and mark for deletion */
10869 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10870 rc = pMedium->i_removeBackReference(mData->mUuid);
10871 if (FAILED(rc))
10872 throw rc;
10873 implicitAtts.push_back(pAtt);
10874 continue;
10875 }
10876
10877 /* Was this medium attached before? */
10878 if (!i_findAttachment(oldAtts, pMedium))
10879 {
10880 /* no: de-associate */
10881 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10882 rc = pMedium->i_removeBackReference(mData->mUuid);
10883 if (FAILED(rc))
10884 throw rc;
10885 continue;
10886 }
10887 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10888 }
10889
10890 /* If there are implicit attachments to delete, throw away the lock
10891 * map contents (which will unlock all media) since the medium
10892 * attachments will be rolled back. Below we need to completely
10893 * recreate the lock map anyway since it is infinitely complex to
10894 * do this incrementally (would need reconstructing each attachment
10895 * change, which would be extremely hairy). */
10896 if (implicitAtts.size() != 0)
10897 {
10898 ErrorInfoKeeper eik;
10899
10900 HRESULT rc1 = lockedMediaMap->Clear();
10901 AssertComRC(rc1);
10902 }
10903
10904 /* rollback hard disk changes */
10905 mMediaData.rollback();
10906
10907 MultiResult mrc(S_OK);
10908
10909 // Delete unused implicit diffs.
10910 if (implicitAtts.size() != 0)
10911 {
10912 alock.release();
10913
10914 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10915 {
10916 // Remove medium associated with this attachment.
10917 ComObjPtr<MediumAttachment> pAtt = *it;
10918 Assert(pAtt);
10919 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10920 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10921 Assert(pMedium);
10922
10923 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10924 // continue on delete failure, just collect error messages
10925 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10926 pMedium->i_getLocationFull().c_str() ));
10927 mrc = rc;
10928 }
10929
10930 alock.acquire();
10931
10932 /* if there is a VM recreate media lock map as mentioned above,
10933 * otherwise it is a waste of time and we leave things unlocked */
10934 if (aOnline)
10935 {
10936 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10937 /* must never be NULL, but better safe than sorry */
10938 if (!pMachine.isNull())
10939 {
10940 alock.release();
10941 rc = mData->mSession.mMachine->i_lockMedia();
10942 alock.acquire();
10943 if (FAILED(rc))
10944 throw rc;
10945 }
10946 }
10947 }
10948 }
10949 catch (HRESULT aRC) {rc = aRC;}
10950
10951 if (mData->mMachineState == MachineState_SettingUp)
10952 i_setMachineState(oldState);
10953
10954 /* unlock all hard disks we locked when there is no VM */
10955 if (!aOnline)
10956 {
10957 ErrorInfoKeeper eik;
10958
10959 HRESULT rc1 = lockedMediaMap->Clear();
10960 AssertComRC(rc1);
10961 }
10962
10963 return rc;
10964}
10965
10966
10967/**
10968 * Looks through the given list of media attachments for one with the given parameters
10969 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10970 * can be searched as well if needed.
10971 *
10972 * @param list
10973 * @param aControllerName
10974 * @param aControllerPort
10975 * @param aDevice
10976 * @return
10977 */
10978MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10979 IN_BSTR aControllerName,
10980 LONG aControllerPort,
10981 LONG aDevice)
10982{
10983 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10984 {
10985 MediumAttachment *pAttach = *it;
10986 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10987 return pAttach;
10988 }
10989
10990 return NULL;
10991}
10992
10993/**
10994 * Looks through the given list of media attachments for one with the given parameters
10995 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10996 * can be searched as well if needed.
10997 *
10998 * @param list
10999 * @param aControllerName
11000 * @param aControllerPort
11001 * @param aDevice
11002 * @return
11003 */
11004MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11005 ComObjPtr<Medium> pMedium)
11006{
11007 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11008 {
11009 MediumAttachment *pAttach = *it;
11010 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11011 if (pMediumThis == pMedium)
11012 return pAttach;
11013 }
11014
11015 return NULL;
11016}
11017
11018/**
11019 * Looks through the given list of media attachments for one with the given parameters
11020 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11021 * can be searched as well if needed.
11022 *
11023 * @param list
11024 * @param aControllerName
11025 * @param aControllerPort
11026 * @param aDevice
11027 * @return
11028 */
11029MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11030 Guid &id)
11031{
11032 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11033 {
11034 MediumAttachment *pAttach = *it;
11035 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11036 if (pMediumThis->i_getId() == id)
11037 return pAttach;
11038 }
11039
11040 return NULL;
11041}
11042
11043/**
11044 * Main implementation for Machine::DetachDevice. This also gets called
11045 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11046 *
11047 * @param pAttach Medium attachment to detach.
11048 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11049 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11050 * SnapshotMachine, and this must be its snapshot.
11051 * @return
11052 */
11053HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11054 AutoWriteLock &writeLock,
11055 Snapshot *pSnapshot)
11056{
11057 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11058 DeviceType_T mediumType = pAttach->i_getType();
11059
11060 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11061
11062 if (pAttach->i_isImplicit())
11063 {
11064 /* attempt to implicitly delete the implicitly created diff */
11065
11066 /// @todo move the implicit flag from MediumAttachment to Medium
11067 /// and forbid any hard disk operation when it is implicit. Or maybe
11068 /// a special media state for it to make it even more simple.
11069
11070 Assert(mMediaData.isBackedUp());
11071
11072 /* will release the lock before the potentially lengthy operation, so
11073 * protect with the special state */
11074 MachineState_T oldState = mData->mMachineState;
11075 i_setMachineState(MachineState_SettingUp);
11076
11077 writeLock.release();
11078
11079 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11080 true /*aWait*/);
11081
11082 writeLock.acquire();
11083
11084 i_setMachineState(oldState);
11085
11086 if (FAILED(rc)) return rc;
11087 }
11088
11089 i_setModified(IsModified_Storage);
11090 mMediaData.backup();
11091 mMediaData->mAttachments.remove(pAttach);
11092
11093 if (!oldmedium.isNull())
11094 {
11095 // if this is from a snapshot, do not defer detachment to commitMedia()
11096 if (pSnapshot)
11097 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11098 // else if non-hard disk media, do not defer detachment to commitMedia() either
11099 else if (mediumType != DeviceType_HardDisk)
11100 oldmedium->i_removeBackReference(mData->mUuid);
11101 }
11102
11103 return S_OK;
11104}
11105
11106/**
11107 * Goes thru all media of the given list and
11108 *
11109 * 1) calls i_detachDevice() on each of them for this machine and
11110 * 2) adds all Medium objects found in the process to the given list,
11111 * depending on cleanupMode.
11112 *
11113 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11114 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11115 * media to the list.
11116 *
11117 * This gets called from Machine::Unregister, both for the actual Machine and
11118 * the SnapshotMachine objects that might be found in the snapshots.
11119 *
11120 * Requires caller and locking. The machine lock must be passed in because it
11121 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11122 *
11123 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11124 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11125 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11126 * Full, then all media get added;
11127 * otherwise no media get added.
11128 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11129 * @return
11130 */
11131HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11132 Snapshot *pSnapshot,
11133 CleanupMode_T cleanupMode,
11134 MediaList &llMedia)
11135{
11136 Assert(isWriteLockOnCurrentThread());
11137
11138 HRESULT rc;
11139
11140 // make a temporary list because i_detachDevice invalidates iterators into
11141 // mMediaData->mAttachments
11142 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11143
11144 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11145 {
11146 ComObjPtr<MediumAttachment> &pAttach = *it;
11147 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11148
11149 if (!pMedium.isNull())
11150 {
11151 AutoCaller mac(pMedium);
11152 if (FAILED(mac.rc())) return mac.rc();
11153 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11154 DeviceType_T devType = pMedium->i_getDeviceType();
11155 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11156 && devType == DeviceType_HardDisk)
11157 || (cleanupMode == CleanupMode_Full)
11158 )
11159 {
11160 llMedia.push_back(pMedium);
11161 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11162 /* Not allowed to keep this lock as below we need the parent
11163 * medium lock, and the lock order is parent to child. */
11164 lock.release();
11165 /*
11166 * Search for medias which are not attached to any machine, but
11167 * in the chain to an attached disk. Mediums are only consided
11168 * if they are:
11169 * - have only one child
11170 * - no references to any machines
11171 * - are of normal medium type
11172 */
11173 while (!pParent.isNull())
11174 {
11175 AutoCaller mac1(pParent);
11176 if (FAILED(mac1.rc())) return mac1.rc();
11177 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11178 if (pParent->i_getChildren().size() == 1)
11179 {
11180 if ( pParent->i_getMachineBackRefCount() == 0
11181 && pParent->i_getType() == MediumType_Normal
11182 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11183 llMedia.push_back(pParent);
11184 }
11185 else
11186 break;
11187 pParent = pParent->i_getParent();
11188 }
11189 }
11190 }
11191
11192 // real machine: then we need to use the proper method
11193 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11194
11195 if (FAILED(rc))
11196 return rc;
11197 }
11198
11199 return S_OK;
11200}
11201
11202/**
11203 * Perform deferred hard disk detachments.
11204 *
11205 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11206 * backed up).
11207 *
11208 * If @a aOnline is @c true then this method will also unlock the old hard disks
11209 * for which the new implicit diffs were created and will lock these new diffs for
11210 * writing.
11211 *
11212 * @param aOnline Whether the VM was online prior to this operation.
11213 *
11214 * @note Locks this object for writing!
11215 */
11216void Machine::i_commitMedia(bool aOnline /*= false*/)
11217{
11218 AutoCaller autoCaller(this);
11219 AssertComRCReturnVoid(autoCaller.rc());
11220
11221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11222
11223 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11224
11225 HRESULT rc = S_OK;
11226
11227 /* no attach/detach operations -- nothing to do */
11228 if (!mMediaData.isBackedUp())
11229 return;
11230
11231 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11232 bool fMediaNeedsLocking = false;
11233
11234 /* enumerate new attachments */
11235 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11236 it != mMediaData->mAttachments.end();
11237 ++it)
11238 {
11239 MediumAttachment *pAttach = *it;
11240
11241 pAttach->i_commit();
11242
11243 Medium* pMedium = pAttach->i_getMedium();
11244 bool fImplicit = pAttach->i_isImplicit();
11245
11246 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11247 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11248 fImplicit));
11249
11250 /** @todo convert all this Machine-based voodoo to MediumAttachment
11251 * based commit logic. */
11252 if (fImplicit)
11253 {
11254 /* convert implicit attachment to normal */
11255 pAttach->i_setImplicit(false);
11256
11257 if ( aOnline
11258 && pMedium
11259 && pAttach->i_getType() == DeviceType_HardDisk
11260 )
11261 {
11262 /* update the appropriate lock list */
11263 MediumLockList *pMediumLockList;
11264 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11265 AssertComRC(rc);
11266 if (pMediumLockList)
11267 {
11268 /* unlock if there's a need to change the locking */
11269 if (!fMediaNeedsLocking)
11270 {
11271 rc = mData->mSession.mLockedMedia.Unlock();
11272 AssertComRC(rc);
11273 fMediaNeedsLocking = true;
11274 }
11275 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11276 AssertComRC(rc);
11277 rc = pMediumLockList->Append(pMedium, true);
11278 AssertComRC(rc);
11279 }
11280 }
11281
11282 continue;
11283 }
11284
11285 if (pMedium)
11286 {
11287 /* was this medium attached before? */
11288 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11289 {
11290 MediumAttachment *pOldAttach = *oldIt;
11291 if (pOldAttach->i_getMedium() == pMedium)
11292 {
11293 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11294
11295 /* yes: remove from old to avoid de-association */
11296 oldAtts.erase(oldIt);
11297 break;
11298 }
11299 }
11300 }
11301 }
11302
11303 /* enumerate remaining old attachments and de-associate from the
11304 * current machine state */
11305 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11306 {
11307 MediumAttachment *pAttach = *it;
11308 Medium* pMedium = pAttach->i_getMedium();
11309
11310 /* Detach only hard disks, since DVD/floppy media is detached
11311 * instantly in MountMedium. */
11312 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11313 {
11314 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11315
11316 /* now de-associate from the current machine state */
11317 rc = pMedium->i_removeBackReference(mData->mUuid);
11318 AssertComRC(rc);
11319
11320 if (aOnline)
11321 {
11322 /* unlock since medium is not used anymore */
11323 MediumLockList *pMediumLockList;
11324 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11325 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11326 {
11327 /* this happens for online snapshots, there the attachment
11328 * is changing, but only to a diff image created under
11329 * the old one, so there is no separate lock list */
11330 Assert(!pMediumLockList);
11331 }
11332 else
11333 {
11334 AssertComRC(rc);
11335 if (pMediumLockList)
11336 {
11337 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11338 AssertComRC(rc);
11339 }
11340 }
11341 }
11342 }
11343 }
11344
11345 /* take media locks again so that the locking state is consistent */
11346 if (fMediaNeedsLocking)
11347 {
11348 Assert(aOnline);
11349 rc = mData->mSession.mLockedMedia.Lock();
11350 AssertComRC(rc);
11351 }
11352
11353 /* commit the hard disk changes */
11354 mMediaData.commit();
11355
11356 if (i_isSessionMachine())
11357 {
11358 /*
11359 * Update the parent machine to point to the new owner.
11360 * This is necessary because the stored parent will point to the
11361 * session machine otherwise and cause crashes or errors later
11362 * when the session machine gets invalid.
11363 */
11364 /** @todo Change the MediumAttachment class to behave like any other
11365 * class in this regard by creating peer MediumAttachment
11366 * objects for session machines and share the data with the peer
11367 * machine.
11368 */
11369 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11370 it != mMediaData->mAttachments.end();
11371 ++it)
11372 (*it)->i_updateParentMachine(mPeer);
11373
11374 /* attach new data to the primary machine and reshare it */
11375 mPeer->mMediaData.attach(mMediaData);
11376 }
11377
11378 return;
11379}
11380
11381/**
11382 * Perform deferred deletion of implicitly created diffs.
11383 *
11384 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11385 * backed up).
11386 *
11387 * @note Locks this object for writing!
11388 */
11389void Machine::i_rollbackMedia()
11390{
11391 AutoCaller autoCaller(this);
11392 AssertComRCReturnVoid(autoCaller.rc());
11393
11394 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11395 LogFlowThisFunc(("Entering rollbackMedia\n"));
11396
11397 HRESULT rc = S_OK;
11398
11399 /* no attach/detach operations -- nothing to do */
11400 if (!mMediaData.isBackedUp())
11401 return;
11402
11403 /* enumerate new attachments */
11404 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11405 it != mMediaData->mAttachments.end();
11406 ++it)
11407 {
11408 MediumAttachment *pAttach = *it;
11409 /* Fix up the backrefs for DVD/floppy media. */
11410 if (pAttach->i_getType() != DeviceType_HardDisk)
11411 {
11412 Medium* pMedium = pAttach->i_getMedium();
11413 if (pMedium)
11414 {
11415 rc = pMedium->i_removeBackReference(mData->mUuid);
11416 AssertComRC(rc);
11417 }
11418 }
11419
11420 (*it)->i_rollback();
11421
11422 pAttach = *it;
11423 /* Fix up the backrefs for DVD/floppy media. */
11424 if (pAttach->i_getType() != DeviceType_HardDisk)
11425 {
11426 Medium* pMedium = pAttach->i_getMedium();
11427 if (pMedium)
11428 {
11429 rc = pMedium->i_addBackReference(mData->mUuid);
11430 AssertComRC(rc);
11431 }
11432 }
11433 }
11434
11435 /** @todo convert all this Machine-based voodoo to MediumAttachment
11436 * based rollback logic. */
11437 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11438
11439 return;
11440}
11441
11442/**
11443 * Returns true if the settings file is located in the directory named exactly
11444 * as the machine; this means, among other things, that the machine directory
11445 * should be auto-renamed.
11446 *
11447 * @param aSettingsDir if not NULL, the full machine settings file directory
11448 * name will be assigned there.
11449 *
11450 * @note Doesn't lock anything.
11451 * @note Not thread safe (must be called from this object's lock).
11452 */
11453bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11454{
11455 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11456 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11457 if (aSettingsDir)
11458 *aSettingsDir = strMachineDirName;
11459 strMachineDirName.stripPath(); // vmname
11460 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11461 strConfigFileOnly.stripPath() // vmname.vbox
11462 .stripSuffix(); // vmname
11463 /** @todo hack, make somehow use of ComposeMachineFilename */
11464 if (mUserData->s.fDirectoryIncludesUUID)
11465 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11466
11467 AssertReturn(!strMachineDirName.isEmpty(), false);
11468 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11469
11470 return strMachineDirName == strConfigFileOnly;
11471}
11472
11473/**
11474 * Discards all changes to machine settings.
11475 *
11476 * @param aNotify Whether to notify the direct session about changes or not.
11477 *
11478 * @note Locks objects for writing!
11479 */
11480void Machine::i_rollback(bool aNotify)
11481{
11482 AutoCaller autoCaller(this);
11483 AssertComRCReturn(autoCaller.rc(), (void)0);
11484
11485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11486
11487 if (!mStorageControllers.isNull())
11488 {
11489 if (mStorageControllers.isBackedUp())
11490 {
11491 /* unitialize all new devices (absent in the backed up list). */
11492 StorageControllerList::const_iterator it = mStorageControllers->begin();
11493 StorageControllerList *backedList = mStorageControllers.backedUpData();
11494 while (it != mStorageControllers->end())
11495 {
11496 if ( std::find(backedList->begin(), backedList->end(), *it)
11497 == backedList->end()
11498 )
11499 {
11500 (*it)->uninit();
11501 }
11502 ++it;
11503 }
11504
11505 /* restore the list */
11506 mStorageControllers.rollback();
11507 }
11508
11509 /* rollback any changes to devices after restoring the list */
11510 if (mData->flModifications & IsModified_Storage)
11511 {
11512 StorageControllerList::const_iterator it = mStorageControllers->begin();
11513 while (it != mStorageControllers->end())
11514 {
11515 (*it)->i_rollback();
11516 ++it;
11517 }
11518 }
11519 }
11520
11521 if (!mUSBControllers.isNull())
11522 {
11523 if (mUSBControllers.isBackedUp())
11524 {
11525 /* unitialize all new devices (absent in the backed up list). */
11526 USBControllerList::const_iterator it = mUSBControllers->begin();
11527 USBControllerList *backedList = mUSBControllers.backedUpData();
11528 while (it != mUSBControllers->end())
11529 {
11530 if ( std::find(backedList->begin(), backedList->end(), *it)
11531 == backedList->end()
11532 )
11533 {
11534 (*it)->uninit();
11535 }
11536 ++it;
11537 }
11538
11539 /* restore the list */
11540 mUSBControllers.rollback();
11541 }
11542
11543 /* rollback any changes to devices after restoring the list */
11544 if (mData->flModifications & IsModified_USB)
11545 {
11546 USBControllerList::const_iterator it = mUSBControllers->begin();
11547 while (it != mUSBControllers->end())
11548 {
11549 (*it)->i_rollback();
11550 ++it;
11551 }
11552 }
11553 }
11554
11555 mUserData.rollback();
11556
11557 mHWData.rollback();
11558
11559 if (mData->flModifications & IsModified_Storage)
11560 i_rollbackMedia();
11561
11562 if (mBIOSSettings)
11563 mBIOSSettings->i_rollback();
11564
11565 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11566 mVRDEServer->i_rollback();
11567
11568 if (mAudioAdapter)
11569 mAudioAdapter->i_rollback();
11570
11571 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11572 mUSBDeviceFilters->i_rollback();
11573
11574 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11575 mBandwidthControl->i_rollback();
11576
11577 if (!mHWData.isNull())
11578 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11579 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11580 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11581 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11582
11583 if (mData->flModifications & IsModified_NetworkAdapters)
11584 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11585 if ( mNetworkAdapters[slot]
11586 && mNetworkAdapters[slot]->i_isModified())
11587 {
11588 mNetworkAdapters[slot]->i_rollback();
11589 networkAdapters[slot] = mNetworkAdapters[slot];
11590 }
11591
11592 if (mData->flModifications & IsModified_SerialPorts)
11593 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11594 if ( mSerialPorts[slot]
11595 && mSerialPorts[slot]->i_isModified())
11596 {
11597 mSerialPorts[slot]->i_rollback();
11598 serialPorts[slot] = mSerialPorts[slot];
11599 }
11600
11601 if (mData->flModifications & IsModified_ParallelPorts)
11602 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11603 if ( mParallelPorts[slot]
11604 && mParallelPorts[slot]->i_isModified())
11605 {
11606 mParallelPorts[slot]->i_rollback();
11607 parallelPorts[slot] = mParallelPorts[slot];
11608 }
11609
11610 if (aNotify)
11611 {
11612 /* inform the direct session about changes */
11613
11614 ComObjPtr<Machine> that = this;
11615 uint32_t flModifications = mData->flModifications;
11616 alock.release();
11617
11618 if (flModifications & IsModified_SharedFolders)
11619 that->i_onSharedFolderChange();
11620
11621 if (flModifications & IsModified_VRDEServer)
11622 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11623 if (flModifications & IsModified_USB)
11624 that->i_onUSBControllerChange();
11625
11626 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11627 if (networkAdapters[slot])
11628 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11629 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11630 if (serialPorts[slot])
11631 that->i_onSerialPortChange(serialPorts[slot]);
11632 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11633 if (parallelPorts[slot])
11634 that->i_onParallelPortChange(parallelPorts[slot]);
11635
11636 if (flModifications & IsModified_Storage)
11637 that->i_onStorageControllerChange();
11638
11639#if 0
11640 if (flModifications & IsModified_BandwidthControl)
11641 that->onBandwidthControlChange();
11642#endif
11643 }
11644}
11645
11646/**
11647 * Commits all the changes to machine settings.
11648 *
11649 * Note that this operation is supposed to never fail.
11650 *
11651 * @note Locks this object and children for writing.
11652 */
11653void Machine::i_commit()
11654{
11655 AutoCaller autoCaller(this);
11656 AssertComRCReturnVoid(autoCaller.rc());
11657
11658 AutoCaller peerCaller(mPeer);
11659 AssertComRCReturnVoid(peerCaller.rc());
11660
11661 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11662
11663 /*
11664 * use safe commit to ensure Snapshot machines (that share mUserData)
11665 * will still refer to a valid memory location
11666 */
11667 mUserData.commitCopy();
11668
11669 mHWData.commit();
11670
11671 if (mMediaData.isBackedUp())
11672 i_commitMedia(Global::IsOnline(mData->mMachineState));
11673
11674 mBIOSSettings->i_commit();
11675 mVRDEServer->i_commit();
11676 mAudioAdapter->i_commit();
11677 mUSBDeviceFilters->i_commit();
11678 mBandwidthControl->i_commit();
11679
11680 /* Since mNetworkAdapters is a list which might have been changed (resized)
11681 * without using the Backupable<> template we need to handle the copying
11682 * of the list entries manually, including the creation of peers for the
11683 * new objects. */
11684 bool commitNetworkAdapters = false;
11685 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11686 if (mPeer)
11687 {
11688 /* commit everything, even the ones which will go away */
11689 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11690 mNetworkAdapters[slot]->i_commit();
11691 /* copy over the new entries, creating a peer and uninit the original */
11692 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11693 for (size_t slot = 0; slot < newSize; slot++)
11694 {
11695 /* look if this adapter has a peer device */
11696 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11697 if (!peer)
11698 {
11699 /* no peer means the adapter is a newly created one;
11700 * create a peer owning data this data share it with */
11701 peer.createObject();
11702 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11703 }
11704 mPeer->mNetworkAdapters[slot] = peer;
11705 }
11706 /* uninit any no longer needed network adapters */
11707 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11708 mNetworkAdapters[slot]->uninit();
11709 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11710 {
11711 if (mPeer->mNetworkAdapters[slot])
11712 mPeer->mNetworkAdapters[slot]->uninit();
11713 }
11714 /* Keep the original network adapter count until this point, so that
11715 * discarding a chipset type change will not lose settings. */
11716 mNetworkAdapters.resize(newSize);
11717 mPeer->mNetworkAdapters.resize(newSize);
11718 }
11719 else
11720 {
11721 /* we have no peer (our parent is the newly created machine);
11722 * just commit changes to the network adapters */
11723 commitNetworkAdapters = true;
11724 }
11725 if (commitNetworkAdapters)
11726 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11727 mNetworkAdapters[slot]->i_commit();
11728
11729 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11730 mSerialPorts[slot]->i_commit();
11731 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11732 mParallelPorts[slot]->i_commit();
11733
11734 bool commitStorageControllers = false;
11735
11736 if (mStorageControllers.isBackedUp())
11737 {
11738 mStorageControllers.commit();
11739
11740 if (mPeer)
11741 {
11742 /* Commit all changes to new controllers (this will reshare data with
11743 * peers for those who have peers) */
11744 StorageControllerList *newList = new StorageControllerList();
11745 StorageControllerList::const_iterator it = mStorageControllers->begin();
11746 while (it != mStorageControllers->end())
11747 {
11748 (*it)->i_commit();
11749
11750 /* look if this controller has a peer device */
11751 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11752 if (!peer)
11753 {
11754 /* no peer means the device is a newly created one;
11755 * create a peer owning data this device share it with */
11756 peer.createObject();
11757 peer->init(mPeer, *it, true /* aReshare */);
11758 }
11759 else
11760 {
11761 /* remove peer from the old list */
11762 mPeer->mStorageControllers->remove(peer);
11763 }
11764 /* and add it to the new list */
11765 newList->push_back(peer);
11766
11767 ++it;
11768 }
11769
11770 /* uninit old peer's controllers that are left */
11771 it = mPeer->mStorageControllers->begin();
11772 while (it != mPeer->mStorageControllers->end())
11773 {
11774 (*it)->uninit();
11775 ++it;
11776 }
11777
11778 /* attach new list of controllers to our peer */
11779 mPeer->mStorageControllers.attach(newList);
11780 }
11781 else
11782 {
11783 /* we have no peer (our parent is the newly created machine);
11784 * just commit changes to devices */
11785 commitStorageControllers = true;
11786 }
11787 }
11788 else
11789 {
11790 /* the list of controllers itself is not changed,
11791 * just commit changes to controllers themselves */
11792 commitStorageControllers = true;
11793 }
11794
11795 if (commitStorageControllers)
11796 {
11797 StorageControllerList::const_iterator it = mStorageControllers->begin();
11798 while (it != mStorageControllers->end())
11799 {
11800 (*it)->i_commit();
11801 ++it;
11802 }
11803 }
11804
11805 bool commitUSBControllers = false;
11806
11807 if (mUSBControllers.isBackedUp())
11808 {
11809 mUSBControllers.commit();
11810
11811 if (mPeer)
11812 {
11813 /* Commit all changes to new controllers (this will reshare data with
11814 * peers for those who have peers) */
11815 USBControllerList *newList = new USBControllerList();
11816 USBControllerList::const_iterator it = mUSBControllers->begin();
11817 while (it != mUSBControllers->end())
11818 {
11819 (*it)->i_commit();
11820
11821 /* look if this controller has a peer device */
11822 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11823 if (!peer)
11824 {
11825 /* no peer means the device is a newly created one;
11826 * create a peer owning data this device share it with */
11827 peer.createObject();
11828 peer->init(mPeer, *it, true /* aReshare */);
11829 }
11830 else
11831 {
11832 /* remove peer from the old list */
11833 mPeer->mUSBControllers->remove(peer);
11834 }
11835 /* and add it to the new list */
11836 newList->push_back(peer);
11837
11838 ++it;
11839 }
11840
11841 /* uninit old peer's controllers that are left */
11842 it = mPeer->mUSBControllers->begin();
11843 while (it != mPeer->mUSBControllers->end())
11844 {
11845 (*it)->uninit();
11846 ++it;
11847 }
11848
11849 /* attach new list of controllers to our peer */
11850 mPeer->mUSBControllers.attach(newList);
11851 }
11852 else
11853 {
11854 /* we have no peer (our parent is the newly created machine);
11855 * just commit changes to devices */
11856 commitUSBControllers = true;
11857 }
11858 }
11859 else
11860 {
11861 /* the list of controllers itself is not changed,
11862 * just commit changes to controllers themselves */
11863 commitUSBControllers = true;
11864 }
11865
11866 if (commitUSBControllers)
11867 {
11868 USBControllerList::const_iterator it = mUSBControllers->begin();
11869 while (it != mUSBControllers->end())
11870 {
11871 (*it)->i_commit();
11872 ++it;
11873 }
11874 }
11875
11876 if (i_isSessionMachine())
11877 {
11878 /* attach new data to the primary machine and reshare it */
11879 mPeer->mUserData.attach(mUserData);
11880 mPeer->mHWData.attach(mHWData);
11881 /* mMediaData is reshared by fixupMedia */
11882 // mPeer->mMediaData.attach(mMediaData);
11883 Assert(mPeer->mMediaData.data() == mMediaData.data());
11884 }
11885}
11886
11887/**
11888 * Copies all the hardware data from the given machine.
11889 *
11890 * Currently, only called when the VM is being restored from a snapshot. In
11891 * particular, this implies that the VM is not running during this method's
11892 * call.
11893 *
11894 * @note This method must be called from under this object's lock.
11895 *
11896 * @note This method doesn't call #commit(), so all data remains backed up and
11897 * unsaved.
11898 */
11899void Machine::i_copyFrom(Machine *aThat)
11900{
11901 AssertReturnVoid(!i_isSnapshotMachine());
11902 AssertReturnVoid(aThat->i_isSnapshotMachine());
11903
11904 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11905
11906 mHWData.assignCopy(aThat->mHWData);
11907
11908 // create copies of all shared folders (mHWData after attaching a copy
11909 // contains just references to original objects)
11910 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11911 it != mHWData->mSharedFolders.end();
11912 ++it)
11913 {
11914 ComObjPtr<SharedFolder> folder;
11915 folder.createObject();
11916 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11917 AssertComRC(rc);
11918 *it = folder;
11919 }
11920
11921 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11922 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11923 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11924 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11925 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11926
11927 /* create private copies of all controllers */
11928 mStorageControllers.backup();
11929 mStorageControllers->clear();
11930 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11931 it != aThat->mStorageControllers->end();
11932 ++it)
11933 {
11934 ComObjPtr<StorageController> ctrl;
11935 ctrl.createObject();
11936 ctrl->initCopy(this, *it);
11937 mStorageControllers->push_back(ctrl);
11938 }
11939
11940 /* create private copies of all USB controllers */
11941 mUSBControllers.backup();
11942 mUSBControllers->clear();
11943 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11944 it != aThat->mUSBControllers->end();
11945 ++it)
11946 {
11947 ComObjPtr<USBController> ctrl;
11948 ctrl.createObject();
11949 ctrl->initCopy(this, *it);
11950 mUSBControllers->push_back(ctrl);
11951 }
11952
11953 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11954 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11955 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11956 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11957 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11958 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11959 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11960}
11961
11962/**
11963 * Returns whether the given storage controller is hotplug capable.
11964 *
11965 * @returns true if the controller supports hotplugging
11966 * false otherwise.
11967 * @param enmCtrlType The controller type to check for.
11968 */
11969bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11970{
11971 ComPtr<ISystemProperties> systemProperties;
11972 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11973 if (FAILED(rc))
11974 return false;
11975
11976 BOOL aHotplugCapable = FALSE;
11977 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11978
11979 return RT_BOOL(aHotplugCapable);
11980}
11981
11982#ifdef VBOX_WITH_RESOURCE_USAGE_API
11983
11984void Machine::i_getDiskList(MediaList &list)
11985{
11986 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11987 it != mMediaData->mAttachments.end();
11988 ++it)
11989 {
11990 MediumAttachment* pAttach = *it;
11991 /* just in case */
11992 AssertStmt(pAttach, continue);
11993
11994 AutoCaller localAutoCallerA(pAttach);
11995 if (FAILED(localAutoCallerA.rc())) continue;
11996
11997 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11998
11999 if (pAttach->i_getType() == DeviceType_HardDisk)
12000 list.push_back(pAttach->i_getMedium());
12001 }
12002}
12003
12004void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12005{
12006 AssertReturnVoid(isWriteLockOnCurrentThread());
12007 AssertPtrReturnVoid(aCollector);
12008
12009 pm::CollectorHAL *hal = aCollector->getHAL();
12010 /* Create sub metrics */
12011 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12012 "Percentage of processor time spent in user mode by the VM process.");
12013 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12014 "Percentage of processor time spent in kernel mode by the VM process.");
12015 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12016 "Size of resident portion of VM process in memory.");
12017 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12018 "Actual size of all VM disks combined.");
12019 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12020 "Network receive rate.");
12021 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12022 "Network transmit rate.");
12023 /* Create and register base metrics */
12024 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12025 cpuLoadUser, cpuLoadKernel);
12026 aCollector->registerBaseMetric(cpuLoad);
12027 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12028 ramUsageUsed);
12029 aCollector->registerBaseMetric(ramUsage);
12030 MediaList disks;
12031 i_getDiskList(disks);
12032 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12033 diskUsageUsed);
12034 aCollector->registerBaseMetric(diskUsage);
12035
12036 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12037 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12038 new pm::AggregateAvg()));
12039 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12040 new pm::AggregateMin()));
12041 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12042 new pm::AggregateMax()));
12043 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12044 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12045 new pm::AggregateAvg()));
12046 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12047 new pm::AggregateMin()));
12048 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12049 new pm::AggregateMax()));
12050
12051 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12052 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12053 new pm::AggregateAvg()));
12054 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12055 new pm::AggregateMin()));
12056 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12057 new pm::AggregateMax()));
12058
12059 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12060 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12061 new pm::AggregateAvg()));
12062 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12063 new pm::AggregateMin()));
12064 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12065 new pm::AggregateMax()));
12066
12067
12068 /* Guest metrics collector */
12069 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12070 aCollector->registerGuest(mCollectorGuest);
12071 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12072 this, __PRETTY_FUNCTION__, mCollectorGuest));
12073
12074 /* Create sub metrics */
12075 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12076 "Percentage of processor time spent in user mode as seen by the guest.");
12077 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12078 "Percentage of processor time spent in kernel mode as seen by the guest.");
12079 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12080 "Percentage of processor time spent idling as seen by the guest.");
12081
12082 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12083 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12084 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12085 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12086 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12087 pm::SubMetric *guestMemCache = new pm::SubMetric(
12088 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12089
12090 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12091 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12092
12093 /* Create and register base metrics */
12094 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12095 machineNetRx, machineNetTx);
12096 aCollector->registerBaseMetric(machineNetRate);
12097
12098 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12099 guestLoadUser, guestLoadKernel, guestLoadIdle);
12100 aCollector->registerBaseMetric(guestCpuLoad);
12101
12102 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12103 guestMemTotal, guestMemFree,
12104 guestMemBalloon, guestMemShared,
12105 guestMemCache, guestPagedTotal);
12106 aCollector->registerBaseMetric(guestCpuMem);
12107
12108 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12109 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12110 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12111 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12112
12113 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12114 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12115 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12116 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12117
12118 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12119 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12120 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12121 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12122
12123 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12124 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12125 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12126 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12127
12128 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12129 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12130 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12131 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12132
12133 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12134 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12135 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12136 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12137
12138 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12139 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12140 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12141 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12142
12143 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12144 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12145 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12146 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12147
12148 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12149 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12150 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12151 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12152
12153 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12154 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12155 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12156 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12157
12158 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12159 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12160 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12161 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12162}
12163
12164void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12165{
12166 AssertReturnVoid(isWriteLockOnCurrentThread());
12167
12168 if (aCollector)
12169 {
12170 aCollector->unregisterMetricsFor(aMachine);
12171 aCollector->unregisterBaseMetricsFor(aMachine);
12172 }
12173}
12174
12175#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12176
12177
12178////////////////////////////////////////////////////////////////////////////////
12179
12180DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12181
12182HRESULT SessionMachine::FinalConstruct()
12183{
12184 LogFlowThisFunc(("\n"));
12185
12186 mClientToken = NULL;
12187
12188 return BaseFinalConstruct();
12189}
12190
12191void SessionMachine::FinalRelease()
12192{
12193 LogFlowThisFunc(("\n"));
12194
12195 Assert(!mClientToken);
12196 /* paranoia, should not hang around any more */
12197 if (mClientToken)
12198 {
12199 delete mClientToken;
12200 mClientToken = NULL;
12201 }
12202
12203 uninit(Uninit::Unexpected);
12204
12205 BaseFinalRelease();
12206}
12207
12208/**
12209 * @note Must be called only by Machine::LockMachine() from its own write lock.
12210 */
12211HRESULT SessionMachine::init(Machine *aMachine)
12212{
12213 LogFlowThisFuncEnter();
12214 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12215
12216 AssertReturn(aMachine, E_INVALIDARG);
12217
12218 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12219
12220 /* Enclose the state transition NotReady->InInit->Ready */
12221 AutoInitSpan autoInitSpan(this);
12222 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12223
12224 HRESULT rc = S_OK;
12225
12226 /* create the machine client token */
12227 try
12228 {
12229 mClientToken = new ClientToken(aMachine, this);
12230 if (!mClientToken->isReady())
12231 {
12232 delete mClientToken;
12233 mClientToken = NULL;
12234 rc = E_FAIL;
12235 }
12236 }
12237 catch (std::bad_alloc &)
12238 {
12239 rc = E_OUTOFMEMORY;
12240 }
12241 if (FAILED(rc))
12242 return rc;
12243
12244 /* memorize the peer Machine */
12245 unconst(mPeer) = aMachine;
12246 /* share the parent pointer */
12247 unconst(mParent) = aMachine->mParent;
12248
12249 /* take the pointers to data to share */
12250 mData.share(aMachine->mData);
12251 mSSData.share(aMachine->mSSData);
12252
12253 mUserData.share(aMachine->mUserData);
12254 mHWData.share(aMachine->mHWData);
12255 mMediaData.share(aMachine->mMediaData);
12256
12257 mStorageControllers.allocate();
12258 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12259 it != aMachine->mStorageControllers->end();
12260 ++it)
12261 {
12262 ComObjPtr<StorageController> ctl;
12263 ctl.createObject();
12264 ctl->init(this, *it);
12265 mStorageControllers->push_back(ctl);
12266 }
12267
12268 mUSBControllers.allocate();
12269 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12270 it != aMachine->mUSBControllers->end();
12271 ++it)
12272 {
12273 ComObjPtr<USBController> ctl;
12274 ctl.createObject();
12275 ctl->init(this, *it);
12276 mUSBControllers->push_back(ctl);
12277 }
12278
12279 unconst(mBIOSSettings).createObject();
12280 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12281 /* create another VRDEServer object that will be mutable */
12282 unconst(mVRDEServer).createObject();
12283 mVRDEServer->init(this, aMachine->mVRDEServer);
12284 /* create another audio adapter object that will be mutable */
12285 unconst(mAudioAdapter).createObject();
12286 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12287 /* create a list of serial ports that will be mutable */
12288 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12289 {
12290 unconst(mSerialPorts[slot]).createObject();
12291 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12292 }
12293 /* create a list of parallel ports that will be mutable */
12294 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12295 {
12296 unconst(mParallelPorts[slot]).createObject();
12297 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12298 }
12299
12300 /* create another USB device filters object that will be mutable */
12301 unconst(mUSBDeviceFilters).createObject();
12302 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12303
12304 /* create a list of network adapters that will be mutable */
12305 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12306 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12307 {
12308 unconst(mNetworkAdapters[slot]).createObject();
12309 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12310 }
12311
12312 /* create another bandwidth control object that will be mutable */
12313 unconst(mBandwidthControl).createObject();
12314 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12315
12316 /* default is to delete saved state on Saved -> PoweredOff transition */
12317 mRemoveSavedState = true;
12318
12319 /* Confirm a successful initialization when it's the case */
12320 autoInitSpan.setSucceeded();
12321
12322 miNATNetworksStarted = 0;
12323
12324 LogFlowThisFuncLeave();
12325 return rc;
12326}
12327
12328/**
12329 * Uninitializes this session object. If the reason is other than
12330 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12331 * or the client watcher code.
12332 *
12333 * @param aReason uninitialization reason
12334 *
12335 * @note Locks mParent + this object for writing.
12336 */
12337void SessionMachine::uninit(Uninit::Reason aReason)
12338{
12339 LogFlowThisFuncEnter();
12340 LogFlowThisFunc(("reason=%d\n", aReason));
12341
12342 /*
12343 * Strongly reference ourselves to prevent this object deletion after
12344 * mData->mSession.mMachine.setNull() below (which can release the last
12345 * reference and call the destructor). Important: this must be done before
12346 * accessing any members (and before AutoUninitSpan that does it as well).
12347 * This self reference will be released as the very last step on return.
12348 */
12349 ComObjPtr<SessionMachine> selfRef = this;
12350
12351 /* Enclose the state transition Ready->InUninit->NotReady */
12352 AutoUninitSpan autoUninitSpan(this);
12353 if (autoUninitSpan.uninitDone())
12354 {
12355 LogFlowThisFunc(("Already uninitialized\n"));
12356 LogFlowThisFuncLeave();
12357 return;
12358 }
12359
12360 if (autoUninitSpan.initFailed())
12361 {
12362 /* We've been called by init() because it's failed. It's not really
12363 * necessary (nor it's safe) to perform the regular uninit sequence
12364 * below, the following is enough.
12365 */
12366 LogFlowThisFunc(("Initialization failed.\n"));
12367 /* destroy the machine client token */
12368 if (mClientToken)
12369 {
12370 delete mClientToken;
12371 mClientToken = NULL;
12372 }
12373 uninitDataAndChildObjects();
12374 mData.free();
12375 unconst(mParent) = NULL;
12376 unconst(mPeer) = NULL;
12377 LogFlowThisFuncLeave();
12378 return;
12379 }
12380
12381 MachineState_T lastState;
12382 {
12383 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12384 lastState = mData->mMachineState;
12385 }
12386 NOREF(lastState);
12387
12388#ifdef VBOX_WITH_USB
12389 // release all captured USB devices, but do this before requesting the locks below
12390 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12391 {
12392 /* Console::captureUSBDevices() is called in the VM process only after
12393 * setting the machine state to Starting or Restoring.
12394 * Console::detachAllUSBDevices() will be called upon successful
12395 * termination. So, we need to release USB devices only if there was
12396 * an abnormal termination of a running VM.
12397 *
12398 * This is identical to SessionMachine::DetachAllUSBDevices except
12399 * for the aAbnormal argument. */
12400 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12401 AssertComRC(rc);
12402 NOREF(rc);
12403
12404 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12405 if (service)
12406 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12407 }
12408#endif /* VBOX_WITH_USB */
12409
12410 // we need to lock this object in uninit() because the lock is shared
12411 // with mPeer (as well as data we modify below). mParent lock is needed
12412 // by several calls to it, and USB needs host lock.
12413 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12414
12415#ifdef VBOX_WITH_RESOURCE_USAGE_API
12416 /*
12417 * It is safe to call Machine::i_unregisterMetrics() here because
12418 * PerformanceCollector::samplerCallback no longer accesses guest methods
12419 * holding the lock.
12420 */
12421 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12422 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12423 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12424 this, __PRETTY_FUNCTION__, mCollectorGuest));
12425 if (mCollectorGuest)
12426 {
12427 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12428 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12429 mCollectorGuest = NULL;
12430 }
12431#endif
12432
12433 if (aReason == Uninit::Abnormal)
12434 {
12435 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12436 Global::IsOnlineOrTransient(lastState)));
12437
12438 /* reset the state to Aborted */
12439 if (mData->mMachineState != MachineState_Aborted)
12440 i_setMachineState(MachineState_Aborted);
12441 }
12442
12443 // any machine settings modified?
12444 if (mData->flModifications)
12445 {
12446 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12447 i_rollback(false /* aNotify */);
12448 }
12449
12450 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12451 || !mConsoleTaskData.mSnapshot);
12452 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12453 {
12454 LogWarningThisFunc(("canceling failed save state request!\n"));
12455 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12456 }
12457 else if (!mConsoleTaskData.mSnapshot.isNull())
12458 {
12459 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12460
12461 /* delete all differencing hard disks created (this will also attach
12462 * their parents back by rolling back mMediaData) */
12463 i_rollbackMedia();
12464
12465 // delete the saved state file (it might have been already created)
12466 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12467 // think it's still in use
12468 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12469 mConsoleTaskData.mSnapshot->uninit();
12470 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12471 }
12472
12473 mData->mSession.mPID = NIL_RTPROCESS;
12474
12475 if (aReason == Uninit::Unexpected)
12476 {
12477 /* Uninitialization didn't come from #checkForDeath(), so tell the
12478 * client watcher thread to update the set of machines that have open
12479 * sessions. */
12480 mParent->i_updateClientWatcher();
12481 }
12482
12483 /* uninitialize all remote controls */
12484 if (mData->mSession.mRemoteControls.size())
12485 {
12486 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12487 mData->mSession.mRemoteControls.size()));
12488
12489 Data::Session::RemoteControlList::iterator it =
12490 mData->mSession.mRemoteControls.begin();
12491 while (it != mData->mSession.mRemoteControls.end())
12492 {
12493 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12494 HRESULT rc = (*it)->Uninitialize();
12495 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12496 if (FAILED(rc))
12497 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12498 ++it;
12499 }
12500 mData->mSession.mRemoteControls.clear();
12501 }
12502
12503 /* Remove all references to the NAT network service. The service will stop
12504 * if all references (also from other VMs) are removed. */
12505 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12506 {
12507 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12508 {
12509 NetworkAttachmentType_T type;
12510 HRESULT hrc;
12511
12512 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12513 if ( SUCCEEDED(hrc)
12514 && type == NetworkAttachmentType_NATNetwork)
12515 {
12516 Bstr name;
12517 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12518 if (SUCCEEDED(hrc))
12519 {
12520 multilock.release();
12521 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12522 mUserData->s.strName.c_str(), name.raw()));
12523 mParent->i_natNetworkRefDec(name.raw());
12524 multilock.acquire();
12525 }
12526 }
12527 }
12528 }
12529
12530 /*
12531 * An expected uninitialization can come only from #checkForDeath().
12532 * Otherwise it means that something's gone really wrong (for example,
12533 * the Session implementation has released the VirtualBox reference
12534 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12535 * etc). However, it's also possible, that the client releases the IPC
12536 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12537 * but the VirtualBox release event comes first to the server process.
12538 * This case is practically possible, so we should not assert on an
12539 * unexpected uninit, just log a warning.
12540 */
12541
12542 if ((aReason == Uninit::Unexpected))
12543 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12544
12545 if (aReason != Uninit::Normal)
12546 {
12547 mData->mSession.mDirectControl.setNull();
12548 }
12549 else
12550 {
12551 /* this must be null here (see #OnSessionEnd()) */
12552 Assert(mData->mSession.mDirectControl.isNull());
12553 Assert(mData->mSession.mState == SessionState_Unlocking);
12554 Assert(!mData->mSession.mProgress.isNull());
12555 }
12556 if (mData->mSession.mProgress)
12557 {
12558 if (aReason == Uninit::Normal)
12559 mData->mSession.mProgress->i_notifyComplete(S_OK);
12560 else
12561 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12562 COM_IIDOF(ISession),
12563 getComponentName(),
12564 tr("The VM session was aborted"));
12565 mData->mSession.mProgress.setNull();
12566 }
12567
12568 /* remove the association between the peer machine and this session machine */
12569 Assert( (SessionMachine*)mData->mSession.mMachine == this
12570 || aReason == Uninit::Unexpected);
12571
12572 /* reset the rest of session data */
12573 mData->mSession.mMachine.setNull();
12574 mData->mSession.mState = SessionState_Unlocked;
12575 mData->mSession.mType.setNull();
12576
12577 /* destroy the machine client token before leaving the exclusive lock */
12578 if (mClientToken)
12579 {
12580 delete mClientToken;
12581 mClientToken = NULL;
12582 }
12583
12584 /* fire an event */
12585 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12586
12587 uninitDataAndChildObjects();
12588
12589 /* free the essential data structure last */
12590 mData.free();
12591
12592 /* release the exclusive lock before setting the below two to NULL */
12593 multilock.release();
12594
12595 unconst(mParent) = NULL;
12596 unconst(mPeer) = NULL;
12597
12598 LogFlowThisFuncLeave();
12599}
12600
12601// util::Lockable interface
12602////////////////////////////////////////////////////////////////////////////////
12603
12604/**
12605 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12606 * with the primary Machine instance (mPeer).
12607 */
12608RWLockHandle *SessionMachine::lockHandle() const
12609{
12610 AssertReturn(mPeer != NULL, NULL);
12611 return mPeer->lockHandle();
12612}
12613
12614// IInternalMachineControl methods
12615////////////////////////////////////////////////////////////////////////////////
12616
12617/**
12618 * Passes collected guest statistics to performance collector object
12619 */
12620HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12621 ULONG aCpuKernel, ULONG aCpuIdle,
12622 ULONG aMemTotal, ULONG aMemFree,
12623 ULONG aMemBalloon, ULONG aMemShared,
12624 ULONG aMemCache, ULONG aPageTotal,
12625 ULONG aAllocVMM, ULONG aFreeVMM,
12626 ULONG aBalloonedVMM, ULONG aSharedVMM,
12627 ULONG aVmNetRx, ULONG aVmNetTx)
12628{
12629#ifdef VBOX_WITH_RESOURCE_USAGE_API
12630 if (mCollectorGuest)
12631 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12632 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12633 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12634 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12635
12636 return S_OK;
12637#else
12638 NOREF(aValidStats);
12639 NOREF(aCpuUser);
12640 NOREF(aCpuKernel);
12641 NOREF(aCpuIdle);
12642 NOREF(aMemTotal);
12643 NOREF(aMemFree);
12644 NOREF(aMemBalloon);
12645 NOREF(aMemShared);
12646 NOREF(aMemCache);
12647 NOREF(aPageTotal);
12648 NOREF(aAllocVMM);
12649 NOREF(aFreeVMM);
12650 NOREF(aBalloonedVMM);
12651 NOREF(aSharedVMM);
12652 NOREF(aVmNetRx);
12653 NOREF(aVmNetTx);
12654 return E_NOTIMPL;
12655#endif
12656}
12657
12658/**
12659 * @note Locks this object for writing.
12660 */
12661HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12662{
12663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12664
12665 mRemoveSavedState = RT_BOOL(aRemove);
12666
12667 return S_OK;
12668}
12669
12670/**
12671 * @note Locks the same as #i_setMachineState() does.
12672 */
12673HRESULT SessionMachine::updateState(MachineState_T aState)
12674{
12675 return i_setMachineState(aState);
12676}
12677
12678/**
12679 * @note Locks this object for writing.
12680 */
12681HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12682{
12683 IProgress* pProgress(aProgress);
12684
12685 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12686
12687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12688
12689 if (mData->mSession.mState != SessionState_Locked)
12690 return VBOX_E_INVALID_OBJECT_STATE;
12691
12692 if (!mData->mSession.mProgress.isNull())
12693 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12694
12695 /* If we didn't reference the NAT network service yet, add a reference to
12696 * force a start */
12697 if (miNATNetworksStarted < 1)
12698 {
12699 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12700 {
12701 NetworkAttachmentType_T type;
12702 HRESULT hrc;
12703 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12704 if ( SUCCEEDED(hrc)
12705 && type == NetworkAttachmentType_NATNetwork)
12706 {
12707 Bstr name;
12708 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12709 if (SUCCEEDED(hrc))
12710 {
12711 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12712 mUserData->s.strName.c_str(), name.raw()));
12713 mPeer->lockHandle()->unlockWrite();
12714 mParent->i_natNetworkRefInc(name.raw());
12715#ifdef RT_LOCK_STRICT
12716 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12717#else
12718 mPeer->lockHandle()->lockWrite();
12719#endif
12720 }
12721 }
12722 }
12723 miNATNetworksStarted++;
12724 }
12725
12726 LogFlowThisFunc(("returns S_OK.\n"));
12727 return S_OK;
12728}
12729
12730/**
12731 * @note Locks this object for writing.
12732 */
12733HRESULT SessionMachine::endPowerUp(LONG aResult)
12734{
12735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12736
12737 if (mData->mSession.mState != SessionState_Locked)
12738 return VBOX_E_INVALID_OBJECT_STATE;
12739
12740 /* Finalize the LaunchVMProcess progress object. */
12741 if (mData->mSession.mProgress)
12742 {
12743 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12744 mData->mSession.mProgress.setNull();
12745 }
12746
12747 if (SUCCEEDED((HRESULT)aResult))
12748 {
12749#ifdef VBOX_WITH_RESOURCE_USAGE_API
12750 /* The VM has been powered up successfully, so it makes sense
12751 * now to offer the performance metrics for a running machine
12752 * object. Doing it earlier wouldn't be safe. */
12753 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12754 mData->mSession.mPID);
12755#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12756 }
12757
12758 return S_OK;
12759}
12760
12761/**
12762 * @note Locks this object for writing.
12763 */
12764HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12765{
12766 LogFlowThisFuncEnter();
12767
12768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12769
12770 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12771 E_FAIL);
12772
12773 /* create a progress object to track operation completion */
12774 ComObjPtr<Progress> pProgress;
12775 pProgress.createObject();
12776 pProgress->init(i_getVirtualBox(),
12777 static_cast<IMachine *>(this) /* aInitiator */,
12778 Bstr(tr("Stopping the virtual machine")).raw(),
12779 FALSE /* aCancelable */);
12780
12781 /* fill in the console task data */
12782 mConsoleTaskData.mLastState = mData->mMachineState;
12783 mConsoleTaskData.mProgress = pProgress;
12784
12785 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12786 i_setMachineState(MachineState_Stopping);
12787
12788 pProgress.queryInterfaceTo(aProgress.asOutParam());
12789
12790 return S_OK;
12791}
12792
12793/**
12794 * @note Locks this object for writing.
12795 */
12796HRESULT SessionMachine::endPoweringDown(LONG aResult,
12797 const com::Utf8Str &aErrMsg)
12798{
12799 LogFlowThisFuncEnter();
12800
12801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12802
12803 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12804 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12805 && mConsoleTaskData.mLastState != MachineState_Null,
12806 E_FAIL);
12807
12808 /*
12809 * On failure, set the state to the state we had when BeginPoweringDown()
12810 * was called (this is expected by Console::PowerDown() and the associated
12811 * task). On success the VM process already changed the state to
12812 * MachineState_PoweredOff, so no need to do anything.
12813 */
12814 if (FAILED(aResult))
12815 i_setMachineState(mConsoleTaskData.mLastState);
12816
12817 /* notify the progress object about operation completion */
12818 Assert(mConsoleTaskData.mProgress);
12819 if (SUCCEEDED(aResult))
12820 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12821 else
12822 {
12823 if (aErrMsg.length())
12824 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12825 COM_IIDOF(ISession),
12826 getComponentName(),
12827 aErrMsg.c_str());
12828 else
12829 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12830 }
12831
12832 /* clear out the temporary saved state data */
12833 mConsoleTaskData.mLastState = MachineState_Null;
12834 mConsoleTaskData.mProgress.setNull();
12835
12836 LogFlowThisFuncLeave();
12837 return S_OK;
12838}
12839
12840
12841/**
12842 * Goes through the USB filters of the given machine to see if the given
12843 * device matches any filter or not.
12844 *
12845 * @note Locks the same as USBController::hasMatchingFilter() does.
12846 */
12847HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12848 BOOL *aMatched,
12849 ULONG *aMaskedInterfaces)
12850{
12851 LogFlowThisFunc(("\n"));
12852
12853#ifdef VBOX_WITH_USB
12854 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12855#else
12856 NOREF(aDevice);
12857 NOREF(aMaskedInterfaces);
12858 *aMatched = FALSE;
12859#endif
12860
12861 return S_OK;
12862}
12863
12864/**
12865 * @note Locks the same as Host::captureUSBDevice() does.
12866 */
12867HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
12868{
12869 LogFlowThisFunc(("\n"));
12870
12871#ifdef VBOX_WITH_USB
12872 /* if captureDeviceForVM() fails, it must have set extended error info */
12873 clearError();
12874 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12875 if (FAILED(rc)) return rc;
12876
12877 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12878 AssertReturn(service, E_FAIL);
12879 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
12880#else
12881 NOREF(aId);
12882 return E_NOTIMPL;
12883#endif
12884}
12885
12886/**
12887 * @note Locks the same as Host::detachUSBDevice() does.
12888 */
12889HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12890 BOOL aDone)
12891{
12892 LogFlowThisFunc(("\n"));
12893
12894#ifdef VBOX_WITH_USB
12895 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12896 AssertReturn(service, E_FAIL);
12897 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12898#else
12899 NOREF(aId);
12900 NOREF(aDone);
12901 return E_NOTIMPL;
12902#endif
12903}
12904
12905/**
12906 * Inserts all machine filters to the USB proxy service and then calls
12907 * Host::autoCaptureUSBDevices().
12908 *
12909 * Called by Console from the VM process upon VM startup.
12910 *
12911 * @note Locks what called methods lock.
12912 */
12913HRESULT SessionMachine::autoCaptureUSBDevices()
12914{
12915 LogFlowThisFunc(("\n"));
12916
12917#ifdef VBOX_WITH_USB
12918 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12919 AssertComRC(rc);
12920 NOREF(rc);
12921
12922 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12923 AssertReturn(service, E_FAIL);
12924 return service->autoCaptureDevicesForVM(this);
12925#else
12926 return S_OK;
12927#endif
12928}
12929
12930/**
12931 * Removes all machine filters from the USB proxy service and then calls
12932 * Host::detachAllUSBDevices().
12933 *
12934 * Called by Console from the VM process upon normal VM termination or by
12935 * SessionMachine::uninit() upon abnormal VM termination (from under the
12936 * Machine/SessionMachine lock).
12937 *
12938 * @note Locks what called methods lock.
12939 */
12940HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12941{
12942 LogFlowThisFunc(("\n"));
12943
12944#ifdef VBOX_WITH_USB
12945 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12946 AssertComRC(rc);
12947 NOREF(rc);
12948
12949 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12950 AssertReturn(service, E_FAIL);
12951 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12952#else
12953 NOREF(aDone);
12954 return S_OK;
12955#endif
12956}
12957
12958/**
12959 * @note Locks this object for writing.
12960 */
12961HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12962 ComPtr<IProgress> &aProgress)
12963{
12964 LogFlowThisFuncEnter();
12965
12966 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12967 /*
12968 * We don't assert below because it might happen that a non-direct session
12969 * informs us it is closed right after we've been uninitialized -- it's ok.
12970 */
12971
12972 /* get IInternalSessionControl interface */
12973 ComPtr<IInternalSessionControl> control(aSession);
12974
12975 ComAssertRet(!control.isNull(), E_INVALIDARG);
12976
12977 /* Creating a Progress object requires the VirtualBox lock, and
12978 * thus locking it here is required by the lock order rules. */
12979 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12980
12981 if (control == mData->mSession.mDirectControl)
12982 {
12983 /* The direct session is being normally closed by the client process
12984 * ----------------------------------------------------------------- */
12985
12986 /* go to the closing state (essential for all open*Session() calls and
12987 * for #checkForDeath()) */
12988 Assert(mData->mSession.mState == SessionState_Locked);
12989 mData->mSession.mState = SessionState_Unlocking;
12990
12991 /* set direct control to NULL to release the remote instance */
12992 mData->mSession.mDirectControl.setNull();
12993 LogFlowThisFunc(("Direct control is set to NULL\n"));
12994
12995 if (mData->mSession.mProgress)
12996 {
12997 /* finalize the progress, someone might wait if a frontend
12998 * closes the session before powering on the VM. */
12999 mData->mSession.mProgress->notifyComplete(E_FAIL,
13000 COM_IIDOF(ISession),
13001 getComponentName(),
13002 tr("The VM session was closed before any attempt to power it on"));
13003 mData->mSession.mProgress.setNull();
13004 }
13005
13006 /* Create the progress object the client will use to wait until
13007 * #checkForDeath() is called to uninitialize this session object after
13008 * it releases the IPC semaphore.
13009 * Note! Because we're "reusing" mProgress here, this must be a proxy
13010 * object just like for LaunchVMProcess. */
13011 Assert(mData->mSession.mProgress.isNull());
13012 ComObjPtr<ProgressProxy> progress;
13013 progress.createObject();
13014 ComPtr<IUnknown> pPeer(mPeer);
13015 progress->init(mParent, pPeer,
13016 Bstr(tr("Closing session")).raw(),
13017 FALSE /* aCancelable */);
13018 progress.queryInterfaceTo(aProgress.asOutParam());
13019 mData->mSession.mProgress = progress;
13020 }
13021 else
13022 {
13023 /* the remote session is being normally closed */
13024 Data::Session::RemoteControlList::iterator it =
13025 mData->mSession.mRemoteControls.begin();
13026 while (it != mData->mSession.mRemoteControls.end())
13027 {
13028 if (control == *it)
13029 break;
13030 ++it;
13031 }
13032 BOOL found = it != mData->mSession.mRemoteControls.end();
13033 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13034 E_INVALIDARG);
13035 // This MUST be erase(it), not remove(*it) as the latter triggers a
13036 // very nasty use after free due to the place where the value "lives".
13037 mData->mSession.mRemoteControls.erase(it);
13038 }
13039
13040 /* signal the client watcher thread, because the client is going away */
13041 mParent->i_updateClientWatcher();
13042
13043 LogFlowThisFuncLeave();
13044 return S_OK;
13045}
13046
13047/**
13048 * @note Locks this object for writing.
13049 */
13050HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
13051 com::Utf8Str &aStateFilePath)
13052{
13053 LogFlowThisFuncEnter();
13054
13055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13056
13057 AssertReturn( mData->mMachineState == MachineState_Paused
13058 && mConsoleTaskData.mLastState == MachineState_Null
13059 && mConsoleTaskData.strStateFilePath.isEmpty(),
13060 E_FAIL);
13061
13062 /* create a progress object to track operation completion */
13063 ComObjPtr<Progress> pProgress;
13064 pProgress.createObject();
13065 pProgress->init(i_getVirtualBox(),
13066 static_cast<IMachine *>(this) /* aInitiator */,
13067 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13068 FALSE /* aCancelable */);
13069
13070 /* stateFilePath is null when the machine is not running */
13071 if (mData->mMachineState == MachineState_Paused)
13072 i_composeSavedStateFilename(aStateFilePath);
13073
13074 /* fill in the console task data */
13075 mConsoleTaskData.mLastState = mData->mMachineState;
13076 mConsoleTaskData.strStateFilePath = aStateFilePath;
13077 mConsoleTaskData.mProgress = pProgress;
13078
13079 /* set the state to Saving (this is expected by Console::SaveState()) */
13080 i_setMachineState(MachineState_Saving);
13081
13082 pProgress.queryInterfaceTo(aProgress.asOutParam());
13083
13084 return S_OK;
13085}
13086
13087/**
13088 * @note Locks mParent + this object for writing.
13089 */
13090HRESULT SessionMachine::endSavingState(LONG aResult,
13091 const com::Utf8Str &aErrMsg)
13092{
13093 LogFlowThisFunc(("\n"));
13094
13095 /* endSavingState() need mParent lock */
13096 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13097
13098 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13099 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13100 && mConsoleTaskData.mLastState != MachineState_Null
13101 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13102 E_FAIL);
13103
13104 /*
13105 * On failure, set the state to the state we had when BeginSavingState()
13106 * was called (this is expected by Console::SaveState() and the associated
13107 * task). On success the VM process already changed the state to
13108 * MachineState_Saved, so no need to do anything.
13109 */
13110 if (FAILED(aResult))
13111 i_setMachineState(mConsoleTaskData.mLastState);
13112
13113 return i_endSavingState(aResult, aErrMsg);
13114}
13115
13116/**
13117 * @note Locks this object for writing.
13118 */
13119HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13120{
13121 LogFlowThisFunc(("\n"));
13122
13123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13124
13125 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13126 || mData->mMachineState == MachineState_Teleported
13127 || mData->mMachineState == MachineState_Aborted
13128 , E_FAIL); /** @todo setError. */
13129
13130 com::Utf8Str stateFilePathFull;
13131 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13132 if (RT_FAILURE(vrc))
13133 return setError(VBOX_E_FILE_ERROR,
13134 tr("Invalid saved state file path '%s' (%Rrc)"),
13135 aSavedStateFile.c_str(),
13136 vrc);
13137
13138 mSSData->strStateFilePath = stateFilePathFull;
13139
13140 /* The below i_setMachineState() will detect the state transition and will
13141 * update the settings file */
13142
13143 return i_setMachineState(MachineState_Saved);
13144}
13145
13146HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13147 std::vector<com::Utf8Str> &aValues,
13148 std::vector<LONG64> &aTimestamps,
13149 std::vector<com::Utf8Str> &aFlags)
13150{
13151 LogFlowThisFunc(("\n"));
13152
13153#ifdef VBOX_WITH_GUEST_PROPS
13154 using namespace guestProp;
13155
13156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13157
13158 size_t cEntries = mHWData->mGuestProperties.size();
13159 aNames.resize(cEntries);
13160 aValues.resize(cEntries);
13161 aTimestamps.resize(cEntries);
13162 aFlags.resize(cEntries);
13163
13164 size_t i = 0;
13165 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13166 it != mHWData->mGuestProperties.end();
13167 ++it, ++i)
13168 {
13169 char szFlags[MAX_FLAGS_LEN + 1];
13170 aNames[i] = it->first;
13171 aValues[i] = it->second.strValue;
13172 aTimestamps[i] = it->second.mTimestamp;
13173
13174 /* If it is NULL, keep it NULL. */
13175 if (it->second.mFlags)
13176 {
13177 writeFlags(it->second.mFlags, szFlags);
13178 aFlags[i] = szFlags;
13179 }
13180 else
13181 aFlags[i] = "";
13182 }
13183 return S_OK;
13184#else
13185 ReturnComNotImplemented();
13186#endif
13187}
13188
13189HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13190 const com::Utf8Str &aValue,
13191 LONG64 aTimestamp,
13192 const com::Utf8Str &aFlags)
13193{
13194 LogFlowThisFunc(("\n"));
13195
13196#ifdef VBOX_WITH_GUEST_PROPS
13197 using namespace guestProp;
13198
13199 try
13200 {
13201 /*
13202 * Convert input up front.
13203 */
13204 uint32_t fFlags = NILFLAG;
13205 if (aFlags.length())
13206 {
13207 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13208 AssertRCReturn(vrc, E_INVALIDARG);
13209 }
13210
13211 /*
13212 * Now grab the object lock, validate the state and do the update.
13213 */
13214
13215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13216
13217 switch (mData->mMachineState)
13218 {
13219 case MachineState_Paused:
13220 case MachineState_Running:
13221 case MachineState_Teleporting:
13222 case MachineState_TeleportingPausedVM:
13223 case MachineState_LiveSnapshotting:
13224 case MachineState_DeletingSnapshotOnline:
13225 case MachineState_DeletingSnapshotPaused:
13226 case MachineState_Saving:
13227 case MachineState_Stopping:
13228 break;
13229
13230 default:
13231 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13232 VBOX_E_INVALID_VM_STATE);
13233 }
13234
13235 i_setModified(IsModified_MachineData);
13236 mHWData.backup();
13237
13238 bool fDelete = !aValue.length();
13239 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13240 if (it != mHWData->mGuestProperties.end())
13241 {
13242 if (!fDelete)
13243 {
13244 it->second.strValue = aValue;
13245 it->second.mTimestamp = aTimestamp;
13246 it->second.mFlags = fFlags;
13247 }
13248 else
13249 mHWData->mGuestProperties.erase(it);
13250
13251 mData->mGuestPropertiesModified = TRUE;
13252 }
13253 else if (!fDelete)
13254 {
13255 HWData::GuestProperty prop;
13256 prop.strValue = aValue;
13257 prop.mTimestamp = aTimestamp;
13258 prop.mFlags = fFlags;
13259
13260 mHWData->mGuestProperties[aName] = prop;
13261 mData->mGuestPropertiesModified = TRUE;
13262 }
13263
13264 /*
13265 * Send a callback notification if appropriate
13266 */
13267 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13268 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13269 RTSTR_MAX,
13270 aName.c_str(),
13271 RTSTR_MAX, NULL)
13272 )
13273 {
13274 alock.release();
13275
13276 mParent->i_onGuestPropertyChange(mData->mUuid,
13277 Bstr(aName).raw(),
13278 Bstr(aValue).raw(),
13279 Bstr(aFlags).raw());
13280 }
13281 }
13282 catch (...)
13283 {
13284 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13285 }
13286 return S_OK;
13287#else
13288 ReturnComNotImplemented();
13289#endif
13290}
13291
13292
13293HRESULT SessionMachine::lockMedia()
13294{
13295 AutoMultiWriteLock2 alock(this->lockHandle(),
13296 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13297
13298 AssertReturn( mData->mMachineState == MachineState_Starting
13299 || mData->mMachineState == MachineState_Restoring
13300 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13301
13302 clearError();
13303 alock.release();
13304 return i_lockMedia();
13305}
13306
13307HRESULT SessionMachine::unlockMedia()
13308{
13309 HRESULT hrc = i_unlockMedia();
13310 return hrc;
13311}
13312
13313HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13314 ComPtr<IMediumAttachment> &aNewAttachment)
13315{
13316 // request the host lock first, since might be calling Host methods for getting host drives;
13317 // next, protect the media tree all the while we're in here, as well as our member variables
13318 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13319 this->lockHandle(),
13320 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13321
13322 IMediumAttachment *iAttach = aAttachment;
13323 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13324
13325 Bstr ctrlName;
13326 LONG lPort;
13327 LONG lDevice;
13328 bool fTempEject;
13329 {
13330 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13331
13332 /* Need to query the details first, as the IMediumAttachment reference
13333 * might be to the original settings, which we are going to change. */
13334 ctrlName = pAttach->i_getControllerName();
13335 lPort = pAttach->i_getPort();
13336 lDevice = pAttach->i_getDevice();
13337 fTempEject = pAttach->i_getTempEject();
13338 }
13339
13340 if (!fTempEject)
13341 {
13342 /* Remember previously mounted medium. The medium before taking the
13343 * backup is not necessarily the same thing. */
13344 ComObjPtr<Medium> oldmedium;
13345 oldmedium = pAttach->i_getMedium();
13346
13347 i_setModified(IsModified_Storage);
13348 mMediaData.backup();
13349
13350 // The backup operation makes the pAttach reference point to the
13351 // old settings. Re-get the correct reference.
13352 pAttach = i_findAttachment(mMediaData->mAttachments,
13353 ctrlName.raw(),
13354 lPort,
13355 lDevice);
13356
13357 {
13358 AutoCaller autoAttachCaller(this);
13359 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13360
13361 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13362 if (!oldmedium.isNull())
13363 oldmedium->i_removeBackReference(mData->mUuid);
13364
13365 pAttach->i_updateMedium(NULL);
13366 pAttach->i_updateEjected();
13367 }
13368
13369 i_setModified(IsModified_Storage);
13370 }
13371 else
13372 {
13373 {
13374 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13375 pAttach->i_updateEjected();
13376 }
13377 }
13378
13379 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13380
13381 return S_OK;
13382}
13383
13384// public methods only for internal purposes
13385/////////////////////////////////////////////////////////////////////////////
13386
13387#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13388/**
13389 * Called from the client watcher thread to check for expected or unexpected
13390 * death of the client process that has a direct session to this machine.
13391 *
13392 * On Win32 and on OS/2, this method is called only when we've got the
13393 * mutex (i.e. the client has either died or terminated normally) so it always
13394 * returns @c true (the client is terminated, the session machine is
13395 * uninitialized).
13396 *
13397 * On other platforms, the method returns @c true if the client process has
13398 * terminated normally or abnormally and the session machine was uninitialized,
13399 * and @c false if the client process is still alive.
13400 *
13401 * @note Locks this object for writing.
13402 */
13403bool SessionMachine::i_checkForDeath()
13404{
13405 Uninit::Reason reason;
13406 bool terminated = false;
13407
13408 /* Enclose autoCaller with a block because calling uninit() from under it
13409 * will deadlock. */
13410 {
13411 AutoCaller autoCaller(this);
13412 if (!autoCaller.isOk())
13413 {
13414 /* return true if not ready, to cause the client watcher to exclude
13415 * the corresponding session from watching */
13416 LogFlowThisFunc(("Already uninitialized!\n"));
13417 return true;
13418 }
13419
13420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13421
13422 /* Determine the reason of death: if the session state is Closing here,
13423 * everything is fine. Otherwise it means that the client did not call
13424 * OnSessionEnd() before it released the IPC semaphore. This may happen
13425 * either because the client process has abnormally terminated, or
13426 * because it simply forgot to call ISession::Close() before exiting. We
13427 * threat the latter also as an abnormal termination (see
13428 * Session::uninit() for details). */
13429 reason = mData->mSession.mState == SessionState_Unlocking ?
13430 Uninit::Normal :
13431 Uninit::Abnormal;
13432
13433 if (mClientToken)
13434 terminated = mClientToken->release();
13435 } /* AutoCaller block */
13436
13437 if (terminated)
13438 uninit(reason);
13439
13440 return terminated;
13441}
13442
13443void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13444{
13445 LogFlowThisFunc(("\n"));
13446
13447 strTokenId.setNull();
13448
13449 AutoCaller autoCaller(this);
13450 AssertComRCReturnVoid(autoCaller.rc());
13451
13452 Assert(mClientToken);
13453 if (mClientToken)
13454 mClientToken->getId(strTokenId);
13455}
13456#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13457IToken *SessionMachine::i_getToken()
13458{
13459 LogFlowThisFunc(("\n"));
13460
13461 AutoCaller autoCaller(this);
13462 AssertComRCReturn(autoCaller.rc(), NULL);
13463
13464 Assert(mClientToken);
13465 if (mClientToken)
13466 return mClientToken->getToken();
13467 else
13468 return NULL;
13469}
13470#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13471
13472Machine::ClientToken *SessionMachine::i_getClientToken()
13473{
13474 LogFlowThisFunc(("\n"));
13475
13476 AutoCaller autoCaller(this);
13477 AssertComRCReturn(autoCaller.rc(), NULL);
13478
13479 return mClientToken;
13480}
13481
13482
13483/**
13484 * @note Locks this object for reading.
13485 */
13486HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13487{
13488 LogFlowThisFunc(("\n"));
13489
13490 AutoCaller autoCaller(this);
13491 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13492
13493 ComPtr<IInternalSessionControl> directControl;
13494 {
13495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13496 directControl = mData->mSession.mDirectControl;
13497 }
13498
13499 /* ignore notifications sent after #OnSessionEnd() is called */
13500 if (!directControl)
13501 return S_OK;
13502
13503 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13504}
13505
13506/**
13507 * @note Locks this object for reading.
13508 */
13509HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13510 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13511 IN_BSTR aGuestIp, LONG aGuestPort)
13512{
13513 LogFlowThisFunc(("\n"));
13514
13515 AutoCaller autoCaller(this);
13516 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13517
13518 ComPtr<IInternalSessionControl> directControl;
13519 {
13520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13521 directControl = mData->mSession.mDirectControl;
13522 }
13523
13524 /* ignore notifications sent after #OnSessionEnd() is called */
13525 if (!directControl)
13526 return S_OK;
13527 /*
13528 * instead acting like callback we ask IVirtualBox deliver corresponding event
13529 */
13530
13531 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13532 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13533 return S_OK;
13534}
13535
13536/**
13537 * @note Locks this object for reading.
13538 */
13539HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13540{
13541 LogFlowThisFunc(("\n"));
13542
13543 AutoCaller autoCaller(this);
13544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13545
13546 ComPtr<IInternalSessionControl> directControl;
13547 {
13548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13549 directControl = mData->mSession.mDirectControl;
13550 }
13551
13552 /* ignore notifications sent after #OnSessionEnd() is called */
13553 if (!directControl)
13554 return S_OK;
13555
13556 return directControl->OnSerialPortChange(serialPort);
13557}
13558
13559/**
13560 * @note Locks this object for reading.
13561 */
13562HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13563{
13564 LogFlowThisFunc(("\n"));
13565
13566 AutoCaller autoCaller(this);
13567 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13568
13569 ComPtr<IInternalSessionControl> directControl;
13570 {
13571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13572 directControl = mData->mSession.mDirectControl;
13573 }
13574
13575 /* ignore notifications sent after #OnSessionEnd() is called */
13576 if (!directControl)
13577 return S_OK;
13578
13579 return directControl->OnParallelPortChange(parallelPort);
13580}
13581
13582/**
13583 * @note Locks this object for reading.
13584 */
13585HRESULT SessionMachine::i_onStorageControllerChange()
13586{
13587 LogFlowThisFunc(("\n"));
13588
13589 AutoCaller autoCaller(this);
13590 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13591
13592 ComPtr<IInternalSessionControl> directControl;
13593 {
13594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13595 directControl = mData->mSession.mDirectControl;
13596 }
13597
13598 /* ignore notifications sent after #OnSessionEnd() is called */
13599 if (!directControl)
13600 return S_OK;
13601
13602 return directControl->OnStorageControllerChange();
13603}
13604
13605/**
13606 * @note Locks this object for reading.
13607 */
13608HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13609{
13610 LogFlowThisFunc(("\n"));
13611
13612 AutoCaller autoCaller(this);
13613 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13614
13615 ComPtr<IInternalSessionControl> directControl;
13616 {
13617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13618 directControl = mData->mSession.mDirectControl;
13619 }
13620
13621 /* ignore notifications sent after #OnSessionEnd() is called */
13622 if (!directControl)
13623 return S_OK;
13624
13625 return directControl->OnMediumChange(aAttachment, aForce);
13626}
13627
13628/**
13629 * @note Locks this object for reading.
13630 */
13631HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13632{
13633 LogFlowThisFunc(("\n"));
13634
13635 AutoCaller autoCaller(this);
13636 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13637
13638 ComPtr<IInternalSessionControl> directControl;
13639 {
13640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13641 directControl = mData->mSession.mDirectControl;
13642 }
13643
13644 /* ignore notifications sent after #OnSessionEnd() is called */
13645 if (!directControl)
13646 return S_OK;
13647
13648 return directControl->OnCPUChange(aCPU, aRemove);
13649}
13650
13651HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13652{
13653 LogFlowThisFunc(("\n"));
13654
13655 AutoCaller autoCaller(this);
13656 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13657
13658 ComPtr<IInternalSessionControl> directControl;
13659 {
13660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13661 directControl = mData->mSession.mDirectControl;
13662 }
13663
13664 /* ignore notifications sent after #OnSessionEnd() is called */
13665 if (!directControl)
13666 return S_OK;
13667
13668 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13669}
13670
13671/**
13672 * @note Locks this object for reading.
13673 */
13674HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13675{
13676 LogFlowThisFunc(("\n"));
13677
13678 AutoCaller autoCaller(this);
13679 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13680
13681 ComPtr<IInternalSessionControl> directControl;
13682 {
13683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13684 directControl = mData->mSession.mDirectControl;
13685 }
13686
13687 /* ignore notifications sent after #OnSessionEnd() is called */
13688 if (!directControl)
13689 return S_OK;
13690
13691 return directControl->OnVRDEServerChange(aRestart);
13692}
13693
13694/**
13695 * @note Locks this object for reading.
13696 */
13697HRESULT SessionMachine::i_onVideoCaptureChange()
13698{
13699 LogFlowThisFunc(("\n"));
13700
13701 AutoCaller autoCaller(this);
13702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13703
13704 ComPtr<IInternalSessionControl> directControl;
13705 {
13706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13707 directControl = mData->mSession.mDirectControl;
13708 }
13709
13710 /* ignore notifications sent after #OnSessionEnd() is called */
13711 if (!directControl)
13712 return S_OK;
13713
13714 return directControl->OnVideoCaptureChange();
13715}
13716
13717/**
13718 * @note Locks this object for reading.
13719 */
13720HRESULT SessionMachine::i_onUSBControllerChange()
13721{
13722 LogFlowThisFunc(("\n"));
13723
13724 AutoCaller autoCaller(this);
13725 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13726
13727 ComPtr<IInternalSessionControl> directControl;
13728 {
13729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13730 directControl = mData->mSession.mDirectControl;
13731 }
13732
13733 /* ignore notifications sent after #OnSessionEnd() is called */
13734 if (!directControl)
13735 return S_OK;
13736
13737 return directControl->OnUSBControllerChange();
13738}
13739
13740/**
13741 * @note Locks this object for reading.
13742 */
13743HRESULT SessionMachine::i_onSharedFolderChange()
13744{
13745 LogFlowThisFunc(("\n"));
13746
13747 AutoCaller autoCaller(this);
13748 AssertComRCReturnRC(autoCaller.rc());
13749
13750 ComPtr<IInternalSessionControl> directControl;
13751 {
13752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13753 directControl = mData->mSession.mDirectControl;
13754 }
13755
13756 /* ignore notifications sent after #OnSessionEnd() is called */
13757 if (!directControl)
13758 return S_OK;
13759
13760 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13761}
13762
13763/**
13764 * @note Locks this object for reading.
13765 */
13766HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13767{
13768 LogFlowThisFunc(("\n"));
13769
13770 AutoCaller autoCaller(this);
13771 AssertComRCReturnRC(autoCaller.rc());
13772
13773 ComPtr<IInternalSessionControl> directControl;
13774 {
13775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13776 directControl = mData->mSession.mDirectControl;
13777 }
13778
13779 /* ignore notifications sent after #OnSessionEnd() is called */
13780 if (!directControl)
13781 return S_OK;
13782
13783 return directControl->OnClipboardModeChange(aClipboardMode);
13784}
13785
13786/**
13787 * @note Locks this object for reading.
13788 */
13789HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13790{
13791 LogFlowThisFunc(("\n"));
13792
13793 AutoCaller autoCaller(this);
13794 AssertComRCReturnRC(autoCaller.rc());
13795
13796 ComPtr<IInternalSessionControl> directControl;
13797 {
13798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13799 directControl = mData->mSession.mDirectControl;
13800 }
13801
13802 /* ignore notifications sent after #OnSessionEnd() is called */
13803 if (!directControl)
13804 return S_OK;
13805
13806 return directControl->OnDnDModeChange(aDnDMode);
13807}
13808
13809/**
13810 * @note Locks this object for reading.
13811 */
13812HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13813{
13814 LogFlowThisFunc(("\n"));
13815
13816 AutoCaller autoCaller(this);
13817 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13818
13819 ComPtr<IInternalSessionControl> directControl;
13820 {
13821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13822 directControl = mData->mSession.mDirectControl;
13823 }
13824
13825 /* ignore notifications sent after #OnSessionEnd() is called */
13826 if (!directControl)
13827 return S_OK;
13828
13829 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13830}
13831
13832/**
13833 * @note Locks this object for reading.
13834 */
13835HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13836{
13837 LogFlowThisFunc(("\n"));
13838
13839 AutoCaller autoCaller(this);
13840 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13841
13842 ComPtr<IInternalSessionControl> directControl;
13843 {
13844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13845 directControl = mData->mSession.mDirectControl;
13846 }
13847
13848 /* ignore notifications sent after #OnSessionEnd() is called */
13849 if (!directControl)
13850 return S_OK;
13851
13852 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13853}
13854
13855/**
13856 * Returns @c true if this machine's USB controller reports it has a matching
13857 * filter for the given USB device and @c false otherwise.
13858 *
13859 * @note locks this object for reading.
13860 */
13861bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13862{
13863 AutoCaller autoCaller(this);
13864 /* silently return if not ready -- this method may be called after the
13865 * direct machine session has been called */
13866 if (!autoCaller.isOk())
13867 return false;
13868
13869#ifdef VBOX_WITH_USB
13870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13871
13872 switch (mData->mMachineState)
13873 {
13874 case MachineState_Starting:
13875 case MachineState_Restoring:
13876 case MachineState_TeleportingIn:
13877 case MachineState_Paused:
13878 case MachineState_Running:
13879 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13880 * elsewhere... */
13881 alock.release();
13882 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13883 default: break;
13884 }
13885#else
13886 NOREF(aDevice);
13887 NOREF(aMaskedIfs);
13888#endif
13889 return false;
13890}
13891
13892/**
13893 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13894 */
13895HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13896 IVirtualBoxErrorInfo *aError,
13897 ULONG aMaskedIfs,
13898 const com::Utf8Str &aCaptureFilename)
13899{
13900 LogFlowThisFunc(("\n"));
13901
13902 AutoCaller autoCaller(this);
13903
13904 /* This notification may happen after the machine object has been
13905 * uninitialized (the session was closed), so don't assert. */
13906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13907
13908 ComPtr<IInternalSessionControl> directControl;
13909 {
13910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13911 directControl = mData->mSession.mDirectControl;
13912 }
13913
13914 /* fail on notifications sent after #OnSessionEnd() is called, it is
13915 * expected by the caller */
13916 if (!directControl)
13917 return E_FAIL;
13918
13919 /* No locks should be held at this point. */
13920 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13921 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13922
13923 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
13924}
13925
13926/**
13927 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13928 */
13929HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13930 IVirtualBoxErrorInfo *aError)
13931{
13932 LogFlowThisFunc(("\n"));
13933
13934 AutoCaller autoCaller(this);
13935
13936 /* This notification may happen after the machine object has been
13937 * uninitialized (the session was closed), so don't assert. */
13938 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13939
13940 ComPtr<IInternalSessionControl> directControl;
13941 {
13942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13943 directControl = mData->mSession.mDirectControl;
13944 }
13945
13946 /* fail on notifications sent after #OnSessionEnd() is called, it is
13947 * expected by the caller */
13948 if (!directControl)
13949 return E_FAIL;
13950
13951 /* No locks should be held at this point. */
13952 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13953 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13954
13955 return directControl->OnUSBDeviceDetach(aId, aError);
13956}
13957
13958// protected methods
13959/////////////////////////////////////////////////////////////////////////////
13960
13961/**
13962 * Helper method to finalize saving the state.
13963 *
13964 * @note Must be called from under this object's lock.
13965 *
13966 * @param aRc S_OK if the snapshot has been taken successfully
13967 * @param aErrMsg human readable error message for failure
13968 *
13969 * @note Locks mParent + this objects for writing.
13970 */
13971HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13972{
13973 LogFlowThisFuncEnter();
13974
13975 AutoCaller autoCaller(this);
13976 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13977
13978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13979
13980 HRESULT rc = S_OK;
13981
13982 if (SUCCEEDED(aRc))
13983 {
13984 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13985
13986 /* save all VM settings */
13987 rc = i_saveSettings(NULL);
13988 // no need to check whether VirtualBox.xml needs saving also since
13989 // we can't have a name change pending at this point
13990 }
13991 else
13992 {
13993 // delete the saved state file (it might have been already created);
13994 // we need not check whether this is shared with a snapshot here because
13995 // we certainly created this saved state file here anew
13996 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13997 }
13998
13999 /* notify the progress object about operation completion */
14000 Assert(mConsoleTaskData.mProgress);
14001 if (SUCCEEDED(aRc))
14002 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
14003 else
14004 {
14005 if (aErrMsg.length())
14006 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
14007 COM_IIDOF(ISession),
14008 getComponentName(),
14009 aErrMsg.c_str());
14010 else
14011 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
14012 }
14013
14014 /* clear out the temporary saved state data */
14015 mConsoleTaskData.mLastState = MachineState_Null;
14016 mConsoleTaskData.strStateFilePath.setNull();
14017 mConsoleTaskData.mProgress.setNull();
14018
14019 LogFlowThisFuncLeave();
14020 return rc;
14021}
14022
14023/**
14024 * Deletes the given file if it is no longer in use by either the current machine state
14025 * (if the machine is "saved") or any of the machine's snapshots.
14026 *
14027 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14028 * but is different for each SnapshotMachine. When calling this, the order of calling this
14029 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14030 * is therefore critical. I know, it's all rather messy.
14031 *
14032 * @param strStateFile
14033 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14034 * the test for whether the saved state file is in use.
14035 */
14036void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14037 Snapshot *pSnapshotToIgnore)
14038{
14039 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14040 if ( (strStateFile.isNotEmpty())
14041 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14042 )
14043 // ... and it must also not be shared with other snapshots
14044 if ( !mData->mFirstSnapshot
14045 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14046 // this checks the SnapshotMachine's state file paths
14047 )
14048 RTFileDelete(strStateFile.c_str());
14049}
14050
14051/**
14052 * Locks the attached media.
14053 *
14054 * All attached hard disks are locked for writing and DVD/floppy are locked for
14055 * reading. Parents of attached hard disks (if any) are locked for reading.
14056 *
14057 * This method also performs accessibility check of all media it locks: if some
14058 * media is inaccessible, the method will return a failure and a bunch of
14059 * extended error info objects per each inaccessible medium.
14060 *
14061 * Note that this method is atomic: if it returns a success, all media are
14062 * locked as described above; on failure no media is locked at all (all
14063 * succeeded individual locks will be undone).
14064 *
14065 * The caller is responsible for doing the necessary state sanity checks.
14066 *
14067 * The locks made by this method must be undone by calling #unlockMedia() when
14068 * no more needed.
14069 */
14070HRESULT SessionMachine::i_lockMedia()
14071{
14072 AutoCaller autoCaller(this);
14073 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14074
14075 AutoMultiWriteLock2 alock(this->lockHandle(),
14076 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14077
14078 /* bail out if trying to lock things with already set up locking */
14079 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14080
14081 MultiResult mrc(S_OK);
14082
14083 /* Collect locking information for all medium objects attached to the VM. */
14084 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14085 it != mMediaData->mAttachments.end();
14086 ++it)
14087 {
14088 MediumAttachment* pAtt = *it;
14089 DeviceType_T devType = pAtt->i_getType();
14090 Medium *pMedium = pAtt->i_getMedium();
14091
14092 MediumLockList *pMediumLockList(new MediumLockList());
14093 // There can be attachments without a medium (floppy/dvd), and thus
14094 // it's impossible to create a medium lock list. It still makes sense
14095 // to have the empty medium lock list in the map in case a medium is
14096 // attached later.
14097 if (pMedium != NULL)
14098 {
14099 MediumType_T mediumType = pMedium->i_getType();
14100 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14101 || mediumType == MediumType_Shareable;
14102 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14103
14104 alock.release();
14105 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14106 !fIsReadOnlyLock /* fMediumLockWrite */,
14107 false /* fMediumLockWriteAll */,
14108 NULL,
14109 *pMediumLockList);
14110 alock.acquire();
14111 if (FAILED(mrc))
14112 {
14113 delete pMediumLockList;
14114 mData->mSession.mLockedMedia.Clear();
14115 break;
14116 }
14117 }
14118
14119 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14120 if (FAILED(rc))
14121 {
14122 mData->mSession.mLockedMedia.Clear();
14123 mrc = setError(rc,
14124 tr("Collecting locking information for all attached media failed"));
14125 break;
14126 }
14127 }
14128
14129 if (SUCCEEDED(mrc))
14130 {
14131 /* Now lock all media. If this fails, nothing is locked. */
14132 alock.release();
14133 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14134 alock.acquire();
14135 if (FAILED(rc))
14136 {
14137 mrc = setError(rc,
14138 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14139 }
14140 }
14141
14142 return mrc;
14143}
14144
14145/**
14146 * Undoes the locks made by by #lockMedia().
14147 */
14148HRESULT SessionMachine::i_unlockMedia()
14149{
14150 AutoCaller autoCaller(this);
14151 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14152
14153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14154
14155 /* we may be holding important error info on the current thread;
14156 * preserve it */
14157 ErrorInfoKeeper eik;
14158
14159 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14160 AssertComRC(rc);
14161 return rc;
14162}
14163
14164/**
14165 * Helper to change the machine state (reimplementation).
14166 *
14167 * @note Locks this object for writing.
14168 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14169 * it can cause crashes in random places due to unexpectedly committing
14170 * the current settings. The caller is responsible for that. The call
14171 * to saveStateSettings is fine, because this method does not commit.
14172 */
14173HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14174{
14175 LogFlowThisFuncEnter();
14176 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14177
14178 AutoCaller autoCaller(this);
14179 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14180
14181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14182
14183 MachineState_T oldMachineState = mData->mMachineState;
14184
14185 AssertMsgReturn(oldMachineState != aMachineState,
14186 ("oldMachineState=%s, aMachineState=%s\n",
14187 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14188 E_FAIL);
14189
14190 HRESULT rc = S_OK;
14191
14192 int stsFlags = 0;
14193 bool deleteSavedState = false;
14194
14195 /* detect some state transitions */
14196
14197 if ( ( oldMachineState == MachineState_Saved
14198 && aMachineState == MachineState_Restoring)
14199 || ( ( oldMachineState == MachineState_PoweredOff
14200 || oldMachineState == MachineState_Teleported
14201 || oldMachineState == MachineState_Aborted
14202 )
14203 && ( aMachineState == MachineState_TeleportingIn
14204 || aMachineState == MachineState_Starting
14205 )
14206 )
14207 )
14208 {
14209 /* The EMT thread is about to start */
14210
14211 /* Nothing to do here for now... */
14212
14213 /// @todo NEWMEDIA don't let mDVDDrive and other children
14214 /// change anything when in the Starting/Restoring state
14215 }
14216 else if ( ( oldMachineState == MachineState_Running
14217 || oldMachineState == MachineState_Paused
14218 || oldMachineState == MachineState_Teleporting
14219 || oldMachineState == MachineState_LiveSnapshotting
14220 || oldMachineState == MachineState_Stuck
14221 || oldMachineState == MachineState_Starting
14222 || oldMachineState == MachineState_Stopping
14223 || oldMachineState == MachineState_Saving
14224 || oldMachineState == MachineState_Restoring
14225 || oldMachineState == MachineState_TeleportingPausedVM
14226 || oldMachineState == MachineState_TeleportingIn
14227 )
14228 && ( aMachineState == MachineState_PoweredOff
14229 || aMachineState == MachineState_Saved
14230 || aMachineState == MachineState_Teleported
14231 || aMachineState == MachineState_Aborted
14232 )
14233 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14234 * snapshot */
14235 && ( mConsoleTaskData.mSnapshot.isNull()
14236 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14237 )
14238 )
14239 {
14240 /* The EMT thread has just stopped, unlock attached media. Note that as
14241 * opposed to locking that is done from Console, we do unlocking here
14242 * because the VM process may have aborted before having a chance to
14243 * properly unlock all media it locked. */
14244
14245 unlockMedia();
14246 }
14247
14248 if (oldMachineState == MachineState_Restoring)
14249 {
14250 if (aMachineState != MachineState_Saved)
14251 {
14252 /*
14253 * delete the saved state file once the machine has finished
14254 * restoring from it (note that Console sets the state from
14255 * Restoring to Saved if the VM couldn't restore successfully,
14256 * to give the user an ability to fix an error and retry --
14257 * we keep the saved state file in this case)
14258 */
14259 deleteSavedState = true;
14260 }
14261 }
14262 else if ( oldMachineState == MachineState_Saved
14263 && ( aMachineState == MachineState_PoweredOff
14264 || aMachineState == MachineState_Aborted
14265 || aMachineState == MachineState_Teleported
14266 )
14267 )
14268 {
14269 /*
14270 * delete the saved state after Console::ForgetSavedState() is called
14271 * or if the VM process (owning a direct VM session) crashed while the
14272 * VM was Saved
14273 */
14274
14275 /// @todo (dmik)
14276 // Not sure that deleting the saved state file just because of the
14277 // client death before it attempted to restore the VM is a good
14278 // thing. But when it crashes we need to go to the Aborted state
14279 // which cannot have the saved state file associated... The only
14280 // way to fix this is to make the Aborted condition not a VM state
14281 // but a bool flag: i.e., when a crash occurs, set it to true and
14282 // change the state to PoweredOff or Saved depending on the
14283 // saved state presence.
14284
14285 deleteSavedState = true;
14286 mData->mCurrentStateModified = TRUE;
14287 stsFlags |= SaveSTS_CurStateModified;
14288 }
14289
14290 if ( aMachineState == MachineState_Starting
14291 || aMachineState == MachineState_Restoring
14292 || aMachineState == MachineState_TeleportingIn
14293 )
14294 {
14295 /* set the current state modified flag to indicate that the current
14296 * state is no more identical to the state in the
14297 * current snapshot */
14298 if (!mData->mCurrentSnapshot.isNull())
14299 {
14300 mData->mCurrentStateModified = TRUE;
14301 stsFlags |= SaveSTS_CurStateModified;
14302 }
14303 }
14304
14305 if (deleteSavedState)
14306 {
14307 if (mRemoveSavedState)
14308 {
14309 Assert(!mSSData->strStateFilePath.isEmpty());
14310
14311 // it is safe to delete the saved state file if ...
14312 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14313 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14314 // ... none of the snapshots share the saved state file
14315 )
14316 RTFileDelete(mSSData->strStateFilePath.c_str());
14317 }
14318
14319 mSSData->strStateFilePath.setNull();
14320 stsFlags |= SaveSTS_StateFilePath;
14321 }
14322
14323 /* redirect to the underlying peer machine */
14324 mPeer->i_setMachineState(aMachineState);
14325
14326 if ( aMachineState == MachineState_PoweredOff
14327 || aMachineState == MachineState_Teleported
14328 || aMachineState == MachineState_Aborted
14329 || aMachineState == MachineState_Saved)
14330 {
14331 /* the machine has stopped execution
14332 * (or the saved state file was adopted) */
14333 stsFlags |= SaveSTS_StateTimeStamp;
14334 }
14335
14336 if ( ( oldMachineState == MachineState_PoweredOff
14337 || oldMachineState == MachineState_Aborted
14338 || oldMachineState == MachineState_Teleported
14339 )
14340 && aMachineState == MachineState_Saved)
14341 {
14342 /* the saved state file was adopted */
14343 Assert(!mSSData->strStateFilePath.isEmpty());
14344 stsFlags |= SaveSTS_StateFilePath;
14345 }
14346
14347#ifdef VBOX_WITH_GUEST_PROPS
14348 if ( aMachineState == MachineState_PoweredOff
14349 || aMachineState == MachineState_Aborted
14350 || aMachineState == MachineState_Teleported)
14351 {
14352 /* Make sure any transient guest properties get removed from the
14353 * property store on shutdown. */
14354 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14355
14356 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14357 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14358 while (it != llGuestProperties.end())
14359 {
14360 const settings::GuestProperty &prop = *it;
14361 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14362 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14363 {
14364 it = llGuestProperties.erase(it);
14365 fNeedsSaving = true;
14366 }
14367 else
14368 {
14369 ++it;
14370 }
14371 }
14372
14373 if (fNeedsSaving)
14374 {
14375 mData->mCurrentStateModified = TRUE;
14376 stsFlags |= SaveSTS_CurStateModified;
14377 }
14378 }
14379#endif /* VBOX_WITH_GUEST_PROPS */
14380
14381 rc = i_saveStateSettings(stsFlags);
14382
14383 if ( ( oldMachineState != MachineState_PoweredOff
14384 && oldMachineState != MachineState_Aborted
14385 && oldMachineState != MachineState_Teleported
14386 )
14387 && ( aMachineState == MachineState_PoweredOff
14388 || aMachineState == MachineState_Aborted
14389 || aMachineState == MachineState_Teleported
14390 )
14391 )
14392 {
14393 /* we've been shut down for any reason */
14394 /* no special action so far */
14395 }
14396
14397 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14398 LogFlowThisFuncLeave();
14399 return rc;
14400}
14401
14402/**
14403 * Sends the current machine state value to the VM process.
14404 *
14405 * @note Locks this object for reading, then calls a client process.
14406 */
14407HRESULT SessionMachine::i_updateMachineStateOnClient()
14408{
14409 AutoCaller autoCaller(this);
14410 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14411
14412 ComPtr<IInternalSessionControl> directControl;
14413 {
14414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14415 AssertReturn(!!mData, E_FAIL);
14416 directControl = mData->mSession.mDirectControl;
14417
14418 /* directControl may be already set to NULL here in #OnSessionEnd()
14419 * called too early by the direct session process while there is still
14420 * some operation (like deleting the snapshot) in progress. The client
14421 * process in this case is waiting inside Session::close() for the
14422 * "end session" process object to complete, while #uninit() called by
14423 * #checkForDeath() on the Watcher thread is waiting for the pending
14424 * operation to complete. For now, we accept this inconsistent behavior
14425 * and simply do nothing here. */
14426
14427 if (mData->mSession.mState == SessionState_Unlocking)
14428 return S_OK;
14429
14430 AssertReturn(!directControl.isNull(), E_FAIL);
14431 }
14432
14433 return directControl->UpdateMachineState(mData->mMachineState);
14434}
14435
14436HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14437{
14438 NOREF(aRemove);
14439 ReturnComNotImplemented();
14440}
14441
14442HRESULT Machine::updateState(MachineState_T aState)
14443{
14444 NOREF(aState);
14445 ReturnComNotImplemented();
14446}
14447
14448HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14449{
14450 NOREF(aProgress);
14451 ReturnComNotImplemented();
14452}
14453
14454HRESULT Machine::endPowerUp(LONG aResult)
14455{
14456 NOREF(aResult);
14457 ReturnComNotImplemented();
14458}
14459
14460HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14461{
14462 NOREF(aProgress);
14463 ReturnComNotImplemented();
14464}
14465
14466HRESULT Machine::endPoweringDown(LONG aResult,
14467 const com::Utf8Str &aErrMsg)
14468{
14469 NOREF(aResult);
14470 NOREF(aErrMsg);
14471 ReturnComNotImplemented();
14472}
14473
14474HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14475 BOOL *aMatched,
14476 ULONG *aMaskedInterfaces)
14477{
14478 NOREF(aDevice);
14479 NOREF(aMatched);
14480 NOREF(aMaskedInterfaces);
14481 ReturnComNotImplemented();
14482
14483}
14484
14485HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14486{
14487 NOREF(aId); NOREF(aCaptureFilename);
14488 ReturnComNotImplemented();
14489}
14490
14491HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14492 BOOL aDone)
14493{
14494 NOREF(aId);
14495 NOREF(aDone);
14496 ReturnComNotImplemented();
14497}
14498
14499HRESULT Machine::autoCaptureUSBDevices()
14500{
14501 ReturnComNotImplemented();
14502}
14503
14504HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14505{
14506 NOREF(aDone);
14507 ReturnComNotImplemented();
14508}
14509
14510HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14511 ComPtr<IProgress> &aProgress)
14512{
14513 NOREF(aSession);
14514 NOREF(aProgress);
14515 ReturnComNotImplemented();
14516}
14517
14518HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14519 com::Utf8Str &aStateFilePath)
14520{
14521 NOREF(aProgress);
14522 NOREF(aStateFilePath);
14523 ReturnComNotImplemented();
14524}
14525
14526HRESULT Machine::endSavingState(LONG aResult,
14527 const com::Utf8Str &aErrMsg)
14528{
14529 NOREF(aResult);
14530 NOREF(aErrMsg);
14531 ReturnComNotImplemented();
14532}
14533
14534HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14535{
14536 NOREF(aSavedStateFile);
14537 ReturnComNotImplemented();
14538}
14539
14540HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14541 const com::Utf8Str &aName,
14542 const com::Utf8Str &aDescription,
14543 const ComPtr<IProgress> &aConsoleProgress,
14544 BOOL aFTakingSnapshotOnline,
14545 com::Utf8Str &aStateFilePath)
14546{
14547 NOREF(aInitiator);
14548 NOREF(aName);
14549 NOREF(aDescription);
14550 NOREF(aConsoleProgress);
14551 NOREF(aFTakingSnapshotOnline);
14552 NOREF(aStateFilePath);
14553 ReturnComNotImplemented();
14554}
14555
14556HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14557{
14558 NOREF(aSuccess);
14559 ReturnComNotImplemented();
14560}
14561
14562HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14563 const com::Guid &aStartId,
14564 const com::Guid &aEndId,
14565 BOOL aDeleteAllChildren,
14566 MachineState_T *aMachineState,
14567 ComPtr<IProgress> &aProgress)
14568{
14569 NOREF(aInitiator);
14570 NOREF(aStartId);
14571 NOREF(aEndId);
14572 NOREF(aDeleteAllChildren);
14573 NOREF(aMachineState);
14574 NOREF(aProgress);
14575 ReturnComNotImplemented();
14576}
14577
14578HRESULT Machine::finishOnlineMergeMedium()
14579{
14580 ReturnComNotImplemented();
14581}
14582
14583HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14584 const ComPtr<ISnapshot> &aSnapshot,
14585 MachineState_T *aMachineState,
14586 ComPtr<IProgress> &aProgress)
14587{
14588 NOREF(aInitiator);
14589 NOREF(aSnapshot);
14590 NOREF(aMachineState);
14591 NOREF(aProgress);
14592 ReturnComNotImplemented();
14593}
14594
14595HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14596 std::vector<com::Utf8Str> &aValues,
14597 std::vector<LONG64> &aTimestamps,
14598 std::vector<com::Utf8Str> &aFlags)
14599{
14600 NOREF(aNames);
14601 NOREF(aValues);
14602 NOREF(aTimestamps);
14603 NOREF(aFlags);
14604 ReturnComNotImplemented();
14605}
14606
14607HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14608 const com::Utf8Str &aValue,
14609 LONG64 aTimestamp,
14610 const com::Utf8Str &aFlags)
14611{
14612 NOREF(aName);
14613 NOREF(aValue);
14614 NOREF(aTimestamp);
14615 NOREF(aFlags);
14616 ReturnComNotImplemented();
14617}
14618
14619HRESULT Machine::lockMedia()
14620{
14621 ReturnComNotImplemented();
14622}
14623
14624HRESULT Machine::unlockMedia()
14625{
14626 ReturnComNotImplemented();
14627}
14628
14629HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14630 ComPtr<IMediumAttachment> &aNewAttachment)
14631{
14632 NOREF(aAttachment);
14633 NOREF(aNewAttachment);
14634 ReturnComNotImplemented();
14635}
14636
14637HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14638 ULONG aCpuUser,
14639 ULONG aCpuKernel,
14640 ULONG aCpuIdle,
14641 ULONG aMemTotal,
14642 ULONG aMemFree,
14643 ULONG aMemBalloon,
14644 ULONG aMemShared,
14645 ULONG aMemCache,
14646 ULONG aPagedTotal,
14647 ULONG aMemAllocTotal,
14648 ULONG aMemFreeTotal,
14649 ULONG aMemBalloonTotal,
14650 ULONG aMemSharedTotal,
14651 ULONG aVmNetRx,
14652 ULONG aVmNetTx)
14653{
14654 NOREF(aValidStats);
14655 NOREF(aCpuUser);
14656 NOREF(aCpuKernel);
14657 NOREF(aCpuIdle);
14658 NOREF(aMemTotal);
14659 NOREF(aMemFree);
14660 NOREF(aMemBalloon);
14661 NOREF(aMemShared);
14662 NOREF(aMemCache);
14663 NOREF(aPagedTotal);
14664 NOREF(aMemAllocTotal);
14665 NOREF(aMemFreeTotal);
14666 NOREF(aMemBalloonTotal);
14667 NOREF(aMemSharedTotal);
14668 NOREF(aVmNetRx);
14669 NOREF(aVmNetTx);
14670 ReturnComNotImplemented();
14671}
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