VirtualBox

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

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

Main/Snapshot: fix handling of saved state with offline snapshots (looked at wrong machine state variable, regression in previous big code structure change), streamline the code path and fix a crash for early failures to take a snapshot. Eliminate a local variable which should always have the same value as the same flag in the task struct, avoiding inconsistent behavior.
Main/Machine: fix sanity check regarding saved state presence when saving the config (resulted in loss of saved state)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 508.6 KB
Line 
1/* $Id: MachineImpl.cpp 55511 2015-04-29 10:16:01Z 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.mLockType = LockType_Null;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
165 mVRAMSize = 8;
166 mAccelerate3DEnabled = false;
167 mAccelerate2DVideoEnabled = false;
168 mMonitorCount = 1;
169 mVideoCaptureWidth = 1024;
170 mVideoCaptureHeight = 768;
171 mVideoCaptureRate = 512;
172 mVideoCaptureFPS = 25;
173 mVideoCaptureMaxTime = 0;
174 mVideoCaptureMaxFileSize = 0;
175 mVideoCaptureEnabled = false;
176 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
177 maVideoCaptureScreens[i] = true;
178
179 mHWVirtExEnabled = true;
180 mHWVirtExNestedPagingEnabled = true;
181#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
182 mHWVirtExLargePagesEnabled = true;
183#else
184 /* Not supported on 32 bits hosts. */
185 mHWVirtExLargePagesEnabled = false;
186#endif
187 mHWVirtExVPIDEnabled = true;
188 mHWVirtExUXEnabled = true;
189 mHWVirtExForceEnabled = false;
190#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
191 mPAEEnabled = true;
192#else
193 mPAEEnabled = false;
194#endif
195 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
196 mSyntheticCpu = false;
197 mTripleFaultReset = false;
198 mHPETEnabled = false;
199
200 /* default boot order: floppy - DVD - HDD */
201 mBootOrder[0] = DeviceType_Floppy;
202 mBootOrder[1] = DeviceType_DVD;
203 mBootOrder[2] = DeviceType_HardDisk;
204 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
205 mBootOrder[i] = DeviceType_Null;
206
207 mClipboardMode = ClipboardMode_Disabled;
208 mDnDMode = DnDMode_Disabled;
209 mGuestPropertyNotificationPatterns = "";
210
211 mFirmwareType = FirmwareType_BIOS;
212 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
213 mPointingHIDType = PointingHIDType_PS2Mouse;
214 mChipsetType = ChipsetType_PIIX3;
215 mParavirtProvider = ParavirtProvider_Default;
216 mEmulatedUSBCardReaderEnabled = FALSE;
217
218 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
219 mCPUAttached[i] = false;
220
221 mIOCacheEnabled = true;
222 mIOCacheSize = 5; /* 5MB */
223
224 /* Maximum CPU execution cap by default. */
225 mCpuExecutionCap = 100;
226}
227
228Machine::HWData::~HWData()
229{
230}
231
232/////////////////////////////////////////////////////////////////////////////
233// Machine::HDData structure
234/////////////////////////////////////////////////////////////////////////////
235
236Machine::MediaData::MediaData()
237{
238}
239
240Machine::MediaData::~MediaData()
241{
242}
243
244/////////////////////////////////////////////////////////////////////////////
245// Machine class
246/////////////////////////////////////////////////////////////////////////////
247
248// constructor / destructor
249/////////////////////////////////////////////////////////////////////////////
250
251Machine::Machine() :
252#ifdef VBOX_WITH_RESOURCE_USAGE_API
253 mCollectorGuest(NULL),
254#endif
255 mPeer(NULL),
256 mParent(NULL),
257 mSerialPorts(),
258 mParallelPorts(),
259 uRegistryNeedsSaving(0)
260{}
261
262Machine::~Machine()
263{}
264
265HRESULT Machine::FinalConstruct()
266{
267 LogFlowThisFunc(("\n"));
268 return BaseFinalConstruct();
269}
270
271void Machine::FinalRelease()
272{
273 LogFlowThisFunc(("\n"));
274 uninit();
275 BaseFinalRelease();
276}
277
278/**
279 * Initializes a new machine instance; this init() variant creates a new, empty machine.
280 * This gets called from VirtualBox::CreateMachine().
281 *
282 * @param aParent Associated parent object
283 * @param strConfigFile Local file system path to the VM settings file (can
284 * be relative to the VirtualBox config directory).
285 * @param strName name for the machine
286 * @param llGroups list of groups for the machine
287 * @param aOsType OS Type of this machine or NULL.
288 * @param aId UUID for the new machine.
289 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
290 *
291 * @return Success indicator. if not S_OK, the machine object is invalid
292 */
293HRESULT Machine::init(VirtualBox *aParent,
294 const Utf8Str &strConfigFile,
295 const Utf8Str &strName,
296 const StringsList &llGroups,
297 GuestOSType *aOsType,
298 const Guid &aId,
299 bool fForceOverwrite,
300 bool fDirectoryIncludesUUID)
301{
302 LogFlowThisFuncEnter();
303 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
304
305 /* Enclose the state transition NotReady->InInit->Ready */
306 AutoInitSpan autoInitSpan(this);
307 AssertReturn(autoInitSpan.isOk(), E_FAIL);
308
309 HRESULT rc = initImpl(aParent, strConfigFile);
310 if (FAILED(rc)) return rc;
311
312 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
313 if (FAILED(rc)) return rc;
314
315 if (SUCCEEDED(rc))
316 {
317 // create an empty machine config
318 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
319
320 rc = initDataAndChildObjects();
321 }
322
323 if (SUCCEEDED(rc))
324 {
325 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
326 mData->mAccessible = TRUE;
327
328 unconst(mData->mUuid) = aId;
329
330 mUserData->s.strName = strName;
331
332 mUserData->s.llGroups = llGroups;
333
334 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
335 // the "name sync" flag determines whether the machine directory gets renamed along
336 // with the machine file; say so if the settings file name is the same as the
337 // settings file parent directory (machine directory)
338 mUserData->s.fNameSync = i_isInOwnDir();
339
340 // initialize the default snapshots folder
341 rc = COMSETTER(SnapshotFolder)(NULL);
342 AssertComRC(rc);
343
344 if (aOsType)
345 {
346 /* Store OS type */
347 mUserData->s.strOsType = aOsType->i_id();
348
349 /* Apply BIOS defaults */
350 mBIOSSettings->i_applyDefaults(aOsType);
351
352 /* Apply network adapters defaults */
353 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
354 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
355
356 /* Apply serial port defaults */
357 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
358 mSerialPorts[slot]->i_applyDefaults(aOsType);
359
360 /* Let the OS type select 64-bit ness. */
361 mHWData->mLongMode = aOsType->i_is64Bit()
362 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363 }
364
365 /* At this point the changing of the current state modification
366 * flag is allowed. */
367 i_allowStateModification();
368
369 /* commit all changes made during the initialization */
370 i_commit();
371 }
372
373 /* Confirm a successful initialization when it's the case */
374 if (SUCCEEDED(rc))
375 {
376 if (mData->mAccessible)
377 autoInitSpan.setSucceeded();
378 else
379 autoInitSpan.setLimited();
380 }
381
382 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
383 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
384 mData->mRegistered,
385 mData->mAccessible,
386 rc));
387
388 LogFlowThisFuncLeave();
389
390 return rc;
391}
392
393/**
394 * Initializes a new instance with data from machine XML (formerly Init_Registered).
395 * Gets called in two modes:
396 *
397 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
398 * UUID is specified and we mark the machine as "registered";
399 *
400 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
401 * and the machine remains unregistered until RegisterMachine() is called.
402 *
403 * @param aParent Associated parent object
404 * @param aConfigFile Local file system path to the VM settings file (can
405 * be relative to the VirtualBox config directory).
406 * @param aId UUID of the machine or NULL (see above).
407 *
408 * @return Success indicator. if not S_OK, the machine object is invalid
409 */
410HRESULT Machine::initFromSettings(VirtualBox *aParent,
411 const Utf8Str &strConfigFile,
412 const Guid *aId)
413{
414 LogFlowThisFuncEnter();
415 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
416
417 /* Enclose the state transition NotReady->InInit->Ready */
418 AutoInitSpan autoInitSpan(this);
419 AssertReturn(autoInitSpan.isOk(), E_FAIL);
420
421 HRESULT rc = initImpl(aParent, strConfigFile);
422 if (FAILED(rc)) return rc;
423
424 if (aId)
425 {
426 // loading a registered VM:
427 unconst(mData->mUuid) = *aId;
428 mData->mRegistered = TRUE;
429 // now load the settings from XML:
430 rc = i_registeredInit();
431 // this calls initDataAndChildObjects() and loadSettings()
432 }
433 else
434 {
435 // opening an unregistered VM (VirtualBox::OpenMachine()):
436 rc = initDataAndChildObjects();
437
438 if (SUCCEEDED(rc))
439 {
440 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
441 mData->mAccessible = TRUE;
442
443 try
444 {
445 // load and parse machine XML; this will throw on XML or logic errors
446 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
447
448 // reject VM UUID duplicates, they can happen if someone
449 // tries to register an already known VM config again
450 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
451 true /* fPermitInaccessible */,
452 false /* aDoSetError */,
453 NULL) != VBOX_E_OBJECT_NOT_FOUND)
454 {
455 throw setError(E_FAIL,
456 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
457 mData->m_strConfigFile.c_str());
458 }
459
460 // use UUID from machine config
461 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
462
463 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
464 NULL /* puuidRegistry */);
465 if (FAILED(rc)) throw rc;
466
467 /* At this point the changing of the current state modification
468 * flag is allowed. */
469 i_allowStateModification();
470
471 i_commit();
472 }
473 catch (HRESULT err)
474 {
475 /* we assume that error info is set by the thrower */
476 rc = err;
477 }
478 catch (...)
479 {
480 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
481 }
482 }
483 }
484
485 /* Confirm a successful initialization when it's the case */
486 if (SUCCEEDED(rc))
487 {
488 if (mData->mAccessible)
489 autoInitSpan.setSucceeded();
490 else
491 {
492 autoInitSpan.setLimited();
493
494 // uninit media from this machine's media registry, or else
495 // reloading the settings will fail
496 mParent->i_unregisterMachineMedia(i_getId());
497 }
498 }
499
500 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
501 "rc=%08X\n",
502 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
503 mData->mRegistered, mData->mAccessible, rc));
504
505 LogFlowThisFuncLeave();
506
507 return rc;
508}
509
510/**
511 * Initializes a new instance from a machine config that is already in memory
512 * (import OVF case). Since we are importing, the UUID in the machine
513 * config is ignored and we always generate a fresh one.
514 *
515 * @param strName Name for the new machine; this overrides what is specified in config and is used
516 * for the settings file as well.
517 * @param config Machine configuration loaded and parsed from XML.
518 *
519 * @return Success indicator. if not S_OK, the machine object is invalid
520 */
521HRESULT Machine::init(VirtualBox *aParent,
522 const Utf8Str &strName,
523 const settings::MachineConfigFile &config)
524{
525 LogFlowThisFuncEnter();
526
527 /* Enclose the state transition NotReady->InInit->Ready */
528 AutoInitSpan autoInitSpan(this);
529 AssertReturn(autoInitSpan.isOk(), E_FAIL);
530
531 Utf8Str strConfigFile;
532 aParent->i_getDefaultMachineFolder(strConfigFile);
533 strConfigFile.append(RTPATH_DELIMITER);
534 strConfigFile.append(strName);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(".vbox");
538
539 HRESULT rc = initImpl(aParent, strConfigFile);
540 if (FAILED(rc)) return rc;
541
542 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
543 if (FAILED(rc)) return rc;
544
545 rc = initDataAndChildObjects();
546
547 if (SUCCEEDED(rc))
548 {
549 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
550 mData->mAccessible = TRUE;
551
552 // create empty machine config for instance data
553 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
554
555 // generate fresh UUID, ignore machine config
556 unconst(mData->mUuid).create();
557
558 rc = i_loadMachineDataFromSettings(config,
559 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
560
561 // override VM name as well, it may be different
562 mUserData->s.strName = strName;
563
564 if (SUCCEEDED(rc))
565 {
566 /* At this point the changing of the current state modification
567 * flag is allowed. */
568 i_allowStateModification();
569
570 /* commit all changes made during the initialization */
571 i_commit();
572 }
573 }
574
575 /* Confirm a successful initialization when it's the case */
576 if (SUCCEEDED(rc))
577 {
578 if (mData->mAccessible)
579 autoInitSpan.setSucceeded();
580 else
581 {
582 /* Ignore all errors from unregistering, they would destroy
583- * the more interesting error information we already have,
584- * pinpointing the issue with the VM config. */
585 ErrorInfoKeeper eik;
586
587 autoInitSpan.setLimited();
588
589 // uninit media from this machine's media registry, or else
590 // reloading the settings will fail
591 mParent->i_unregisterMachineMedia(i_getId());
592 }
593 }
594
595 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
596 "rc=%08X\n",
597 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
598 mData->mRegistered, mData->mAccessible, rc));
599
600 LogFlowThisFuncLeave();
601
602 return rc;
603}
604
605/**
606 * Shared code between the various init() implementations.
607 * @param aParent
608 * @return
609 */
610HRESULT Machine::initImpl(VirtualBox *aParent,
611 const Utf8Str &strConfigFile)
612{
613 LogFlowThisFuncEnter();
614
615 AssertReturn(aParent, E_INVALIDARG);
616 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
617
618 HRESULT rc = S_OK;
619
620 /* share the parent weakly */
621 unconst(mParent) = aParent;
622
623 /* allocate the essential machine data structure (the rest will be
624 * allocated later by initDataAndChildObjects() */
625 mData.allocate();
626
627 /* memorize the config file name (as provided) */
628 mData->m_strConfigFile = strConfigFile;
629
630 /* get the full file name */
631 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
632 if (RT_FAILURE(vrc1))
633 return setError(VBOX_E_FILE_ERROR,
634 tr("Invalid machine settings file name '%s' (%Rrc)"),
635 strConfigFile.c_str(),
636 vrc1);
637
638 LogFlowThisFuncLeave();
639
640 return rc;
641}
642
643/**
644 * Tries to create a machine settings file in the path stored in the machine
645 * instance data. Used when a new machine is created to fail gracefully if
646 * the settings file could not be written (e.g. because machine dir is read-only).
647 * @return
648 */
649HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
650{
651 HRESULT rc = S_OK;
652
653 // when we create a new machine, we must be able to create the settings file
654 RTFILE f = NIL_RTFILE;
655 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
656 if ( RT_SUCCESS(vrc)
657 || vrc == VERR_SHARING_VIOLATION
658 )
659 {
660 if (RT_SUCCESS(vrc))
661 RTFileClose(f);
662 if (!fForceOverwrite)
663 rc = setError(VBOX_E_FILE_ERROR,
664 tr("Machine settings file '%s' already exists"),
665 mData->m_strConfigFileFull.c_str());
666 else
667 {
668 /* try to delete the config file, as otherwise the creation
669 * of a new settings file will fail. */
670 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
671 if (RT_FAILURE(vrc2))
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Could not delete the existing settings file '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(), vrc2);
675 }
676 }
677 else if ( vrc != VERR_FILE_NOT_FOUND
678 && vrc != VERR_PATH_NOT_FOUND
679 )
680 rc = setError(VBOX_E_FILE_ERROR,
681 tr("Invalid machine settings file name '%s' (%Rrc)"),
682 mData->m_strConfigFileFull.c_str(),
683 vrc);
684 return rc;
685}
686
687/**
688 * Initializes the registered machine by loading the settings file.
689 * This method is separated from #init() in order to make it possible to
690 * retry the operation after VirtualBox startup instead of refusing to
691 * startup the whole VirtualBox server in case if the settings file of some
692 * registered VM is invalid or inaccessible.
693 *
694 * @note Must be always called from this object's write lock
695 * (unless called from #init() that doesn't need any locking).
696 * @note Locks the mUSBController method for writing.
697 * @note Subclasses must not call this method.
698 */
699HRESULT Machine::i_registeredInit()
700{
701 AssertReturn(!i_isSessionMachine(), E_FAIL);
702 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
703 AssertReturn(mData->mUuid.isValid(), E_FAIL);
704 AssertReturn(!mData->mAccessible, E_FAIL);
705
706 HRESULT rc = initDataAndChildObjects();
707
708 if (SUCCEEDED(rc))
709 {
710 /* Temporarily reset the registered flag in order to let setters
711 * potentially called from loadSettings() succeed (isMutable() used in
712 * all setters will return FALSE for a Machine instance if mRegistered
713 * is TRUE). */
714 mData->mRegistered = FALSE;
715
716 try
717 {
718 // load and parse machine XML; this will throw on XML or logic errors
719 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
720
721 if (mData->mUuid != mData->pMachineConfigFile->uuid)
722 throw setError(E_FAIL,
723 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
724 mData->pMachineConfigFile->uuid.raw(),
725 mData->m_strConfigFileFull.c_str(),
726 mData->mUuid.toString().c_str(),
727 mParent->i_settingsFilePath().c_str());
728
729 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
730 NULL /* const Guid *puuidRegistry */);
731 if (FAILED(rc)) throw rc;
732 }
733 catch (HRESULT err)
734 {
735 /* we assume that error info is set by the thrower */
736 rc = err;
737 }
738 catch (...)
739 {
740 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
741 }
742
743 /* Restore the registered flag (even on failure) */
744 mData->mRegistered = TRUE;
745 }
746
747 if (SUCCEEDED(rc))
748 {
749 /* Set mAccessible to TRUE only if we successfully locked and loaded
750 * the settings file */
751 mData->mAccessible = TRUE;
752
753 /* commit all changes made during loading the settings file */
754 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
755 /// @todo r=klaus for some reason the settings loading logic backs up
756 // the settings, and therefore a commit is needed. Should probably be changed.
757 }
758 else
759 {
760 /* If the machine is registered, then, instead of returning a
761 * failure, we mark it as inaccessible and set the result to
762 * success to give it a try later */
763
764 /* fetch the current error info */
765 mData->mAccessError = com::ErrorInfo();
766 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
767 mData->mUuid.raw(),
768 mData->mAccessError.getText().raw()));
769
770 /* rollback all changes */
771 i_rollback(false /* aNotify */);
772
773 // uninit media from this machine's media registry, or else
774 // reloading the settings will fail
775 mParent->i_unregisterMachineMedia(i_getId());
776
777 /* uninitialize the common part to make sure all data is reset to
778 * default (null) values */
779 uninitDataAndChildObjects();
780
781 rc = S_OK;
782 }
783
784 return rc;
785}
786
787/**
788 * Uninitializes the instance.
789 * Called either from FinalRelease() or by the parent when it gets destroyed.
790 *
791 * @note The caller of this method must make sure that this object
792 * a) doesn't have active callers on the current thread and b) is not locked
793 * by the current thread; otherwise uninit() will hang either a) due to
794 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
795 * a dead-lock caused by this thread waiting for all callers on the other
796 * threads are done but preventing them from doing so by holding a lock.
797 */
798void Machine::uninit()
799{
800 LogFlowThisFuncEnter();
801
802 Assert(!isWriteLockOnCurrentThread());
803
804 Assert(!uRegistryNeedsSaving);
805 if (uRegistryNeedsSaving)
806 {
807 AutoCaller autoCaller(this);
808 if (SUCCEEDED(autoCaller.rc()))
809 {
810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
811 i_saveSettings(NULL, Machine::SaveS_Force);
812 }
813 }
814
815 /* Enclose the state transition Ready->InUninit->NotReady */
816 AutoUninitSpan autoUninitSpan(this);
817 if (autoUninitSpan.uninitDone())
818 return;
819
820 Assert(!i_isSnapshotMachine());
821 Assert(!i_isSessionMachine());
822 Assert(!!mData);
823
824 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
825 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
826
827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
828
829 if (!mData->mSession.mMachine.isNull())
830 {
831 /* Theoretically, this can only happen if the VirtualBox server has been
832 * terminated while there were clients running that owned open direct
833 * sessions. Since in this case we are definitely called by
834 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
835 * won't happen on the client watcher thread (because it does
836 * VirtualBox::addCaller() for the duration of the
837 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
838 * cannot happen until the VirtualBox caller is released). This is
839 * important, because SessionMachine::uninit() cannot correctly operate
840 * after we return from this method (it expects the Machine instance is
841 * still valid). We'll call it ourselves below.
842 */
843 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
844 (SessionMachine*)mData->mSession.mMachine));
845
846 if (Global::IsOnlineOrTransient(mData->mMachineState))
847 {
848 LogWarningThisFunc(("Setting state to Aborted!\n"));
849 /* set machine state using SessionMachine reimplementation */
850 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
851 }
852
853 /*
854 * Uninitialize SessionMachine using public uninit() to indicate
855 * an unexpected uninitialization.
856 */
857 mData->mSession.mMachine->uninit();
858 /* SessionMachine::uninit() must set mSession.mMachine to null */
859 Assert(mData->mSession.mMachine.isNull());
860 }
861
862 // uninit media from this machine's media registry, if they're still there
863 Guid uuidMachine(i_getId());
864
865 /* the lock is no more necessary (SessionMachine is uninitialized) */
866 alock.release();
867
868 /* XXX This will fail with
869 * "cannot be closed because it is still attached to 1 virtual machines"
870 * because at this point we did not call uninitDataAndChildObjects() yet
871 * and therefore also removeBackReference() for all these mediums was not called! */
872
873 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
874 mParent->i_unregisterMachineMedia(uuidMachine);
875
876 // has machine been modified?
877 if (mData->flModifications)
878 {
879 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
880 i_rollback(false /* aNotify */);
881 }
882
883 if (mData->mAccessible)
884 uninitDataAndChildObjects();
885
886 /* free the essential data structure last */
887 mData.free();
888
889 LogFlowThisFuncLeave();
890}
891
892// Wrapped IMachine properties
893/////////////////////////////////////////////////////////////////////////////
894HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
895{
896 /* mParent is constant during life time, no need to lock */
897 ComObjPtr<VirtualBox> pVirtualBox(mParent);
898 aParent = pVirtualBox;
899
900 return S_OK;
901}
902
903
904HRESULT Machine::getAccessible(BOOL *aAccessible)
905{
906 /* In some cases (medium registry related), it is necessary to be able to
907 * go through the list of all machines. Happens when an inaccessible VM
908 * has a sensible medium registry. */
909 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
911
912 HRESULT rc = S_OK;
913
914 if (!mData->mAccessible)
915 {
916 /* try to initialize the VM once more if not accessible */
917
918 AutoReinitSpan autoReinitSpan(this);
919 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
920
921#ifdef DEBUG
922 LogFlowThisFunc(("Dumping media backreferences\n"));
923 mParent->i_dumpAllBackRefs();
924#endif
925
926 if (mData->pMachineConfigFile)
927 {
928 // reset the XML file to force loadSettings() (called from registeredInit())
929 // to parse it again; the file might have changed
930 delete mData->pMachineConfigFile;
931 mData->pMachineConfigFile = NULL;
932 }
933
934 rc = i_registeredInit();
935
936 if (SUCCEEDED(rc) && mData->mAccessible)
937 {
938 autoReinitSpan.setSucceeded();
939
940 /* make sure interesting parties will notice the accessibility
941 * state change */
942 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
943 mParent->i_onMachineDataChange(mData->mUuid);
944 }
945 }
946
947 if (SUCCEEDED(rc))
948 *aAccessible = mData->mAccessible;
949
950 LogFlowThisFuncLeave();
951
952 return rc;
953}
954
955HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
956{
957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
958
959 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
960 {
961 /* return shortly */
962 aAccessError = NULL;
963 return S_OK;
964 }
965
966 HRESULT rc = S_OK;
967
968 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
969 rc = errorInfo.createObject();
970 if (SUCCEEDED(rc))
971 {
972 errorInfo->init(mData->mAccessError.getResultCode(),
973 mData->mAccessError.getInterfaceID().ref(),
974 Utf8Str(mData->mAccessError.getComponent()).c_str(),
975 Utf8Str(mData->mAccessError.getText()));
976 aAccessError = errorInfo;
977 }
978
979 return rc;
980}
981
982HRESULT Machine::getName(com::Utf8Str &aName)
983{
984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
985
986 aName = mUserData->s.strName;
987
988 return S_OK;
989}
990
991HRESULT Machine::setName(const com::Utf8Str &aName)
992{
993 // prohibit setting a UUID only as the machine name, or else it can
994 // never be found by findMachine()
995 Guid test(aName);
996
997 if (test.isValid())
998 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
999
1000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1001
1002 HRESULT rc = i_checkStateDependency(MutableStateDep);
1003 if (FAILED(rc)) return rc;
1004
1005 i_setModified(IsModified_MachineData);
1006 mUserData.backup();
1007 mUserData->s.strName = aName;
1008
1009 return S_OK;
1010}
1011
1012HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1013{
1014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 aDescription = mUserData->s.strDescription;
1017
1018 return S_OK;
1019}
1020
1021HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1022{
1023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1024
1025 // this can be done in principle in any state as it doesn't affect the VM
1026 // significantly, but play safe by not messing around while complex
1027 // activities are going on
1028 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1029 if (FAILED(rc)) return rc;
1030
1031 i_setModified(IsModified_MachineData);
1032 mUserData.backup();
1033 mUserData->s.strDescription = aDescription;
1034
1035 return S_OK;
1036}
1037
1038HRESULT Machine::getId(com::Guid &aId)
1039{
1040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1041
1042 aId = mData->mUuid;
1043
1044 return S_OK;
1045}
1046
1047HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1048{
1049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1050 aGroups.resize(mUserData->s.llGroups.size());
1051 size_t i = 0;
1052 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1053 it != mUserData->s.llGroups.end(); ++it, ++i)
1054 aGroups[i] = (*it);
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1060{
1061 StringsList llGroups;
1062 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1063 if (FAILED(rc))
1064 return rc;
1065
1066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1067
1068 rc = i_checkStateDependency(MutableOrSavedStateDep);
1069 if (FAILED(rc)) return rc;
1070
1071 i_setModified(IsModified_MachineData);
1072 mUserData.backup();
1073 mUserData->s.llGroups = llGroups;
1074
1075 return S_OK;
1076}
1077
1078HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1079{
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 aOSTypeId = mUserData->s.strOsType;
1083
1084 return S_OK;
1085}
1086
1087HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1088{
1089 /* look up the object by Id to check it is valid */
1090 ComPtr<IGuestOSType> guestOSType;
1091 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1092 if (FAILED(rc)) return rc;
1093
1094 /* when setting, always use the "etalon" value for consistency -- lookup
1095 * by ID is case-insensitive and the input value may have different case */
1096 Bstr osTypeId;
1097 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1098 if (FAILED(rc)) return rc;
1099
1100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1101
1102 rc = i_checkStateDependency(MutableStateDep);
1103 if (FAILED(rc)) return rc;
1104
1105 i_setModified(IsModified_MachineData);
1106 mUserData.backup();
1107 mUserData->s.strOsType = osTypeId;
1108
1109 return S_OK;
1110}
1111
1112HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1113{
1114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1115
1116 *aFirmwareType = mHWData->mFirmwareType;
1117
1118 return S_OK;
1119}
1120
1121HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1122{
1123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1124
1125 HRESULT rc = i_checkStateDependency(MutableStateDep);
1126 if (FAILED(rc)) return rc;
1127
1128 i_setModified(IsModified_MachineData);
1129 mHWData.backup();
1130 mHWData->mFirmwareType = aFirmwareType;
1131
1132 return S_OK;
1133}
1134
1135HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1136{
1137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1138
1139 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1140
1141 return S_OK;
1142}
1143
1144HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1145{
1146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 HRESULT rc = i_checkStateDependency(MutableStateDep);
1149 if (FAILED(rc)) return rc;
1150
1151 i_setModified(IsModified_MachineData);
1152 mHWData.backup();
1153 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aPointingHIDType = mHWData->mPointingHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mPointingHIDType = aPointingHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aChipsetType = mHWData->mChipsetType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 if (aChipsetType != mHWData->mChipsetType)
1198 {
1199 i_setModified(IsModified_MachineData);
1200 mHWData.backup();
1201 mHWData->mChipsetType = aChipsetType;
1202
1203 // Resize network adapter array, to be finalized on commit/rollback.
1204 // We must not throw away entries yet, otherwise settings are lost
1205 // without a way to roll back.
1206 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1207 size_t oldCount = mNetworkAdapters.size();
1208 if (newCount > oldCount)
1209 {
1210 mNetworkAdapters.resize(newCount);
1211 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1212 {
1213 unconst(mNetworkAdapters[slot]).createObject();
1214 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1215 }
1216 }
1217 }
1218
1219 return S_OK;
1220}
1221
1222HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1223{
1224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1225
1226 *aParavirtProvider = mHWData->mParavirtProvider;
1227
1228 return S_OK;
1229}
1230
1231HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1232{
1233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 HRESULT rc = i_checkStateDependency(MutableStateDep);
1236 if (FAILED(rc)) return rc;
1237
1238 if (aParavirtProvider != mHWData->mParavirtProvider)
1239 {
1240 i_setModified(IsModified_MachineData);
1241 mHWData.backup();
1242 mHWData->mParavirtProvider = aParavirtProvider;
1243 }
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1249{
1250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 *aParavirtProvider = mHWData->mParavirtProvider;
1253 switch (mHWData->mParavirtProvider)
1254 {
1255 case ParavirtProvider_None:
1256 case ParavirtProvider_HyperV:
1257 case ParavirtProvider_KVM:
1258 case ParavirtProvider_Minimal:
1259 break;
1260
1261 /* Resolve dynamic provider types to the effective types. */
1262 default:
1263 {
1264 ComPtr<IGuestOSType> ptrGuestOSType;
1265 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1266 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1267
1268 Bstr guestTypeFamilyId;
1269 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1270 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1271 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1272
1273 switch (mHWData->mParavirtProvider)
1274 {
1275 case ParavirtProvider_Legacy:
1276 {
1277 if (fOsXGuest)
1278 *aParavirtProvider = ParavirtProvider_Minimal;
1279 else
1280 *aParavirtProvider = ParavirtProvider_None;
1281 break;
1282 }
1283
1284 case ParavirtProvider_Default:
1285 {
1286 if (fOsXGuest)
1287 *aParavirtProvider = ParavirtProvider_Minimal;
1288 else if ( mUserData->s.strOsType == "Windows10"
1289 || mUserData->s.strOsType == "Windows10_64"
1290 || mUserData->s.strOsType == "Windows81"
1291 || mUserData->s.strOsType == "Windows81_64"
1292 || mUserData->s.strOsType == "Windows8"
1293 || mUserData->s.strOsType == "Windows8_64"
1294 || mUserData->s.strOsType == "Windows7"
1295 || mUserData->s.strOsType == "Windows7_64"
1296 || mUserData->s.strOsType == "WindowsVista"
1297 || mUserData->s.strOsType == "WindowsVista_64"
1298 || mUserData->s.strOsType == "Windows2012"
1299 || mUserData->s.strOsType == "Windows2012_64"
1300 || mUserData->s.strOsType == "Windows2008"
1301 || mUserData->s.strOsType == "Windows2008_64")
1302 {
1303 *aParavirtProvider = ParavirtProvider_HyperV;
1304 }
1305 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1306 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1307 || mUserData->s.strOsType == "Linux"
1308 || mUserData->s.strOsType == "Linux_64"
1309 || mUserData->s.strOsType == "ArchLinux"
1310 || mUserData->s.strOsType == "ArchLinux_64"
1311 || mUserData->s.strOsType == "Debian"
1312 || mUserData->s.strOsType == "Debian_64"
1313 || mUserData->s.strOsType == "Fedora"
1314 || mUserData->s.strOsType == "Fedora_64"
1315 || mUserData->s.strOsType == "Gentoo"
1316 || mUserData->s.strOsType == "Gentoo_64"
1317 || mUserData->s.strOsType == "Mandriva"
1318 || mUserData->s.strOsType == "Mandriva_64"
1319 || mUserData->s.strOsType == "OpenSUSE"
1320 || mUserData->s.strOsType == "OpenSUSE_64"
1321 || mUserData->s.strOsType == "Oracle"
1322 || mUserData->s.strOsType == "Oracle_64"
1323 || mUserData->s.strOsType == "RedHat"
1324 || mUserData->s.strOsType == "RedHat_64"
1325 || mUserData->s.strOsType == "Turbolinux"
1326 || mUserData->s.strOsType == "Turbolinux_64"
1327 || mUserData->s.strOsType == "Ubuntu"
1328 || mUserData->s.strOsType == "Ubuntu_64"
1329 || mUserData->s.strOsType == "Xandros"
1330 || mUserData->s.strOsType == "Xandros_64")
1331 {
1332 *aParavirtProvider = ParavirtProvider_KVM;
1333 }
1334 else
1335 *aParavirtProvider = ParavirtProvider_None;
1336 break;
1337 }
1338 }
1339 break;
1340 }
1341 }
1342
1343 Assert( *aParavirtProvider == ParavirtProvider_None
1344 || *aParavirtProvider == ParavirtProvider_Minimal
1345 || *aParavirtProvider == ParavirtProvider_HyperV
1346 || *aParavirtProvider == ParavirtProvider_KVM);
1347 return S_OK;
1348}
1349
1350HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1351{
1352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1353
1354 aHardwareVersion = mHWData->mHWVersion;
1355
1356 return S_OK;
1357}
1358
1359HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1360{
1361 /* check known version */
1362 Utf8Str hwVersion = aHardwareVersion;
1363 if ( hwVersion.compare("1") != 0
1364 && hwVersion.compare("2") != 0)
1365 return setError(E_INVALIDARG,
1366 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1367
1368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1369
1370 HRESULT rc = i_checkStateDependency(MutableStateDep);
1371 if (FAILED(rc)) return rc;
1372
1373 i_setModified(IsModified_MachineData);
1374 mHWData.backup();
1375 mHWData->mHWVersion = aHardwareVersion;
1376
1377 return S_OK;
1378}
1379
1380HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1381{
1382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 if (!mHWData->mHardwareUUID.isZero())
1385 aHardwareUUID = mHWData->mHardwareUUID;
1386 else
1387 aHardwareUUID = mData->mUuid;
1388
1389 return S_OK;
1390}
1391
1392HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1393{
1394 if (!aHardwareUUID.isValid())
1395 return E_INVALIDARG;
1396
1397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1398
1399 HRESULT rc = i_checkStateDependency(MutableStateDep);
1400 if (FAILED(rc)) return rc;
1401
1402 i_setModified(IsModified_MachineData);
1403 mHWData.backup();
1404 if (aHardwareUUID == mData->mUuid)
1405 mHWData->mHardwareUUID.clear();
1406 else
1407 mHWData->mHardwareUUID = aHardwareUUID;
1408
1409 return S_OK;
1410}
1411
1412HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1413{
1414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 *aMemorySize = mHWData->mMemorySize;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::setMemorySize(ULONG aMemorySize)
1422{
1423 /* check RAM limits */
1424 if ( aMemorySize < MM_RAM_MIN_IN_MB
1425 || aMemorySize > MM_RAM_MAX_IN_MB
1426 )
1427 return setError(E_INVALIDARG,
1428 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1429 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1430
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 HRESULT rc = i_checkStateDependency(MutableStateDep);
1434 if (FAILED(rc)) return rc;
1435
1436 i_setModified(IsModified_MachineData);
1437 mHWData.backup();
1438 mHWData->mMemorySize = aMemorySize;
1439
1440 return S_OK;
1441}
1442
1443HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1444{
1445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1446
1447 *aCPUCount = mHWData->mCPUCount;
1448
1449 return S_OK;
1450}
1451
1452HRESULT Machine::setCPUCount(ULONG aCPUCount)
1453{
1454 /* check CPU limits */
1455 if ( aCPUCount < SchemaDefs::MinCPUCount
1456 || aCPUCount > SchemaDefs::MaxCPUCount
1457 )
1458 return setError(E_INVALIDARG,
1459 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1460 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1461
1462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1463
1464 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1465 if (mHWData->mCPUHotPlugEnabled)
1466 {
1467 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1468 {
1469 if (mHWData->mCPUAttached[idx])
1470 return setError(E_INVALIDARG,
1471 tr("There is still a CPU attached to socket %lu."
1472 "Detach the CPU before removing the socket"),
1473 aCPUCount, idx+1);
1474 }
1475 }
1476
1477 HRESULT rc = i_checkStateDependency(MutableStateDep);
1478 if (FAILED(rc)) return rc;
1479
1480 i_setModified(IsModified_MachineData);
1481 mHWData.backup();
1482 mHWData->mCPUCount = aCPUCount;
1483
1484 return S_OK;
1485}
1486
1487HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1488{
1489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1490
1491 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1492
1493 return S_OK;
1494}
1495
1496HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1497{
1498 HRESULT rc = S_OK;
1499
1500 /* check throttle limits */
1501 if ( aCPUExecutionCap < 1
1502 || aCPUExecutionCap > 100
1503 )
1504 return setError(E_INVALIDARG,
1505 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1506 aCPUExecutionCap, 1, 100);
1507
1508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1509
1510 alock.release();
1511 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1512 alock.acquire();
1513 if (FAILED(rc)) return rc;
1514
1515 i_setModified(IsModified_MachineData);
1516 mHWData.backup();
1517 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1518
1519 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1520 if (Global::IsOnline(mData->mMachineState))
1521 i_saveSettings(NULL);
1522
1523 return S_OK;
1524}
1525
1526HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1527{
1528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1529
1530 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1531
1532 return S_OK;
1533}
1534
1535HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1536{
1537 HRESULT rc = S_OK;
1538
1539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1540
1541 rc = i_checkStateDependency(MutableStateDep);
1542 if (FAILED(rc)) return rc;
1543
1544 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1545 {
1546 if (aCPUHotPlugEnabled)
1547 {
1548 i_setModified(IsModified_MachineData);
1549 mHWData.backup();
1550
1551 /* Add the amount of CPUs currently attached */
1552 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1553 mHWData->mCPUAttached[i] = true;
1554 }
1555 else
1556 {
1557 /*
1558 * We can disable hotplug only if the amount of maximum CPUs is equal
1559 * to the amount of attached CPUs
1560 */
1561 unsigned cCpusAttached = 0;
1562 unsigned iHighestId = 0;
1563
1564 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1565 {
1566 if (mHWData->mCPUAttached[i])
1567 {
1568 cCpusAttached++;
1569 iHighestId = i;
1570 }
1571 }
1572
1573 if ( (cCpusAttached != mHWData->mCPUCount)
1574 || (iHighestId >= mHWData->mCPUCount))
1575 return setError(E_INVALIDARG,
1576 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1577
1578 i_setModified(IsModified_MachineData);
1579 mHWData.backup();
1580 }
1581 }
1582
1583 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1584
1585 return rc;
1586}
1587
1588HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1589{
1590#ifdef VBOX_WITH_USB_CARDREADER
1591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1592
1593 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1594
1595 return S_OK;
1596#else
1597 NOREF(aEmulatedUSBCardReaderEnabled);
1598 return E_NOTIMPL;
1599#endif
1600}
1601
1602HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1603{
1604#ifdef VBOX_WITH_USB_CARDREADER
1605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1606
1607 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1608 if (FAILED(rc)) return rc;
1609
1610 i_setModified(IsModified_MachineData);
1611 mHWData.backup();
1612 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1613
1614 return S_OK;
1615#else
1616 NOREF(aEmulatedUSBCardReaderEnabled);
1617 return E_NOTIMPL;
1618#endif
1619}
1620
1621HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1622{
1623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 *aHPETEnabled = mHWData->mHPETEnabled;
1626
1627 return S_OK;
1628}
1629
1630HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1631{
1632 HRESULT rc = S_OK;
1633
1634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1635
1636 rc = i_checkStateDependency(MutableStateDep);
1637 if (FAILED(rc)) return rc;
1638
1639 i_setModified(IsModified_MachineData);
1640 mHWData.backup();
1641
1642 mHWData->mHPETEnabled = aHPETEnabled;
1643
1644 return rc;
1645}
1646
1647HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1648{
1649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1650
1651 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1652 return S_OK;
1653}
1654
1655HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1656{
1657 HRESULT rc = S_OK;
1658
1659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1660
1661 i_setModified(IsModified_MachineData);
1662 mHWData.backup();
1663 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1664
1665 alock.release();
1666 rc = i_onVideoCaptureChange();
1667 alock.acquire();
1668 if (FAILED(rc))
1669 {
1670 /*
1671 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1672 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1673 * determine if it should start or stop capturing. Therefore we need to manually
1674 * undo change.
1675 */
1676 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1677 return rc;
1678 }
1679
1680 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1681 if (Global::IsOnline(mData->mMachineState))
1682 i_saveSettings(NULL);
1683
1684 return rc;
1685}
1686
1687HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1688{
1689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1690 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1691 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1692 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1693 return S_OK;
1694}
1695
1696HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1697{
1698 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1699 bool fChanged = false;
1700
1701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1702
1703 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1704 {
1705 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1706 {
1707 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1708 fChanged = true;
1709 }
1710 }
1711 if (fChanged)
1712 {
1713 alock.release();
1714 HRESULT rc = i_onVideoCaptureChange();
1715 alock.acquire();
1716 if (FAILED(rc)) return rc;
1717 i_setModified(IsModified_MachineData);
1718
1719 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1720 if (Global::IsOnline(mData->mMachineState))
1721 i_saveSettings(NULL);
1722 }
1723
1724 return S_OK;
1725}
1726
1727HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1728{
1729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1730 if (mHWData->mVideoCaptureFile.isEmpty())
1731 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1732 else
1733 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1734 return S_OK;
1735}
1736
1737HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1738{
1739 Utf8Str strFile(aVideoCaptureFile);
1740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1741
1742 if ( Global::IsOnline(mData->mMachineState)
1743 && mHWData->mVideoCaptureEnabled)
1744 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1745
1746 if (!RTPathStartsWithRoot(strFile.c_str()))
1747 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1748
1749 if (!strFile.isEmpty())
1750 {
1751 Utf8Str defaultFile;
1752 i_getDefaultVideoCaptureFile(defaultFile);
1753 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1754 strFile.setNull();
1755 }
1756
1757 i_setModified(IsModified_MachineData);
1758 mHWData.backup();
1759 mHWData->mVideoCaptureFile = strFile;
1760
1761 return S_OK;
1762}
1763
1764HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1765{
1766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1767 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1768 return S_OK;
1769}
1770
1771HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1772{
1773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1774
1775 if ( Global::IsOnline(mData->mMachineState)
1776 && mHWData->mVideoCaptureEnabled)
1777 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1778
1779 i_setModified(IsModified_MachineData);
1780 mHWData.backup();
1781 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1782
1783 return S_OK;
1784}
1785
1786HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1787{
1788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1789 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1790 return S_OK;
1791}
1792
1793HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1794{
1795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1796
1797 if ( Global::IsOnline(mData->mMachineState)
1798 && mHWData->mVideoCaptureEnabled)
1799 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1800
1801 i_setModified(IsModified_MachineData);
1802 mHWData.backup();
1803 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1804
1805 return S_OK;
1806}
1807
1808HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1809{
1810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1811 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1812 return S_OK;
1813}
1814
1815HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1816{
1817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1818
1819 if ( Global::IsOnline(mData->mMachineState)
1820 && mHWData->mVideoCaptureEnabled)
1821 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1822
1823 i_setModified(IsModified_MachineData);
1824 mHWData.backup();
1825 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1826
1827 return S_OK;
1828}
1829
1830HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1831{
1832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1833 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1834 return S_OK;
1835}
1836
1837HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1838{
1839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1840
1841 if ( Global::IsOnline(mData->mMachineState)
1842 && mHWData->mVideoCaptureEnabled)
1843 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1844
1845 i_setModified(IsModified_MachineData);
1846 mHWData.backup();
1847 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1848
1849 return S_OK;
1850}
1851
1852HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1853{
1854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1855 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1856 return S_OK;
1857}
1858
1859HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1860{
1861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1862
1863 if ( Global::IsOnline(mData->mMachineState)
1864 && mHWData->mVideoCaptureEnabled)
1865 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1866
1867 i_setModified(IsModified_MachineData);
1868 mHWData.backup();
1869 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1870
1871 return S_OK;
1872}
1873
1874HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1875{
1876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1877 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1878 return S_OK;
1879}
1880
1881HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1882{
1883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1884
1885 if ( Global::IsOnline(mData->mMachineState)
1886 && mHWData->mVideoCaptureEnabled)
1887 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1888
1889 i_setModified(IsModified_MachineData);
1890 mHWData.backup();
1891 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1892
1893 return S_OK;
1894}
1895
1896HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1897{
1898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1899
1900 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1901 return S_OK;
1902}
1903
1904HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1905{
1906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1907
1908 if ( Global::IsOnline(mData->mMachineState)
1909 && mHWData->mVideoCaptureEnabled)
1910 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1911
1912 i_setModified(IsModified_MachineData);
1913 mHWData.backup();
1914 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1915
1916 return S_OK;
1917}
1918
1919HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1920{
1921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1922
1923 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1924
1925 return S_OK;
1926}
1927
1928HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1929{
1930 switch (aGraphicsControllerType)
1931 {
1932 case GraphicsControllerType_Null:
1933 case GraphicsControllerType_VBoxVGA:
1934#ifdef VBOX_WITH_VMSVGA
1935 case GraphicsControllerType_VMSVGA:
1936#endif
1937 break;
1938 default:
1939 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1940 }
1941
1942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1943
1944 HRESULT rc = i_checkStateDependency(MutableStateDep);
1945 if (FAILED(rc)) return rc;
1946
1947 i_setModified(IsModified_MachineData);
1948 mHWData.backup();
1949 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1950
1951 return S_OK;
1952}
1953
1954HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1955{
1956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 *aVRAMSize = mHWData->mVRAMSize;
1959
1960 return S_OK;
1961}
1962
1963HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1964{
1965 /* check VRAM limits */
1966 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1967 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1968 return setError(E_INVALIDARG,
1969 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1970 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1971
1972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1973
1974 HRESULT rc = i_checkStateDependency(MutableStateDep);
1975 if (FAILED(rc)) return rc;
1976
1977 i_setModified(IsModified_MachineData);
1978 mHWData.backup();
1979 mHWData->mVRAMSize = aVRAMSize;
1980
1981 return S_OK;
1982}
1983
1984/** @todo this method should not be public */
1985HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1986{
1987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1988
1989 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1990
1991 return S_OK;
1992}
1993
1994/**
1995 * Set the memory balloon size.
1996 *
1997 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1998 * we have to make sure that we never call IGuest from here.
1999 */
2000HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2001{
2002 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2003#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2004 /* check limits */
2005 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2006 return setError(E_INVALIDARG,
2007 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2008 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2009
2010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2011
2012 i_setModified(IsModified_MachineData);
2013 mHWData.backup();
2014 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2015
2016 return S_OK;
2017#else
2018 NOREF(aMemoryBalloonSize);
2019 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2020#endif
2021}
2022
2023HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2024{
2025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2026
2027 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2028 return S_OK;
2029}
2030
2031HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2032{
2033#ifdef VBOX_WITH_PAGE_SHARING
2034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2035
2036 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2037 i_setModified(IsModified_MachineData);
2038 mHWData.backup();
2039 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2040 return S_OK;
2041#else
2042 NOREF(aPageFusionEnabled);
2043 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2044#endif
2045}
2046
2047HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2048{
2049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2050
2051 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2052
2053 return S_OK;
2054}
2055
2056HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2057{
2058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2059
2060 HRESULT rc = i_checkStateDependency(MutableStateDep);
2061 if (FAILED(rc)) return rc;
2062
2063 /** @todo check validity! */
2064
2065 i_setModified(IsModified_MachineData);
2066 mHWData.backup();
2067 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2068
2069 return S_OK;
2070}
2071
2072
2073HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2074{
2075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2076
2077 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2078
2079 return S_OK;
2080}
2081
2082HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2083{
2084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2085
2086 HRESULT rc = i_checkStateDependency(MutableStateDep);
2087 if (FAILED(rc)) return rc;
2088
2089 /** @todo check validity! */
2090 i_setModified(IsModified_MachineData);
2091 mHWData.backup();
2092 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2093
2094 return S_OK;
2095}
2096
2097HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2098{
2099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2100
2101 *aMonitorCount = mHWData->mMonitorCount;
2102
2103 return S_OK;
2104}
2105
2106HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2107{
2108 /* make sure monitor count is a sensible number */
2109 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2110 return setError(E_INVALIDARG,
2111 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2112 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2113
2114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2115
2116 HRESULT rc = i_checkStateDependency(MutableStateDep);
2117 if (FAILED(rc)) return rc;
2118
2119 i_setModified(IsModified_MachineData);
2120 mHWData.backup();
2121 mHWData->mMonitorCount = aMonitorCount;
2122
2123 return S_OK;
2124}
2125
2126HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2127{
2128 /* mBIOSSettings is constant during life time, no need to lock */
2129 aBIOSSettings = mBIOSSettings;
2130
2131 return S_OK;
2132}
2133
2134HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2135{
2136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2137
2138 switch (aProperty)
2139 {
2140 case CPUPropertyType_PAE:
2141 *aValue = mHWData->mPAEEnabled;
2142 break;
2143
2144 case CPUPropertyType_Synthetic:
2145 *aValue = mHWData->mSyntheticCpu;
2146 break;
2147
2148 case CPUPropertyType_LongMode:
2149 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2150 *aValue = TRUE;
2151 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2152 *aValue = FALSE;
2153#if HC_ARCH_BITS == 64
2154 else
2155 *aValue = TRUE;
2156#else
2157 else
2158 {
2159 *aValue = FALSE;
2160
2161 ComPtr<IGuestOSType> ptrGuestOSType;
2162 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2163 if (SUCCEEDED(hrc2))
2164 {
2165 BOOL fIs64Bit = FALSE;
2166 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2167 if (SUCCEEDED(hrc2) && fIs64Bit)
2168 {
2169 ComObjPtr<Host> ptrHost = mParent->i_host();
2170 alock.release();
2171
2172 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2173 if (FAILED(hrc2))
2174 *aValue = FALSE;
2175 }
2176 }
2177 }
2178#endif
2179 break;
2180
2181 case CPUPropertyType_TripleFaultReset:
2182 *aValue = mHWData->mTripleFaultReset;
2183 break;
2184
2185 default:
2186 return E_INVALIDARG;
2187 }
2188 return S_OK;
2189}
2190
2191HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2192{
2193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2194
2195 HRESULT rc = i_checkStateDependency(MutableStateDep);
2196 if (FAILED(rc)) return rc;
2197
2198 switch (aProperty)
2199 {
2200 case CPUPropertyType_PAE:
2201 i_setModified(IsModified_MachineData);
2202 mHWData.backup();
2203 mHWData->mPAEEnabled = !!aValue;
2204 break;
2205
2206 case CPUPropertyType_Synthetic:
2207 i_setModified(IsModified_MachineData);
2208 mHWData.backup();
2209 mHWData->mSyntheticCpu = !!aValue;
2210 break;
2211
2212 case CPUPropertyType_LongMode:
2213 i_setModified(IsModified_MachineData);
2214 mHWData.backup();
2215 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2216 break;
2217
2218 case CPUPropertyType_TripleFaultReset:
2219 i_setModified(IsModified_MachineData);
2220 mHWData.backup();
2221 mHWData->mTripleFaultReset = !!aValue;
2222 break;
2223
2224 default:
2225 return E_INVALIDARG;
2226 }
2227 return S_OK;
2228}
2229
2230HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2231{
2232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2233
2234 switch(aId)
2235 {
2236 case 0x0:
2237 case 0x1:
2238 case 0x2:
2239 case 0x3:
2240 case 0x4:
2241 case 0x5:
2242 case 0x6:
2243 case 0x7:
2244 case 0x8:
2245 case 0x9:
2246 case 0xA:
2247 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2248 return E_INVALIDARG;
2249
2250 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2251 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2252 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2253 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2254 break;
2255
2256 case 0x80000000:
2257 case 0x80000001:
2258 case 0x80000002:
2259 case 0x80000003:
2260 case 0x80000004:
2261 case 0x80000005:
2262 case 0x80000006:
2263 case 0x80000007:
2264 case 0x80000008:
2265 case 0x80000009:
2266 case 0x8000000A:
2267 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2268 return E_INVALIDARG;
2269
2270 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2271 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2272 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2273 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2274 break;
2275
2276 default:
2277 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2278 }
2279 return S_OK;
2280}
2281
2282
2283HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2284{
2285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2286
2287 HRESULT rc = i_checkStateDependency(MutableStateDep);
2288 if (FAILED(rc)) return rc;
2289
2290 switch(aId)
2291 {
2292 case 0x0:
2293 case 0x1:
2294 case 0x2:
2295 case 0x3:
2296 case 0x4:
2297 case 0x5:
2298 case 0x6:
2299 case 0x7:
2300 case 0x8:
2301 case 0x9:
2302 case 0xA:
2303 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2304 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2305 i_setModified(IsModified_MachineData);
2306 mHWData.backup();
2307 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2308 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2309 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2310 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2311 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2312 break;
2313
2314 case 0x80000000:
2315 case 0x80000001:
2316 case 0x80000002:
2317 case 0x80000003:
2318 case 0x80000004:
2319 case 0x80000005:
2320 case 0x80000006:
2321 case 0x80000007:
2322 case 0x80000008:
2323 case 0x80000009:
2324 case 0x8000000A:
2325 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2326 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2330 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2331 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2332 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2333 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2334 break;
2335
2336 default:
2337 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2338 }
2339 return S_OK;
2340}
2341
2342HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2343{
2344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2345
2346 HRESULT rc = i_checkStateDependency(MutableStateDep);
2347 if (FAILED(rc)) return rc;
2348
2349 switch(aId)
2350 {
2351 case 0x0:
2352 case 0x1:
2353 case 0x2:
2354 case 0x3:
2355 case 0x4:
2356 case 0x5:
2357 case 0x6:
2358 case 0x7:
2359 case 0x8:
2360 case 0x9:
2361 case 0xA:
2362 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2363 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2364 i_setModified(IsModified_MachineData);
2365 mHWData.backup();
2366 /* Invalidate leaf. */
2367 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2368 break;
2369
2370 case 0x80000000:
2371 case 0x80000001:
2372 case 0x80000002:
2373 case 0x80000003:
2374 case 0x80000004:
2375 case 0x80000005:
2376 case 0x80000006:
2377 case 0x80000007:
2378 case 0x80000008:
2379 case 0x80000009:
2380 case 0x8000000A:
2381 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2382 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2383 i_setModified(IsModified_MachineData);
2384 mHWData.backup();
2385 /* Invalidate leaf. */
2386 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2387 break;
2388
2389 default:
2390 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2391 }
2392 return S_OK;
2393}
2394
2395HRESULT Machine::removeAllCPUIDLeaves()
2396{
2397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2398
2399 HRESULT rc = i_checkStateDependency(MutableStateDep);
2400 if (FAILED(rc)) return rc;
2401
2402 i_setModified(IsModified_MachineData);
2403 mHWData.backup();
2404
2405 /* Invalidate all standard leafs. */
2406 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2407 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2408
2409 /* Invalidate all extended leafs. */
2410 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2411 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2412
2413 return S_OK;
2414}
2415HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2416{
2417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2418
2419 switch(aProperty)
2420 {
2421 case HWVirtExPropertyType_Enabled:
2422 *aValue = mHWData->mHWVirtExEnabled;
2423 break;
2424
2425 case HWVirtExPropertyType_VPID:
2426 *aValue = mHWData->mHWVirtExVPIDEnabled;
2427 break;
2428
2429 case HWVirtExPropertyType_NestedPaging:
2430 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2431 break;
2432
2433 case HWVirtExPropertyType_UnrestrictedExecution:
2434 *aValue = mHWData->mHWVirtExUXEnabled;
2435 break;
2436
2437 case HWVirtExPropertyType_LargePages:
2438 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2439#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2440 *aValue = FALSE;
2441#endif
2442 break;
2443
2444 case HWVirtExPropertyType_Force:
2445 *aValue = mHWData->mHWVirtExForceEnabled;
2446 break;
2447
2448 default:
2449 return E_INVALIDARG;
2450 }
2451 return S_OK;
2452}
2453
2454HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2455{
2456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2457
2458 HRESULT rc = i_checkStateDependency(MutableStateDep);
2459 if (FAILED(rc)) return rc;
2460
2461 switch(aProperty)
2462 {
2463 case HWVirtExPropertyType_Enabled:
2464 i_setModified(IsModified_MachineData);
2465 mHWData.backup();
2466 mHWData->mHWVirtExEnabled = !!aValue;
2467 break;
2468
2469 case HWVirtExPropertyType_VPID:
2470 i_setModified(IsModified_MachineData);
2471 mHWData.backup();
2472 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2473 break;
2474
2475 case HWVirtExPropertyType_NestedPaging:
2476 i_setModified(IsModified_MachineData);
2477 mHWData.backup();
2478 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2479 break;
2480
2481 case HWVirtExPropertyType_UnrestrictedExecution:
2482 i_setModified(IsModified_MachineData);
2483 mHWData.backup();
2484 mHWData->mHWVirtExUXEnabled = !!aValue;
2485 break;
2486
2487 case HWVirtExPropertyType_LargePages:
2488 i_setModified(IsModified_MachineData);
2489 mHWData.backup();
2490 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2491 break;
2492
2493 case HWVirtExPropertyType_Force:
2494 i_setModified(IsModified_MachineData);
2495 mHWData.backup();
2496 mHWData->mHWVirtExForceEnabled = !!aValue;
2497 break;
2498
2499 default:
2500 return E_INVALIDARG;
2501 }
2502
2503 return S_OK;
2504}
2505
2506HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2507{
2508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2509
2510 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2511
2512 return S_OK;
2513}
2514
2515HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2516{
2517 /* @todo (r=dmik):
2518 * 1. Allow to change the name of the snapshot folder containing snapshots
2519 * 2. Rename the folder on disk instead of just changing the property
2520 * value (to be smart and not to leave garbage). Note that it cannot be
2521 * done here because the change may be rolled back. Thus, the right
2522 * place is #saveSettings().
2523 */
2524
2525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2526
2527 HRESULT rc = i_checkStateDependency(MutableStateDep);
2528 if (FAILED(rc)) return rc;
2529
2530 if (!mData->mCurrentSnapshot.isNull())
2531 return setError(E_FAIL,
2532 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2533
2534 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2535
2536 if (strSnapshotFolder.isEmpty())
2537 strSnapshotFolder = "Snapshots";
2538 int vrc = i_calculateFullPath(strSnapshotFolder,
2539 strSnapshotFolder);
2540 if (RT_FAILURE(vrc))
2541 return setError(E_FAIL,
2542 tr("Invalid snapshot folder '%s' (%Rrc)"),
2543 strSnapshotFolder.c_str(), vrc);
2544
2545 i_setModified(IsModified_MachineData);
2546 mUserData.backup();
2547
2548 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2549
2550 return S_OK;
2551}
2552
2553HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2554{
2555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2556
2557 aMediumAttachments.resize(mMediaData->mAttachments.size());
2558 size_t i = 0;
2559 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2560 it != mMediaData->mAttachments.end(); ++it, ++i)
2561 aMediumAttachments[i] = *it;
2562
2563 return S_OK;
2564}
2565
2566HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2567{
2568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2569
2570 Assert(!!mVRDEServer);
2571
2572 aVRDEServer = mVRDEServer;
2573
2574 return S_OK;
2575}
2576
2577HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2578{
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581 aAudioAdapter = mAudioAdapter;
2582
2583 return S_OK;
2584}
2585
2586HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2587{
2588#ifdef VBOX_WITH_VUSB
2589 clearError();
2590 MultiResult rc(S_OK);
2591
2592# ifdef VBOX_WITH_USB
2593 rc = mParent->i_host()->i_checkUSBProxyService();
2594 if (FAILED(rc)) return rc;
2595# endif
2596
2597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2598
2599 USBControllerList data = *mUSBControllers.data();
2600 aUSBControllers.resize(data.size());
2601 size_t i = 0;
2602 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2603 aUSBControllers[i] = *it;
2604
2605 return S_OK;
2606#else
2607 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2608 * extended error info to indicate that USB is simply not available
2609 * (w/o treating it as a failure), for example, as in OSE */
2610 NOREF(aUSBControllers);
2611 ReturnComNotImplemented();
2612#endif /* VBOX_WITH_VUSB */
2613}
2614
2615HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2616{
2617#ifdef VBOX_WITH_VUSB
2618 clearError();
2619 MultiResult rc(S_OK);
2620
2621# ifdef VBOX_WITH_USB
2622 rc = mParent->i_host()->i_checkUSBProxyService();
2623 if (FAILED(rc)) return rc;
2624# endif
2625
2626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2627
2628 aUSBDeviceFilters = mUSBDeviceFilters;
2629 return rc;
2630#else
2631 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2632 * extended error info to indicate that USB is simply not available
2633 * (w/o treating it as a failure), for example, as in OSE */
2634 NOREF(aUSBDeviceFilters);
2635 ReturnComNotImplemented();
2636#endif /* VBOX_WITH_VUSB */
2637}
2638
2639HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2640{
2641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 aSettingsFilePath = mData->m_strConfigFileFull;
2644
2645 return S_OK;
2646}
2647
2648HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2649{
2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2653 if (FAILED(rc)) return rc;
2654
2655 if (!mData->pMachineConfigFile->fileExists())
2656 // this is a new machine, and no config file exists yet:
2657 *aSettingsModified = TRUE;
2658 else
2659 *aSettingsModified = (mData->flModifications != 0);
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2665{
2666
2667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2668
2669 *aSessionState = mData->mSession.mState;
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2675{
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 aSessionType = mData->mSession.mType;
2679
2680 return S_OK;
2681}
2682
2683HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2684{
2685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2686
2687 *aSessionPID = mData->mSession.mPID;
2688
2689 return S_OK;
2690}
2691
2692HRESULT Machine::getState(MachineState_T *aState)
2693{
2694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 *aState = mData->mMachineState;
2697 Assert(mData->mMachineState != MachineState_Null);
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2703{
2704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2707
2708 return S_OK;
2709}
2710
2711HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2712{
2713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 aStateFilePath = mSSData->strStateFilePath;
2716
2717 return S_OK;
2718}
2719
2720HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2721{
2722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2723
2724 i_getLogFolder(aLogFolder);
2725
2726 return S_OK;
2727}
2728
2729HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2730{
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 aCurrentSnapshot = mData->mCurrentSnapshot;
2734
2735 return S_OK;
2736}
2737
2738HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2739{
2740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2741
2742 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2743 ? 0
2744 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2745
2746 return S_OK;
2747}
2748
2749HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2750{
2751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2752
2753 /* Note: for machines with no snapshots, we always return FALSE
2754 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2755 * reasons :) */
2756
2757 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2758 ? FALSE
2759 : mData->mCurrentStateModified;
2760
2761 return S_OK;
2762}
2763
2764HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2765{
2766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2767
2768 aSharedFolders.resize(mHWData->mSharedFolders.size());
2769 size_t i = 0;
2770 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2771 it != mHWData->mSharedFolders.end(); ++i, ++it)
2772 aSharedFolders[i] = *it;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2778{
2779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 *aClipboardMode = mHWData->mClipboardMode;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2787{
2788 HRESULT rc = S_OK;
2789
2790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 alock.release();
2793 rc = i_onClipboardModeChange(aClipboardMode);
2794 alock.acquire();
2795 if (FAILED(rc)) return rc;
2796
2797 i_setModified(IsModified_MachineData);
2798 mHWData.backup();
2799 mHWData->mClipboardMode = aClipboardMode;
2800
2801 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2802 if (Global::IsOnline(mData->mMachineState))
2803 i_saveSettings(NULL);
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 *aDnDMode = mHWData->mDnDMode;
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2818{
2819 HRESULT rc = S_OK;
2820
2821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2822
2823 alock.release();
2824 rc = i_onDnDModeChange(aDnDMode);
2825
2826 alock.acquire();
2827 if (FAILED(rc)) return rc;
2828
2829 i_setModified(IsModified_MachineData);
2830 mHWData.backup();
2831 mHWData->mDnDMode = aDnDMode;
2832
2833 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2834 if (Global::IsOnline(mData->mMachineState))
2835 i_saveSettings(NULL);
2836
2837 return S_OK;
2838}
2839
2840HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2841{
2842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2843
2844 try
2845 {
2846 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2847 }
2848 catch (...)
2849 {
2850 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2851 }
2852
2853 return S_OK;
2854}
2855
2856HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2857{
2858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2859
2860 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2861 if (FAILED(rc)) return rc;
2862
2863 i_setModified(IsModified_MachineData);
2864 mHWData.backup();
2865 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2866 return rc;
2867}
2868
2869HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872 StorageControllerList data = *mStorageControllers.data();
2873 size_t i = 0;
2874 aStorageControllers.resize(data.size());
2875 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2876 aStorageControllers[i] = *it;
2877 return S_OK;
2878}
2879
2880HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2881{
2882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 *aEnabled = mUserData->s.fTeleporterEnabled;
2885
2886 return S_OK;
2887}
2888
2889HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2890{
2891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 /* Only allow it to be set to true when PoweredOff or Aborted.
2894 (Clearing it is always permitted.) */
2895 if ( aTeleporterEnabled
2896 && mData->mRegistered
2897 && ( !i_isSessionMachine()
2898 || ( mData->mMachineState != MachineState_PoweredOff
2899 && mData->mMachineState != MachineState_Teleported
2900 && mData->mMachineState != MachineState_Aborted
2901 )
2902 )
2903 )
2904 return setError(VBOX_E_INVALID_VM_STATE,
2905 tr("The machine is not powered off (state is %s)"),
2906 Global::stringifyMachineState(mData->mMachineState));
2907
2908 i_setModified(IsModified_MachineData);
2909 mUserData.backup();
2910 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2911
2912 return S_OK;
2913}
2914
2915HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2916{
2917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2920
2921 return S_OK;
2922}
2923
2924HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2925{
2926 if (aTeleporterPort >= _64K)
2927 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2928
2929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2930
2931 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2932 if (FAILED(rc)) return rc;
2933
2934 i_setModified(IsModified_MachineData);
2935 mUserData.backup();
2936 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2937
2938 return S_OK;
2939}
2940
2941HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2942{
2943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2944
2945 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2946
2947 return S_OK;
2948}
2949
2950HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2951{
2952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2955 if (FAILED(rc)) return rc;
2956
2957 i_setModified(IsModified_MachineData);
2958 mUserData.backup();
2959 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2960
2961 return S_OK;
2962}
2963
2964HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2965{
2966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2967 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2968
2969 return S_OK;
2970}
2971
2972HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2973{
2974 /*
2975 * Hash the password first.
2976 */
2977 com::Utf8Str aT = aTeleporterPassword;
2978
2979 if (!aT.isEmpty())
2980 {
2981 if (VBoxIsPasswordHashed(&aT))
2982 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2983 VBoxHashPassword(&aT);
2984 }
2985
2986 /*
2987 * Do the update.
2988 */
2989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2990 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2991 if (SUCCEEDED(hrc))
2992 {
2993 i_setModified(IsModified_MachineData);
2994 mUserData.backup();
2995 mUserData->s.strTeleporterPassword = aT;
2996 }
2997
2998 return hrc;
2999}
3000
3001HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3002{
3003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3004
3005 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3006 return S_OK;
3007}
3008
3009HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3010{
3011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3012
3013 /* @todo deal with running state change. */
3014 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3015 if (FAILED(rc)) return rc;
3016
3017 i_setModified(IsModified_MachineData);
3018 mUserData.backup();
3019 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3020 return S_OK;
3021}
3022
3023HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3024{
3025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3026
3027 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3028 return S_OK;
3029}
3030
3031HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3032{
3033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3034
3035 /* @todo deal with running state change. */
3036 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3037 if (FAILED(rc)) return rc;
3038
3039 i_setModified(IsModified_MachineData);
3040 mUserData.backup();
3041 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3042 return S_OK;
3043}
3044
3045HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3046{
3047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3050 return S_OK;
3051}
3052
3053HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3054{
3055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 /* @todo deal with running state change. */
3058 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3059 if (FAILED(rc)) return rc;
3060
3061 i_setModified(IsModified_MachineData);
3062 mUserData.backup();
3063 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3064 return S_OK;
3065}
3066
3067HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3068{
3069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3070
3071 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3072
3073 return S_OK;
3074}
3075
3076HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3077{
3078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 /* @todo deal with running state change. */
3081 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3082 if (FAILED(rc)) return rc;
3083
3084 i_setModified(IsModified_MachineData);
3085 mUserData.backup();
3086 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3087
3088 return S_OK;
3089}
3090
3091HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3092{
3093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3096 return S_OK;
3097}
3098
3099HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3100{
3101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3102
3103 /* @todo deal with running state change. */
3104 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3105 if (FAILED(rc)) return rc;
3106
3107 i_setModified(IsModified_MachineData);
3108 mUserData.backup();
3109 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3110 return S_OK;
3111}
3112
3113HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3114{
3115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3118
3119 return S_OK;
3120}
3121
3122HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3123{
3124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3125
3126 /* Only allow it to be set to true when PoweredOff or Aborted.
3127 (Clearing it is always permitted.) */
3128 if ( aRTCUseUTC
3129 && mData->mRegistered
3130 && ( !i_isSessionMachine()
3131 || ( mData->mMachineState != MachineState_PoweredOff
3132 && mData->mMachineState != MachineState_Teleported
3133 && mData->mMachineState != MachineState_Aborted
3134 )
3135 )
3136 )
3137 return setError(VBOX_E_INVALID_VM_STATE,
3138 tr("The machine is not powered off (state is %s)"),
3139 Global::stringifyMachineState(mData->mMachineState));
3140
3141 i_setModified(IsModified_MachineData);
3142 mUserData.backup();
3143 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3144
3145 return S_OK;
3146}
3147
3148HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3149{
3150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3151
3152 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3153
3154 return S_OK;
3155}
3156
3157HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3158{
3159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 HRESULT rc = i_checkStateDependency(MutableStateDep);
3162 if (FAILED(rc)) return rc;
3163
3164 i_setModified(IsModified_MachineData);
3165 mHWData.backup();
3166 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3167
3168 return S_OK;
3169}
3170
3171HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3172{
3173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3174
3175 *aIOCacheSize = mHWData->mIOCacheSize;
3176
3177 return S_OK;
3178}
3179
3180HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3181{
3182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 HRESULT rc = i_checkStateDependency(MutableStateDep);
3185 if (FAILED(rc)) return rc;
3186
3187 i_setModified(IsModified_MachineData);
3188 mHWData.backup();
3189 mHWData->mIOCacheSize = aIOCacheSize;
3190
3191 return S_OK;
3192}
3193
3194
3195/**
3196 * @note Locks objects!
3197 */
3198HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3199 LockType_T aLockType)
3200
3201{
3202 /* check the session state */
3203 SessionState_T state;
3204 HRESULT rc = aSession->COMGETTER(State)(&state);
3205 if (FAILED(rc)) return rc;
3206
3207 if (state != SessionState_Unlocked)
3208 return setError(VBOX_E_INVALID_OBJECT_STATE,
3209 tr("The given session is busy"));
3210
3211 // get the client's IInternalSessionControl interface
3212 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3213 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3214 E_INVALIDARG);
3215
3216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3217
3218 if (!mData->mRegistered)
3219 return setError(E_UNEXPECTED,
3220 tr("The machine '%s' is not registered"),
3221 mUserData->s.strName.c_str());
3222
3223 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3224
3225 SessionState_T oldState = mData->mSession.mState;
3226 /* Hack: in case the session is closing and there is a progress object
3227 * which allows waiting for the session to be closed, take the opportunity
3228 * and do a limited wait (max. 1 second). This helps a lot when the system
3229 * is busy and thus session closing can take a little while. */
3230 if ( mData->mSession.mState == SessionState_Unlocking
3231 && mData->mSession.mProgress)
3232 {
3233 alock.release();
3234 mData->mSession.mProgress->WaitForCompletion(1000);
3235 alock.acquire();
3236 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3237 }
3238
3239 // try again now
3240 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3241 // (i.e. session machine exists)
3242 && (aLockType == LockType_Shared) // caller wants a shared link to the
3243 // existing session that holds the write lock:
3244 )
3245 {
3246 // OK, share the session... we are now dealing with three processes:
3247 // 1) VBoxSVC (where this code runs);
3248 // 2) process C: the caller's client process (who wants a shared session);
3249 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3250
3251 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3252 ComAssertRet(mData->mSession.mLockType == LockType_Write || mData->mSession.mLockType == LockType_VM, E_FAIL);
3253 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3254 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3255 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3256 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3257
3258 /*
3259 * Release the lock before calling the client process. It's safe here
3260 * since the only thing to do after we get the lock again is to add
3261 * the remote control to the list (which doesn't directly influence
3262 * anything).
3263 */
3264 alock.release();
3265
3266 // get the console of the session holding the write lock (this is a remote call)
3267 ComPtr<IConsole> pConsoleW;
3268 if (mData->mSession.mLockType == LockType_VM)
3269 {
3270 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3271 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3272 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3273 if (FAILED(rc))
3274 // the failure may occur w/o any error info (from RPC), so provide one
3275 return setError(VBOX_E_VM_ERROR,
3276 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3277 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3278 }
3279
3280 // share the session machine and W's console with the caller's session
3281 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3282 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3283 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3284
3285 if (FAILED(rc))
3286 // the failure may occur w/o any error info (from RPC), so provide one
3287 return setError(VBOX_E_VM_ERROR,
3288 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3289 alock.acquire();
3290
3291 // need to revalidate the state after acquiring the lock again
3292 if (mData->mSession.mState != SessionState_Locked)
3293 {
3294 pSessionControl->Uninitialize();
3295 return setError(VBOX_E_INVALID_SESSION_STATE,
3296 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3297 mUserData->s.strName.c_str());
3298 }
3299
3300 // add the caller's session to the list
3301 mData->mSession.mRemoteControls.push_back(pSessionControl);
3302 }
3303 else if ( mData->mSession.mState == SessionState_Locked
3304 || mData->mSession.mState == SessionState_Unlocking
3305 )
3306 {
3307 // sharing not permitted, or machine still unlocking:
3308 return setError(VBOX_E_INVALID_OBJECT_STATE,
3309 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3310 mUserData->s.strName.c_str());
3311 }
3312 else
3313 {
3314 // machine is not locked: then write-lock the machine (create the session machine)
3315
3316 // must not be busy
3317 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3318
3319 // get the caller's session PID
3320 RTPROCESS pid = NIL_RTPROCESS;
3321 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3322 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3323 Assert(pid != NIL_RTPROCESS);
3324
3325 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3326
3327 if (fLaunchingVMProcess)
3328 {
3329 if (mData->mSession.mPID == NIL_RTPROCESS)
3330 {
3331 // two or more clients racing for a lock, the one which set the
3332 // session state to Spawning will win, the others will get an
3333 // error as we can't decide here if waiting a little would help
3334 // (only for shared locks this would avoid an error)
3335 return setError(VBOX_E_INVALID_OBJECT_STATE,
3336 tr("The machine '%s' already has a lock request pending"),
3337 mUserData->s.strName.c_str());
3338 }
3339
3340 // this machine is awaiting for a spawning session to be opened:
3341 // then the calling process must be the one that got started by
3342 // LaunchVMProcess()
3343
3344 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3345 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3346
3347#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3348 /* Hardened windows builds spawns three processes when a VM is
3349 launched, the 3rd one is the one that will end up here. */
3350 RTPROCESS ppid;
3351 int rc = RTProcQueryParent(pid, &ppid);
3352 if (RT_SUCCESS(rc))
3353 rc = RTProcQueryParent(ppid, &ppid);
3354 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3355 || rc == VERR_ACCESS_DENIED)
3356 {
3357 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3358 mData->mSession.mPID = pid;
3359 }
3360#endif
3361
3362 if (mData->mSession.mPID != pid)
3363 return setError(E_ACCESSDENIED,
3364 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3365 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3366 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3367 }
3368
3369 // create the mutable SessionMachine from the current machine
3370 ComObjPtr<SessionMachine> sessionMachine;
3371 sessionMachine.createObject();
3372 rc = sessionMachine->init(this);
3373 AssertComRC(rc);
3374
3375 /* NOTE: doing return from this function after this point but
3376 * before the end is forbidden since it may call SessionMachine::uninit()
3377 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3378 * lock while still holding the Machine lock in alock so that a deadlock
3379 * is possible due to the wrong lock order. */
3380
3381 if (SUCCEEDED(rc))
3382 {
3383 /*
3384 * Set the session state to Spawning to protect against subsequent
3385 * attempts to open a session and to unregister the machine after
3386 * we release the lock.
3387 */
3388 SessionState_T origState = mData->mSession.mState;
3389 mData->mSession.mState = SessionState_Spawning;
3390
3391#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3392 /* Get the client token ID to be passed to the client process */
3393 Utf8Str strTokenId;
3394 sessionMachine->i_getTokenId(strTokenId);
3395 Assert(!strTokenId.isEmpty());
3396#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3397 /* Get the client token to be passed to the client process */
3398 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3399 /* The token is now "owned" by pToken, fix refcount */
3400 if (!pToken.isNull())
3401 pToken->Release();
3402#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3403
3404 /*
3405 * Release the lock before calling the client process -- it will call
3406 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3407 * because the state is Spawning, so that LaunchVMProcess() and
3408 * LockMachine() calls will fail. This method, called before we
3409 * acquire the lock again, will fail because of the wrong PID.
3410 *
3411 * Note that mData->mSession.mRemoteControls accessed outside
3412 * the lock may not be modified when state is Spawning, so it's safe.
3413 */
3414 alock.release();
3415
3416 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3417#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3418 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3419#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3420 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3421 /* Now the token is owned by the client process. */
3422 pToken.setNull();
3423#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3424 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3425
3426 /* The failure may occur w/o any error info (from RPC), so provide one */
3427 if (FAILED(rc))
3428 setError(VBOX_E_VM_ERROR,
3429 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3430
3431 if ( SUCCEEDED(rc)
3432 && fLaunchingVMProcess
3433 )
3434 {
3435 /* complete the remote session initialization */
3436
3437 /* get the console from the direct session */
3438 ComPtr<IConsole> console;
3439 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3440 ComAssertComRC(rc);
3441
3442 if (SUCCEEDED(rc) && !console)
3443 {
3444 ComAssert(!!console);
3445 rc = E_FAIL;
3446 }
3447
3448 /* assign machine & console to the remote session */
3449 if (SUCCEEDED(rc))
3450 {
3451 /*
3452 * after LaunchVMProcess(), the first and the only
3453 * entry in remoteControls is that remote session
3454 */
3455 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3456 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3457 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3458
3459 /* The failure may occur w/o any error info (from RPC), so provide one */
3460 if (FAILED(rc))
3461 setError(VBOX_E_VM_ERROR,
3462 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3463 }
3464
3465 if (FAILED(rc))
3466 pSessionControl->Uninitialize();
3467 }
3468
3469 /* acquire the lock again */
3470 alock.acquire();
3471
3472 /* Restore the session state */
3473 mData->mSession.mState = origState;
3474 }
3475
3476 // finalize spawning anyway (this is why we don't return on errors above)
3477 if (fLaunchingVMProcess)
3478 {
3479 /* Note that the progress object is finalized later */
3480 /** @todo Consider checking mData->mSession.mProgress for cancellation
3481 * around here. */
3482
3483 /* We don't reset mSession.mPID here because it is necessary for
3484 * SessionMachine::uninit() to reap the child process later. */
3485
3486 if (FAILED(rc))
3487 {
3488 /* Close the remote session, remove the remote control from the list
3489 * and reset session state to Closed (@note keep the code in sync
3490 * with the relevant part in checkForSpawnFailure()). */
3491
3492 Assert(mData->mSession.mRemoteControls.size() == 1);
3493 if (mData->mSession.mRemoteControls.size() == 1)
3494 {
3495 ErrorInfoKeeper eik;
3496 mData->mSession.mRemoteControls.front()->Uninitialize();
3497 }
3498
3499 mData->mSession.mRemoteControls.clear();
3500 mData->mSession.mState = SessionState_Unlocked;
3501 }
3502 }
3503 else
3504 {
3505 /* memorize PID of the directly opened session */
3506 if (SUCCEEDED(rc))
3507 mData->mSession.mPID = pid;
3508 }
3509
3510 if (SUCCEEDED(rc))
3511 {
3512 mData->mSession.mLockType = aLockType;
3513 /* memorize the direct session control and cache IUnknown for it */
3514 mData->mSession.mDirectControl = pSessionControl;
3515 mData->mSession.mState = SessionState_Locked;
3516 /* associate the SessionMachine with this Machine */
3517 mData->mSession.mMachine = sessionMachine;
3518
3519 /* request an IUnknown pointer early from the remote party for later
3520 * identity checks (it will be internally cached within mDirectControl
3521 * at least on XPCOM) */
3522 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3523 NOREF(unk);
3524 }
3525
3526 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3527 * would break the lock order */
3528 alock.release();
3529
3530 /* uninitialize the created session machine on failure */
3531 if (FAILED(rc))
3532 sessionMachine->uninit();
3533 }
3534
3535 if (SUCCEEDED(rc))
3536 {
3537 /*
3538 * tell the client watcher thread to update the set of
3539 * machines that have open sessions
3540 */
3541 mParent->i_updateClientWatcher();
3542
3543 if (oldState != SessionState_Locked)
3544 /* fire an event */
3545 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3546 }
3547
3548 return rc;
3549}
3550
3551/**
3552 * @note Locks objects!
3553 */
3554HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3555 const com::Utf8Str &aType,
3556 const com::Utf8Str &aEnvironment,
3557 ComPtr<IProgress> &aProgress)
3558{
3559 Utf8Str strFrontend(aType);
3560 /* "emergencystop" doesn't need the session, so skip the checks/interface
3561 * retrieval. This code doesn't quite fit in here, but introducing a
3562 * special API method would be even more effort, and would require explicit
3563 * support by every API client. It's better to hide the feature a bit. */
3564 if (strFrontend != "emergencystop")
3565 CheckComArgNotNull(aSession);
3566
3567 HRESULT rc = S_OK;
3568 if (strFrontend.isEmpty())
3569 {
3570 Bstr bstrFrontend;
3571 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3572 if (FAILED(rc))
3573 return rc;
3574 strFrontend = bstrFrontend;
3575 if (strFrontend.isEmpty())
3576 {
3577 ComPtr<ISystemProperties> systemProperties;
3578 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3579 if (FAILED(rc))
3580 return rc;
3581 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3582 if (FAILED(rc))
3583 return rc;
3584 strFrontend = bstrFrontend;
3585 }
3586 /* paranoia - emergencystop is not a valid default */
3587 if (strFrontend == "emergencystop")
3588 strFrontend = Utf8Str::Empty;
3589 }
3590 /* default frontend: Qt GUI */
3591 if (strFrontend.isEmpty())
3592 strFrontend = "GUI/Qt";
3593
3594 if (strFrontend != "emergencystop")
3595 {
3596 /* check the session state */
3597 SessionState_T state;
3598 rc = aSession->COMGETTER(State)(&state);
3599 if (FAILED(rc))
3600 return rc;
3601
3602 if (state != SessionState_Unlocked)
3603 return setError(VBOX_E_INVALID_OBJECT_STATE,
3604 tr("The given session is busy"));
3605
3606 /* get the IInternalSessionControl interface */
3607 ComPtr<IInternalSessionControl> control(aSession);
3608 ComAssertMsgRet(!control.isNull(),
3609 ("No IInternalSessionControl interface"),
3610 E_INVALIDARG);
3611
3612 /* get the teleporter enable state for the progress object init. */
3613 BOOL fTeleporterEnabled;
3614 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3615 if (FAILED(rc))
3616 return rc;
3617
3618 /* create a progress object */
3619 ComObjPtr<ProgressProxy> progress;
3620 progress.createObject();
3621 rc = progress->init(mParent,
3622 static_cast<IMachine*>(this),
3623 Bstr(tr("Starting VM")).raw(),
3624 TRUE /* aCancelable */,
3625 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3626 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3627 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3628 2 /* uFirstOperationWeight */,
3629 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3630
3631 if (SUCCEEDED(rc))
3632 {
3633 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3634 if (SUCCEEDED(rc))
3635 {
3636 aProgress = progress;
3637
3638 /* signal the client watcher thread */
3639 mParent->i_updateClientWatcher();
3640
3641 /* fire an event */
3642 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3643 }
3644 }
3645 }
3646 else
3647 {
3648 /* no progress object - either instant success or failure */
3649 aProgress = NULL;
3650
3651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3652
3653 if (mData->mSession.mState != SessionState_Locked)
3654 return setError(VBOX_E_INVALID_OBJECT_STATE,
3655 tr("The machine '%s' is not locked by a session"),
3656 mUserData->s.strName.c_str());
3657
3658 /* must have a VM process associated - do not kill normal API clients
3659 * with an open session */
3660 if (!Global::IsOnline(mData->mMachineState))
3661 return setError(VBOX_E_INVALID_OBJECT_STATE,
3662 tr("The machine '%s' does not have a VM process"),
3663 mUserData->s.strName.c_str());
3664
3665 /* forcibly terminate the VM process */
3666 if (mData->mSession.mPID != NIL_RTPROCESS)
3667 RTProcTerminate(mData->mSession.mPID);
3668
3669 /* signal the client watcher thread, as most likely the client has
3670 * been terminated */
3671 mParent->i_updateClientWatcher();
3672 }
3673
3674 return rc;
3675}
3676
3677HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3678{
3679 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3680 return setError(E_INVALIDARG,
3681 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3682 aPosition, SchemaDefs::MaxBootPosition);
3683
3684 if (aDevice == DeviceType_USB)
3685 return setError(E_NOTIMPL,
3686 tr("Booting from USB device is currently not supported"));
3687
3688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3689
3690 HRESULT rc = i_checkStateDependency(MutableStateDep);
3691 if (FAILED(rc)) return rc;
3692
3693 i_setModified(IsModified_MachineData);
3694 mHWData.backup();
3695 mHWData->mBootOrder[aPosition - 1] = aDevice;
3696
3697 return S_OK;
3698}
3699
3700HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3701{
3702 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3703 return setError(E_INVALIDARG,
3704 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3705 aPosition, SchemaDefs::MaxBootPosition);
3706
3707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3708
3709 *aDevice = mHWData->mBootOrder[aPosition - 1];
3710
3711 return S_OK;
3712}
3713
3714HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3715 LONG aControllerPort,
3716 LONG aDevice,
3717 DeviceType_T aType,
3718 const ComPtr<IMedium> &aMedium)
3719{
3720 IMedium *aM = aMedium;
3721 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3722 aName.c_str(), aControllerPort, aDevice, aType, aM));
3723
3724 // request the host lock first, since might be calling Host methods for getting host drives;
3725 // next, protect the media tree all the while we're in here, as well as our member variables
3726 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3727 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3728
3729 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3730 if (FAILED(rc)) return rc;
3731
3732 /// @todo NEWMEDIA implicit machine registration
3733 if (!mData->mRegistered)
3734 return setError(VBOX_E_INVALID_OBJECT_STATE,
3735 tr("Cannot attach storage devices to an unregistered machine"));
3736
3737 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3738
3739 /* Check for an existing controller. */
3740 ComObjPtr<StorageController> ctl;
3741 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3742 if (FAILED(rc)) return rc;
3743
3744 StorageControllerType_T ctrlType;
3745 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3746 if (FAILED(rc))
3747 return setError(E_FAIL,
3748 tr("Could not get type of controller '%s'"),
3749 aName.c_str());
3750
3751 bool fSilent = false;
3752 Utf8Str strReconfig;
3753
3754 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3755 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3756 if ( mData->mMachineState == MachineState_Paused
3757 && strReconfig == "1")
3758 fSilent = true;
3759
3760 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3761 bool fHotplug = false;
3762 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3763 fHotplug = true;
3764
3765 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3766 return setError(VBOX_E_INVALID_VM_STATE,
3767 tr("Controller '%s' does not support hotplugging"),
3768 aName.c_str());
3769
3770 // check that the port and device are not out of range
3771 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3772 if (FAILED(rc)) return rc;
3773
3774 /* check if the device slot is already busy */
3775 MediumAttachment *pAttachTemp;
3776 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3777 Bstr(aName).raw(),
3778 aControllerPort,
3779 aDevice)))
3780 {
3781 Medium *pMedium = pAttachTemp->i_getMedium();
3782 if (pMedium)
3783 {
3784 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3785 return setError(VBOX_E_OBJECT_IN_USE,
3786 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3787 pMedium->i_getLocationFull().c_str(),
3788 aControllerPort,
3789 aDevice,
3790 aName.c_str());
3791 }
3792 else
3793 return setError(VBOX_E_OBJECT_IN_USE,
3794 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3795 aControllerPort, aDevice, aName.c_str());
3796 }
3797
3798 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3799 if (aMedium && medium.isNull())
3800 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3801
3802 AutoCaller mediumCaller(medium);
3803 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3804
3805 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3806
3807 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3808 && !medium.isNull()
3809 )
3810 return setError(VBOX_E_OBJECT_IN_USE,
3811 tr("Medium '%s' is already attached to this virtual machine"),
3812 medium->i_getLocationFull().c_str());
3813
3814 if (!medium.isNull())
3815 {
3816 MediumType_T mtype = medium->i_getType();
3817 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3818 // For DVDs it's not written to the config file, so needs no global config
3819 // version bump. For floppies it's a new attribute "type", which is ignored
3820 // by older VirtualBox version, so needs no global config version bump either.
3821 // For hard disks this type is not accepted.
3822 if (mtype == MediumType_MultiAttach)
3823 {
3824 // This type is new with VirtualBox 4.0 and therefore requires settings
3825 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3826 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3827 // two reasons: The medium type is a property of the media registry tree, which
3828 // can reside in the global config file (for pre-4.0 media); we would therefore
3829 // possibly need to bump the global config version. We don't want to do that though
3830 // because that might make downgrading to pre-4.0 impossible.
3831 // As a result, we can only use these two new types if the medium is NOT in the
3832 // global registry:
3833 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3834 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3835 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3836 )
3837 return setError(VBOX_E_INVALID_OBJECT_STATE,
3838 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3839 "to machines that were created with VirtualBox 4.0 or later"),
3840 medium->i_getLocationFull().c_str());
3841 }
3842 }
3843
3844 bool fIndirect = false;
3845 if (!medium.isNull())
3846 fIndirect = medium->i_isReadOnly();
3847 bool associate = true;
3848
3849 do
3850 {
3851 if ( aType == DeviceType_HardDisk
3852 && mMediaData.isBackedUp())
3853 {
3854 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3855
3856 /* check if the medium was attached to the VM before we started
3857 * changing attachments in which case the attachment just needs to
3858 * be restored */
3859 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3860 {
3861 AssertReturn(!fIndirect, E_FAIL);
3862
3863 /* see if it's the same bus/channel/device */
3864 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3865 {
3866 /* the simplest case: restore the whole attachment
3867 * and return, nothing else to do */
3868 mMediaData->mAttachments.push_back(pAttachTemp);
3869
3870 /* Reattach the medium to the VM. */
3871 if (fHotplug || fSilent)
3872 {
3873 mediumLock.release();
3874 treeLock.release();
3875 alock.release();
3876
3877 MediumLockList *pMediumLockList(new MediumLockList());
3878
3879 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3880 true /* fMediumLockWrite */,
3881 false /* fMediumLockWriteAll */,
3882 NULL,
3883 *pMediumLockList);
3884 alock.acquire();
3885 if (FAILED(rc))
3886 delete pMediumLockList;
3887 else
3888 {
3889 mData->mSession.mLockedMedia.Unlock();
3890 alock.release();
3891 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3892 mData->mSession.mLockedMedia.Lock();
3893 alock.acquire();
3894 }
3895 alock.release();
3896
3897 if (SUCCEEDED(rc))
3898 {
3899 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3900 /* Remove lock list in case of error. */
3901 if (FAILED(rc))
3902 {
3903 mData->mSession.mLockedMedia.Unlock();
3904 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3905 mData->mSession.mLockedMedia.Lock();
3906 }
3907 }
3908 }
3909
3910 return S_OK;
3911 }
3912
3913 /* bus/channel/device differ; we need a new attachment object,
3914 * but don't try to associate it again */
3915 associate = false;
3916 break;
3917 }
3918 }
3919
3920 /* go further only if the attachment is to be indirect */
3921 if (!fIndirect)
3922 break;
3923
3924 /* perform the so called smart attachment logic for indirect
3925 * attachments. Note that smart attachment is only applicable to base
3926 * hard disks. */
3927
3928 if (medium->i_getParent().isNull())
3929 {
3930 /* first, investigate the backup copy of the current hard disk
3931 * attachments to make it possible to re-attach existing diffs to
3932 * another device slot w/o losing their contents */
3933 if (mMediaData.isBackedUp())
3934 {
3935 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3936
3937 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3938 uint32_t foundLevel = 0;
3939
3940 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3941 {
3942 uint32_t level = 0;
3943 MediumAttachment *pAttach = *it;
3944 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3945 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3946 if (pMedium.isNull())
3947 continue;
3948
3949 if (pMedium->i_getBase(&level) == medium)
3950 {
3951 /* skip the hard disk if its currently attached (we
3952 * cannot attach the same hard disk twice) */
3953 if (i_findAttachment(mMediaData->mAttachments,
3954 pMedium))
3955 continue;
3956
3957 /* matched device, channel and bus (i.e. attached to the
3958 * same place) will win and immediately stop the search;
3959 * otherwise the attachment that has the youngest
3960 * descendant of medium will be used
3961 */
3962 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3963 {
3964 /* the simplest case: restore the whole attachment
3965 * and return, nothing else to do */
3966 mMediaData->mAttachments.push_back(*it);
3967
3968 /* Reattach the medium to the VM. */
3969 if (fHotplug || fSilent)
3970 {
3971 mediumLock.release();
3972 treeLock.release();
3973 alock.release();
3974
3975 MediumLockList *pMediumLockList(new MediumLockList());
3976
3977 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3978 true /* fMediumLockWrite */,
3979 false /* fMediumLockWriteAll */,
3980 NULL,
3981 *pMediumLockList);
3982 alock.acquire();
3983 if (FAILED(rc))
3984 delete pMediumLockList;
3985 else
3986 {
3987 mData->mSession.mLockedMedia.Unlock();
3988 alock.release();
3989 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3990 mData->mSession.mLockedMedia.Lock();
3991 alock.acquire();
3992 }
3993 alock.release();
3994
3995 if (SUCCEEDED(rc))
3996 {
3997 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3998 /* Remove lock list in case of error. */
3999 if (FAILED(rc))
4000 {
4001 mData->mSession.mLockedMedia.Unlock();
4002 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4003 mData->mSession.mLockedMedia.Lock();
4004 }
4005 }
4006 }
4007
4008 return S_OK;
4009 }
4010 else if ( foundIt == oldAtts.end()
4011 || level > foundLevel /* prefer younger */
4012 )
4013 {
4014 foundIt = it;
4015 foundLevel = level;
4016 }
4017 }
4018 }
4019
4020 if (foundIt != oldAtts.end())
4021 {
4022 /* use the previously attached hard disk */
4023 medium = (*foundIt)->i_getMedium();
4024 mediumCaller.attach(medium);
4025 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4026 mediumLock.attach(medium);
4027 /* not implicit, doesn't require association with this VM */
4028 fIndirect = false;
4029 associate = false;
4030 /* go right to the MediumAttachment creation */
4031 break;
4032 }
4033 }
4034
4035 /* must give up the medium lock and medium tree lock as below we
4036 * go over snapshots, which needs a lock with higher lock order. */
4037 mediumLock.release();
4038 treeLock.release();
4039
4040 /* then, search through snapshots for the best diff in the given
4041 * hard disk's chain to base the new diff on */
4042
4043 ComObjPtr<Medium> base;
4044 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4045 while (snap)
4046 {
4047 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4048
4049 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4050
4051 MediumAttachment *pAttachFound = NULL;
4052 uint32_t foundLevel = 0;
4053
4054 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4055 {
4056 MediumAttachment *pAttach = *it;
4057 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4058 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4059 if (pMedium.isNull())
4060 continue;
4061
4062 uint32_t level = 0;
4063 if (pMedium->i_getBase(&level) == medium)
4064 {
4065 /* matched device, channel and bus (i.e. attached to the
4066 * same place) will win and immediately stop the search;
4067 * otherwise the attachment that has the youngest
4068 * descendant of medium will be used
4069 */
4070 if ( pAttach->i_getDevice() == aDevice
4071 && pAttach->i_getPort() == aControllerPort
4072 && pAttach->i_getControllerName() == aName
4073 )
4074 {
4075 pAttachFound = pAttach;
4076 break;
4077 }
4078 else if ( !pAttachFound
4079 || level > foundLevel /* prefer younger */
4080 )
4081 {
4082 pAttachFound = pAttach;
4083 foundLevel = level;
4084 }
4085 }
4086 }
4087
4088 if (pAttachFound)
4089 {
4090 base = pAttachFound->i_getMedium();
4091 break;
4092 }
4093
4094 snap = snap->i_getParent();
4095 }
4096
4097 /* re-lock medium tree and the medium, as we need it below */
4098 treeLock.acquire();
4099 mediumLock.acquire();
4100
4101 /* found a suitable diff, use it as a base */
4102 if (!base.isNull())
4103 {
4104 medium = base;
4105 mediumCaller.attach(medium);
4106 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4107 mediumLock.attach(medium);
4108 }
4109 }
4110
4111 Utf8Str strFullSnapshotFolder;
4112 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4113
4114 ComObjPtr<Medium> diff;
4115 diff.createObject();
4116 // store this diff in the same registry as the parent
4117 Guid uuidRegistryParent;
4118 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4119 {
4120 // parent image has no registry: this can happen if we're attaching a new immutable
4121 // image that has not yet been attached (medium then points to the base and we're
4122 // creating the diff image for the immutable, and the parent is not yet registered);
4123 // put the parent in the machine registry then
4124 mediumLock.release();
4125 treeLock.release();
4126 alock.release();
4127 i_addMediumToRegistry(medium);
4128 alock.acquire();
4129 treeLock.acquire();
4130 mediumLock.acquire();
4131 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4132 }
4133 rc = diff->init(mParent,
4134 medium->i_getPreferredDiffFormat(),
4135 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4136 uuidRegistryParent,
4137 DeviceType_HardDisk);
4138 if (FAILED(rc)) return rc;
4139
4140 /* Apply the normal locking logic to the entire chain. */
4141 MediumLockList *pMediumLockList(new MediumLockList());
4142 mediumLock.release();
4143 treeLock.release();
4144 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4145 true /* fMediumLockWrite */,
4146 false /* fMediumLockWriteAll */,
4147 medium,
4148 *pMediumLockList);
4149 treeLock.acquire();
4150 mediumLock.acquire();
4151 if (SUCCEEDED(rc))
4152 {
4153 mediumLock.release();
4154 treeLock.release();
4155 rc = pMediumLockList->Lock();
4156 treeLock.acquire();
4157 mediumLock.acquire();
4158 if (FAILED(rc))
4159 setError(rc,
4160 tr("Could not lock medium when creating diff '%s'"),
4161 diff->i_getLocationFull().c_str());
4162 else
4163 {
4164 /* will release the lock before the potentially lengthy
4165 * operation, so protect with the special state */
4166 MachineState_T oldState = mData->mMachineState;
4167 i_setMachineState(MachineState_SettingUp);
4168
4169 mediumLock.release();
4170 treeLock.release();
4171 alock.release();
4172
4173 rc = medium->i_createDiffStorage(diff,
4174 MediumVariant_Standard,
4175 pMediumLockList,
4176 NULL /* aProgress */,
4177 true /* aWait */);
4178
4179 alock.acquire();
4180 treeLock.acquire();
4181 mediumLock.acquire();
4182
4183 i_setMachineState(oldState);
4184 }
4185 }
4186
4187 /* Unlock the media and free the associated memory. */
4188 delete pMediumLockList;
4189
4190 if (FAILED(rc)) return rc;
4191
4192 /* use the created diff for the actual attachment */
4193 medium = diff;
4194 mediumCaller.attach(medium);
4195 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4196 mediumLock.attach(medium);
4197 }
4198 while (0);
4199
4200 ComObjPtr<MediumAttachment> attachment;
4201 attachment.createObject();
4202 rc = attachment->init(this,
4203 medium,
4204 aName,
4205 aControllerPort,
4206 aDevice,
4207 aType,
4208 fIndirect,
4209 false /* fPassthrough */,
4210 false /* fTempEject */,
4211 false /* fNonRotational */,
4212 false /* fDiscard */,
4213 fHotplug /* fHotPluggable */,
4214 Utf8Str::Empty);
4215 if (FAILED(rc)) return rc;
4216
4217 if (associate && !medium.isNull())
4218 {
4219 // as the last step, associate the medium to the VM
4220 rc = medium->i_addBackReference(mData->mUuid);
4221 // here we can fail because of Deleting, or being in process of creating a Diff
4222 if (FAILED(rc)) return rc;
4223
4224 mediumLock.release();
4225 treeLock.release();
4226 alock.release();
4227 i_addMediumToRegistry(medium);
4228 alock.acquire();
4229 treeLock.acquire();
4230 mediumLock.acquire();
4231 }
4232
4233 /* success: finally remember the attachment */
4234 i_setModified(IsModified_Storage);
4235 mMediaData.backup();
4236 mMediaData->mAttachments.push_back(attachment);
4237
4238 mediumLock.release();
4239 treeLock.release();
4240 alock.release();
4241
4242 if (fHotplug || fSilent)
4243 {
4244 if (!medium.isNull())
4245 {
4246 MediumLockList *pMediumLockList(new MediumLockList());
4247
4248 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4249 true /* fMediumLockWrite */,
4250 false /* fMediumLockWriteAll */,
4251 NULL,
4252 *pMediumLockList);
4253 alock.acquire();
4254 if (FAILED(rc))
4255 delete pMediumLockList;
4256 else
4257 {
4258 mData->mSession.mLockedMedia.Unlock();
4259 alock.release();
4260 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4261 mData->mSession.mLockedMedia.Lock();
4262 alock.acquire();
4263 }
4264 alock.release();
4265 }
4266
4267 if (SUCCEEDED(rc))
4268 {
4269 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4270 /* Remove lock list in case of error. */
4271 if (FAILED(rc))
4272 {
4273 mData->mSession.mLockedMedia.Unlock();
4274 mData->mSession.mLockedMedia.Remove(attachment);
4275 mData->mSession.mLockedMedia.Lock();
4276 }
4277 }
4278 }
4279
4280 /* Save modified registries, but skip this machine as it's the caller's
4281 * job to save its settings like all other settings changes. */
4282 mParent->i_unmarkRegistryModified(i_getId());
4283 mParent->i_saveModifiedRegistries();
4284
4285 return rc;
4286}
4287
4288HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4289 LONG aDevice)
4290{
4291 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4292 aName.c_str(), aControllerPort, aDevice));
4293
4294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4295
4296 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4297 if (FAILED(rc)) return rc;
4298
4299 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4300
4301 /* Check for an existing controller. */
4302 ComObjPtr<StorageController> ctl;
4303 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4304 if (FAILED(rc)) return rc;
4305
4306 StorageControllerType_T ctrlType;
4307 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4308 if (FAILED(rc))
4309 return setError(E_FAIL,
4310 tr("Could not get type of controller '%s'"),
4311 aName.c_str());
4312
4313 bool fSilent = false;
4314 Utf8Str strReconfig;
4315
4316 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4317 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4318 if ( mData->mMachineState == MachineState_Paused
4319 && strReconfig == "1")
4320 fSilent = true;
4321
4322 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4323 bool fHotplug = false;
4324 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4325 fHotplug = true;
4326
4327 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4328 return setError(VBOX_E_INVALID_VM_STATE,
4329 tr("Controller '%s' does not support hotplugging"),
4330 aName.c_str());
4331
4332 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4333 Bstr(aName).raw(),
4334 aControllerPort,
4335 aDevice);
4336 if (!pAttach)
4337 return setError(VBOX_E_OBJECT_NOT_FOUND,
4338 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4339 aDevice, aControllerPort, aName.c_str());
4340
4341 if (fHotplug && !pAttach->i_getHotPluggable())
4342 return setError(VBOX_E_NOT_SUPPORTED,
4343 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4344 aDevice, aControllerPort, aName.c_str());
4345
4346 /*
4347 * The VM has to detach the device before we delete any implicit diffs.
4348 * If this fails we can roll back without loosing data.
4349 */
4350 if (fHotplug || fSilent)
4351 {
4352 alock.release();
4353 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4354 alock.acquire();
4355 }
4356 if (FAILED(rc)) return rc;
4357
4358 /* If we are here everything went well and we can delete the implicit now. */
4359 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4360
4361 alock.release();
4362
4363 /* Save modified registries, but skip this machine as it's the caller's
4364 * job to save its settings like all other settings changes. */
4365 mParent->i_unmarkRegistryModified(i_getId());
4366 mParent->i_saveModifiedRegistries();
4367
4368 return rc;
4369}
4370
4371HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4372 LONG aDevice, BOOL aPassthrough)
4373{
4374 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4375 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4376
4377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4378
4379 HRESULT rc = i_checkStateDependency(MutableStateDep);
4380 if (FAILED(rc)) return rc;
4381
4382 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4383
4384 if (Global::IsOnlineOrTransient(mData->mMachineState))
4385 return setError(VBOX_E_INVALID_VM_STATE,
4386 tr("Invalid machine state: %s"),
4387 Global::stringifyMachineState(mData->mMachineState));
4388
4389 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4390 Bstr(aName).raw(),
4391 aControllerPort,
4392 aDevice);
4393 if (!pAttach)
4394 return setError(VBOX_E_OBJECT_NOT_FOUND,
4395 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4396 aDevice, aControllerPort, aName.c_str());
4397
4398
4399 i_setModified(IsModified_Storage);
4400 mMediaData.backup();
4401
4402 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4403
4404 if (pAttach->i_getType() != DeviceType_DVD)
4405 return setError(E_INVALIDARG,
4406 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4407 aDevice, aControllerPort, aName.c_str());
4408 pAttach->i_updatePassthrough(!!aPassthrough);
4409
4410 return S_OK;
4411}
4412
4413HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4414 LONG aDevice, BOOL aTemporaryEject)
4415{
4416
4417 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4418 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4419
4420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4421
4422 HRESULT rc = i_checkStateDependency(MutableStateDep);
4423 if (FAILED(rc)) return rc;
4424
4425 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4426 Bstr(aName).raw(),
4427 aControllerPort,
4428 aDevice);
4429 if (!pAttach)
4430 return setError(VBOX_E_OBJECT_NOT_FOUND,
4431 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4432 aDevice, aControllerPort, aName.c_str());
4433
4434
4435 i_setModified(IsModified_Storage);
4436 mMediaData.backup();
4437
4438 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4439
4440 if (pAttach->i_getType() != DeviceType_DVD)
4441 return setError(E_INVALIDARG,
4442 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4443 aDevice, aControllerPort, aName.c_str());
4444 pAttach->i_updateTempEject(!!aTemporaryEject);
4445
4446 return S_OK;
4447}
4448
4449HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4450 LONG aDevice, BOOL aNonRotational)
4451{
4452
4453 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4454 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4455
4456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4457
4458 HRESULT rc = i_checkStateDependency(MutableStateDep);
4459 if (FAILED(rc)) return rc;
4460
4461 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4462
4463 if (Global::IsOnlineOrTransient(mData->mMachineState))
4464 return setError(VBOX_E_INVALID_VM_STATE,
4465 tr("Invalid machine state: %s"),
4466 Global::stringifyMachineState(mData->mMachineState));
4467
4468 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4469 Bstr(aName).raw(),
4470 aControllerPort,
4471 aDevice);
4472 if (!pAttach)
4473 return setError(VBOX_E_OBJECT_NOT_FOUND,
4474 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4475 aDevice, aControllerPort, aName.c_str());
4476
4477
4478 i_setModified(IsModified_Storage);
4479 mMediaData.backup();
4480
4481 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4482
4483 if (pAttach->i_getType() != DeviceType_HardDisk)
4484 return setError(E_INVALIDARG,
4485 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"),
4486 aDevice, aControllerPort, aName.c_str());
4487 pAttach->i_updateNonRotational(!!aNonRotational);
4488
4489 return S_OK;
4490}
4491
4492HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4493 LONG aDevice, BOOL aDiscard)
4494{
4495
4496 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4497 aName.c_str(), aControllerPort, aDevice, aDiscard));
4498
4499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4500
4501 HRESULT rc = i_checkStateDependency(MutableStateDep);
4502 if (FAILED(rc)) return rc;
4503
4504 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4505
4506 if (Global::IsOnlineOrTransient(mData->mMachineState))
4507 return setError(VBOX_E_INVALID_VM_STATE,
4508 tr("Invalid machine state: %s"),
4509 Global::stringifyMachineState(mData->mMachineState));
4510
4511 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4512 Bstr(aName).raw(),
4513 aControllerPort,
4514 aDevice);
4515 if (!pAttach)
4516 return setError(VBOX_E_OBJECT_NOT_FOUND,
4517 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4518 aDevice, aControllerPort, aName.c_str());
4519
4520
4521 i_setModified(IsModified_Storage);
4522 mMediaData.backup();
4523
4524 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4525
4526 if (pAttach->i_getType() != DeviceType_HardDisk)
4527 return setError(E_INVALIDARG,
4528 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"),
4529 aDevice, aControllerPort, aName.c_str());
4530 pAttach->i_updateDiscard(!!aDiscard);
4531
4532 return S_OK;
4533}
4534
4535HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4536 LONG aDevice, BOOL aHotPluggable)
4537{
4538 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4539 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4540
4541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4542
4543 HRESULT rc = i_checkStateDependency(MutableStateDep);
4544 if (FAILED(rc)) return rc;
4545
4546 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4547
4548 if (Global::IsOnlineOrTransient(mData->mMachineState))
4549 return setError(VBOX_E_INVALID_VM_STATE,
4550 tr("Invalid machine state: %s"),
4551 Global::stringifyMachineState(mData->mMachineState));
4552
4553 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4554 Bstr(aName).raw(),
4555 aControllerPort,
4556 aDevice);
4557 if (!pAttach)
4558 return setError(VBOX_E_OBJECT_NOT_FOUND,
4559 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4560 aDevice, aControllerPort, aName.c_str());
4561
4562 /* Check for an existing controller. */
4563 ComObjPtr<StorageController> ctl;
4564 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4565 if (FAILED(rc)) return rc;
4566
4567 StorageControllerType_T ctrlType;
4568 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4569 if (FAILED(rc))
4570 return setError(E_FAIL,
4571 tr("Could not get type of controller '%s'"),
4572 aName.c_str());
4573
4574 if (!i_isControllerHotplugCapable(ctrlType))
4575 return setError(VBOX_E_NOT_SUPPORTED,
4576 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4577 aName.c_str());
4578
4579 i_setModified(IsModified_Storage);
4580 mMediaData.backup();
4581
4582 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4583
4584 if (pAttach->i_getType() == DeviceType_Floppy)
4585 return setError(E_INVALIDARG,
4586 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"),
4587 aDevice, aControllerPort, aName.c_str());
4588 pAttach->i_updateHotPluggable(!!aHotPluggable);
4589
4590 return S_OK;
4591}
4592
4593HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4594 LONG aDevice)
4595{
4596 int rc = S_OK;
4597 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4598 aName.c_str(), aControllerPort, aDevice));
4599
4600 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4601
4602 return rc;
4603}
4604
4605HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4606 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4607{
4608 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4609 aName.c_str(), aControllerPort, aDevice));
4610
4611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4612
4613 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4614 if (FAILED(rc)) return rc;
4615
4616 if (Global::IsOnlineOrTransient(mData->mMachineState))
4617 return setError(VBOX_E_INVALID_VM_STATE,
4618 tr("Invalid machine state: %s"),
4619 Global::stringifyMachineState(mData->mMachineState));
4620
4621 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4622 Bstr(aName).raw(),
4623 aControllerPort,
4624 aDevice);
4625 if (!pAttach)
4626 return setError(VBOX_E_OBJECT_NOT_FOUND,
4627 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4628 aDevice, aControllerPort, aName.c_str());
4629
4630
4631 i_setModified(IsModified_Storage);
4632 mMediaData.backup();
4633
4634 IBandwidthGroup *iB = aBandwidthGroup;
4635 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4636 if (aBandwidthGroup && group.isNull())
4637 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4638
4639 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4640
4641 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4642 if (strBandwidthGroupOld.isNotEmpty())
4643 {
4644 /* Get the bandwidth group object and release it - this must not fail. */
4645 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4646 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4647 Assert(SUCCEEDED(rc));
4648
4649 pBandwidthGroupOld->i_release();
4650 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4651 }
4652
4653 if (!group.isNull())
4654 {
4655 group->i_reference();
4656 pAttach->i_updateBandwidthGroup(group->i_getName());
4657 }
4658
4659 return S_OK;
4660}
4661
4662HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4663 LONG aControllerPort,
4664 LONG aDevice,
4665 DeviceType_T aType)
4666{
4667 HRESULT rc = S_OK;
4668
4669 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4670 aName.c_str(), aControllerPort, aDevice, aType));
4671
4672 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4673
4674 return rc;
4675}
4676
4677
4678HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4679 LONG aControllerPort,
4680 LONG aDevice,
4681 BOOL aForce)
4682{
4683 int rc = S_OK;
4684 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4685 aName.c_str(), aControllerPort, aForce));
4686
4687 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4688
4689 return rc;
4690}
4691
4692HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4693 LONG aControllerPort,
4694 LONG aDevice,
4695 const ComPtr<IMedium> &aMedium,
4696 BOOL aForce)
4697{
4698 int rc = S_OK;
4699 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4700 aName.c_str(), aControllerPort, aDevice, aForce));
4701
4702 // request the host lock first, since might be calling Host methods for getting host drives;
4703 // next, protect the media tree all the while we're in here, as well as our member variables
4704 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4705 this->lockHandle(),
4706 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4707
4708 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4709 Bstr(aName).raw(),
4710 aControllerPort,
4711 aDevice);
4712 if (pAttach.isNull())
4713 return setError(VBOX_E_OBJECT_NOT_FOUND,
4714 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4715 aDevice, aControllerPort, aName.c_str());
4716
4717 /* Remember previously mounted medium. The medium before taking the
4718 * backup is not necessarily the same thing. */
4719 ComObjPtr<Medium> oldmedium;
4720 oldmedium = pAttach->i_getMedium();
4721
4722 IMedium *iM = aMedium;
4723 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4724 if (aMedium && pMedium.isNull())
4725 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4726
4727 AutoCaller mediumCaller(pMedium);
4728 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4729
4730 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4731 if (pMedium)
4732 {
4733 DeviceType_T mediumType = pAttach->i_getType();
4734 switch (mediumType)
4735 {
4736 case DeviceType_DVD:
4737 case DeviceType_Floppy:
4738 break;
4739
4740 default:
4741 return setError(VBOX_E_INVALID_OBJECT_STATE,
4742 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4743 aControllerPort,
4744 aDevice,
4745 aName.c_str());
4746 }
4747 }
4748
4749 i_setModified(IsModified_Storage);
4750 mMediaData.backup();
4751
4752 {
4753 // The backup operation makes the pAttach reference point to the
4754 // old settings. Re-get the correct reference.
4755 pAttach = i_findAttachment(mMediaData->mAttachments,
4756 Bstr(aName).raw(),
4757 aControllerPort,
4758 aDevice);
4759 if (!oldmedium.isNull())
4760 oldmedium->i_removeBackReference(mData->mUuid);
4761 if (!pMedium.isNull())
4762 {
4763 pMedium->i_addBackReference(mData->mUuid);
4764
4765 mediumLock.release();
4766 multiLock.release();
4767 i_addMediumToRegistry(pMedium);
4768 multiLock.acquire();
4769 mediumLock.acquire();
4770 }
4771
4772 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4773 pAttach->i_updateMedium(pMedium);
4774 }
4775
4776 i_setModified(IsModified_Storage);
4777
4778 mediumLock.release();
4779 multiLock.release();
4780 rc = i_onMediumChange(pAttach, aForce);
4781 multiLock.acquire();
4782 mediumLock.acquire();
4783
4784 /* On error roll back this change only. */
4785 if (FAILED(rc))
4786 {
4787 if (!pMedium.isNull())
4788 pMedium->i_removeBackReference(mData->mUuid);
4789 pAttach = i_findAttachment(mMediaData->mAttachments,
4790 Bstr(aName).raw(),
4791 aControllerPort,
4792 aDevice);
4793 /* If the attachment is gone in the meantime, bail out. */
4794 if (pAttach.isNull())
4795 return rc;
4796 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4797 if (!oldmedium.isNull())
4798 oldmedium->i_addBackReference(mData->mUuid);
4799 pAttach->i_updateMedium(oldmedium);
4800 }
4801
4802 mediumLock.release();
4803 multiLock.release();
4804
4805 /* Save modified registries, but skip this machine as it's the caller's
4806 * job to save its settings like all other settings changes. */
4807 mParent->i_unmarkRegistryModified(i_getId());
4808 mParent->i_saveModifiedRegistries();
4809
4810 return rc;
4811}
4812HRESULT Machine::getMedium(const com::Utf8Str &aName,
4813 LONG aControllerPort,
4814 LONG aDevice,
4815 ComPtr<IMedium> &aMedium)
4816{
4817 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4818 aName.c_str(), aControllerPort, aDevice));
4819
4820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4821
4822 aMedium = NULL;
4823
4824 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4825 Bstr(aName).raw(),
4826 aControllerPort,
4827 aDevice);
4828 if (pAttach.isNull())
4829 return setError(VBOX_E_OBJECT_NOT_FOUND,
4830 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4831 aDevice, aControllerPort, aName.c_str());
4832
4833 aMedium = pAttach->i_getMedium();
4834
4835 return S_OK;
4836}
4837
4838HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4839{
4840
4841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4842
4843 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4844
4845 return S_OK;
4846}
4847
4848HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4849{
4850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4851
4852 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4853
4854 return S_OK;
4855}
4856
4857HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4858{
4859 /* Do not assert if slot is out of range, just return the advertised
4860 status. testdriver/vbox.py triggers this in logVmInfo. */
4861 if (aSlot >= mNetworkAdapters.size())
4862 return setError(E_INVALIDARG,
4863 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4864 aSlot, mNetworkAdapters.size());
4865
4866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4867
4868 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4869
4870 return S_OK;
4871}
4872
4873HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4874{
4875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4876
4877 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4878 size_t i = 0;
4879 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4880 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4881 ++it, ++i)
4882 aKeys[i] = it->first;
4883
4884 return S_OK;
4885}
4886
4887 /**
4888 * @note Locks this object for reading.
4889 */
4890HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4891 com::Utf8Str &aValue)
4892{
4893 /* start with nothing found */
4894 aValue = "";
4895
4896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4897
4898 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4899 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4900 // found:
4901 aValue = it->second; // source is a Utf8Str
4902
4903 /* return the result to caller (may be empty) */
4904 return S_OK;
4905}
4906
4907 /**
4908 * @note Locks mParent for writing + this object for writing.
4909 */
4910HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4911{
4912 Utf8Str strOldValue; // empty
4913
4914 // locking note: we only hold the read lock briefly to look up the old value,
4915 // then release it and call the onExtraCanChange callbacks. There is a small
4916 // chance of a race insofar as the callback might be called twice if two callers
4917 // change the same key at the same time, but that's a much better solution
4918 // than the deadlock we had here before. The actual changing of the extradata
4919 // is then performed under the write lock and race-free.
4920
4921 // look up the old value first; if nothing has changed then we need not do anything
4922 {
4923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4924
4925 // For snapshots don't even think about allowing changes, extradata
4926 // is global for a machine, so there is nothing snapshot specific.
4927 if (i_isSnapshotMachine())
4928 return setError(VBOX_E_INVALID_VM_STATE,
4929 tr("Cannot set extradata for a snapshot"));
4930
4931 // check if the right IMachine instance is used
4932 if (mData->mRegistered && !i_isSessionMachine())
4933 return setError(VBOX_E_INVALID_VM_STATE,
4934 tr("Cannot set extradata for an immutable machine"));
4935
4936 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4937 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4938 strOldValue = it->second;
4939 }
4940
4941 bool fChanged;
4942 if ((fChanged = (strOldValue != aValue)))
4943 {
4944 // ask for permission from all listeners outside the locks;
4945 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4946 // lock to copy the list of callbacks to invoke
4947 Bstr error;
4948 Bstr bstrValue(aValue);
4949
4950 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4951 {
4952 const char *sep = error.isEmpty() ? "" : ": ";
4953 CBSTR err = error.raw();
4954 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4955 sep, err));
4956 return setError(E_ACCESSDENIED,
4957 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4958 aKey.c_str(),
4959 aValue.c_str(),
4960 sep,
4961 err);
4962 }
4963
4964 // data is changing and change not vetoed: then write it out under the lock
4965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4966
4967 if (aValue.isEmpty())
4968 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4969 else
4970 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4971 // creates a new key if needed
4972
4973 bool fNeedsGlobalSaveSettings = false;
4974 // This saving of settings is tricky: there is no "old state" for the
4975 // extradata items at all (unlike all other settings), so the old/new
4976 // settings comparison would give a wrong result!
4977 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4978
4979 if (fNeedsGlobalSaveSettings)
4980 {
4981 // save the global settings; for that we should hold only the VirtualBox lock
4982 alock.release();
4983 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4984 mParent->i_saveSettings();
4985 }
4986 }
4987
4988 // fire notification outside the lock
4989 if (fChanged)
4990 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4991
4992 return S_OK;
4993}
4994
4995HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4996{
4997 aProgress = NULL;
4998 NOREF(aSettingsFilePath);
4999 ReturnComNotImplemented();
5000}
5001
5002HRESULT Machine::saveSettings()
5003{
5004 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5005
5006 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5007 if (FAILED(rc)) return rc;
5008
5009 /* the settings file path may never be null */
5010 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5011
5012 /* save all VM data excluding snapshots */
5013 bool fNeedsGlobalSaveSettings = false;
5014 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5015 mlock.release();
5016
5017 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5018 {
5019 // save the global settings; for that we should hold only the VirtualBox lock
5020 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5021 rc = mParent->i_saveSettings();
5022 }
5023
5024 return rc;
5025}
5026
5027
5028HRESULT Machine::discardSettings()
5029{
5030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5031
5032 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5033 if (FAILED(rc)) return rc;
5034
5035 /*
5036 * during this rollback, the session will be notified if data has
5037 * been actually changed
5038 */
5039 i_rollback(true /* aNotify */);
5040
5041 return S_OK;
5042}
5043
5044/** @note Locks objects! */
5045HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5046 std::vector<ComPtr<IMedium> > &aMedia)
5047{
5048 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5049 AutoLimitedCaller autoCaller(this);
5050 AssertComRCReturnRC(autoCaller.rc());
5051
5052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5053
5054 Guid id(i_getId());
5055
5056 if (mData->mSession.mState != SessionState_Unlocked)
5057 return setError(VBOX_E_INVALID_OBJECT_STATE,
5058 tr("Cannot unregister the machine '%s' while it is locked"),
5059 mUserData->s.strName.c_str());
5060
5061 // wait for state dependents to drop to zero
5062 i_ensureNoStateDependencies();
5063
5064 if (!mData->mAccessible)
5065 {
5066 // inaccessible maschines can only be unregistered; uninitialize ourselves
5067 // here because currently there may be no unregistered that are inaccessible
5068 // (this state combination is not supported). Note releasing the caller and
5069 // leaving the lock before calling uninit()
5070 alock.release();
5071 autoCaller.release();
5072
5073 uninit();
5074
5075 mParent->i_unregisterMachine(this, id);
5076 // calls VirtualBox::i_saveSettings()
5077
5078 return S_OK;
5079 }
5080
5081 HRESULT rc = S_OK;
5082
5083 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5084 // discard saved state
5085 if (mData->mMachineState == MachineState_Saved)
5086 {
5087 // add the saved state file to the list of files the caller should delete
5088 Assert(!mSSData->strStateFilePath.isEmpty());
5089 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5090
5091 mSSData->strStateFilePath.setNull();
5092
5093 // unconditionally set the machine state to powered off, we now
5094 // know no session has locked the machine
5095 mData->mMachineState = MachineState_PoweredOff;
5096 }
5097
5098 size_t cSnapshots = 0;
5099 if (mData->mFirstSnapshot)
5100 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5101 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5102 // fail now before we start detaching media
5103 return setError(VBOX_E_INVALID_OBJECT_STATE,
5104 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5105 mUserData->s.strName.c_str(), cSnapshots);
5106
5107 // This list collects the medium objects from all medium attachments
5108 // which we will detach from the machine and its snapshots, in a specific
5109 // order which allows for closing all media without getting "media in use"
5110 // errors, simply by going through the list from the front to the back:
5111 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5112 // and must be closed before the parent media from the snapshots, or closing the parents
5113 // will fail because they still have children);
5114 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5115 // the root ("first") snapshot of the machine.
5116 MediaList llMedia;
5117
5118 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5119 && mMediaData->mAttachments.size()
5120 )
5121 {
5122 // we have media attachments: detach them all and add the Medium objects to our list
5123 if (aCleanupMode != CleanupMode_UnregisterOnly)
5124 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5125 else
5126 return setError(VBOX_E_INVALID_OBJECT_STATE,
5127 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5128 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5129 }
5130
5131 if (cSnapshots)
5132 {
5133 // add the media from the medium attachments of the snapshots to llMedia
5134 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5135 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5136 // into the children first
5137
5138 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5139 MachineState_T oldState = mData->mMachineState;
5140 mData->mMachineState = MachineState_DeletingSnapshot;
5141
5142 // make a copy of the first snapshot so the refcount does not drop to 0
5143 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5144 // because of the AutoCaller voodoo)
5145 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5146
5147 // GO!
5148 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5149
5150 mData->mMachineState = oldState;
5151 }
5152
5153 if (FAILED(rc))
5154 {
5155 i_rollbackMedia();
5156 return rc;
5157 }
5158
5159 // commit all the media changes made above
5160 i_commitMedia();
5161
5162 mData->mRegistered = false;
5163
5164 // machine lock no longer needed
5165 alock.release();
5166
5167 // return media to caller
5168 size_t i = 0;
5169 aMedia.resize(llMedia.size());
5170 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5171 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5172
5173 mParent->i_unregisterMachine(this, id);
5174 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5175
5176 return S_OK;
5177}
5178
5179/**
5180 * Task record for deleting a machine config.
5181 */
5182struct Machine::DeleteConfigTask
5183 : public Machine::Task
5184{
5185 DeleteConfigTask(Machine *m,
5186 Progress *p,
5187 const Utf8Str &t,
5188 const RTCList<ComPtr<IMedium> > &llMediums,
5189 const StringsList &llFilesToDelete)
5190 : Task(m, p, t),
5191 m_llMediums(llMediums),
5192 m_llFilesToDelete(llFilesToDelete)
5193 {}
5194
5195 void handler()
5196 {
5197 m_pMachine->i_deleteConfigHandler(*this);
5198 }
5199
5200 RTCList<ComPtr<IMedium> > m_llMediums;
5201 StringsList m_llFilesToDelete;
5202};
5203
5204/**
5205 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5206 * SessionMachine::taskHandler().
5207 *
5208 * @note Locks this object for writing.
5209 *
5210 * @param task
5211 * @return
5212 */
5213void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5214{
5215 LogFlowThisFuncEnter();
5216
5217 AutoCaller autoCaller(this);
5218 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5219 if (FAILED(autoCaller.rc()))
5220 {
5221 /* we might have been uninitialized because the session was accidentally
5222 * closed by the client, so don't assert */
5223 HRESULT rc = setError(E_FAIL,
5224 tr("The session has been accidentally closed"));
5225 task.m_pProgress->i_notifyComplete(rc);
5226 LogFlowThisFuncLeave();
5227 return;
5228 }
5229
5230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5231
5232 HRESULT rc = S_OK;
5233
5234 try
5235 {
5236 ULONG uLogHistoryCount = 3;
5237 ComPtr<ISystemProperties> systemProperties;
5238 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5239 if (FAILED(rc)) throw rc;
5240
5241 if (!systemProperties.isNull())
5242 {
5243 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5244 if (FAILED(rc)) throw rc;
5245 }
5246
5247 MachineState_T oldState = mData->mMachineState;
5248 i_setMachineState(MachineState_SettingUp);
5249 alock.release();
5250 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5251 {
5252 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5253 {
5254 AutoCaller mac(pMedium);
5255 if (FAILED(mac.rc())) throw mac.rc();
5256 Utf8Str strLocation = pMedium->i_getLocationFull();
5257 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5258 if (FAILED(rc)) throw rc;
5259 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5260 }
5261 if (pMedium->i_isMediumFormatFile())
5262 {
5263 ComPtr<IProgress> pProgress2;
5264 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5265 if (FAILED(rc)) throw rc;
5266 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5267 if (FAILED(rc)) throw rc;
5268 /* Check the result of the asynchronous process. */
5269 LONG iRc;
5270 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5271 if (FAILED(rc)) throw rc;
5272 /* If the thread of the progress object has an error, then
5273 * retrieve the error info from there, or it'll be lost. */
5274 if (FAILED(iRc))
5275 throw setError(ProgressErrorInfo(pProgress2));
5276 }
5277
5278 /* Close the medium, deliberately without checking the return
5279 * code, and without leaving any trace in the error info, as
5280 * a failure here is a very minor issue, which shouldn't happen
5281 * as above we even managed to delete the medium. */
5282 {
5283 ErrorInfoKeeper eik;
5284 pMedium->Close();
5285 }
5286 }
5287 i_setMachineState(oldState);
5288 alock.acquire();
5289
5290 // delete the files pushed on the task list by Machine::Delete()
5291 // (this includes saved states of the machine and snapshots and
5292 // medium storage files from the IMedium list passed in, and the
5293 // machine XML file)
5294 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5295 while (it != task.m_llFilesToDelete.end())
5296 {
5297 const Utf8Str &strFile = *it;
5298 LogFunc(("Deleting file %s\n", strFile.c_str()));
5299 int vrc = RTFileDelete(strFile.c_str());
5300 if (RT_FAILURE(vrc))
5301 throw setError(VBOX_E_IPRT_ERROR,
5302 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5303
5304 ++it;
5305 if (it == task.m_llFilesToDelete.end())
5306 {
5307 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5308 if (FAILED(rc)) throw rc;
5309 break;
5310 }
5311
5312 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5313 if (FAILED(rc)) throw rc;
5314 }
5315
5316 /* delete the settings only when the file actually exists */
5317 if (mData->pMachineConfigFile->fileExists())
5318 {
5319 /* Delete any backup or uncommitted XML files. Ignore failures.
5320 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5321 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5322 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5323 RTFileDelete(otherXml.c_str());
5324 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5325 RTFileDelete(otherXml.c_str());
5326
5327 /* delete the Logs folder, nothing important should be left
5328 * there (we don't check for errors because the user might have
5329 * some private files there that we don't want to delete) */
5330 Utf8Str logFolder;
5331 getLogFolder(logFolder);
5332 Assert(logFolder.length());
5333 if (RTDirExists(logFolder.c_str()))
5334 {
5335 /* Delete all VBox.log[.N] files from the Logs folder
5336 * (this must be in sync with the rotation logic in
5337 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5338 * files that may have been created by the GUI. */
5339 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5340 logFolder.c_str(), RTPATH_DELIMITER);
5341 RTFileDelete(log.c_str());
5342 log = Utf8StrFmt("%s%cVBox.png",
5343 logFolder.c_str(), RTPATH_DELIMITER);
5344 RTFileDelete(log.c_str());
5345 for (int i = uLogHistoryCount; i > 0; i--)
5346 {
5347 log = Utf8StrFmt("%s%cVBox.log.%d",
5348 logFolder.c_str(), RTPATH_DELIMITER, i);
5349 RTFileDelete(log.c_str());
5350 log = Utf8StrFmt("%s%cVBox.png.%d",
5351 logFolder.c_str(), RTPATH_DELIMITER, i);
5352 RTFileDelete(log.c_str());
5353 }
5354#if defined(RT_OS_WINDOWS)
5355 log = Utf8StrFmt("%s%cVBoxStartup.log",
5356 logFolder.c_str(), RTPATH_DELIMITER);
5357 RTFileDelete(log.c_str());
5358#endif
5359
5360 RTDirRemove(logFolder.c_str());
5361 }
5362
5363 /* delete the Snapshots folder, nothing important should be left
5364 * there (we don't check for errors because the user might have
5365 * some private files there that we don't want to delete) */
5366 Utf8Str strFullSnapshotFolder;
5367 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5368 Assert(!strFullSnapshotFolder.isEmpty());
5369 if (RTDirExists(strFullSnapshotFolder.c_str()))
5370 RTDirRemove(strFullSnapshotFolder.c_str());
5371
5372 // delete the directory that contains the settings file, but only
5373 // if it matches the VM name
5374 Utf8Str settingsDir;
5375 if (i_isInOwnDir(&settingsDir))
5376 RTDirRemove(settingsDir.c_str());
5377 }
5378
5379 alock.release();
5380
5381 mParent->i_saveModifiedRegistries();
5382 }
5383 catch (HRESULT aRC) { rc = aRC; }
5384
5385 task.m_pProgress->i_notifyComplete(rc);
5386
5387 LogFlowThisFuncLeave();
5388}
5389
5390HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5391{
5392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5393
5394 HRESULT rc = i_checkStateDependency(MutableStateDep);
5395 if (FAILED(rc)) return rc;
5396
5397 if (mData->mRegistered)
5398 return setError(VBOX_E_INVALID_VM_STATE,
5399 tr("Cannot delete settings of a registered machine"));
5400
5401 // collect files to delete
5402 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5403 if (mData->pMachineConfigFile->fileExists())
5404 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5405
5406 RTCList<ComPtr<IMedium> > llMediums;
5407 for (size_t i = 0; i < aMedia.size(); ++i)
5408 {
5409 IMedium *pIMedium(aMedia[i]);
5410 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5411 if (pMedium.isNull())
5412 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5413 SafeArray<BSTR> ids;
5414 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5415 if (FAILED(rc)) return rc;
5416 /* At this point the medium should not have any back references
5417 * anymore. If it has it is attached to another VM and *must* not
5418 * deleted. */
5419 if (ids.size() < 1)
5420 llMediums.append(pMedium);
5421 }
5422
5423 ComObjPtr<Progress> pProgress;
5424 pProgress.createObject();
5425 rc = pProgress->init(i_getVirtualBox(),
5426 static_cast<IMachine*>(this) /* aInitiator */,
5427 Bstr(tr("Deleting files")).raw(),
5428 true /* fCancellable */,
5429 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5430 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5431 if (FAILED(rc))
5432 return rc;
5433
5434 /* create and start the task on a separate thread (note that it will not
5435 * start working until we release alock) */
5436 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5437 rc = pTask->createThread();
5438 if (FAILED(rc))
5439 return rc;
5440
5441 pProgress.queryInterfaceTo(aProgress.asOutParam());
5442
5443 LogFlowFuncLeave();
5444
5445 return S_OK;
5446}
5447
5448HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5449{
5450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5451
5452 ComObjPtr<Snapshot> pSnapshot;
5453 HRESULT rc;
5454
5455 if (aNameOrId.isEmpty())
5456 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5457 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5458 else
5459 {
5460 Guid uuid(aNameOrId);
5461 if (uuid.isValid())
5462 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5463 else
5464 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5465 }
5466 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5467
5468 return rc;
5469}
5470
5471HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5472{
5473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5474
5475 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5476 if (FAILED(rc)) return rc;
5477
5478 ComObjPtr<SharedFolder> sharedFolder;
5479 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5480 if (SUCCEEDED(rc))
5481 return setError(VBOX_E_OBJECT_IN_USE,
5482 tr("Shared folder named '%s' already exists"),
5483 aName.c_str());
5484
5485 sharedFolder.createObject();
5486 rc = sharedFolder->init(i_getMachine(),
5487 aName,
5488 aHostPath,
5489 !!aWritable,
5490 !!aAutomount,
5491 true /* fFailOnError */);
5492 if (FAILED(rc)) return rc;
5493
5494 i_setModified(IsModified_SharedFolders);
5495 mHWData.backup();
5496 mHWData->mSharedFolders.push_back(sharedFolder);
5497
5498 /* inform the direct session if any */
5499 alock.release();
5500 i_onSharedFolderChange();
5501
5502 return S_OK;
5503}
5504
5505HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5506{
5507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5508
5509 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5510 if (FAILED(rc)) return rc;
5511
5512 ComObjPtr<SharedFolder> sharedFolder;
5513 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5514 if (FAILED(rc)) return rc;
5515
5516 i_setModified(IsModified_SharedFolders);
5517 mHWData.backup();
5518 mHWData->mSharedFolders.remove(sharedFolder);
5519
5520 /* inform the direct session if any */
5521 alock.release();
5522 i_onSharedFolderChange();
5523
5524 return S_OK;
5525}
5526
5527HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5528{
5529 /* start with No */
5530 *aCanShow = FALSE;
5531
5532 ComPtr<IInternalSessionControl> directControl;
5533 {
5534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5535
5536 if (mData->mSession.mState != SessionState_Locked)
5537 return setError(VBOX_E_INVALID_VM_STATE,
5538 tr("Machine is not locked for session (session state: %s)"),
5539 Global::stringifySessionState(mData->mSession.mState));
5540
5541 if (mData->mSession.mLockType == LockType_VM)
5542 directControl = mData->mSession.mDirectControl;
5543 }
5544
5545 /* ignore calls made after #OnSessionEnd() is called */
5546 if (!directControl)
5547 return S_OK;
5548
5549 LONG64 dummy;
5550 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5551}
5552
5553HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5554{
5555 ComPtr<IInternalSessionControl> directControl;
5556 {
5557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5558
5559 if (mData->mSession.mState != SessionState_Locked)
5560 return setError(E_FAIL,
5561 tr("Machine is not locked for session (session state: %s)"),
5562 Global::stringifySessionState(mData->mSession.mState));
5563
5564 if (mData->mSession.mLockType == LockType_VM)
5565 directControl = mData->mSession.mDirectControl;
5566 }
5567
5568 /* ignore calls made after #OnSessionEnd() is called */
5569 if (!directControl)
5570 return S_OK;
5571
5572 BOOL dummy;
5573 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5574}
5575
5576#ifdef VBOX_WITH_GUEST_PROPS
5577/**
5578 * Look up a guest property in VBoxSVC's internal structures.
5579 */
5580HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5581 com::Utf8Str &aValue,
5582 LONG64 *aTimestamp,
5583 com::Utf8Str &aFlags) const
5584{
5585 using namespace guestProp;
5586
5587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5588 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5589
5590 if (it != mHWData->mGuestProperties.end())
5591 {
5592 char szFlags[MAX_FLAGS_LEN + 1];
5593 aValue = it->second.strValue;
5594 *aTimestamp = it->second.mTimestamp;
5595 writeFlags(it->second.mFlags, szFlags);
5596 aFlags = Utf8Str(szFlags);
5597 }
5598
5599 return S_OK;
5600}
5601
5602/**
5603 * Query the VM that a guest property belongs to for the property.
5604 * @returns E_ACCESSDENIED if the VM process is not available or not
5605 * currently handling queries and the lookup should then be done in
5606 * VBoxSVC.
5607 */
5608HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5609 com::Utf8Str &aValue,
5610 LONG64 *aTimestamp,
5611 com::Utf8Str &aFlags) const
5612{
5613 HRESULT rc = S_OK;
5614 BSTR bValue = NULL;
5615 BSTR bFlags = NULL;
5616
5617 ComPtr<IInternalSessionControl> directControl;
5618 {
5619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5620 if (mData->mSession.mLockType == LockType_VM)
5621 directControl = mData->mSession.mDirectControl;
5622 }
5623
5624 /* ignore calls made after #OnSessionEnd() is called */
5625 if (!directControl)
5626 rc = E_ACCESSDENIED;
5627 else
5628 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5629 0 /* accessMode */,
5630 &bValue, aTimestamp, &bFlags);
5631
5632 aValue = bValue;
5633 aFlags = bFlags;
5634
5635 return rc;
5636}
5637#endif // VBOX_WITH_GUEST_PROPS
5638
5639HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5640 com::Utf8Str &aValue,
5641 LONG64 *aTimestamp,
5642 com::Utf8Str &aFlags)
5643{
5644#ifndef VBOX_WITH_GUEST_PROPS
5645 ReturnComNotImplemented();
5646#else // VBOX_WITH_GUEST_PROPS
5647
5648 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5649
5650 if (rc == E_ACCESSDENIED)
5651 /* The VM is not running or the service is not (yet) accessible */
5652 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5653 return rc;
5654#endif // VBOX_WITH_GUEST_PROPS
5655}
5656
5657HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5658{
5659 LONG64 dummyTimestamp;
5660 com::Utf8Str dummyFlags;
5661 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5662 return rc;
5663
5664}
5665HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5666{
5667 com::Utf8Str dummyFlags;
5668 com::Utf8Str dummyValue;
5669 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5670 return rc;
5671}
5672
5673#ifdef VBOX_WITH_GUEST_PROPS
5674/**
5675 * Set a guest property in VBoxSVC's internal structures.
5676 */
5677HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5678 const com::Utf8Str &aFlags, bool fDelete)
5679{
5680 using namespace guestProp;
5681
5682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5683 HRESULT rc = S_OK;
5684
5685 rc = i_checkStateDependency(MutableOrSavedStateDep);
5686 if (FAILED(rc)) return rc;
5687
5688 try
5689 {
5690 uint32_t fFlags = NILFLAG;
5691 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5692 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5693
5694 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5695 if (it == mHWData->mGuestProperties.end())
5696 {
5697 if (!fDelete)
5698 {
5699 i_setModified(IsModified_MachineData);
5700 mHWData.backupEx();
5701
5702 RTTIMESPEC time;
5703 HWData::GuestProperty prop;
5704 prop.strValue = Bstr(aValue).raw();
5705 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5706 prop.mFlags = fFlags;
5707 mHWData->mGuestProperties[aName] = prop;
5708 }
5709 }
5710 else
5711 {
5712 if (it->second.mFlags & (RDONLYHOST))
5713 {
5714 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5715 }
5716 else
5717 {
5718 i_setModified(IsModified_MachineData);
5719 mHWData.backupEx();
5720
5721 /* The backupEx() operation invalidates our iterator,
5722 * so get a new one. */
5723 it = mHWData->mGuestProperties.find(aName);
5724 Assert(it != mHWData->mGuestProperties.end());
5725
5726 if (!fDelete)
5727 {
5728 RTTIMESPEC time;
5729 it->second.strValue = aValue;
5730 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5731 it->second.mFlags = fFlags;
5732 }
5733 else
5734 mHWData->mGuestProperties.erase(it);
5735 }
5736 }
5737
5738 if ( SUCCEEDED(rc)
5739 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5740 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5741 RTSTR_MAX,
5742 aName.c_str(),
5743 RTSTR_MAX,
5744 NULL)
5745 )
5746 )
5747 {
5748 alock.release();
5749
5750 mParent->i_onGuestPropertyChange(mData->mUuid,
5751 Bstr(aName).raw(),
5752 Bstr(aValue).raw(),
5753 Bstr(aFlags).raw());
5754 }
5755 }
5756 catch (std::bad_alloc &)
5757 {
5758 rc = E_OUTOFMEMORY;
5759 }
5760
5761 return rc;
5762}
5763
5764/**
5765 * Set a property on the VM that that property belongs to.
5766 * @returns E_ACCESSDENIED if the VM process is not available or not
5767 * currently handling queries and the setting should then be done in
5768 * VBoxSVC.
5769 */
5770HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5771 const com::Utf8Str &aFlags, bool fDelete)
5772{
5773 HRESULT rc;
5774
5775 try
5776 {
5777 ComPtr<IInternalSessionControl> directControl;
5778 {
5779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5780 if (mData->mSession.mLockType == LockType_VM)
5781 directControl = mData->mSession.mDirectControl;
5782 }
5783
5784 BSTR dummy = NULL; /* will not be changed (setter) */
5785 LONG64 dummy64;
5786 if (!directControl)
5787 rc = E_ACCESSDENIED;
5788 else
5789 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5790 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5791 fDelete? 2: 1 /* accessMode */,
5792 &dummy, &dummy64, &dummy);
5793 }
5794 catch (std::bad_alloc &)
5795 {
5796 rc = E_OUTOFMEMORY;
5797 }
5798
5799 return rc;
5800}
5801#endif // VBOX_WITH_GUEST_PROPS
5802
5803HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5804 const com::Utf8Str &aFlags)
5805{
5806#ifndef VBOX_WITH_GUEST_PROPS
5807 ReturnComNotImplemented();
5808#else // VBOX_WITH_GUEST_PROPS
5809 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5810 if (rc == E_ACCESSDENIED)
5811 /* The VM is not running or the service is not (yet) accessible */
5812 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5813 return rc;
5814#endif // VBOX_WITH_GUEST_PROPS
5815}
5816
5817HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5818{
5819 return setGuestProperty(aProperty, aValue, "");
5820}
5821
5822HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5823{
5824#ifndef VBOX_WITH_GUEST_PROPS
5825 ReturnComNotImplemented();
5826#else // VBOX_WITH_GUEST_PROPS
5827 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5828 if (rc == E_ACCESSDENIED)
5829 /* The VM is not running or the service is not (yet) accessible */
5830 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5831 return rc;
5832#endif // VBOX_WITH_GUEST_PROPS
5833}
5834
5835#ifdef VBOX_WITH_GUEST_PROPS
5836/**
5837 * Enumerate the guest properties in VBoxSVC's internal structures.
5838 */
5839HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5840 std::vector<com::Utf8Str> &aNames,
5841 std::vector<com::Utf8Str> &aValues,
5842 std::vector<LONG64> &aTimestamps,
5843 std::vector<com::Utf8Str> &aFlags)
5844{
5845 using namespace guestProp;
5846
5847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5848 Utf8Str strPatterns(aPatterns);
5849
5850 HWData::GuestPropertyMap propMap;
5851
5852 /*
5853 * Look for matching patterns and build up a list.
5854 */
5855 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5856 while (it != mHWData->mGuestProperties.end())
5857 {
5858 if ( strPatterns.isEmpty()
5859 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5860 RTSTR_MAX,
5861 it->first.c_str(),
5862 RTSTR_MAX,
5863 NULL)
5864 )
5865 propMap.insert(*it);
5866 it++;
5867 }
5868
5869 alock.release();
5870
5871 /*
5872 * And build up the arrays for returning the property information.
5873 */
5874 size_t cEntries = propMap.size();
5875
5876 aNames.resize(cEntries);
5877 aValues.resize(cEntries);
5878 aTimestamps.resize(cEntries);
5879 aFlags.resize(cEntries);
5880
5881 char szFlags[MAX_FLAGS_LEN + 1];
5882 size_t i= 0;
5883 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5884 {
5885 aNames[i] = it->first;
5886 aValues[i] = it->second.strValue;
5887 aTimestamps[i] = it->second.mTimestamp;
5888 writeFlags(it->second.mFlags, szFlags);
5889 aFlags[i] = Utf8Str(szFlags);
5890 }
5891
5892 return S_OK;
5893}
5894
5895/**
5896 * Enumerate the properties managed by a VM.
5897 * @returns E_ACCESSDENIED if the VM process is not available or not
5898 * currently handling queries and the setting should then be done in
5899 * VBoxSVC.
5900 */
5901HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5902 std::vector<com::Utf8Str> &aNames,
5903 std::vector<com::Utf8Str> &aValues,
5904 std::vector<LONG64> &aTimestamps,
5905 std::vector<com::Utf8Str> &aFlags)
5906{
5907 HRESULT rc;
5908 ComPtr<IInternalSessionControl> directControl;
5909 {
5910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5911 if (mData->mSession.mLockType == LockType_VM)
5912 directControl = mData->mSession.mDirectControl;
5913 }
5914
5915 com::SafeArray<BSTR> bNames;
5916 com::SafeArray<BSTR> bValues;
5917 com::SafeArray<LONG64> bTimestamps;
5918 com::SafeArray<BSTR> bFlags;
5919
5920 if (!directControl)
5921 rc = E_ACCESSDENIED;
5922 else
5923 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5924 ComSafeArrayAsOutParam(bNames),
5925 ComSafeArrayAsOutParam(bValues),
5926 ComSafeArrayAsOutParam(bTimestamps),
5927 ComSafeArrayAsOutParam(bFlags));
5928 size_t i;
5929 aNames.resize(bNames.size());
5930 for (i = 0; i < bNames.size(); ++i)
5931 aNames[i] = Utf8Str(bNames[i]);
5932 aValues.resize(bValues.size());
5933 for (i = 0; i < bValues.size(); ++i)
5934 aValues[i] = Utf8Str(bValues[i]);
5935 aTimestamps.resize(bTimestamps.size());
5936 for (i = 0; i < bTimestamps.size(); ++i)
5937 aTimestamps[i] = bTimestamps[i];
5938 aFlags.resize(bFlags.size());
5939 for (i = 0; i < bFlags.size(); ++i)
5940 aFlags[i] = Utf8Str(bFlags[i]);
5941
5942 return rc;
5943}
5944#endif // VBOX_WITH_GUEST_PROPS
5945HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5946 std::vector<com::Utf8Str> &aNames,
5947 std::vector<com::Utf8Str> &aValues,
5948 std::vector<LONG64> &aTimestamps,
5949 std::vector<com::Utf8Str> &aFlags)
5950{
5951#ifndef VBOX_WITH_GUEST_PROPS
5952 ReturnComNotImplemented();
5953#else // VBOX_WITH_GUEST_PROPS
5954
5955 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5956
5957 if (rc == E_ACCESSDENIED)
5958 /* The VM is not running or the service is not (yet) accessible */
5959 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5960 return rc;
5961#endif // VBOX_WITH_GUEST_PROPS
5962}
5963
5964HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5965 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5966{
5967 MediaData::AttachmentList atts;
5968
5969 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5970 if (FAILED(rc)) return rc;
5971
5972 size_t i = 0;
5973 aMediumAttachments.resize(atts.size());
5974 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5975 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5976
5977 return S_OK;
5978}
5979
5980HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5981 LONG aControllerPort,
5982 LONG aDevice,
5983 ComPtr<IMediumAttachment> &aAttachment)
5984{
5985 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5986 aName.c_str(), aControllerPort, aDevice));
5987
5988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5989
5990 aAttachment = NULL;
5991
5992 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5993 Bstr(aName).raw(),
5994 aControllerPort,
5995 aDevice);
5996 if (pAttach.isNull())
5997 return setError(VBOX_E_OBJECT_NOT_FOUND,
5998 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5999 aDevice, aControllerPort, aName.c_str());
6000
6001 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6002
6003 return S_OK;
6004}
6005
6006
6007HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6008 StorageBus_T aConnectionType,
6009 ComPtr<IStorageController> &aController)
6010{
6011 if ( (aConnectionType <= StorageBus_Null)
6012 || (aConnectionType > StorageBus_USB))
6013 return setError(E_INVALIDARG,
6014 tr("Invalid connection type: %d"),
6015 aConnectionType);
6016
6017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6018
6019 HRESULT rc = i_checkStateDependency(MutableStateDep);
6020 if (FAILED(rc)) return rc;
6021
6022 /* try to find one with the name first. */
6023 ComObjPtr<StorageController> ctrl;
6024
6025 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6026 if (SUCCEEDED(rc))
6027 return setError(VBOX_E_OBJECT_IN_USE,
6028 tr("Storage controller named '%s' already exists"),
6029 aName.c_str());
6030
6031 ctrl.createObject();
6032
6033 /* get a new instance number for the storage controller */
6034 ULONG ulInstance = 0;
6035 bool fBootable = true;
6036 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6037 it != mStorageControllers->end();
6038 ++it)
6039 {
6040 if ((*it)->i_getStorageBus() == aConnectionType)
6041 {
6042 ULONG ulCurInst = (*it)->i_getInstance();
6043
6044 if (ulCurInst >= ulInstance)
6045 ulInstance = ulCurInst + 1;
6046
6047 /* Only one controller of each type can be marked as bootable. */
6048 if ((*it)->i_getBootable())
6049 fBootable = false;
6050 }
6051 }
6052
6053 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6054 if (FAILED(rc)) return rc;
6055
6056 i_setModified(IsModified_Storage);
6057 mStorageControllers.backup();
6058 mStorageControllers->push_back(ctrl);
6059
6060 ctrl.queryInterfaceTo(aController.asOutParam());
6061
6062 /* inform the direct session if any */
6063 alock.release();
6064 i_onStorageControllerChange();
6065
6066 return S_OK;
6067}
6068
6069HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6070 ComPtr<IStorageController> &aStorageController)
6071{
6072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6073
6074 ComObjPtr<StorageController> ctrl;
6075
6076 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6077 if (SUCCEEDED(rc))
6078 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6079
6080 return rc;
6081}
6082
6083HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6084 ComPtr<IStorageController> &aStorageController)
6085{
6086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6087
6088 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6089 it != mStorageControllers->end();
6090 ++it)
6091 {
6092 if ((*it)->i_getInstance() == aInstance)
6093 {
6094 (*it).queryInterfaceTo(aStorageController.asOutParam());
6095 return S_OK;
6096 }
6097 }
6098
6099 return setError(VBOX_E_OBJECT_NOT_FOUND,
6100 tr("Could not find a storage controller with instance number '%lu'"),
6101 aInstance);
6102}
6103
6104HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6105{
6106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6107
6108 HRESULT rc = i_checkStateDependency(MutableStateDep);
6109 if (FAILED(rc)) return rc;
6110
6111 ComObjPtr<StorageController> ctrl;
6112
6113 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6114 if (SUCCEEDED(rc))
6115 {
6116 /* Ensure that only one controller of each type is marked as bootable. */
6117 if (aBootable == TRUE)
6118 {
6119 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6120 it != mStorageControllers->end();
6121 ++it)
6122 {
6123 ComObjPtr<StorageController> aCtrl = (*it);
6124
6125 if ( (aCtrl->i_getName() != aName)
6126 && aCtrl->i_getBootable() == TRUE
6127 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6128 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6129 {
6130 aCtrl->i_setBootable(FALSE);
6131 break;
6132 }
6133 }
6134 }
6135
6136 if (SUCCEEDED(rc))
6137 {
6138 ctrl->i_setBootable(aBootable);
6139 i_setModified(IsModified_Storage);
6140 }
6141 }
6142
6143 if (SUCCEEDED(rc))
6144 {
6145 /* inform the direct session if any */
6146 alock.release();
6147 i_onStorageControllerChange();
6148 }
6149
6150 return rc;
6151}
6152
6153HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6154{
6155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6156
6157 HRESULT rc = i_checkStateDependency(MutableStateDep);
6158 if (FAILED(rc)) return rc;
6159
6160 ComObjPtr<StorageController> ctrl;
6161 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6162 if (FAILED(rc)) return rc;
6163
6164 {
6165 /* find all attached devices to the appropriate storage controller and detach them all */
6166 // make a temporary list because detachDevice invalidates iterators into
6167 // mMediaData->mAttachments
6168 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6169
6170 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6171 it != llAttachments2.end();
6172 ++it)
6173 {
6174 MediumAttachment *pAttachTemp = *it;
6175
6176 AutoCaller localAutoCaller(pAttachTemp);
6177 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6178
6179 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6180
6181 if (pAttachTemp->i_getControllerName() == aName)
6182 {
6183 rc = i_detachDevice(pAttachTemp, alock, NULL);
6184 if (FAILED(rc)) return rc;
6185 }
6186 }
6187 }
6188
6189 /* We can remove it now. */
6190 i_setModified(IsModified_Storage);
6191 mStorageControllers.backup();
6192
6193 ctrl->i_unshare();
6194
6195 mStorageControllers->remove(ctrl);
6196
6197 /* inform the direct session if any */
6198 alock.release();
6199 i_onStorageControllerChange();
6200
6201 return S_OK;
6202}
6203
6204HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6205 ComPtr<IUSBController> &aController)
6206{
6207 if ( (aType <= USBControllerType_Null)
6208 || (aType >= USBControllerType_Last))
6209 return setError(E_INVALIDARG,
6210 tr("Invalid USB controller type: %d"),
6211 aType);
6212
6213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6214
6215 HRESULT rc = i_checkStateDependency(MutableStateDep);
6216 if (FAILED(rc)) return rc;
6217
6218 /* try to find one with the same type first. */
6219 ComObjPtr<USBController> ctrl;
6220
6221 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6222 if (SUCCEEDED(rc))
6223 return setError(VBOX_E_OBJECT_IN_USE,
6224 tr("USB controller named '%s' already exists"),
6225 aName.c_str());
6226
6227 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6228 ULONG maxInstances;
6229 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6230 if (FAILED(rc))
6231 return rc;
6232
6233 ULONG cInstances = i_getUSBControllerCountByType(aType);
6234 if (cInstances >= maxInstances)
6235 return setError(E_INVALIDARG,
6236 tr("Too many USB controllers of this type"));
6237
6238 ctrl.createObject();
6239
6240 rc = ctrl->init(this, aName, aType);
6241 if (FAILED(rc)) return rc;
6242
6243 i_setModified(IsModified_USB);
6244 mUSBControllers.backup();
6245 mUSBControllers->push_back(ctrl);
6246
6247 ctrl.queryInterfaceTo(aController.asOutParam());
6248
6249 /* inform the direct session if any */
6250 alock.release();
6251 i_onUSBControllerChange();
6252
6253 return S_OK;
6254}
6255
6256HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6257{
6258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6259
6260 ComObjPtr<USBController> ctrl;
6261
6262 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6263 if (SUCCEEDED(rc))
6264 ctrl.queryInterfaceTo(aController.asOutParam());
6265
6266 return rc;
6267}
6268
6269HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6270 ULONG *aControllers)
6271{
6272 if ( (aType <= USBControllerType_Null)
6273 || (aType >= USBControllerType_Last))
6274 return setError(E_INVALIDARG,
6275 tr("Invalid USB controller type: %d"),
6276 aType);
6277
6278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6279
6280 ComObjPtr<USBController> ctrl;
6281
6282 *aControllers = i_getUSBControllerCountByType(aType);
6283
6284 return S_OK;
6285}
6286
6287HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6288{
6289
6290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6291
6292 HRESULT rc = i_checkStateDependency(MutableStateDep);
6293 if (FAILED(rc)) return rc;
6294
6295 ComObjPtr<USBController> ctrl;
6296 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6297 if (FAILED(rc)) return rc;
6298
6299 i_setModified(IsModified_USB);
6300 mUSBControllers.backup();
6301
6302 ctrl->i_unshare();
6303
6304 mUSBControllers->remove(ctrl);
6305
6306 /* inform the direct session if any */
6307 alock.release();
6308 i_onUSBControllerChange();
6309
6310 return S_OK;
6311}
6312
6313HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6314 ULONG *aOriginX,
6315 ULONG *aOriginY,
6316 ULONG *aWidth,
6317 ULONG *aHeight,
6318 BOOL *aEnabled)
6319{
6320 uint32_t u32OriginX= 0;
6321 uint32_t u32OriginY= 0;
6322 uint32_t u32Width = 0;
6323 uint32_t u32Height = 0;
6324 uint16_t u16Flags = 0;
6325
6326 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6327 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6328 if (RT_FAILURE(vrc))
6329 {
6330#ifdef RT_OS_WINDOWS
6331 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6332 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6333 * So just assign fEnable to TRUE again.
6334 * The right fix would be to change GUI API wrappers to make sure that parameters
6335 * are changed only if API succeeds.
6336 */
6337 *aEnabled = TRUE;
6338#endif
6339 return setError(VBOX_E_IPRT_ERROR,
6340 tr("Saved guest size is not available (%Rrc)"),
6341 vrc);
6342 }
6343
6344 *aOriginX = u32OriginX;
6345 *aOriginY = u32OriginY;
6346 *aWidth = u32Width;
6347 *aHeight = u32Height;
6348 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6349
6350 return S_OK;
6351}
6352
6353HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6354{
6355 if (aScreenId != 0)
6356 return E_NOTIMPL;
6357
6358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6359
6360 uint8_t *pu8Data = NULL;
6361 uint32_t cbData = 0;
6362 uint32_t u32Width = 0;
6363 uint32_t u32Height = 0;
6364
6365 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6366
6367 if (RT_FAILURE(vrc))
6368 return setError(VBOX_E_IPRT_ERROR,
6369 tr("Saved screenshot data is not available (%Rrc)"),
6370 vrc);
6371
6372 *aSize = cbData;
6373 *aWidth = u32Width;
6374 *aHeight = u32Height;
6375
6376 freeSavedDisplayScreenshot(pu8Data);
6377
6378 return S_OK;
6379}
6380
6381HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6382 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6383{
6384 if (aScreenId != 0)
6385 return E_NOTIMPL;
6386
6387 if ( aBitmapFormat != BitmapFormat_BGR0
6388 && aBitmapFormat != BitmapFormat_BGRA
6389 && aBitmapFormat != BitmapFormat_RGBA
6390 && aBitmapFormat != BitmapFormat_PNG)
6391 return setError(E_NOTIMPL,
6392 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6393
6394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6395
6396 uint8_t *pu8Data = NULL;
6397 uint32_t cbData = 0;
6398 uint32_t u32Width = 0;
6399 uint32_t u32Height = 0;
6400
6401 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6402
6403 if (RT_FAILURE(vrc))
6404 return setError(VBOX_E_IPRT_ERROR,
6405 tr("Saved thumbnail data is not available (%Rrc)"),
6406 vrc);
6407
6408 HRESULT hr = S_OK;
6409
6410 *aWidth = u32Width;
6411 *aHeight = u32Height;
6412
6413 if (cbData > 0)
6414 {
6415 /* Convert pixels to the format expected by the API caller. */
6416 if (aBitmapFormat == BitmapFormat_BGR0)
6417 {
6418 /* [0] B, [1] G, [2] R, [3] 0. */
6419 aData.resize(cbData);
6420 memcpy(&aData.front(), pu8Data, cbData);
6421 }
6422 else if (aBitmapFormat == BitmapFormat_BGRA)
6423 {
6424 /* [0] B, [1] G, [2] R, [3] A. */
6425 aData.resize(cbData);
6426 for (uint32_t i = 0; i < cbData; i += 4)
6427 {
6428 aData[i] = pu8Data[i];
6429 aData[i + 1] = pu8Data[i + 1];
6430 aData[i + 2] = pu8Data[i + 2];
6431 aData[i + 3] = 0xff;
6432 }
6433 }
6434 else if (aBitmapFormat == BitmapFormat_RGBA)
6435 {
6436 /* [0] R, [1] G, [2] B, [3] A. */
6437 aData.resize(cbData);
6438 for (uint32_t i = 0; i < cbData; i += 4)
6439 {
6440 aData[i] = pu8Data[i + 2];
6441 aData[i + 1] = pu8Data[i + 1];
6442 aData[i + 2] = pu8Data[i];
6443 aData[i + 3] = 0xff;
6444 }
6445 }
6446 else if (aBitmapFormat == BitmapFormat_PNG)
6447 {
6448 uint8_t *pu8PNG = NULL;
6449 uint32_t cbPNG = 0;
6450 uint32_t cxPNG = 0;
6451 uint32_t cyPNG = 0;
6452
6453 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6454
6455 if (RT_SUCCESS(vrc))
6456 {
6457 aData.resize(cbPNG);
6458 if (cbPNG)
6459 memcpy(&aData.front(), pu8PNG, cbPNG);
6460 }
6461 else
6462 hr = setError(VBOX_E_IPRT_ERROR,
6463 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6464 vrc);
6465
6466 RTMemFree(pu8PNG);
6467 }
6468 }
6469
6470 freeSavedDisplayScreenshot(pu8Data);
6471
6472 return hr;
6473}
6474
6475HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6476{
6477 if (aScreenId != 0)
6478 return E_NOTIMPL;
6479
6480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6481
6482 uint8_t *pu8Data = NULL;
6483 uint32_t cbData = 0;
6484 uint32_t u32Width = 0;
6485 uint32_t u32Height = 0;
6486
6487 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6488
6489 if (RT_FAILURE(vrc))
6490 return setError(VBOX_E_IPRT_ERROR,
6491 tr("Saved screenshot data is not available (%Rrc)"),
6492 vrc);
6493
6494 *aSize = cbData;
6495 *aWidth = u32Width;
6496 *aHeight = u32Height;
6497
6498 freeSavedDisplayScreenshot(pu8Data);
6499
6500 return S_OK;
6501}
6502
6503HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6504{
6505 if (aScreenId != 0)
6506 return E_NOTIMPL;
6507
6508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6509
6510 uint8_t *pu8Data = NULL;
6511 uint32_t cbData = 0;
6512 uint32_t u32Width = 0;
6513 uint32_t u32Height = 0;
6514
6515 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6516
6517 if (RT_FAILURE(vrc))
6518 return setError(VBOX_E_IPRT_ERROR,
6519 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6520 vrc);
6521
6522 *aWidth = u32Width;
6523 *aHeight = u32Height;
6524
6525 aData.resize(cbData);
6526 if (cbData)
6527 memcpy(&aData.front(), pu8Data, cbData);
6528
6529 freeSavedDisplayScreenshot(pu8Data);
6530
6531 return S_OK;
6532}
6533
6534HRESULT Machine::hotPlugCPU(ULONG aCpu)
6535{
6536 HRESULT rc = S_OK;
6537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 if (!mHWData->mCPUHotPlugEnabled)
6540 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6541
6542 if (aCpu >= mHWData->mCPUCount)
6543 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6544
6545 if (mHWData->mCPUAttached[aCpu])
6546 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6547
6548 alock.release();
6549 rc = i_onCPUChange(aCpu, false);
6550 alock.acquire();
6551 if (FAILED(rc)) return rc;
6552
6553 i_setModified(IsModified_MachineData);
6554 mHWData.backup();
6555 mHWData->mCPUAttached[aCpu] = true;
6556
6557 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6558 if (Global::IsOnline(mData->mMachineState))
6559 i_saveSettings(NULL);
6560
6561 return S_OK;
6562}
6563
6564HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6565{
6566 HRESULT rc = S_OK;
6567
6568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6569
6570 if (!mHWData->mCPUHotPlugEnabled)
6571 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6572
6573 if (aCpu >= SchemaDefs::MaxCPUCount)
6574 return setError(E_INVALIDARG,
6575 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6576 SchemaDefs::MaxCPUCount);
6577
6578 if (!mHWData->mCPUAttached[aCpu])
6579 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6580
6581 /* CPU 0 can't be detached */
6582 if (aCpu == 0)
6583 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6584
6585 alock.release();
6586 rc = i_onCPUChange(aCpu, true);
6587 alock.acquire();
6588 if (FAILED(rc)) return rc;
6589
6590 i_setModified(IsModified_MachineData);
6591 mHWData.backup();
6592 mHWData->mCPUAttached[aCpu] = false;
6593
6594 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6595 if (Global::IsOnline(mData->mMachineState))
6596 i_saveSettings(NULL);
6597
6598 return S_OK;
6599}
6600
6601HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6602{
6603 *aAttached = false;
6604
6605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6606
6607 /* If hotplug is enabled the CPU is always enabled. */
6608 if (!mHWData->mCPUHotPlugEnabled)
6609 {
6610 if (aCpu < mHWData->mCPUCount)
6611 *aAttached = true;
6612 }
6613 else
6614 {
6615 if (aCpu < SchemaDefs::MaxCPUCount)
6616 *aAttached = mHWData->mCPUAttached[aCpu];
6617 }
6618
6619 return S_OK;
6620}
6621
6622HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6623{
6624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6625
6626 Utf8Str log = i_queryLogFilename(aIdx);
6627 if (!RTFileExists(log.c_str()))
6628 log.setNull();
6629 aFilename = log;
6630
6631 return S_OK;
6632}
6633
6634HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6635{
6636 if (aSize < 0)
6637 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6638
6639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6640
6641 HRESULT rc = S_OK;
6642 Utf8Str log = i_queryLogFilename(aIdx);
6643
6644 /* do not unnecessarily hold the lock while doing something which does
6645 * not need the lock and potentially takes a long time. */
6646 alock.release();
6647
6648 /* Limit the chunk size to 32K for now, as that gives better performance
6649 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6650 * One byte expands to approx. 25 bytes of breathtaking XML. */
6651 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6652 aData.resize(cbData);
6653
6654 RTFILE LogFile;
6655 int vrc = RTFileOpen(&LogFile, log.c_str(),
6656 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6657 if (RT_SUCCESS(vrc))
6658 {
6659 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6660 if (RT_SUCCESS(vrc))
6661 aData.resize(cbData);
6662 else
6663 rc = setError(VBOX_E_IPRT_ERROR,
6664 tr("Could not read log file '%s' (%Rrc)"),
6665 log.c_str(), vrc);
6666 RTFileClose(LogFile);
6667 }
6668 else
6669 rc = setError(VBOX_E_IPRT_ERROR,
6670 tr("Could not open log file '%s' (%Rrc)"),
6671 log.c_str(), vrc);
6672
6673 if (FAILED(rc))
6674 aData.resize(0);
6675
6676 return rc;
6677}
6678
6679
6680/**
6681 * Currently this method doesn't attach device to the running VM,
6682 * just makes sure it's plugged on next VM start.
6683 */
6684HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6685{
6686 // lock scope
6687 {
6688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6689
6690 HRESULT rc = i_checkStateDependency(MutableStateDep);
6691 if (FAILED(rc)) return rc;
6692
6693 ChipsetType_T aChipset = ChipsetType_PIIX3;
6694 COMGETTER(ChipsetType)(&aChipset);
6695
6696 if (aChipset != ChipsetType_ICH9)
6697 {
6698 return setError(E_INVALIDARG,
6699 tr("Host PCI attachment only supported with ICH9 chipset"));
6700 }
6701
6702 // check if device with this host PCI address already attached
6703 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6704 it != mHWData->mPCIDeviceAssignments.end();
6705 ++it)
6706 {
6707 LONG iHostAddress = -1;
6708 ComPtr<PCIDeviceAttachment> pAttach;
6709 pAttach = *it;
6710 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6711 if (iHostAddress == aHostAddress)
6712 return setError(E_INVALIDARG,
6713 tr("Device with host PCI address already attached to this VM"));
6714 }
6715
6716 ComObjPtr<PCIDeviceAttachment> pda;
6717 char name[32];
6718
6719 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6720 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6721 Bstr bname(name);
6722 pda.createObject();
6723 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6724 i_setModified(IsModified_MachineData);
6725 mHWData.backup();
6726 mHWData->mPCIDeviceAssignments.push_back(pda);
6727 }
6728
6729 return S_OK;
6730}
6731
6732/**
6733 * Currently this method doesn't detach device from the running VM,
6734 * just makes sure it's not plugged on next VM start.
6735 */
6736HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6737{
6738 ComObjPtr<PCIDeviceAttachment> pAttach;
6739 bool fRemoved = false;
6740 HRESULT rc;
6741
6742 // lock scope
6743 {
6744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6745
6746 rc = i_checkStateDependency(MutableStateDep);
6747 if (FAILED(rc)) return rc;
6748
6749 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6750 it != mHWData->mPCIDeviceAssignments.end();
6751 ++it)
6752 {
6753 LONG iHostAddress = -1;
6754 pAttach = *it;
6755 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6756 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6757 {
6758 i_setModified(IsModified_MachineData);
6759 mHWData.backup();
6760 mHWData->mPCIDeviceAssignments.remove(pAttach);
6761 fRemoved = true;
6762 break;
6763 }
6764 }
6765 }
6766
6767
6768 /* Fire event outside of the lock */
6769 if (fRemoved)
6770 {
6771 Assert(!pAttach.isNull());
6772 ComPtr<IEventSource> es;
6773 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6774 Assert(SUCCEEDED(rc));
6775 Bstr mid;
6776 rc = this->COMGETTER(Id)(mid.asOutParam());
6777 Assert(SUCCEEDED(rc));
6778 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6779 }
6780
6781 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6782 tr("No host PCI device %08x attached"),
6783 aHostAddress
6784 );
6785}
6786
6787HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6788{
6789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6790
6791 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6792
6793 size_t i = 0;
6794 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6795 it != mHWData->mPCIDeviceAssignments.end();
6796 ++i, ++it)
6797 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6798
6799 return S_OK;
6800}
6801
6802HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6803{
6804 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6805
6806 return S_OK;
6807}
6808
6809HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6810{
6811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6812
6813 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6814
6815 return S_OK;
6816}
6817
6818HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6819{
6820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6821 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6822 if (SUCCEEDED(hrc))
6823 {
6824 hrc = mHWData.backupEx();
6825 if (SUCCEEDED(hrc))
6826 {
6827 i_setModified(IsModified_MachineData);
6828 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6829 }
6830 }
6831 return hrc;
6832}
6833
6834HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6835{
6836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6837 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6838 return S_OK;
6839}
6840
6841HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6842{
6843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6844 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6845 if (SUCCEEDED(hrc))
6846 {
6847 hrc = mHWData.backupEx();
6848 if (SUCCEEDED(hrc))
6849 {
6850 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6851 if (SUCCEEDED(hrc))
6852 i_setModified(IsModified_MachineData);
6853 }
6854 }
6855 return hrc;
6856}
6857
6858HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6859{
6860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6861
6862 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6863
6864 return S_OK;
6865}
6866
6867HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6868{
6869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6870 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6871 if (SUCCEEDED(hrc))
6872 {
6873 hrc = mHWData.backupEx();
6874 if (SUCCEEDED(hrc))
6875 {
6876 i_setModified(IsModified_MachineData);
6877 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6878 }
6879 }
6880 return hrc;
6881}
6882
6883HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6884{
6885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6886
6887 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6888
6889 return S_OK;
6890}
6891
6892HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6893{
6894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6895
6896 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6897 if ( SUCCEEDED(hrc)
6898 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6899 {
6900 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6901 int vrc;
6902
6903 if (aAutostartEnabled)
6904 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6905 else
6906 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6907
6908 if (RT_SUCCESS(vrc))
6909 {
6910 hrc = mHWData.backupEx();
6911 if (SUCCEEDED(hrc))
6912 {
6913 i_setModified(IsModified_MachineData);
6914 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6915 }
6916 }
6917 else if (vrc == VERR_NOT_SUPPORTED)
6918 hrc = setError(VBOX_E_NOT_SUPPORTED,
6919 tr("The VM autostart feature is not supported on this platform"));
6920 else if (vrc == VERR_PATH_NOT_FOUND)
6921 hrc = setError(E_FAIL,
6922 tr("The path to the autostart database is not set"));
6923 else
6924 hrc = setError(E_UNEXPECTED,
6925 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6926 aAutostartEnabled ? "Adding" : "Removing",
6927 mUserData->s.strName.c_str(), vrc);
6928 }
6929 return hrc;
6930}
6931
6932HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6933{
6934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6935
6936 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6937
6938 return S_OK;
6939}
6940
6941HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6942{
6943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6944 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6945 if (SUCCEEDED(hrc))
6946 {
6947 hrc = mHWData.backupEx();
6948 if (SUCCEEDED(hrc))
6949 {
6950 i_setModified(IsModified_MachineData);
6951 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6952 }
6953 }
6954 return hrc;
6955}
6956
6957HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6958{
6959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6960
6961 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6962
6963 return S_OK;
6964}
6965
6966HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6967{
6968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6969 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6970 if ( SUCCEEDED(hrc)
6971 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6972 {
6973 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6974 int vrc;
6975
6976 if (aAutostopType != AutostopType_Disabled)
6977 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6978 else
6979 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6980
6981 if (RT_SUCCESS(vrc))
6982 {
6983 hrc = mHWData.backupEx();
6984 if (SUCCEEDED(hrc))
6985 {
6986 i_setModified(IsModified_MachineData);
6987 mHWData->mAutostart.enmAutostopType = aAutostopType;
6988 }
6989 }
6990 else if (vrc == VERR_NOT_SUPPORTED)
6991 hrc = setError(VBOX_E_NOT_SUPPORTED,
6992 tr("The VM autostop feature is not supported on this platform"));
6993 else if (vrc == VERR_PATH_NOT_FOUND)
6994 hrc = setError(E_FAIL,
6995 tr("The path to the autostart database is not set"));
6996 else
6997 hrc = setError(E_UNEXPECTED,
6998 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6999 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7000 mUserData->s.strName.c_str(), vrc);
7001 }
7002 return hrc;
7003}
7004
7005HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7006{
7007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7008
7009 aDefaultFrontend = mHWData->mDefaultFrontend;
7010
7011 return S_OK;
7012}
7013
7014HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7015{
7016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7017 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7018 if (SUCCEEDED(hrc))
7019 {
7020 hrc = mHWData.backupEx();
7021 if (SUCCEEDED(hrc))
7022 {
7023 i_setModified(IsModified_MachineData);
7024 mHWData->mDefaultFrontend = aDefaultFrontend;
7025 }
7026 }
7027 return hrc;
7028}
7029
7030HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7031{
7032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7033 size_t cbIcon = mUserData->mIcon.size();
7034 aIcon.resize(cbIcon);
7035 if (cbIcon)
7036 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7037 return S_OK;
7038}
7039
7040HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7041{
7042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7043 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7044 if (SUCCEEDED(hrc))
7045 {
7046 i_setModified(IsModified_MachineData);
7047 mUserData.backup();
7048 size_t cbIcon = aIcon.size();
7049 mUserData->mIcon.resize(cbIcon);
7050 if (cbIcon)
7051 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7052 }
7053 return hrc;
7054}
7055
7056HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7057{
7058#ifdef VBOX_WITH_USB
7059 *aUSBProxyAvailable = true;
7060#else
7061 *aUSBProxyAvailable = false;
7062#endif
7063 return S_OK;
7064}
7065
7066HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7067 ComPtr<IProgress> &aProgress)
7068{
7069 ComObjPtr<Progress> pP;
7070 Progress *ppP = pP;
7071 IProgress *iP = static_cast<IProgress *>(ppP);
7072 IProgress **pProgress = &iP;
7073
7074 IMachine *pTarget = aTarget;
7075
7076 /* Convert the options. */
7077 RTCList<CloneOptions_T> optList;
7078 if (aOptions.size())
7079 for (size_t i = 0; i < aOptions.size(); ++i)
7080 optList.append(aOptions[i]);
7081
7082 if (optList.contains(CloneOptions_Link))
7083 {
7084 if (!i_isSnapshotMachine())
7085 return setError(E_INVALIDARG,
7086 tr("Linked clone can only be created from a snapshot"));
7087 if (aMode != CloneMode_MachineState)
7088 return setError(E_INVALIDARG,
7089 tr("Linked clone can only be created for a single machine state"));
7090 }
7091 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7092
7093 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7094
7095 HRESULT rc = pWorker->start(pProgress);
7096
7097 pP = static_cast<Progress *>(*pProgress);
7098 pP.queryInterfaceTo(aProgress.asOutParam());
7099
7100 return rc;
7101
7102}
7103
7104HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7105{
7106 NOREF(aProgress);
7107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7108
7109 // This check should always fail.
7110 HRESULT rc = i_checkStateDependency(MutableStateDep);
7111 if (FAILED(rc)) return rc;
7112
7113 AssertFailedReturn(E_NOTIMPL);
7114}
7115
7116HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7117{
7118 NOREF(aSavedStateFile);
7119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7120
7121 // This check should always fail.
7122 HRESULT rc = i_checkStateDependency(MutableStateDep);
7123 if (FAILED(rc)) return rc;
7124
7125 AssertFailedReturn(E_NOTIMPL);
7126}
7127
7128HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7129{
7130 NOREF(aFRemoveFile);
7131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7132
7133 // This check should always fail.
7134 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7135 if (FAILED(rc)) return rc;
7136
7137 AssertFailedReturn(E_NOTIMPL);
7138}
7139
7140// public methods for internal purposes
7141/////////////////////////////////////////////////////////////////////////////
7142
7143/**
7144 * Adds the given IsModified_* flag to the dirty flags of the machine.
7145 * This must be called either during i_loadSettings or under the machine write lock.
7146 * @param fl
7147 */
7148void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7149{
7150 mData->flModifications |= fl;
7151 if (fAllowStateModification && i_isStateModificationAllowed())
7152 mData->mCurrentStateModified = true;
7153}
7154
7155/**
7156 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7157 * care of the write locking.
7158 *
7159 * @param fModifications The flag to add.
7160 */
7161void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7162{
7163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7164 i_setModified(fModification, fAllowStateModification);
7165}
7166
7167/**
7168 * Saves the registry entry of this machine to the given configuration node.
7169 *
7170 * @param aEntryNode Node to save the registry entry to.
7171 *
7172 * @note locks this object for reading.
7173 */
7174HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7175{
7176 AutoLimitedCaller autoCaller(this);
7177 AssertComRCReturnRC(autoCaller.rc());
7178
7179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7180
7181 data.uuid = mData->mUuid;
7182 data.strSettingsFile = mData->m_strConfigFile;
7183
7184 return S_OK;
7185}
7186
7187/**
7188 * Calculates the absolute path of the given path taking the directory of the
7189 * machine settings file as the current directory.
7190 *
7191 * @param aPath Path to calculate the absolute path for.
7192 * @param aResult Where to put the result (used only on success, can be the
7193 * same Utf8Str instance as passed in @a aPath).
7194 * @return IPRT result.
7195 *
7196 * @note Locks this object for reading.
7197 */
7198int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7199{
7200 AutoCaller autoCaller(this);
7201 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7202
7203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7204
7205 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7206
7207 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7208
7209 strSettingsDir.stripFilename();
7210 char folder[RTPATH_MAX];
7211 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7212 if (RT_SUCCESS(vrc))
7213 aResult = folder;
7214
7215 return vrc;
7216}
7217
7218/**
7219 * Copies strSource to strTarget, making it relative to the machine folder
7220 * if it is a subdirectory thereof, or simply copying it otherwise.
7221 *
7222 * @param strSource Path to evaluate and copy.
7223 * @param strTarget Buffer to receive target path.
7224 *
7225 * @note Locks this object for reading.
7226 */
7227void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7228 Utf8Str &strTarget)
7229{
7230 AutoCaller autoCaller(this);
7231 AssertComRCReturn(autoCaller.rc(), (void)0);
7232
7233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7234
7235 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7236 // use strTarget as a temporary buffer to hold the machine settings dir
7237 strTarget = mData->m_strConfigFileFull;
7238 strTarget.stripFilename();
7239 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7240 {
7241 // is relative: then append what's left
7242 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7243 // for empty paths (only possible for subdirs) use "." to avoid
7244 // triggering default settings for not present config attributes.
7245 if (strTarget.isEmpty())
7246 strTarget = ".";
7247 }
7248 else
7249 // is not relative: then overwrite
7250 strTarget = strSource;
7251}
7252
7253/**
7254 * Returns the full path to the machine's log folder in the
7255 * \a aLogFolder argument.
7256 */
7257void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7258{
7259 AutoCaller autoCaller(this);
7260 AssertComRCReturnVoid(autoCaller.rc());
7261
7262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7263
7264 char szTmp[RTPATH_MAX];
7265 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7266 if (RT_SUCCESS(vrc))
7267 {
7268 if (szTmp[0] && !mUserData.isNull())
7269 {
7270 char szTmp2[RTPATH_MAX];
7271 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7272 if (RT_SUCCESS(vrc))
7273 aLogFolder = BstrFmt("%s%c%s",
7274 szTmp2,
7275 RTPATH_DELIMITER,
7276 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7277 }
7278 else
7279 vrc = VERR_PATH_IS_RELATIVE;
7280 }
7281
7282 if (RT_FAILURE(vrc))
7283 {
7284 // fallback if VBOX_USER_LOGHOME is not set or invalid
7285 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7286 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7287 aLogFolder.append(RTPATH_DELIMITER);
7288 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7289 }
7290}
7291
7292/**
7293 * Returns the full path to the machine's log file for an given index.
7294 */
7295Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7296 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7297{
7298 Utf8Str logFolder;
7299 getLogFolder(logFolder);
7300 Assert(logFolder.length());
7301 Utf8Str log;
7302 if (idx == 0)
7303 log = Utf8StrFmt("%s%cVBox.log",
7304 logFolder.c_str(), RTPATH_DELIMITER);
7305 else
7306 log = Utf8StrFmt("%s%cVBox.log.%d",
7307 logFolder.c_str(), RTPATH_DELIMITER, idx);
7308 return log;
7309}
7310
7311/**
7312 * Returns the full path to the machine's (hardened) startup log file.
7313 */
7314Utf8Str Machine::i_getStartupLogFilename(void)
7315{
7316 Utf8Str strFilename;
7317 getLogFolder(strFilename);
7318 Assert(strFilename.length());
7319 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7320 return strFilename;
7321}
7322
7323
7324/**
7325 * Composes a unique saved state filename based on the current system time. The filename is
7326 * granular to the second so this will work so long as no more than one snapshot is taken on
7327 * a machine per second.
7328 *
7329 * Before version 4.1, we used this formula for saved state files:
7330 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7331 * which no longer works because saved state files can now be shared between the saved state of the
7332 * "saved" machine and an online snapshot, and the following would cause problems:
7333 * 1) save machine
7334 * 2) create online snapshot from that machine state --> reusing saved state file
7335 * 3) save machine again --> filename would be reused, breaking the online snapshot
7336 *
7337 * So instead we now use a timestamp.
7338 *
7339 * @param str
7340 */
7341
7342void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7343{
7344 AutoCaller autoCaller(this);
7345 AssertComRCReturnVoid(autoCaller.rc());
7346
7347 {
7348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7349 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7350 }
7351
7352 RTTIMESPEC ts;
7353 RTTimeNow(&ts);
7354 RTTIME time;
7355 RTTimeExplode(&time, &ts);
7356
7357 strStateFilePath += RTPATH_DELIMITER;
7358 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7359 time.i32Year, time.u8Month, time.u8MonthDay,
7360 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7361}
7362
7363/**
7364 * Returns the full path to the default video capture file.
7365 */
7366void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7367{
7368 AutoCaller autoCaller(this);
7369 AssertComRCReturnVoid(autoCaller.rc());
7370
7371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7372
7373 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7374 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7375 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7376}
7377
7378/**
7379 * Returns whether at least one USB controller is present for the VM.
7380 */
7381bool Machine::i_isUSBControllerPresent()
7382{
7383 AutoCaller autoCaller(this);
7384 AssertComRCReturn(autoCaller.rc(), false);
7385
7386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7387
7388 return (mUSBControllers->size() > 0);
7389}
7390
7391/**
7392 * @note Locks this object for writing, calls the client process
7393 * (inside the lock).
7394 */
7395HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7396 const Utf8Str &strFrontend,
7397 const Utf8Str &strEnvironment,
7398 ProgressProxy *aProgress)
7399{
7400 LogFlowThisFuncEnter();
7401
7402 AssertReturn(aControl, E_FAIL);
7403 AssertReturn(aProgress, E_FAIL);
7404 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7405
7406 AutoCaller autoCaller(this);
7407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7408
7409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7410
7411 if (!mData->mRegistered)
7412 return setError(E_UNEXPECTED,
7413 tr("The machine '%s' is not registered"),
7414 mUserData->s.strName.c_str());
7415
7416 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7417
7418 /* The process started when launching a VM with separate UI/VM processes is always
7419 * the UI process, i.e. needs special handling as it won't claim the session. */
7420 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7421
7422 if (fSeparate)
7423 {
7424 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mType.compare("headless", Utf8Str::CaseInsensitive))
7425 return setError(VBOX_E_INVALID_OBJECT_STATE,
7426 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7427 mUserData->s.strName.c_str());
7428 }
7429 else
7430 {
7431 if ( mData->mSession.mState == SessionState_Locked
7432 || mData->mSession.mState == SessionState_Spawning
7433 || mData->mSession.mState == SessionState_Unlocking)
7434 return setError(VBOX_E_INVALID_OBJECT_STATE,
7435 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7436 mUserData->s.strName.c_str());
7437
7438 /* may not be busy */
7439 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7440 }
7441
7442 /* get the path to the executable */
7443 char szPath[RTPATH_MAX];
7444 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7445 size_t cchBufLeft = strlen(szPath);
7446 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7447 szPath[cchBufLeft] = 0;
7448 char *pszNamePart = szPath + cchBufLeft;
7449 cchBufLeft = sizeof(szPath) - cchBufLeft;
7450
7451 int vrc = VINF_SUCCESS;
7452 RTPROCESS pid = NIL_RTPROCESS;
7453
7454 RTENV env = RTENV_DEFAULT;
7455
7456 if (!strEnvironment.isEmpty())
7457 {
7458 char *newEnvStr = NULL;
7459
7460 do
7461 {
7462 /* clone the current environment */
7463 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7464 AssertRCBreakStmt(vrc2, vrc = vrc2);
7465
7466 newEnvStr = RTStrDup(strEnvironment.c_str());
7467 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7468
7469 /* put new variables to the environment
7470 * (ignore empty variable names here since RTEnv API
7471 * intentionally doesn't do that) */
7472 char *var = newEnvStr;
7473 for (char *p = newEnvStr; *p; ++p)
7474 {
7475 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7476 {
7477 *p = '\0';
7478 if (*var)
7479 {
7480 char *val = strchr(var, '=');
7481 if (val)
7482 {
7483 *val++ = '\0';
7484 vrc2 = RTEnvSetEx(env, var, val);
7485 }
7486 else
7487 vrc2 = RTEnvUnsetEx(env, var);
7488 if (RT_FAILURE(vrc2))
7489 break;
7490 }
7491 var = p + 1;
7492 }
7493 }
7494 if (RT_SUCCESS(vrc2) && *var)
7495 vrc2 = RTEnvPutEx(env, var);
7496
7497 AssertRCBreakStmt(vrc2, vrc = vrc2);
7498 }
7499 while (0);
7500
7501 if (newEnvStr != NULL)
7502 RTStrFree(newEnvStr);
7503 }
7504
7505 /* Hardened startup logging */
7506#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7507 Utf8Str strSupStartLogArg("--sup-startup-log=");
7508 {
7509 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7510 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7511 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7512 {
7513 Utf8Str strStartupLogDir = strStartupLogFile;
7514 strStartupLogDir.stripFilename();
7515 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7516 file without stripping the file. */
7517 }
7518 strSupStartLogArg.append(strStartupLogFile);
7519 }
7520 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7521#else
7522 const char *pszSupStartupLogArg = NULL;
7523#endif
7524
7525
7526#ifdef VBOX_WITH_QTGUI
7527 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7528 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7529 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7530 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7531 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7532 {
7533# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7534 /* Modify the base path so that we don't need to use ".." below. */
7535 RTPathStripTrailingSlash(szPath);
7536 RTPathStripFilename(szPath);
7537 cchBufLeft = strlen(szPath);
7538 pszNamePart = szPath + cchBufLeft;
7539 cchBufLeft = sizeof(szPath) - cchBufLeft;
7540
7541# define OSX_APP_NAME "VirtualBoxVM"
7542# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7543
7544 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7545 if ( strAppOverride.contains(".")
7546 || strAppOverride.contains("/")
7547 || strAppOverride.contains("\\")
7548 || strAppOverride.contains(":"))
7549 strAppOverride.setNull();
7550 Utf8Str strAppPath;
7551 if (!strAppOverride.isEmpty())
7552 {
7553 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7554 Utf8Str strFullPath(szPath);
7555 strFullPath.append(strAppPath);
7556 /* there is a race, but people using this deserve the failure */
7557 if (!RTFileExists(strFullPath.c_str()))
7558 strAppOverride.setNull();
7559 }
7560 if (strAppOverride.isEmpty())
7561 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7562 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7563 strcpy(pszNamePart, strAppPath.c_str());
7564# else
7565 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7566 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7567 strcpy(pszNamePart, s_szVirtualBox_exe);
7568# endif
7569
7570 Utf8Str idStr = mData->mUuid.toString();
7571 const char *apszArgs[] =
7572 {
7573 szPath,
7574 "--comment", mUserData->s.strName.c_str(),
7575 "--startvm", idStr.c_str(),
7576 "--no-startvm-errormsgbox",
7577 NULL, /* For "--separate". */
7578 NULL, /* For "--sup-startup-log". */
7579 NULL
7580 };
7581 unsigned iArg = 6;
7582 if (fSeparate)
7583 apszArgs[iArg++] = "--separate";
7584 apszArgs[iArg++] = pszSupStartupLogArg;
7585
7586 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7587 }
7588#else /* !VBOX_WITH_QTGUI */
7589 if (0)
7590 ;
7591#endif /* VBOX_WITH_QTGUI */
7592
7593 else
7594
7595#ifdef VBOX_WITH_VBOXSDL
7596 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7597 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7598 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7599 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7600 {
7601 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7602 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7603 strcpy(pszNamePart, s_szVBoxSDL_exe);
7604
7605 Utf8Str idStr = mData->mUuid.toString();
7606 const char *apszArgs[] =
7607 {
7608 szPath,
7609 "--comment", mUserData->s.strName.c_str(),
7610 "--startvm", idStr.c_str(),
7611 NULL, /* For "--separate". */
7612 NULL, /* For "--sup-startup-log". */
7613 NULL
7614 };
7615 unsigned iArg = 5;
7616 if (fSeparate)
7617 apszArgs[iArg++] = "--separate";
7618 apszArgs[iArg++] = pszSupStartupLogArg;
7619
7620 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7621 }
7622#else /* !VBOX_WITH_VBOXSDL */
7623 if (0)
7624 ;
7625#endif /* !VBOX_WITH_VBOXSDL */
7626
7627 else
7628
7629#ifdef VBOX_WITH_HEADLESS
7630 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7631 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7632 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7633 )
7634 {
7635 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7636 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7637 * and a VM works even if the server has not been installed.
7638 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7639 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7640 * differently in 4.0 and 3.x.
7641 */
7642 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7643 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7644 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7645
7646 Utf8Str idStr = mData->mUuid.toString();
7647 const char *apszArgs[] =
7648 {
7649 szPath,
7650 "--comment", mUserData->s.strName.c_str(),
7651 "--startvm", idStr.c_str(),
7652 "--vrde", "config",
7653 NULL, /* For "--capture". */
7654 NULL, /* For "--sup-startup-log". */
7655 NULL
7656 };
7657 unsigned iArg = 7;
7658 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7659 apszArgs[iArg++] = "--capture";
7660 apszArgs[iArg++] = pszSupStartupLogArg;
7661
7662# ifdef RT_OS_WINDOWS
7663 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7664# else
7665 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7666# endif
7667 }
7668#else /* !VBOX_WITH_HEADLESS */
7669 if (0)
7670 ;
7671#endif /* !VBOX_WITH_HEADLESS */
7672 else
7673 {
7674 RTEnvDestroy(env);
7675 return setError(E_INVALIDARG,
7676 tr("Invalid frontend name: '%s'"),
7677 strFrontend.c_str());
7678 }
7679
7680 RTEnvDestroy(env);
7681
7682 if (RT_FAILURE(vrc))
7683 return setError(VBOX_E_IPRT_ERROR,
7684 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7685 mUserData->s.strName.c_str(), vrc);
7686
7687 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7688
7689 if (!fSeparate)
7690 {
7691 /*
7692 * Note that we don't release the lock here before calling the client,
7693 * because it doesn't need to call us back if called with a NULL argument.
7694 * Releasing the lock here is dangerous because we didn't prepare the
7695 * launch data yet, but the client we've just started may happen to be
7696 * too fast and call LockMachine() that will fail (because of PID, etc.),
7697 * so that the Machine will never get out of the Spawning session state.
7698 */
7699
7700 /* inform the session that it will be a remote one */
7701 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7702#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7703 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7704#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7705 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7706#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7707 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7708
7709 if (FAILED(rc))
7710 {
7711 /* restore the session state */
7712 mData->mSession.mState = SessionState_Unlocked;
7713 alock.release();
7714 mParent->i_addProcessToReap(pid);
7715 /* The failure may occur w/o any error info (from RPC), so provide one */
7716 return setError(VBOX_E_VM_ERROR,
7717 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7718 }
7719
7720 /* attach launch data to the machine */
7721 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7722 mData->mSession.mRemoteControls.push_back(aControl);
7723 mData->mSession.mProgress = aProgress;
7724 mData->mSession.mPID = pid;
7725 mData->mSession.mState = SessionState_Spawning;
7726 mData->mSession.mType = strFrontend;
7727 }
7728 else
7729 {
7730 /* For separate UI process we declare the launch as completed instantly, as the
7731 * actual headless VM start may or may not come. No point in remembering anything
7732 * yet, as what matters for us is when the headless VM gets started. */
7733 aProgress->i_notifyComplete(S_OK);
7734 }
7735
7736 alock.release();
7737 mParent->i_addProcessToReap(pid);
7738
7739 LogFlowThisFuncLeave();
7740 return S_OK;
7741}
7742
7743/**
7744 * Returns @c true if the given session machine instance has an open direct
7745 * session (and optionally also for direct sessions which are closing) and
7746 * returns the session control machine instance if so.
7747 *
7748 * Note that when the method returns @c false, the arguments remain unchanged.
7749 *
7750 * @param aMachine Session machine object.
7751 * @param aControl Direct session control object (optional).
7752 * @param aAllowClosing If true then additionally a session which is currently
7753 * being closed will also be allowed.
7754 *
7755 * @note locks this object for reading.
7756 */
7757bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7758 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7759 bool aAllowClosing /*= false*/)
7760{
7761 AutoLimitedCaller autoCaller(this);
7762 AssertComRCReturn(autoCaller.rc(), false);
7763
7764 /* just return false for inaccessible machines */
7765 if (getObjectState().getState() != ObjectState::Ready)
7766 return false;
7767
7768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7769
7770 if ( ( mData->mSession.mState == SessionState_Locked
7771 && mData->mSession.mLockType == LockType_VM)
7772 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7773 )
7774 {
7775 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7776
7777 aMachine = mData->mSession.mMachine;
7778
7779 if (aControl != NULL)
7780 *aControl = mData->mSession.mDirectControl;
7781
7782 return true;
7783 }
7784
7785 return false;
7786}
7787
7788/**
7789 * Returns @c true if the given machine has an spawning direct session.
7790 *
7791 * @note locks this object for reading.
7792 */
7793bool Machine::i_isSessionSpawning()
7794{
7795 AutoLimitedCaller autoCaller(this);
7796 AssertComRCReturn(autoCaller.rc(), false);
7797
7798 /* just return false for inaccessible machines */
7799 if (getObjectState().getState() != ObjectState::Ready)
7800 return false;
7801
7802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7803
7804 if (mData->mSession.mState == SessionState_Spawning)
7805 return true;
7806
7807 return false;
7808}
7809
7810/**
7811 * Called from the client watcher thread to check for unexpected client process
7812 * death during Session_Spawning state (e.g. before it successfully opened a
7813 * direct session).
7814 *
7815 * On Win32 and on OS/2, this method is called only when we've got the
7816 * direct client's process termination notification, so it always returns @c
7817 * true.
7818 *
7819 * On other platforms, this method returns @c true if the client process is
7820 * terminated and @c false if it's still alive.
7821 *
7822 * @note Locks this object for writing.
7823 */
7824bool Machine::i_checkForSpawnFailure()
7825{
7826 AutoCaller autoCaller(this);
7827 if (!autoCaller.isOk())
7828 {
7829 /* nothing to do */
7830 LogFlowThisFunc(("Already uninitialized!\n"));
7831 return true;
7832 }
7833
7834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7835
7836 if (mData->mSession.mState != SessionState_Spawning)
7837 {
7838 /* nothing to do */
7839 LogFlowThisFunc(("Not spawning any more!\n"));
7840 return true;
7841 }
7842
7843 HRESULT rc = S_OK;
7844
7845 /* PID not yet initialized, skip check. */
7846 if (mData->mSession.mPID == NIL_RTPROCESS)
7847 return false;
7848
7849 RTPROCSTATUS status;
7850 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7851
7852 if (vrc != VERR_PROCESS_RUNNING)
7853 {
7854 Utf8Str strExtraInfo;
7855
7856#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7857 /* If the startup logfile exists and is of non-zero length, tell the
7858 user to look there for more details to encourage them to attach it
7859 when reporting startup issues. */
7860 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7861 uint64_t cbStartupLogFile = 0;
7862 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7863 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7864 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7865#endif
7866
7867 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7868 rc = setError(E_FAIL,
7869 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7870 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7871 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7872 rc = setError(E_FAIL,
7873 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7874 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7875 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7876 rc = setError(E_FAIL,
7877 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7878 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7879 else
7880 rc = setError(E_FAIL,
7881 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7882 i_getName().c_str(), vrc, strExtraInfo.c_str());
7883 }
7884
7885 if (FAILED(rc))
7886 {
7887 /* Close the remote session, remove the remote control from the list
7888 * and reset session state to Closed (@note keep the code in sync with
7889 * the relevant part in LockMachine()). */
7890
7891 Assert(mData->mSession.mRemoteControls.size() == 1);
7892 if (mData->mSession.mRemoteControls.size() == 1)
7893 {
7894 ErrorInfoKeeper eik;
7895 mData->mSession.mRemoteControls.front()->Uninitialize();
7896 }
7897
7898 mData->mSession.mRemoteControls.clear();
7899 mData->mSession.mState = SessionState_Unlocked;
7900
7901 /* finalize the progress after setting the state */
7902 if (!mData->mSession.mProgress.isNull())
7903 {
7904 mData->mSession.mProgress->notifyComplete(rc);
7905 mData->mSession.mProgress.setNull();
7906 }
7907
7908 mData->mSession.mPID = NIL_RTPROCESS;
7909
7910 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7911 return true;
7912 }
7913
7914 return false;
7915}
7916
7917/**
7918 * Checks whether the machine can be registered. If so, commits and saves
7919 * all settings.
7920 *
7921 * @note Must be called from mParent's write lock. Locks this object and
7922 * children for writing.
7923 */
7924HRESULT Machine::i_prepareRegister()
7925{
7926 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7927
7928 AutoLimitedCaller autoCaller(this);
7929 AssertComRCReturnRC(autoCaller.rc());
7930
7931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7932
7933 /* wait for state dependents to drop to zero */
7934 i_ensureNoStateDependencies();
7935
7936 if (!mData->mAccessible)
7937 return setError(VBOX_E_INVALID_OBJECT_STATE,
7938 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7939 mUserData->s.strName.c_str(),
7940 mData->mUuid.toString().c_str());
7941
7942 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7943
7944 if (mData->mRegistered)
7945 return setError(VBOX_E_INVALID_OBJECT_STATE,
7946 tr("The machine '%s' with UUID {%s} is already registered"),
7947 mUserData->s.strName.c_str(),
7948 mData->mUuid.toString().c_str());
7949
7950 HRESULT rc = S_OK;
7951
7952 // Ensure the settings are saved. If we are going to be registered and
7953 // no config file exists yet, create it by calling i_saveSettings() too.
7954 if ( (mData->flModifications)
7955 || (!mData->pMachineConfigFile->fileExists())
7956 )
7957 {
7958 rc = i_saveSettings(NULL);
7959 // no need to check whether VirtualBox.xml needs saving too since
7960 // we can't have a machine XML file rename pending
7961 if (FAILED(rc)) return rc;
7962 }
7963
7964 /* more config checking goes here */
7965
7966 if (SUCCEEDED(rc))
7967 {
7968 /* we may have had implicit modifications we want to fix on success */
7969 i_commit();
7970
7971 mData->mRegistered = true;
7972 }
7973 else
7974 {
7975 /* we may have had implicit modifications we want to cancel on failure*/
7976 i_rollback(false /* aNotify */);
7977 }
7978
7979 return rc;
7980}
7981
7982/**
7983 * Increases the number of objects dependent on the machine state or on the
7984 * registered state. Guarantees that these two states will not change at least
7985 * until #releaseStateDependency() is called.
7986 *
7987 * Depending on the @a aDepType value, additional state checks may be made.
7988 * These checks will set extended error info on failure. See
7989 * #checkStateDependency() for more info.
7990 *
7991 * If this method returns a failure, the dependency is not added and the caller
7992 * is not allowed to rely on any particular machine state or registration state
7993 * value and may return the failed result code to the upper level.
7994 *
7995 * @param aDepType Dependency type to add.
7996 * @param aState Current machine state (NULL if not interested).
7997 * @param aRegistered Current registered state (NULL if not interested).
7998 *
7999 * @note Locks this object for writing.
8000 */
8001HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8002 MachineState_T *aState /* = NULL */,
8003 BOOL *aRegistered /* = NULL */)
8004{
8005 AutoCaller autoCaller(this);
8006 AssertComRCReturnRC(autoCaller.rc());
8007
8008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8009
8010 HRESULT rc = i_checkStateDependency(aDepType);
8011 if (FAILED(rc)) return rc;
8012
8013 {
8014 if (mData->mMachineStateChangePending != 0)
8015 {
8016 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8017 * drop to zero so don't add more. It may make sense to wait a bit
8018 * and retry before reporting an error (since the pending state
8019 * transition should be really quick) but let's just assert for
8020 * now to see if it ever happens on practice. */
8021
8022 AssertFailed();
8023
8024 return setError(E_ACCESSDENIED,
8025 tr("Machine state change is in progress. Please retry the operation later."));
8026 }
8027
8028 ++mData->mMachineStateDeps;
8029 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8030 }
8031
8032 if (aState)
8033 *aState = mData->mMachineState;
8034 if (aRegistered)
8035 *aRegistered = mData->mRegistered;
8036
8037 return S_OK;
8038}
8039
8040/**
8041 * Decreases the number of objects dependent on the machine state.
8042 * Must always complete the #addStateDependency() call after the state
8043 * dependency is no more necessary.
8044 */
8045void Machine::i_releaseStateDependency()
8046{
8047 AutoCaller autoCaller(this);
8048 AssertComRCReturnVoid(autoCaller.rc());
8049
8050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8051
8052 /* releaseStateDependency() w/o addStateDependency()? */
8053 AssertReturnVoid(mData->mMachineStateDeps != 0);
8054 -- mData->mMachineStateDeps;
8055
8056 if (mData->mMachineStateDeps == 0)
8057 {
8058 /* inform i_ensureNoStateDependencies() that there are no more deps */
8059 if (mData->mMachineStateChangePending != 0)
8060 {
8061 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8062 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8063 }
8064 }
8065}
8066
8067Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8068{
8069 /* start with nothing found */
8070 Utf8Str strResult("");
8071
8072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8073
8074 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8075 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8076 // found:
8077 strResult = it->second; // source is a Utf8Str
8078
8079 return strResult;
8080}
8081
8082// protected methods
8083/////////////////////////////////////////////////////////////////////////////
8084
8085/**
8086 * Performs machine state checks based on the @a aDepType value. If a check
8087 * fails, this method will set extended error info, otherwise it will return
8088 * S_OK. It is supposed, that on failure, the caller will immediately return
8089 * the return value of this method to the upper level.
8090 *
8091 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8092 *
8093 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8094 * current state of this machine object allows to change settings of the
8095 * machine (i.e. the machine is not registered, or registered but not running
8096 * and not saved). It is useful to call this method from Machine setters
8097 * before performing any change.
8098 *
8099 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8100 * as for MutableStateDep except that if the machine is saved, S_OK is also
8101 * returned. This is useful in setters which allow changing machine
8102 * properties when it is in the saved state.
8103 *
8104 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8105 * if the current state of this machine object allows to change runtime
8106 * changeable settings of the machine (i.e. the machine is not registered, or
8107 * registered but either running or not running and not saved). It is useful
8108 * to call this method from Machine setters before performing any changes to
8109 * runtime changeable settings.
8110 *
8111 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8112 * the same as for MutableOrRunningStateDep except that if the machine is
8113 * saved, S_OK is also returned. This is useful in setters which allow
8114 * changing runtime and saved state changeable machine properties.
8115 *
8116 * @param aDepType Dependency type to check.
8117 *
8118 * @note Non Machine based classes should use #addStateDependency() and
8119 * #releaseStateDependency() methods or the smart AutoStateDependency
8120 * template.
8121 *
8122 * @note This method must be called from under this object's read or write
8123 * lock.
8124 */
8125HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8126{
8127 switch (aDepType)
8128 {
8129 case AnyStateDep:
8130 {
8131 break;
8132 }
8133 case MutableStateDep:
8134 {
8135 if ( mData->mRegistered
8136 && ( !i_isSessionMachine()
8137 || ( mData->mMachineState != MachineState_Aborted
8138 && mData->mMachineState != MachineState_Teleported
8139 && mData->mMachineState != MachineState_PoweredOff
8140 )
8141 )
8142 )
8143 return setError(VBOX_E_INVALID_VM_STATE,
8144 tr("The machine is not mutable (state is %s)"),
8145 Global::stringifyMachineState(mData->mMachineState));
8146 break;
8147 }
8148 case MutableOrSavedStateDep:
8149 {
8150 if ( mData->mRegistered
8151 && ( !i_isSessionMachine()
8152 || ( mData->mMachineState != MachineState_Aborted
8153 && mData->mMachineState != MachineState_Teleported
8154 && mData->mMachineState != MachineState_Saved
8155 && mData->mMachineState != MachineState_PoweredOff
8156 )
8157 )
8158 )
8159 return setError(VBOX_E_INVALID_VM_STATE,
8160 tr("The machine is not mutable (state is %s)"),
8161 Global::stringifyMachineState(mData->mMachineState));
8162 break;
8163 }
8164 case MutableOrRunningStateDep:
8165 {
8166 if ( mData->mRegistered
8167 && ( !i_isSessionMachine()
8168 || ( mData->mMachineState != MachineState_Aborted
8169 && mData->mMachineState != MachineState_Teleported
8170 && mData->mMachineState != MachineState_PoweredOff
8171 && !Global::IsOnline(mData->mMachineState)
8172 )
8173 )
8174 )
8175 return setError(VBOX_E_INVALID_VM_STATE,
8176 tr("The machine is not mutable (state is %s)"),
8177 Global::stringifyMachineState(mData->mMachineState));
8178 break;
8179 }
8180 case MutableOrSavedOrRunningStateDep:
8181 {
8182 if ( mData->mRegistered
8183 && ( !i_isSessionMachine()
8184 || ( mData->mMachineState != MachineState_Aborted
8185 && mData->mMachineState != MachineState_Teleported
8186 && mData->mMachineState != MachineState_Saved
8187 && mData->mMachineState != MachineState_PoweredOff
8188 && !Global::IsOnline(mData->mMachineState)
8189 )
8190 )
8191 )
8192 return setError(VBOX_E_INVALID_VM_STATE,
8193 tr("The machine is not mutable (state is %s)"),
8194 Global::stringifyMachineState(mData->mMachineState));
8195 break;
8196 }
8197 }
8198
8199 return S_OK;
8200}
8201
8202/**
8203 * Helper to initialize all associated child objects and allocate data
8204 * structures.
8205 *
8206 * This method must be called as a part of the object's initialization procedure
8207 * (usually done in the #init() method).
8208 *
8209 * @note Must be called only from #init() or from #registeredInit().
8210 */
8211HRESULT Machine::initDataAndChildObjects()
8212{
8213 AutoCaller autoCaller(this);
8214 AssertComRCReturnRC(autoCaller.rc());
8215 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8216 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8217
8218 AssertReturn(!mData->mAccessible, E_FAIL);
8219
8220 /* allocate data structures */
8221 mSSData.allocate();
8222 mUserData.allocate();
8223 mHWData.allocate();
8224 mMediaData.allocate();
8225 mStorageControllers.allocate();
8226 mUSBControllers.allocate();
8227
8228 /* initialize mOSTypeId */
8229 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8230
8231 /* create associated BIOS settings object */
8232 unconst(mBIOSSettings).createObject();
8233 mBIOSSettings->init(this);
8234
8235 /* create an associated VRDE object (default is disabled) */
8236 unconst(mVRDEServer).createObject();
8237 mVRDEServer->init(this);
8238
8239 /* create associated serial port objects */
8240 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8241 {
8242 unconst(mSerialPorts[slot]).createObject();
8243 mSerialPorts[slot]->init(this, slot);
8244 }
8245
8246 /* create associated parallel port objects */
8247 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8248 {
8249 unconst(mParallelPorts[slot]).createObject();
8250 mParallelPorts[slot]->init(this, slot);
8251 }
8252
8253 /* create the audio adapter object (always present, default is disabled) */
8254 unconst(mAudioAdapter).createObject();
8255 mAudioAdapter->init(this);
8256
8257 /* create the USB device filters object (always present) */
8258 unconst(mUSBDeviceFilters).createObject();
8259 mUSBDeviceFilters->init(this);
8260
8261 /* create associated network adapter objects */
8262 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8263 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8264 {
8265 unconst(mNetworkAdapters[slot]).createObject();
8266 mNetworkAdapters[slot]->init(this, slot);
8267 }
8268
8269 /* create the bandwidth control */
8270 unconst(mBandwidthControl).createObject();
8271 mBandwidthControl->init(this);
8272
8273 return S_OK;
8274}
8275
8276/**
8277 * Helper to uninitialize all associated child objects and to free all data
8278 * structures.
8279 *
8280 * This method must be called as a part of the object's uninitialization
8281 * procedure (usually done in the #uninit() method).
8282 *
8283 * @note Must be called only from #uninit() or from #registeredInit().
8284 */
8285void Machine::uninitDataAndChildObjects()
8286{
8287 AutoCaller autoCaller(this);
8288 AssertComRCReturnVoid(autoCaller.rc());
8289 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8290 || getObjectState().getState() == ObjectState::Limited);
8291
8292 /* tell all our other child objects we've been uninitialized */
8293 if (mBandwidthControl)
8294 {
8295 mBandwidthControl->uninit();
8296 unconst(mBandwidthControl).setNull();
8297 }
8298
8299 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8300 {
8301 if (mNetworkAdapters[slot])
8302 {
8303 mNetworkAdapters[slot]->uninit();
8304 unconst(mNetworkAdapters[slot]).setNull();
8305 }
8306 }
8307
8308 if (mUSBDeviceFilters)
8309 {
8310 mUSBDeviceFilters->uninit();
8311 unconst(mUSBDeviceFilters).setNull();
8312 }
8313
8314 if (mAudioAdapter)
8315 {
8316 mAudioAdapter->uninit();
8317 unconst(mAudioAdapter).setNull();
8318 }
8319
8320 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8321 {
8322 if (mParallelPorts[slot])
8323 {
8324 mParallelPorts[slot]->uninit();
8325 unconst(mParallelPorts[slot]).setNull();
8326 }
8327 }
8328
8329 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8330 {
8331 if (mSerialPorts[slot])
8332 {
8333 mSerialPorts[slot]->uninit();
8334 unconst(mSerialPorts[slot]).setNull();
8335 }
8336 }
8337
8338 if (mVRDEServer)
8339 {
8340 mVRDEServer->uninit();
8341 unconst(mVRDEServer).setNull();
8342 }
8343
8344 if (mBIOSSettings)
8345 {
8346 mBIOSSettings->uninit();
8347 unconst(mBIOSSettings).setNull();
8348 }
8349
8350 /* Deassociate media (only when a real Machine or a SnapshotMachine
8351 * instance is uninitialized; SessionMachine instances refer to real
8352 * Machine media). This is necessary for a clean re-initialization of
8353 * the VM after successfully re-checking the accessibility state. Note
8354 * that in case of normal Machine or SnapshotMachine uninitialization (as
8355 * a result of unregistering or deleting the snapshot), outdated media
8356 * attachments will already be uninitialized and deleted, so this
8357 * code will not affect them. */
8358 if ( !!mMediaData
8359 && (!i_isSessionMachine())
8360 )
8361 {
8362 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8363 it != mMediaData->mAttachments.end();
8364 ++it)
8365 {
8366 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8367 if (pMedium.isNull())
8368 continue;
8369 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8370 AssertComRC(rc);
8371 }
8372 }
8373
8374 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8375 {
8376 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8377 if (mData->mFirstSnapshot)
8378 {
8379 // snapshots tree is protected by machine write lock; strictly
8380 // this isn't necessary here since we're deleting the entire
8381 // machine, but otherwise we assert in Snapshot::uninit()
8382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8383 mData->mFirstSnapshot->uninit();
8384 mData->mFirstSnapshot.setNull();
8385 }
8386
8387 mData->mCurrentSnapshot.setNull();
8388 }
8389
8390 /* free data structures (the essential mData structure is not freed here
8391 * since it may be still in use) */
8392 mMediaData.free();
8393 mStorageControllers.free();
8394 mUSBControllers.free();
8395 mHWData.free();
8396 mUserData.free();
8397 mSSData.free();
8398}
8399
8400/**
8401 * Returns a pointer to the Machine object for this machine that acts like a
8402 * parent for complex machine data objects such as shared folders, etc.
8403 *
8404 * For primary Machine objects and for SnapshotMachine objects, returns this
8405 * object's pointer itself. For SessionMachine objects, returns the peer
8406 * (primary) machine pointer.
8407 */
8408Machine* Machine::i_getMachine()
8409{
8410 if (i_isSessionMachine())
8411 return (Machine*)mPeer;
8412 return this;
8413}
8414
8415/**
8416 * Makes sure that there are no machine state dependents. If necessary, waits
8417 * for the number of dependents to drop to zero.
8418 *
8419 * Make sure this method is called from under this object's write lock to
8420 * guarantee that no new dependents may be added when this method returns
8421 * control to the caller.
8422 *
8423 * @note Locks this object for writing. The lock will be released while waiting
8424 * (if necessary).
8425 *
8426 * @warning To be used only in methods that change the machine state!
8427 */
8428void Machine::i_ensureNoStateDependencies()
8429{
8430 AssertReturnVoid(isWriteLockOnCurrentThread());
8431
8432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8433
8434 /* Wait for all state dependents if necessary */
8435 if (mData->mMachineStateDeps != 0)
8436 {
8437 /* lazy semaphore creation */
8438 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8439 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8440
8441 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8442 mData->mMachineStateDeps));
8443
8444 ++mData->mMachineStateChangePending;
8445
8446 /* reset the semaphore before waiting, the last dependent will signal
8447 * it */
8448 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8449
8450 alock.release();
8451
8452 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8453
8454 alock.acquire();
8455
8456 -- mData->mMachineStateChangePending;
8457 }
8458}
8459
8460/**
8461 * Changes the machine state and informs callbacks.
8462 *
8463 * This method is not intended to fail so it either returns S_OK or asserts (and
8464 * returns a failure).
8465 *
8466 * @note Locks this object for writing.
8467 */
8468HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8469{
8470 LogFlowThisFuncEnter();
8471 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8472 Assert(aMachineState != MachineState_Null);
8473
8474 AutoCaller autoCaller(this);
8475 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8476
8477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8478
8479 /* wait for state dependents to drop to zero */
8480 i_ensureNoStateDependencies();
8481
8482 MachineState_T const enmOldState = mData->mMachineState;
8483 if (enmOldState != aMachineState)
8484 {
8485 mData->mMachineState = aMachineState;
8486 RTTimeNow(&mData->mLastStateChange);
8487
8488#ifdef VBOX_WITH_DTRACE_R3_MAIN
8489 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8490#endif
8491 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8492 }
8493
8494 LogFlowThisFuncLeave();
8495 return S_OK;
8496}
8497
8498/**
8499 * Searches for a shared folder with the given logical name
8500 * in the collection of shared folders.
8501 *
8502 * @param aName logical name of the shared folder
8503 * @param aSharedFolder where to return the found object
8504 * @param aSetError whether to set the error info if the folder is
8505 * not found
8506 * @return
8507 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8508 *
8509 * @note
8510 * must be called from under the object's lock!
8511 */
8512HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8513 ComObjPtr<SharedFolder> &aSharedFolder,
8514 bool aSetError /* = false */)
8515{
8516 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8517 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8518 it != mHWData->mSharedFolders.end();
8519 ++it)
8520 {
8521 SharedFolder *pSF = *it;
8522 AutoCaller autoCaller(pSF);
8523 if (pSF->i_getName() == aName)
8524 {
8525 aSharedFolder = pSF;
8526 rc = S_OK;
8527 break;
8528 }
8529 }
8530
8531 if (aSetError && FAILED(rc))
8532 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8533
8534 return rc;
8535}
8536
8537/**
8538 * Initializes all machine instance data from the given settings structures
8539 * from XML. The exception is the machine UUID which needs special handling
8540 * depending on the caller's use case, so the caller needs to set that herself.
8541 *
8542 * This gets called in several contexts during machine initialization:
8543 *
8544 * -- When machine XML exists on disk already and needs to be loaded into memory,
8545 * for example, from registeredInit() to load all registered machines on
8546 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8547 * attached to the machine should be part of some media registry already.
8548 *
8549 * -- During OVF import, when a machine config has been constructed from an
8550 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8551 * ensure that the media listed as attachments in the config (which have
8552 * been imported from the OVF) receive the correct registry ID.
8553 *
8554 * -- During VM cloning.
8555 *
8556 * @param config Machine settings from XML.
8557 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8558 * for each attached medium in the config.
8559 * @return
8560 */
8561HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8562 const Guid *puuidRegistry)
8563{
8564 // copy name, description, OS type, teleporter, UTC etc.
8565 mUserData->s = config.machineUserData;
8566
8567 // Decode the Icon overide data from config userdata and set onto Machine.
8568 #define DECODE_STR_MAX _1M
8569 const char* pszStr = config.machineUserData.ovIcon.c_str();
8570 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8571 if (cbOut > DECODE_STR_MAX)
8572 return setError(E_FAIL,
8573 tr("Icon Data too long.'%d' > '%d'"),
8574 cbOut,
8575 DECODE_STR_MAX);
8576 mUserData->mIcon.resize(cbOut);
8577 int vrc = VINF_SUCCESS;
8578 if (cbOut)
8579 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8580 if (RT_FAILURE(vrc))
8581 {
8582 mUserData->mIcon.resize(0);
8583 return setError(E_FAIL,
8584 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8585 pszStr,
8586 vrc);
8587 }
8588
8589 // look up the object by Id to check it is valid
8590 ComPtr<IGuestOSType> guestOSType;
8591 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8592 guestOSType.asOutParam());
8593 if (FAILED(rc)) return rc;
8594
8595 // stateFile (optional)
8596 if (config.strStateFile.isEmpty())
8597 mSSData->strStateFilePath.setNull();
8598 else
8599 {
8600 Utf8Str stateFilePathFull(config.strStateFile);
8601 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8602 if (RT_FAILURE(vrc))
8603 return setError(E_FAIL,
8604 tr("Invalid saved state file path '%s' (%Rrc)"),
8605 config.strStateFile.c_str(),
8606 vrc);
8607 mSSData->strStateFilePath = stateFilePathFull;
8608 }
8609
8610 // snapshot folder needs special processing so set it again
8611 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8612 if (FAILED(rc)) return rc;
8613
8614 /* Copy the extra data items (Not in any case config is already the same as
8615 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8616 * make sure the extra data map is copied). */
8617 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8618
8619 /* currentStateModified (optional, default is true) */
8620 mData->mCurrentStateModified = config.fCurrentStateModified;
8621
8622 mData->mLastStateChange = config.timeLastStateChange;
8623
8624 /*
8625 * note: all mUserData members must be assigned prior this point because
8626 * we need to commit changes in order to let mUserData be shared by all
8627 * snapshot machine instances.
8628 */
8629 mUserData.commitCopy();
8630
8631 // machine registry, if present (must be loaded before snapshots)
8632 if (config.canHaveOwnMediaRegistry())
8633 {
8634 // determine machine folder
8635 Utf8Str strMachineFolder = i_getSettingsFileFull();
8636 strMachineFolder.stripFilename();
8637 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8638 config.mediaRegistry,
8639 strMachineFolder);
8640 if (FAILED(rc)) return rc;
8641 }
8642
8643 /* Snapshot node (optional) */
8644 size_t cRootSnapshots;
8645 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8646 {
8647 // there must be only one root snapshot
8648 Assert(cRootSnapshots == 1);
8649
8650 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8651
8652 rc = i_loadSnapshot(snap,
8653 config.uuidCurrentSnapshot,
8654 NULL); // no parent == first snapshot
8655 if (FAILED(rc)) return rc;
8656 }
8657
8658 // hardware data
8659 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8660 if (FAILED(rc)) return rc;
8661
8662 // load storage controllers
8663 rc = i_loadStorageControllers(config.storageMachine,
8664 puuidRegistry,
8665 NULL /* puuidSnapshot */);
8666 if (FAILED(rc)) return rc;
8667
8668 /*
8669 * NOTE: the assignment below must be the last thing to do,
8670 * otherwise it will be not possible to change the settings
8671 * somewhere in the code above because all setters will be
8672 * blocked by i_checkStateDependency(MutableStateDep).
8673 */
8674
8675 /* set the machine state to Aborted or Saved when appropriate */
8676 if (config.fAborted)
8677 {
8678 mSSData->strStateFilePath.setNull();
8679
8680 /* no need to use i_setMachineState() during init() */
8681 mData->mMachineState = MachineState_Aborted;
8682 }
8683 else if (!mSSData->strStateFilePath.isEmpty())
8684 {
8685 /* no need to use i_setMachineState() during init() */
8686 mData->mMachineState = MachineState_Saved;
8687 }
8688
8689 // after loading settings, we are no longer different from the XML on disk
8690 mData->flModifications = 0;
8691
8692 return S_OK;
8693}
8694
8695/**
8696 * Recursively loads all snapshots starting from the given.
8697 *
8698 * @param aNode <Snapshot> node.
8699 * @param aCurSnapshotId Current snapshot ID from the settings file.
8700 * @param aParentSnapshot Parent snapshot.
8701 */
8702HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8703 const Guid &aCurSnapshotId,
8704 Snapshot *aParentSnapshot)
8705{
8706 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8707 AssertReturn(!i_isSessionMachine(), E_FAIL);
8708
8709 HRESULT rc = S_OK;
8710
8711 Utf8Str strStateFile;
8712 if (!data.strStateFile.isEmpty())
8713 {
8714 /* optional */
8715 strStateFile = data.strStateFile;
8716 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8717 if (RT_FAILURE(vrc))
8718 return setError(E_FAIL,
8719 tr("Invalid saved state file path '%s' (%Rrc)"),
8720 strStateFile.c_str(),
8721 vrc);
8722 }
8723
8724 /* create a snapshot machine object */
8725 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8726 pSnapshotMachine.createObject();
8727 rc = pSnapshotMachine->initFromSettings(this,
8728 data.hardware,
8729 &data.debugging,
8730 &data.autostart,
8731 data.storage,
8732 data.uuid.ref(),
8733 strStateFile);
8734 if (FAILED(rc)) return rc;
8735
8736 /* create a snapshot object */
8737 ComObjPtr<Snapshot> pSnapshot;
8738 pSnapshot.createObject();
8739 /* initialize the snapshot */
8740 rc = pSnapshot->init(mParent, // VirtualBox object
8741 data.uuid,
8742 data.strName,
8743 data.strDescription,
8744 data.timestamp,
8745 pSnapshotMachine,
8746 aParentSnapshot);
8747 if (FAILED(rc)) return rc;
8748
8749 /* memorize the first snapshot if necessary */
8750 if (!mData->mFirstSnapshot)
8751 mData->mFirstSnapshot = pSnapshot;
8752
8753 /* memorize the current snapshot when appropriate */
8754 if ( !mData->mCurrentSnapshot
8755 && pSnapshot->i_getId() == aCurSnapshotId
8756 )
8757 mData->mCurrentSnapshot = pSnapshot;
8758
8759 // now create the children
8760 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8761 it != data.llChildSnapshots.end();
8762 ++it)
8763 {
8764 const settings::Snapshot &childData = *it;
8765 // recurse
8766 rc = i_loadSnapshot(childData,
8767 aCurSnapshotId,
8768 pSnapshot); // parent = the one we created above
8769 if (FAILED(rc)) return rc;
8770 }
8771
8772 return rc;
8773}
8774
8775/**
8776 * Loads settings into mHWData.
8777 *
8778 * @param data Reference to the hardware settings.
8779 * @param pDbg Pointer to the debugging settings.
8780 * @param pAutostart Pointer to the autostart settings.
8781 */
8782HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8783 const settings::Autostart *pAutostart)
8784{
8785 AssertReturn(!i_isSessionMachine(), E_FAIL);
8786
8787 HRESULT rc = S_OK;
8788
8789 try
8790 {
8791 /* The hardware version attribute (optional). */
8792 mHWData->mHWVersion = data.strVersion;
8793 mHWData->mHardwareUUID = data.uuid;
8794
8795 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8796 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8797 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8798 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8799 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8800 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8801 mHWData->mPAEEnabled = data.fPAE;
8802 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8803 mHWData->mLongMode = data.enmLongMode;
8804 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8805 mHWData->mCPUCount = data.cCPUs;
8806 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8807 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8808
8809 // cpu
8810 if (mHWData->mCPUHotPlugEnabled)
8811 {
8812 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8813 it != data.llCpus.end();
8814 ++it)
8815 {
8816 const settings::Cpu &cpu = *it;
8817
8818 mHWData->mCPUAttached[cpu.ulId] = true;
8819 }
8820 }
8821
8822 // cpuid leafs
8823 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8824 it != data.llCpuIdLeafs.end();
8825 ++it)
8826 {
8827 const settings::CpuIdLeaf &leaf = *it;
8828
8829 switch (leaf.ulId)
8830 {
8831 case 0x0:
8832 case 0x1:
8833 case 0x2:
8834 case 0x3:
8835 case 0x4:
8836 case 0x5:
8837 case 0x6:
8838 case 0x7:
8839 case 0x8:
8840 case 0x9:
8841 case 0xA:
8842 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8843 break;
8844
8845 case 0x80000000:
8846 case 0x80000001:
8847 case 0x80000002:
8848 case 0x80000003:
8849 case 0x80000004:
8850 case 0x80000005:
8851 case 0x80000006:
8852 case 0x80000007:
8853 case 0x80000008:
8854 case 0x80000009:
8855 case 0x8000000A:
8856 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8857 break;
8858
8859 default:
8860 /* just ignore */
8861 break;
8862 }
8863 }
8864
8865 mHWData->mMemorySize = data.ulMemorySizeMB;
8866 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8867
8868 // boot order
8869 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8870 {
8871 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8872 if (it == data.mapBootOrder.end())
8873 mHWData->mBootOrder[i] = DeviceType_Null;
8874 else
8875 mHWData->mBootOrder[i] = it->second;
8876 }
8877
8878 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8879 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8880 mHWData->mMonitorCount = data.cMonitors;
8881 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8882 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8883 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8884 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8885 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8886 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8887 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8888 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8889 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8890 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8891 if (!data.strVideoCaptureFile.isEmpty())
8892 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8893 else
8894 mHWData->mVideoCaptureFile.setNull();
8895 mHWData->mFirmwareType = data.firmwareType;
8896 mHWData->mPointingHIDType = data.pointingHIDType;
8897 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8898 mHWData->mChipsetType = data.chipsetType;
8899 mHWData->mParavirtProvider = data.paravirtProvider;
8900 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8901 mHWData->mHPETEnabled = data.fHPETEnabled;
8902
8903 /* VRDEServer */
8904 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8905 if (FAILED(rc)) return rc;
8906
8907 /* BIOS */
8908 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8909 if (FAILED(rc)) return rc;
8910
8911 // Bandwidth control (must come before network adapters)
8912 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8913 if (FAILED(rc)) return rc;
8914
8915 /* Shared folders */
8916 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8917 it != data.usbSettings.llUSBControllers.end();
8918 ++it)
8919 {
8920 const settings::USBController &settingsCtrl = *it;
8921 ComObjPtr<USBController> newCtrl;
8922
8923 newCtrl.createObject();
8924 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8925 mUSBControllers->push_back(newCtrl);
8926 }
8927
8928 /* USB device filters */
8929 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8930 if (FAILED(rc)) return rc;
8931
8932 // network adapters
8933 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8934 size_t oldCount = mNetworkAdapters.size();
8935 if (newCount > oldCount)
8936 {
8937 mNetworkAdapters.resize(newCount);
8938 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8939 {
8940 unconst(mNetworkAdapters[slot]).createObject();
8941 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8942 }
8943 }
8944 else if (newCount < oldCount)
8945 mNetworkAdapters.resize(newCount);
8946 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8947 it != data.llNetworkAdapters.end();
8948 ++it)
8949 {
8950 const settings::NetworkAdapter &nic = *it;
8951
8952 /* slot unicity is guaranteed by XML Schema */
8953 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8954 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8955 if (FAILED(rc)) return rc;
8956 }
8957
8958 // serial ports
8959 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8960 it != data.llSerialPorts.end();
8961 ++it)
8962 {
8963 const settings::SerialPort &s = *it;
8964
8965 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8966 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8967 if (FAILED(rc)) return rc;
8968 }
8969
8970 // parallel ports (optional)
8971 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8972 it != data.llParallelPorts.end();
8973 ++it)
8974 {
8975 const settings::ParallelPort &p = *it;
8976
8977 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8978 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8979 if (FAILED(rc)) return rc;
8980 }
8981
8982 /* AudioAdapter */
8983 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8984 if (FAILED(rc)) return rc;
8985
8986 /* Shared folders */
8987 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8988 it != data.llSharedFolders.end();
8989 ++it)
8990 {
8991 const settings::SharedFolder &sf = *it;
8992
8993 ComObjPtr<SharedFolder> sharedFolder;
8994 /* Check for double entries. Not allowed! */
8995 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8996 if (SUCCEEDED(rc))
8997 return setError(VBOX_E_OBJECT_IN_USE,
8998 tr("Shared folder named '%s' already exists"),
8999 sf.strName.c_str());
9000
9001 /* Create the new shared folder. Don't break on error. This will be
9002 * reported when the machine starts. */
9003 sharedFolder.createObject();
9004 rc = sharedFolder->init(i_getMachine(),
9005 sf.strName,
9006 sf.strHostPath,
9007 RT_BOOL(sf.fWritable),
9008 RT_BOOL(sf.fAutoMount),
9009 false /* fFailOnError */);
9010 if (FAILED(rc)) return rc;
9011 mHWData->mSharedFolders.push_back(sharedFolder);
9012 }
9013
9014 // Clipboard
9015 mHWData->mClipboardMode = data.clipboardMode;
9016
9017 // drag'n'drop
9018 mHWData->mDnDMode = data.dndMode;
9019
9020 // guest settings
9021 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9022
9023 // IO settings
9024 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9025 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9026
9027 // Host PCI devices
9028 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9029 it != data.pciAttachments.end();
9030 ++it)
9031 {
9032 const settings::HostPCIDeviceAttachment &hpda = *it;
9033 ComObjPtr<PCIDeviceAttachment> pda;
9034
9035 pda.createObject();
9036 pda->i_loadSettings(this, hpda);
9037 mHWData->mPCIDeviceAssignments.push_back(pda);
9038 }
9039
9040 /*
9041 * (The following isn't really real hardware, but it lives in HWData
9042 * for reasons of convenience.)
9043 */
9044
9045#ifdef VBOX_WITH_GUEST_PROPS
9046 /* Guest properties (optional) */
9047 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9048 it != data.llGuestProperties.end();
9049 ++it)
9050 {
9051 const settings::GuestProperty &prop = *it;
9052 uint32_t fFlags = guestProp::NILFLAG;
9053 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9054 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9055 mHWData->mGuestProperties[prop.strName] = property;
9056 }
9057
9058 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9059#endif /* VBOX_WITH_GUEST_PROPS defined */
9060
9061 rc = i_loadDebugging(pDbg);
9062 if (FAILED(rc))
9063 return rc;
9064
9065 mHWData->mAutostart = *pAutostart;
9066
9067 /* default frontend */
9068 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9069 }
9070 catch(std::bad_alloc &)
9071 {
9072 return E_OUTOFMEMORY;
9073 }
9074
9075 AssertComRC(rc);
9076 return rc;
9077}
9078
9079/**
9080 * Called from Machine::loadHardware() to load the debugging settings of the
9081 * machine.
9082 *
9083 * @param pDbg Pointer to the settings.
9084 */
9085HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9086{
9087 mHWData->mDebugging = *pDbg;
9088 /* no more processing currently required, this will probably change. */
9089 return S_OK;
9090}
9091
9092/**
9093 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9094 *
9095 * @param data
9096 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9097 * @param puuidSnapshot
9098 * @return
9099 */
9100HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9101 const Guid *puuidRegistry,
9102 const Guid *puuidSnapshot)
9103{
9104 AssertReturn(!i_isSessionMachine(), E_FAIL);
9105
9106 HRESULT rc = S_OK;
9107
9108 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9109 it != data.llStorageControllers.end();
9110 ++it)
9111 {
9112 const settings::StorageController &ctlData = *it;
9113
9114 ComObjPtr<StorageController> pCtl;
9115 /* Try to find one with the name first. */
9116 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9117 if (SUCCEEDED(rc))
9118 return setError(VBOX_E_OBJECT_IN_USE,
9119 tr("Storage controller named '%s' already exists"),
9120 ctlData.strName.c_str());
9121
9122 pCtl.createObject();
9123 rc = pCtl->init(this,
9124 ctlData.strName,
9125 ctlData.storageBus,
9126 ctlData.ulInstance,
9127 ctlData.fBootable);
9128 if (FAILED(rc)) return rc;
9129
9130 mStorageControllers->push_back(pCtl);
9131
9132 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9133 if (FAILED(rc)) return rc;
9134
9135 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9136 if (FAILED(rc)) return rc;
9137
9138 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9139 if (FAILED(rc)) return rc;
9140
9141 /* Set IDE emulation settings (only for AHCI controller). */
9142 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9143 {
9144 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9145 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9146 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9147 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9148 )
9149 return rc;
9150 }
9151
9152 /* Load the attached devices now. */
9153 rc = i_loadStorageDevices(pCtl,
9154 ctlData,
9155 puuidRegistry,
9156 puuidSnapshot);
9157 if (FAILED(rc)) return rc;
9158 }
9159
9160 return S_OK;
9161}
9162
9163/**
9164 * Called from i_loadStorageControllers for a controller's devices.
9165 *
9166 * @param aStorageController
9167 * @param data
9168 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9169 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9170 * @return
9171 */
9172HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9173 const settings::StorageController &data,
9174 const Guid *puuidRegistry,
9175 const Guid *puuidSnapshot)
9176{
9177 HRESULT rc = S_OK;
9178
9179 /* paranoia: detect duplicate attachments */
9180 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9181 it != data.llAttachedDevices.end();
9182 ++it)
9183 {
9184 const settings::AttachedDevice &ad = *it;
9185
9186 for (settings::AttachedDevicesList::const_iterator it2 = it;
9187 it2 != data.llAttachedDevices.end();
9188 ++it2)
9189 {
9190 if (it == it2)
9191 continue;
9192
9193 const settings::AttachedDevice &ad2 = *it2;
9194
9195 if ( ad.lPort == ad2.lPort
9196 && ad.lDevice == ad2.lDevice)
9197 {
9198 return setError(E_FAIL,
9199 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9200 aStorageController->i_getName().c_str(),
9201 ad.lPort,
9202 ad.lDevice,
9203 mUserData->s.strName.c_str());
9204 }
9205 }
9206 }
9207
9208 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9209 it != data.llAttachedDevices.end();
9210 ++it)
9211 {
9212 const settings::AttachedDevice &dev = *it;
9213 ComObjPtr<Medium> medium;
9214
9215 switch (dev.deviceType)
9216 {
9217 case DeviceType_Floppy:
9218 case DeviceType_DVD:
9219 if (dev.strHostDriveSrc.isNotEmpty())
9220 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9221 false /* fRefresh */, medium);
9222 else
9223 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9224 dev.uuid,
9225 false /* fRefresh */,
9226 false /* aSetError */,
9227 medium);
9228 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9229 // This is not an error. The host drive or UUID might have vanished, so just go
9230 // ahead without this removeable medium attachment
9231 rc = S_OK;
9232 break;
9233
9234 case DeviceType_HardDisk:
9235 {
9236 /* find a hard disk by UUID */
9237 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9238 if (FAILED(rc))
9239 {
9240 if (i_isSnapshotMachine())
9241 {
9242 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9243 // so the user knows that the bad disk is in a snapshot somewhere
9244 com::ErrorInfo info;
9245 return setError(E_FAIL,
9246 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9247 puuidSnapshot->raw(),
9248 info.getText().raw());
9249 }
9250 else
9251 return rc;
9252 }
9253
9254 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9255
9256 if (medium->i_getType() == MediumType_Immutable)
9257 {
9258 if (i_isSnapshotMachine())
9259 return setError(E_FAIL,
9260 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9261 "of the virtual machine '%s' ('%s')"),
9262 medium->i_getLocationFull().c_str(),
9263 dev.uuid.raw(),
9264 puuidSnapshot->raw(),
9265 mUserData->s.strName.c_str(),
9266 mData->m_strConfigFileFull.c_str());
9267
9268 return setError(E_FAIL,
9269 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9270 medium->i_getLocationFull().c_str(),
9271 dev.uuid.raw(),
9272 mUserData->s.strName.c_str(),
9273 mData->m_strConfigFileFull.c_str());
9274 }
9275
9276 if (medium->i_getType() == MediumType_MultiAttach)
9277 {
9278 if (i_isSnapshotMachine())
9279 return setError(E_FAIL,
9280 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9281 "of the virtual machine '%s' ('%s')"),
9282 medium->i_getLocationFull().c_str(),
9283 dev.uuid.raw(),
9284 puuidSnapshot->raw(),
9285 mUserData->s.strName.c_str(),
9286 mData->m_strConfigFileFull.c_str());
9287
9288 return setError(E_FAIL,
9289 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9290 medium->i_getLocationFull().c_str(),
9291 dev.uuid.raw(),
9292 mUserData->s.strName.c_str(),
9293 mData->m_strConfigFileFull.c_str());
9294 }
9295
9296 if ( !i_isSnapshotMachine()
9297 && medium->i_getChildren().size() != 0
9298 )
9299 return setError(E_FAIL,
9300 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9301 "because it has %d differencing child hard disks"),
9302 medium->i_getLocationFull().c_str(),
9303 dev.uuid.raw(),
9304 mUserData->s.strName.c_str(),
9305 mData->m_strConfigFileFull.c_str(),
9306 medium->i_getChildren().size());
9307
9308 if (i_findAttachment(mMediaData->mAttachments,
9309 medium))
9310 return setError(E_FAIL,
9311 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9312 medium->i_getLocationFull().c_str(),
9313 dev.uuid.raw(),
9314 mUserData->s.strName.c_str(),
9315 mData->m_strConfigFileFull.c_str());
9316
9317 break;
9318 }
9319
9320 default:
9321 return setError(E_FAIL,
9322 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9323 medium->i_getLocationFull().c_str(),
9324 mUserData->s.strName.c_str(),
9325 mData->m_strConfigFileFull.c_str());
9326 }
9327
9328 if (FAILED(rc))
9329 break;
9330
9331 /* Bandwidth groups are loaded at this point. */
9332 ComObjPtr<BandwidthGroup> pBwGroup;
9333
9334 if (!dev.strBwGroup.isEmpty())
9335 {
9336 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9337 if (FAILED(rc))
9338 return setError(E_FAIL,
9339 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9340 medium->i_getLocationFull().c_str(),
9341 dev.strBwGroup.c_str(),
9342 mUserData->s.strName.c_str(),
9343 mData->m_strConfigFileFull.c_str());
9344 pBwGroup->i_reference();
9345 }
9346
9347 const Bstr controllerName = aStorageController->i_getName();
9348 ComObjPtr<MediumAttachment> pAttachment;
9349 pAttachment.createObject();
9350 rc = pAttachment->init(this,
9351 medium,
9352 controllerName,
9353 dev.lPort,
9354 dev.lDevice,
9355 dev.deviceType,
9356 false,
9357 dev.fPassThrough,
9358 dev.fTempEject,
9359 dev.fNonRotational,
9360 dev.fDiscard,
9361 dev.fHotPluggable,
9362 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9363 if (FAILED(rc)) break;
9364
9365 /* associate the medium with this machine and snapshot */
9366 if (!medium.isNull())
9367 {
9368 AutoCaller medCaller(medium);
9369 if (FAILED(medCaller.rc())) return medCaller.rc();
9370 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9371
9372 if (i_isSnapshotMachine())
9373 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9374 else
9375 rc = medium->i_addBackReference(mData->mUuid);
9376 /* If the medium->addBackReference fails it sets an appropriate
9377 * error message, so no need to do any guesswork here. */
9378
9379 if (puuidRegistry)
9380 // caller wants registry ID to be set on all attached media (OVF import case)
9381 medium->i_addRegistry(*puuidRegistry);
9382 }
9383
9384 if (FAILED(rc))
9385 break;
9386
9387 /* back up mMediaData to let registeredInit() properly rollback on failure
9388 * (= limited accessibility) */
9389 i_setModified(IsModified_Storage);
9390 mMediaData.backup();
9391 mMediaData->mAttachments.push_back(pAttachment);
9392 }
9393
9394 return rc;
9395}
9396
9397/**
9398 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9399 *
9400 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9401 * @param aSnapshot where to return the found snapshot
9402 * @param aSetError true to set extended error info on failure
9403 */
9404HRESULT Machine::i_findSnapshotById(const Guid &aId,
9405 ComObjPtr<Snapshot> &aSnapshot,
9406 bool aSetError /* = false */)
9407{
9408 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9409
9410 if (!mData->mFirstSnapshot)
9411 {
9412 if (aSetError)
9413 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9414 return E_FAIL;
9415 }
9416
9417 if (aId.isZero())
9418 aSnapshot = mData->mFirstSnapshot;
9419 else
9420 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9421
9422 if (!aSnapshot)
9423 {
9424 if (aSetError)
9425 return setError(E_FAIL,
9426 tr("Could not find a snapshot with UUID {%s}"),
9427 aId.toString().c_str());
9428 return E_FAIL;
9429 }
9430
9431 return S_OK;
9432}
9433
9434/**
9435 * Returns the snapshot with the given name or fails of no such snapshot.
9436 *
9437 * @param aName snapshot name to find
9438 * @param aSnapshot where to return the found snapshot
9439 * @param aSetError true to set extended error info on failure
9440 */
9441HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9442 ComObjPtr<Snapshot> &aSnapshot,
9443 bool aSetError /* = false */)
9444{
9445 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9446
9447 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9448
9449 if (!mData->mFirstSnapshot)
9450 {
9451 if (aSetError)
9452 return setError(VBOX_E_OBJECT_NOT_FOUND,
9453 tr("This machine does not have any snapshots"));
9454 return VBOX_E_OBJECT_NOT_FOUND;
9455 }
9456
9457 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9458
9459 if (!aSnapshot)
9460 {
9461 if (aSetError)
9462 return setError(VBOX_E_OBJECT_NOT_FOUND,
9463 tr("Could not find a snapshot named '%s'"), strName.c_str());
9464 return VBOX_E_OBJECT_NOT_FOUND;
9465 }
9466
9467 return S_OK;
9468}
9469
9470/**
9471 * Returns a storage controller object with the given name.
9472 *
9473 * @param aName storage controller name to find
9474 * @param aStorageController where to return the found storage controller
9475 * @param aSetError true to set extended error info on failure
9476 */
9477HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9478 ComObjPtr<StorageController> &aStorageController,
9479 bool aSetError /* = false */)
9480{
9481 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9482
9483 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9484 it != mStorageControllers->end();
9485 ++it)
9486 {
9487 if ((*it)->i_getName() == aName)
9488 {
9489 aStorageController = (*it);
9490 return S_OK;
9491 }
9492 }
9493
9494 if (aSetError)
9495 return setError(VBOX_E_OBJECT_NOT_FOUND,
9496 tr("Could not find a storage controller named '%s'"),
9497 aName.c_str());
9498 return VBOX_E_OBJECT_NOT_FOUND;
9499}
9500
9501/**
9502 * Returns a USB controller object with the given name.
9503 *
9504 * @param aName USB controller name to find
9505 * @param aUSBController where to return the found USB controller
9506 * @param aSetError true to set extended error info on failure
9507 */
9508HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9509 ComObjPtr<USBController> &aUSBController,
9510 bool aSetError /* = false */)
9511{
9512 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9513
9514 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9515 it != mUSBControllers->end();
9516 ++it)
9517 {
9518 if ((*it)->i_getName() == aName)
9519 {
9520 aUSBController = (*it);
9521 return S_OK;
9522 }
9523 }
9524
9525 if (aSetError)
9526 return setError(VBOX_E_OBJECT_NOT_FOUND,
9527 tr("Could not find a storage controller named '%s'"),
9528 aName.c_str());
9529 return VBOX_E_OBJECT_NOT_FOUND;
9530}
9531
9532/**
9533 * Returns the number of USB controller instance of the given type.
9534 *
9535 * @param enmType USB controller type.
9536 */
9537ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9538{
9539 ULONG cCtrls = 0;
9540
9541 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9542 it != mUSBControllers->end();
9543 ++it)
9544 {
9545 if ((*it)->i_getControllerType() == enmType)
9546 cCtrls++;
9547 }
9548
9549 return cCtrls;
9550}
9551
9552HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9553 MediaData::AttachmentList &atts)
9554{
9555 AutoCaller autoCaller(this);
9556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9557
9558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9559
9560 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9561 it != mMediaData->mAttachments.end();
9562 ++it)
9563 {
9564 const ComObjPtr<MediumAttachment> &pAtt = *it;
9565 // should never happen, but deal with NULL pointers in the list.
9566 AssertStmt(!pAtt.isNull(), continue);
9567
9568 // getControllerName() needs caller+read lock
9569 AutoCaller autoAttCaller(pAtt);
9570 if (FAILED(autoAttCaller.rc()))
9571 {
9572 atts.clear();
9573 return autoAttCaller.rc();
9574 }
9575 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9576
9577 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9578 atts.push_back(pAtt);
9579 }
9580
9581 return S_OK;
9582}
9583
9584
9585/**
9586 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9587 * file if the machine name was changed and about creating a new settings file
9588 * if this is a new machine.
9589 *
9590 * @note Must be never called directly but only from #saveSettings().
9591 */
9592HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9593{
9594 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9595
9596 HRESULT rc = S_OK;
9597
9598 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9599
9600 /// @todo need to handle primary group change, too
9601
9602 /* attempt to rename the settings file if machine name is changed */
9603 if ( mUserData->s.fNameSync
9604 && mUserData.isBackedUp()
9605 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9606 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9607 )
9608 {
9609 bool dirRenamed = false;
9610 bool fileRenamed = false;
9611
9612 Utf8Str configFile, newConfigFile;
9613 Utf8Str configFilePrev, newConfigFilePrev;
9614 Utf8Str configDir, newConfigDir;
9615
9616 do
9617 {
9618 int vrc = VINF_SUCCESS;
9619
9620 Utf8Str name = mUserData.backedUpData()->s.strName;
9621 Utf8Str newName = mUserData->s.strName;
9622 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9623 if (group == "/")
9624 group.setNull();
9625 Utf8Str newGroup = mUserData->s.llGroups.front();
9626 if (newGroup == "/")
9627 newGroup.setNull();
9628
9629 configFile = mData->m_strConfigFileFull;
9630
9631 /* first, rename the directory if it matches the group and machine name */
9632 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9633 group.c_str(), RTPATH_DELIMITER, name.c_str());
9634 /** @todo hack, make somehow use of ComposeMachineFilename */
9635 if (mUserData->s.fDirectoryIncludesUUID)
9636 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9637 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9638 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9639 /** @todo hack, make somehow use of ComposeMachineFilename */
9640 if (mUserData->s.fDirectoryIncludesUUID)
9641 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9642 configDir = configFile;
9643 configDir.stripFilename();
9644 newConfigDir = configDir;
9645 if ( configDir.length() >= groupPlusName.length()
9646 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9647 groupPlusName.c_str()))
9648 {
9649 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9650 Utf8Str newConfigBaseDir(newConfigDir);
9651 newConfigDir.append(newGroupPlusName);
9652 /* consistency: use \ if appropriate on the platform */
9653 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9654 /* new dir and old dir cannot be equal here because of 'if'
9655 * above and because name != newName */
9656 Assert(configDir != newConfigDir);
9657 if (!fSettingsFileIsNew)
9658 {
9659 /* perform real rename only if the machine is not new */
9660 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9661 if ( vrc == VERR_FILE_NOT_FOUND
9662 || vrc == VERR_PATH_NOT_FOUND)
9663 {
9664 /* create the parent directory, then retry renaming */
9665 Utf8Str parent(newConfigDir);
9666 parent.stripFilename();
9667 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9668 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9669 }
9670 if (RT_FAILURE(vrc))
9671 {
9672 rc = setError(E_FAIL,
9673 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9674 configDir.c_str(),
9675 newConfigDir.c_str(),
9676 vrc);
9677 break;
9678 }
9679 /* delete subdirectories which are no longer needed */
9680 Utf8Str dir(configDir);
9681 dir.stripFilename();
9682 while (dir != newConfigBaseDir && dir != ".")
9683 {
9684 vrc = RTDirRemove(dir.c_str());
9685 if (RT_FAILURE(vrc))
9686 break;
9687 dir.stripFilename();
9688 }
9689 dirRenamed = true;
9690 }
9691 }
9692
9693 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9694 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9695
9696 /* then try to rename the settings file itself */
9697 if (newConfigFile != configFile)
9698 {
9699 /* get the path to old settings file in renamed directory */
9700 configFile = Utf8StrFmt("%s%c%s",
9701 newConfigDir.c_str(),
9702 RTPATH_DELIMITER,
9703 RTPathFilename(configFile.c_str()));
9704 if (!fSettingsFileIsNew)
9705 {
9706 /* perform real rename only if the machine is not new */
9707 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9708 if (RT_FAILURE(vrc))
9709 {
9710 rc = setError(E_FAIL,
9711 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9712 configFile.c_str(),
9713 newConfigFile.c_str(),
9714 vrc);
9715 break;
9716 }
9717 fileRenamed = true;
9718 configFilePrev = configFile;
9719 configFilePrev += "-prev";
9720 newConfigFilePrev = newConfigFile;
9721 newConfigFilePrev += "-prev";
9722 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9723 }
9724 }
9725
9726 // update m_strConfigFileFull amd mConfigFile
9727 mData->m_strConfigFileFull = newConfigFile;
9728 // compute the relative path too
9729 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9730
9731 // store the old and new so that VirtualBox::i_saveSettings() can update
9732 // the media registry
9733 if ( mData->mRegistered
9734 && (configDir != newConfigDir || configFile != newConfigFile))
9735 {
9736 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9737
9738 if (pfNeedsGlobalSaveSettings)
9739 *pfNeedsGlobalSaveSettings = true;
9740 }
9741
9742 // in the saved state file path, replace the old directory with the new directory
9743 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9744 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9745
9746 // and do the same thing for the saved state file paths of all the online snapshots
9747 if (mData->mFirstSnapshot)
9748 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9749 newConfigDir.c_str());
9750 }
9751 while (0);
9752
9753 if (FAILED(rc))
9754 {
9755 /* silently try to rename everything back */
9756 if (fileRenamed)
9757 {
9758 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9759 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9760 }
9761 if (dirRenamed)
9762 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9763 }
9764
9765 if (FAILED(rc)) return rc;
9766 }
9767
9768 if (fSettingsFileIsNew)
9769 {
9770 /* create a virgin config file */
9771 int vrc = VINF_SUCCESS;
9772
9773 /* ensure the settings directory exists */
9774 Utf8Str path(mData->m_strConfigFileFull);
9775 path.stripFilename();
9776 if (!RTDirExists(path.c_str()))
9777 {
9778 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9779 if (RT_FAILURE(vrc))
9780 {
9781 return setError(E_FAIL,
9782 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9783 path.c_str(),
9784 vrc);
9785 }
9786 }
9787
9788 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9789 path = Utf8Str(mData->m_strConfigFileFull);
9790 RTFILE f = NIL_RTFILE;
9791 vrc = RTFileOpen(&f, path.c_str(),
9792 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9793 if (RT_FAILURE(vrc))
9794 return setError(E_FAIL,
9795 tr("Could not create the settings file '%s' (%Rrc)"),
9796 path.c_str(),
9797 vrc);
9798 RTFileClose(f);
9799 }
9800
9801 return rc;
9802}
9803
9804/**
9805 * Saves and commits machine data, user data and hardware data.
9806 *
9807 * Note that on failure, the data remains uncommitted.
9808 *
9809 * @a aFlags may combine the following flags:
9810 *
9811 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9812 * Used when saving settings after an operation that makes them 100%
9813 * correspond to the settings from the current snapshot.
9814 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9815 * #isReallyModified() returns false. This is necessary for cases when we
9816 * change machine data directly, not through the backup()/commit() mechanism.
9817 * - SaveS_Force: settings will be saved without doing a deep compare of the
9818 * settings structures. This is used when this is called because snapshots
9819 * have changed to avoid the overhead of the deep compare.
9820 *
9821 * @note Must be called from under this object's write lock. Locks children for
9822 * writing.
9823 *
9824 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9825 * initialized to false and that will be set to true by this function if
9826 * the caller must invoke VirtualBox::i_saveSettings() because the global
9827 * settings have changed. This will happen if a machine rename has been
9828 * saved and the global machine and media registries will therefore need
9829 * updating.
9830 */
9831HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9832 int aFlags /*= 0*/)
9833{
9834 LogFlowThisFuncEnter();
9835
9836 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9837
9838 /* make sure child objects are unable to modify the settings while we are
9839 * saving them */
9840 i_ensureNoStateDependencies();
9841
9842 AssertReturn(!i_isSnapshotMachine(),
9843 E_FAIL);
9844
9845 HRESULT rc = S_OK;
9846 bool fNeedsWrite = false;
9847
9848 /* First, prepare to save settings. It will care about renaming the
9849 * settings directory and file if the machine name was changed and about
9850 * creating a new settings file if this is a new machine. */
9851 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9852 if (FAILED(rc)) return rc;
9853
9854 // keep a pointer to the current settings structures
9855 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9856 settings::MachineConfigFile *pNewConfig = NULL;
9857
9858 try
9859 {
9860 // make a fresh one to have everyone write stuff into
9861 pNewConfig = new settings::MachineConfigFile(NULL);
9862 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9863
9864 // now go and copy all the settings data from COM to the settings structures
9865 // (this calles i_saveSettings() on all the COM objects in the machine)
9866 i_copyMachineDataToSettings(*pNewConfig);
9867
9868 if (aFlags & SaveS_ResetCurStateModified)
9869 {
9870 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9871 mData->mCurrentStateModified = FALSE;
9872 fNeedsWrite = true; // always, no need to compare
9873 }
9874 else if (aFlags & SaveS_Force)
9875 {
9876 fNeedsWrite = true; // always, no need to compare
9877 }
9878 else
9879 {
9880 if (!mData->mCurrentStateModified)
9881 {
9882 // do a deep compare of the settings that we just saved with the settings
9883 // previously stored in the config file; this invokes MachineConfigFile::operator==
9884 // which does a deep compare of all the settings, which is expensive but less expensive
9885 // than writing out XML in vain
9886 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9887
9888 // could still be modified if any settings changed
9889 mData->mCurrentStateModified = fAnySettingsChanged;
9890
9891 fNeedsWrite = fAnySettingsChanged;
9892 }
9893 else
9894 fNeedsWrite = true;
9895 }
9896
9897 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9898
9899 if (fNeedsWrite)
9900 // now spit it all out!
9901 pNewConfig->write(mData->m_strConfigFileFull);
9902
9903 mData->pMachineConfigFile = pNewConfig;
9904 delete pOldConfig;
9905 i_commit();
9906
9907 // after saving settings, we are no longer different from the XML on disk
9908 mData->flModifications = 0;
9909 }
9910 catch (HRESULT err)
9911 {
9912 // we assume that error info is set by the thrower
9913 rc = err;
9914
9915 // restore old config
9916 delete pNewConfig;
9917 mData->pMachineConfigFile = pOldConfig;
9918 }
9919 catch (...)
9920 {
9921 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9922 }
9923
9924 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9925 {
9926 /* Fire the data change event, even on failure (since we've already
9927 * committed all data). This is done only for SessionMachines because
9928 * mutable Machine instances are always not registered (i.e. private
9929 * to the client process that creates them) and thus don't need to
9930 * inform callbacks. */
9931 if (i_isSessionMachine())
9932 mParent->i_onMachineDataChange(mData->mUuid);
9933 }
9934
9935 LogFlowThisFunc(("rc=%08X\n", rc));
9936 LogFlowThisFuncLeave();
9937 return rc;
9938}
9939
9940/**
9941 * Implementation for saving the machine settings into the given
9942 * settings::MachineConfigFile instance. This copies machine extradata
9943 * from the previous machine config file in the instance data, if any.
9944 *
9945 * This gets called from two locations:
9946 *
9947 * -- Machine::i_saveSettings(), during the regular XML writing;
9948 *
9949 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9950 * exported to OVF and we write the VirtualBox proprietary XML
9951 * into a <vbox:Machine> tag.
9952 *
9953 * This routine fills all the fields in there, including snapshots, *except*
9954 * for the following:
9955 *
9956 * -- fCurrentStateModified. There is some special logic associated with that.
9957 *
9958 * The caller can then call MachineConfigFile::write() or do something else
9959 * with it.
9960 *
9961 * Caller must hold the machine lock!
9962 *
9963 * This throws XML errors and HRESULT, so the caller must have a catch block!
9964 */
9965void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9966{
9967 // deep copy extradata
9968 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9969
9970 config.uuid = mData->mUuid;
9971
9972 // copy name, description, OS type, teleport, UTC etc.
9973 config.machineUserData = mUserData->s;
9974
9975 // Encode the Icon Override data from Machine and store on config userdata.
9976 std::vector<BYTE> iconByte;
9977 getIcon(iconByte);
9978 ssize_t cbData = iconByte.size();
9979 if (cbData > 0)
9980 {
9981 ssize_t cchOut = RTBase64EncodedLength(cbData);
9982 Utf8Str strIconData;
9983 strIconData.reserve(cchOut+1);
9984 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9985 strIconData.mutableRaw(), strIconData.capacity(),
9986 NULL);
9987 if (RT_FAILURE(vrc))
9988 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9989 strIconData.jolt();
9990 config.machineUserData.ovIcon = strIconData;
9991 }
9992 else
9993 config.machineUserData.ovIcon.setNull();
9994
9995 if ( mData->mMachineState == MachineState_Saved
9996 || mData->mMachineState == MachineState_Restoring
9997 // when doing certain snapshot operations we may or may not have
9998 // a saved state in the current state, so keep everything as is
9999 || ( ( mData->mMachineState == MachineState_Snapshotting
10000 || mData->mMachineState == MachineState_DeletingSnapshot)
10001 && (!mSSData->strStateFilePath.isEmpty())
10002 )
10003 )
10004 {
10005 Assert(!mSSData->strStateFilePath.isEmpty());
10006 /* try to make the file name relative to the settings file dir */
10007 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10008 }
10009 else
10010 {
10011 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10012 config.strStateFile.setNull();
10013 }
10014
10015 if (mData->mCurrentSnapshot)
10016 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10017 else
10018 config.uuidCurrentSnapshot.clear();
10019
10020 config.timeLastStateChange = mData->mLastStateChange;
10021 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10022 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10023
10024 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10025 if (FAILED(rc)) throw rc;
10026
10027 rc = i_saveStorageControllers(config.storageMachine);
10028 if (FAILED(rc)) throw rc;
10029
10030 // save machine's media registry if this is VirtualBox 4.0 or later
10031 if (config.canHaveOwnMediaRegistry())
10032 {
10033 // determine machine folder
10034 Utf8Str strMachineFolder = i_getSettingsFileFull();
10035 strMachineFolder.stripFilename();
10036 mParent->i_saveMediaRegistry(config.mediaRegistry,
10037 i_getId(), // only media with registry ID == machine UUID
10038 strMachineFolder);
10039 // this throws HRESULT
10040 }
10041
10042 // save snapshots
10043 rc = i_saveAllSnapshots(config);
10044 if (FAILED(rc)) throw rc;
10045}
10046
10047/**
10048 * Saves all snapshots of the machine into the given machine config file. Called
10049 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10050 * @param config
10051 * @return
10052 */
10053HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10054{
10055 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10056
10057 HRESULT rc = S_OK;
10058
10059 try
10060 {
10061 config.llFirstSnapshot.clear();
10062
10063 if (mData->mFirstSnapshot)
10064 {
10065 // the settings use a list for "the first snapshot"
10066 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10067
10068 // get reference to the snapshot on the list and work on that
10069 // element straight in the list to avoid excessive copying later
10070 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10071 if (FAILED(rc)) throw rc;
10072 }
10073
10074// if (mType == IsSessionMachine)
10075// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10076
10077 }
10078 catch (HRESULT err)
10079 {
10080 /* we assume that error info is set by the thrower */
10081 rc = err;
10082 }
10083 catch (...)
10084 {
10085 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10086 }
10087
10088 return rc;
10089}
10090
10091/**
10092 * Saves the VM hardware configuration. It is assumed that the
10093 * given node is empty.
10094 *
10095 * @param data Reference to the settings object for the hardware config.
10096 * @param pDbg Pointer to the settings object for the debugging config
10097 * which happens to live in mHWData.
10098 * @param pAutostart Pointer to the settings object for the autostart config
10099 * which happens to live in mHWData.
10100 */
10101HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10102 settings::Autostart *pAutostart)
10103{
10104 HRESULT rc = S_OK;
10105
10106 try
10107 {
10108 /* The hardware version attribute (optional).
10109 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10110 if ( mHWData->mHWVersion == "1"
10111 && mSSData->strStateFilePath.isEmpty()
10112 )
10113 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10114 other point needs to be found where this can be done. */
10115
10116 data.strVersion = mHWData->mHWVersion;
10117 data.uuid = mHWData->mHardwareUUID;
10118
10119 // CPU
10120 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10121 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10122 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10123 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10124 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10125 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10126 data.fPAE = !!mHWData->mPAEEnabled;
10127 data.enmLongMode = mHWData->mLongMode;
10128 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10129 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10130
10131 /* Standard and Extended CPUID leafs. */
10132 data.llCpuIdLeafs.clear();
10133 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10134 {
10135 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10136 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10137 }
10138 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10139 {
10140 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10141 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10142 }
10143
10144 data.cCPUs = mHWData->mCPUCount;
10145 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10146 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10147
10148 data.llCpus.clear();
10149 if (data.fCpuHotPlug)
10150 {
10151 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10152 {
10153 if (mHWData->mCPUAttached[idx])
10154 {
10155 settings::Cpu cpu;
10156 cpu.ulId = idx;
10157 data.llCpus.push_back(cpu);
10158 }
10159 }
10160 }
10161
10162 // memory
10163 data.ulMemorySizeMB = mHWData->mMemorySize;
10164 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10165
10166 // firmware
10167 data.firmwareType = mHWData->mFirmwareType;
10168
10169 // HID
10170 data.pointingHIDType = mHWData->mPointingHIDType;
10171 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10172
10173 // chipset
10174 data.chipsetType = mHWData->mChipsetType;
10175
10176 // paravirt
10177 data.paravirtProvider = mHWData->mParavirtProvider;
10178
10179
10180 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10181
10182 // HPET
10183 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10184
10185 // boot order
10186 data.mapBootOrder.clear();
10187 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10188 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10189
10190 // display
10191 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10192 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10193 data.cMonitors = mHWData->mMonitorCount;
10194 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10195 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10196 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10197 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10198 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10199 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10200 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10201 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10202 {
10203 if (mHWData->maVideoCaptureScreens[i])
10204 ASMBitSet(&data.u64VideoCaptureScreens, i);
10205 else
10206 ASMBitClear(&data.u64VideoCaptureScreens, i);
10207 }
10208 /* store relative video capture file if possible */
10209 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10210
10211 /* VRDEServer settings (optional) */
10212 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10213 if (FAILED(rc)) throw rc;
10214
10215 /* BIOS (required) */
10216 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10217 if (FAILED(rc)) throw rc;
10218
10219 /* USB Controller (required) */
10220 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10221 {
10222 ComObjPtr<USBController> ctrl = *it;
10223 settings::USBController settingsCtrl;
10224
10225 settingsCtrl.strName = ctrl->i_getName();
10226 settingsCtrl.enmType = ctrl->i_getControllerType();
10227
10228 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10229 }
10230
10231 /* USB device filters (required) */
10232 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10233 if (FAILED(rc)) throw rc;
10234
10235 /* Network adapters (required) */
10236 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10237 data.llNetworkAdapters.clear();
10238 /* Write out only the nominal number of network adapters for this
10239 * chipset type. Since Machine::commit() hasn't been called there
10240 * may be extra NIC settings in the vector. */
10241 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10242 {
10243 settings::NetworkAdapter nic;
10244 nic.ulSlot = (uint32_t)slot;
10245 /* paranoia check... must not be NULL, but must not crash either. */
10246 if (mNetworkAdapters[slot])
10247 {
10248 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10249 if (FAILED(rc)) throw rc;
10250
10251 data.llNetworkAdapters.push_back(nic);
10252 }
10253 }
10254
10255 /* Serial ports */
10256 data.llSerialPorts.clear();
10257 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10258 {
10259 settings::SerialPort s;
10260 s.ulSlot = slot;
10261 rc = mSerialPorts[slot]->i_saveSettings(s);
10262 if (FAILED(rc)) return rc;
10263
10264 data.llSerialPorts.push_back(s);
10265 }
10266
10267 /* Parallel ports */
10268 data.llParallelPorts.clear();
10269 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10270 {
10271 settings::ParallelPort p;
10272 p.ulSlot = slot;
10273 rc = mParallelPorts[slot]->i_saveSettings(p);
10274 if (FAILED(rc)) return rc;
10275
10276 data.llParallelPorts.push_back(p);
10277 }
10278
10279 /* Audio adapter */
10280 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10281 if (FAILED(rc)) return rc;
10282
10283 /* Shared folders */
10284 data.llSharedFolders.clear();
10285 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10286 it != mHWData->mSharedFolders.end();
10287 ++it)
10288 {
10289 SharedFolder *pSF = *it;
10290 AutoCaller sfCaller(pSF);
10291 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10292 settings::SharedFolder sf;
10293 sf.strName = pSF->i_getName();
10294 sf.strHostPath = pSF->i_getHostPath();
10295 sf.fWritable = !!pSF->i_isWritable();
10296 sf.fAutoMount = !!pSF->i_isAutoMounted();
10297
10298 data.llSharedFolders.push_back(sf);
10299 }
10300
10301 // clipboard
10302 data.clipboardMode = mHWData->mClipboardMode;
10303
10304 // drag'n'drop
10305 data.dndMode = mHWData->mDnDMode;
10306
10307 /* Guest */
10308 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10309
10310 // IO settings
10311 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10312 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10313
10314 /* BandwidthControl (required) */
10315 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10316 if (FAILED(rc)) throw rc;
10317
10318 /* Host PCI devices */
10319 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10320 it != mHWData->mPCIDeviceAssignments.end();
10321 ++it)
10322 {
10323 ComObjPtr<PCIDeviceAttachment> pda = *it;
10324 settings::HostPCIDeviceAttachment hpda;
10325
10326 rc = pda->i_saveSettings(hpda);
10327 if (FAILED(rc)) throw rc;
10328
10329 data.pciAttachments.push_back(hpda);
10330 }
10331
10332
10333 // guest properties
10334 data.llGuestProperties.clear();
10335#ifdef VBOX_WITH_GUEST_PROPS
10336 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10337 it != mHWData->mGuestProperties.end();
10338 ++it)
10339 {
10340 HWData::GuestProperty property = it->second;
10341
10342 /* Remove transient guest properties at shutdown unless we
10343 * are saving state */
10344 if ( ( mData->mMachineState == MachineState_PoweredOff
10345 || mData->mMachineState == MachineState_Aborted
10346 || mData->mMachineState == MachineState_Teleported)
10347 && ( property.mFlags & guestProp::TRANSIENT
10348 || property.mFlags & guestProp::TRANSRESET))
10349 continue;
10350 settings::GuestProperty prop;
10351 prop.strName = it->first;
10352 prop.strValue = property.strValue;
10353 prop.timestamp = property.mTimestamp;
10354 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10355 guestProp::writeFlags(property.mFlags, szFlags);
10356 prop.strFlags = szFlags;
10357
10358 data.llGuestProperties.push_back(prop);
10359 }
10360
10361 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10362 /* I presume this doesn't require a backup(). */
10363 mData->mGuestPropertiesModified = FALSE;
10364#endif /* VBOX_WITH_GUEST_PROPS defined */
10365
10366 *pDbg = mHWData->mDebugging;
10367 *pAutostart = mHWData->mAutostart;
10368
10369 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10370 }
10371 catch(std::bad_alloc &)
10372 {
10373 return E_OUTOFMEMORY;
10374 }
10375
10376 AssertComRC(rc);
10377 return rc;
10378}
10379
10380/**
10381 * Saves the storage controller configuration.
10382 *
10383 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10384 */
10385HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10386{
10387 data.llStorageControllers.clear();
10388
10389 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10390 it != mStorageControllers->end();
10391 ++it)
10392 {
10393 HRESULT rc;
10394 ComObjPtr<StorageController> pCtl = *it;
10395
10396 settings::StorageController ctl;
10397 ctl.strName = pCtl->i_getName();
10398 ctl.controllerType = pCtl->i_getControllerType();
10399 ctl.storageBus = pCtl->i_getStorageBus();
10400 ctl.ulInstance = pCtl->i_getInstance();
10401 ctl.fBootable = pCtl->i_getBootable();
10402
10403 /* Save the port count. */
10404 ULONG portCount;
10405 rc = pCtl->COMGETTER(PortCount)(&portCount);
10406 ComAssertComRCRet(rc, rc);
10407 ctl.ulPortCount = portCount;
10408
10409 /* Save fUseHostIOCache */
10410 BOOL fUseHostIOCache;
10411 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10412 ComAssertComRCRet(rc, rc);
10413 ctl.fUseHostIOCache = !!fUseHostIOCache;
10414
10415 /* Save IDE emulation settings. */
10416 if (ctl.controllerType == StorageControllerType_IntelAhci)
10417 {
10418 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10419 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10420 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10421 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10422 )
10423 ComAssertComRCRet(rc, rc);
10424 }
10425
10426 /* save the devices now. */
10427 rc = i_saveStorageDevices(pCtl, ctl);
10428 ComAssertComRCRet(rc, rc);
10429
10430 data.llStorageControllers.push_back(ctl);
10431 }
10432
10433 return S_OK;
10434}
10435
10436/**
10437 * Saves the hard disk configuration.
10438 */
10439HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10440 settings::StorageController &data)
10441{
10442 MediaData::AttachmentList atts;
10443
10444 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10445 if (FAILED(rc)) return rc;
10446
10447 data.llAttachedDevices.clear();
10448 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10449 it != atts.end();
10450 ++it)
10451 {
10452 settings::AttachedDevice dev;
10453 IMediumAttachment *iA = *it;
10454 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10455 Medium *pMedium = pAttach->i_getMedium();
10456
10457 dev.deviceType = pAttach->i_getType();
10458 dev.lPort = pAttach->i_getPort();
10459 dev.lDevice = pAttach->i_getDevice();
10460 dev.fPassThrough = pAttach->i_getPassthrough();
10461 dev.fHotPluggable = pAttach->i_getHotPluggable();
10462 if (pMedium)
10463 {
10464 if (pMedium->i_isHostDrive())
10465 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10466 else
10467 dev.uuid = pMedium->i_getId();
10468 dev.fTempEject = pAttach->i_getTempEject();
10469 dev.fNonRotational = pAttach->i_getNonRotational();
10470 dev.fDiscard = pAttach->i_getDiscard();
10471 }
10472
10473 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10474
10475 data.llAttachedDevices.push_back(dev);
10476 }
10477
10478 return S_OK;
10479}
10480
10481/**
10482 * Saves machine state settings as defined by aFlags
10483 * (SaveSTS_* values).
10484 *
10485 * @param aFlags Combination of SaveSTS_* flags.
10486 *
10487 * @note Locks objects for writing.
10488 */
10489HRESULT Machine::i_saveStateSettings(int aFlags)
10490{
10491 if (aFlags == 0)
10492 return S_OK;
10493
10494 AutoCaller autoCaller(this);
10495 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10496
10497 /* This object's write lock is also necessary to serialize file access
10498 * (prevent concurrent reads and writes) */
10499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10500
10501 HRESULT rc = S_OK;
10502
10503 Assert(mData->pMachineConfigFile);
10504
10505 try
10506 {
10507 if (aFlags & SaveSTS_CurStateModified)
10508 mData->pMachineConfigFile->fCurrentStateModified = true;
10509
10510 if (aFlags & SaveSTS_StateFilePath)
10511 {
10512 if (!mSSData->strStateFilePath.isEmpty())
10513 /* try to make the file name relative to the settings file dir */
10514 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10515 else
10516 mData->pMachineConfigFile->strStateFile.setNull();
10517 }
10518
10519 if (aFlags & SaveSTS_StateTimeStamp)
10520 {
10521 Assert( mData->mMachineState != MachineState_Aborted
10522 || mSSData->strStateFilePath.isEmpty());
10523
10524 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10525
10526 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10527//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10528 }
10529
10530 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10531 }
10532 catch (...)
10533 {
10534 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10535 }
10536
10537 return rc;
10538}
10539
10540/**
10541 * Ensures that the given medium is added to a media registry. If this machine
10542 * was created with 4.0 or later, then the machine registry is used. Otherwise
10543 * the global VirtualBox media registry is used.
10544 *
10545 * Caller must NOT hold machine lock, media tree or any medium locks!
10546 *
10547 * @param pMedium
10548 */
10549void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10550{
10551 /* Paranoia checks: do not hold machine or media tree locks. */
10552 AssertReturnVoid(!isWriteLockOnCurrentThread());
10553 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10554
10555 ComObjPtr<Medium> pBase;
10556 {
10557 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10558 pBase = pMedium->i_getBase();
10559 }
10560
10561 /* Paranoia checks: do not hold medium locks. */
10562 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10563 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10564
10565 // decide which medium registry to use now that the medium is attached:
10566 Guid uuid;
10567 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10568 // machine XML is VirtualBox 4.0 or higher:
10569 uuid = i_getId(); // machine UUID
10570 else
10571 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10572
10573 if (pMedium->i_addRegistry(uuid))
10574 mParent->i_markRegistryModified(uuid);
10575
10576 /* For more complex hard disk structures it can happen that the base
10577 * medium isn't yet associated with any medium registry. Do that now. */
10578 if (pMedium != pBase)
10579 {
10580 /* Tree lock needed by Medium::addRegistry when recursing. */
10581 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10582 if (pBase->i_addRegistryRecursive(uuid))
10583 {
10584 treeLock.release();
10585 mParent->i_markRegistryModified(uuid);
10586 }
10587 }
10588}
10589
10590/**
10591 * Creates differencing hard disks for all normal hard disks attached to this
10592 * machine and a new set of attachments to refer to created disks.
10593 *
10594 * Used when taking a snapshot or when deleting the current state. Gets called
10595 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10596 *
10597 * This method assumes that mMediaData contains the original hard disk attachments
10598 * it needs to create diffs for. On success, these attachments will be replaced
10599 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10600 * called to delete created diffs which will also rollback mMediaData and restore
10601 * whatever was backed up before calling this method.
10602 *
10603 * Attachments with non-normal hard disks are left as is.
10604 *
10605 * If @a aOnline is @c false then the original hard disks that require implicit
10606 * diffs will be locked for reading. Otherwise it is assumed that they are
10607 * already locked for writing (when the VM was started). Note that in the latter
10608 * case it is responsibility of the caller to lock the newly created diffs for
10609 * writing if this method succeeds.
10610 *
10611 * @param aProgress Progress object to run (must contain at least as
10612 * many operations left as the number of hard disks
10613 * attached).
10614 * @param aOnline Whether the VM was online prior to this operation.
10615 *
10616 * @note The progress object is not marked as completed, neither on success nor
10617 * on failure. This is a responsibility of the caller.
10618 *
10619 * @note Locks this object and the media tree for writing.
10620 */
10621HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10622 ULONG aWeight,
10623 bool aOnline)
10624{
10625 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10626
10627 AutoCaller autoCaller(this);
10628 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10629
10630 AutoMultiWriteLock2 alock(this->lockHandle(),
10631 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10632
10633 /* must be in a protective state because we release the lock below */
10634 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10635 || mData->mMachineState == MachineState_OnlineSnapshotting
10636 || mData->mMachineState == MachineState_LiveSnapshotting
10637 || mData->mMachineState == MachineState_RestoringSnapshot
10638 || mData->mMachineState == MachineState_DeletingSnapshot
10639 , E_FAIL);
10640
10641 HRESULT rc = S_OK;
10642
10643 // use appropriate locked media map (online or offline)
10644 MediumLockListMap lockedMediaOffline;
10645 MediumLockListMap *lockedMediaMap;
10646 if (aOnline)
10647 lockedMediaMap = &mData->mSession.mLockedMedia;
10648 else
10649 lockedMediaMap = &lockedMediaOffline;
10650
10651 try
10652 {
10653 if (!aOnline)
10654 {
10655 /* lock all attached hard disks early to detect "in use"
10656 * situations before creating actual diffs */
10657 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10658 it != mMediaData->mAttachments.end();
10659 ++it)
10660 {
10661 MediumAttachment* pAtt = *it;
10662 if (pAtt->i_getType() == DeviceType_HardDisk)
10663 {
10664 Medium* pMedium = pAtt->i_getMedium();
10665 Assert(pMedium);
10666
10667 MediumLockList *pMediumLockList(new MediumLockList());
10668 alock.release();
10669 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10670 false /* fMediumLockWrite */,
10671 false /* fMediumLockWriteAll */,
10672 NULL,
10673 *pMediumLockList);
10674 alock.acquire();
10675 if (FAILED(rc))
10676 {
10677 delete pMediumLockList;
10678 throw rc;
10679 }
10680 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10681 if (FAILED(rc))
10682 {
10683 throw setError(rc,
10684 tr("Collecting locking information for all attached media failed"));
10685 }
10686 }
10687 }
10688
10689 /* Now lock all media. If this fails, nothing is locked. */
10690 alock.release();
10691 rc = lockedMediaMap->Lock();
10692 alock.acquire();
10693 if (FAILED(rc))
10694 {
10695 throw setError(rc,
10696 tr("Locking of attached media failed"));
10697 }
10698 }
10699
10700 /* remember the current list (note that we don't use backup() since
10701 * mMediaData may be already backed up) */
10702 MediaData::AttachmentList atts = mMediaData->mAttachments;
10703
10704 /* start from scratch */
10705 mMediaData->mAttachments.clear();
10706
10707 /* go through remembered attachments and create diffs for normal hard
10708 * disks and attach them */
10709 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10710 it != atts.end();
10711 ++it)
10712 {
10713 MediumAttachment* pAtt = *it;
10714
10715 DeviceType_T devType = pAtt->i_getType();
10716 Medium* pMedium = pAtt->i_getMedium();
10717
10718 if ( devType != DeviceType_HardDisk
10719 || pMedium == NULL
10720 || pMedium->i_getType() != MediumType_Normal)
10721 {
10722 /* copy the attachment as is */
10723
10724 /** @todo the progress object created in SessionMachine::TakeSnaphot
10725 * only expects operations for hard disks. Later other
10726 * device types need to show up in the progress as well. */
10727 if (devType == DeviceType_HardDisk)
10728 {
10729 if (pMedium == NULL)
10730 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10731 aWeight); // weight
10732 else
10733 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10734 pMedium->i_getBase()->i_getName().c_str()).raw(),
10735 aWeight); // weight
10736 }
10737
10738 mMediaData->mAttachments.push_back(pAtt);
10739 continue;
10740 }
10741
10742 /* need a diff */
10743 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10744 pMedium->i_getBase()->i_getName().c_str()).raw(),
10745 aWeight); // weight
10746
10747 Utf8Str strFullSnapshotFolder;
10748 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10749
10750 ComObjPtr<Medium> diff;
10751 diff.createObject();
10752 // store the diff in the same registry as the parent
10753 // (this cannot fail here because we can't create implicit diffs for
10754 // unregistered images)
10755 Guid uuidRegistryParent;
10756 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10757 Assert(fInRegistry); NOREF(fInRegistry);
10758 rc = diff->init(mParent,
10759 pMedium->i_getPreferredDiffFormat(),
10760 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10761 uuidRegistryParent,
10762 DeviceType_HardDisk);
10763 if (FAILED(rc)) throw rc;
10764
10765 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10766 * the push_back? Looks like we're going to release medium with the
10767 * wrong kind of lock (general issue with if we fail anywhere at all)
10768 * and an orphaned VDI in the snapshots folder. */
10769
10770 /* update the appropriate lock list */
10771 MediumLockList *pMediumLockList;
10772 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10773 AssertComRCThrowRC(rc);
10774 if (aOnline)
10775 {
10776 alock.release();
10777 /* The currently attached medium will be read-only, change
10778 * the lock type to read. */
10779 rc = pMediumLockList->Update(pMedium, false);
10780 alock.acquire();
10781 AssertComRCThrowRC(rc);
10782 }
10783
10784 /* release the locks before the potentially lengthy operation */
10785 alock.release();
10786 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10787 pMediumLockList,
10788 NULL /* aProgress */,
10789 true /* aWait */);
10790 alock.acquire();
10791 if (FAILED(rc)) throw rc;
10792
10793 /* actual lock list update is done in Medium::commitMedia */
10794
10795 rc = diff->i_addBackReference(mData->mUuid);
10796 AssertComRCThrowRC(rc);
10797
10798 /* add a new attachment */
10799 ComObjPtr<MediumAttachment> attachment;
10800 attachment.createObject();
10801 rc = attachment->init(this,
10802 diff,
10803 pAtt->i_getControllerName(),
10804 pAtt->i_getPort(),
10805 pAtt->i_getDevice(),
10806 DeviceType_HardDisk,
10807 true /* aImplicit */,
10808 false /* aPassthrough */,
10809 false /* aTempEject */,
10810 pAtt->i_getNonRotational(),
10811 pAtt->i_getDiscard(),
10812 pAtt->i_getHotPluggable(),
10813 pAtt->i_getBandwidthGroup());
10814 if (FAILED(rc)) throw rc;
10815
10816 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10817 AssertComRCThrowRC(rc);
10818 mMediaData->mAttachments.push_back(attachment);
10819 }
10820 }
10821 catch (HRESULT aRC) { rc = aRC; }
10822
10823 /* unlock all hard disks we locked when there is no VM */
10824 if (!aOnline)
10825 {
10826 ErrorInfoKeeper eik;
10827
10828 HRESULT rc1 = lockedMediaMap->Clear();
10829 AssertComRC(rc1);
10830 }
10831
10832 return rc;
10833}
10834
10835/**
10836 * Deletes implicit differencing hard disks created either by
10837 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10838 *
10839 * Note that to delete hard disks created by #AttachDevice() this method is
10840 * called from #fixupMedia() when the changes are rolled back.
10841 *
10842 * @note Locks this object and the media tree for writing.
10843 */
10844HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10845{
10846 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10847
10848 AutoCaller autoCaller(this);
10849 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10850
10851 AutoMultiWriteLock2 alock(this->lockHandle(),
10852 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10853
10854 /* We absolutely must have backed up state. */
10855 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10856
10857 /* Check if there are any implicitly created diff images. */
10858 bool fImplicitDiffs = false;
10859 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10860 it != mMediaData->mAttachments.end();
10861 ++it)
10862 {
10863 const ComObjPtr<MediumAttachment> &pAtt = *it;
10864 if (pAtt->i_isImplicit())
10865 {
10866 fImplicitDiffs = true;
10867 break;
10868 }
10869 }
10870 /* If there is nothing to do, leave early. This saves lots of image locking
10871 * effort. It also avoids a MachineStateChanged event without real reason.
10872 * This is important e.g. when loading a VM config, because there should be
10873 * no events. Otherwise API clients can become thoroughly confused for
10874 * inaccessible VMs (the code for loading VM configs uses this method for
10875 * cleanup if the config makes no sense), as they take such events as an
10876 * indication that the VM is alive, and they would force the VM config to
10877 * be reread, leading to an endless loop. */
10878 if (!fImplicitDiffs)
10879 return S_OK;
10880
10881 HRESULT rc = S_OK;
10882 MachineState_T oldState = mData->mMachineState;
10883
10884 /* will release the lock before the potentially lengthy operation,
10885 * so protect with the special state (unless already protected) */
10886 if ( oldState != MachineState_Snapshotting
10887 && oldState != MachineState_OnlineSnapshotting
10888 && oldState != MachineState_LiveSnapshotting
10889 && oldState != MachineState_RestoringSnapshot
10890 && oldState != MachineState_DeletingSnapshot
10891 && oldState != MachineState_DeletingSnapshotOnline
10892 && oldState != MachineState_DeletingSnapshotPaused
10893 )
10894 i_setMachineState(MachineState_SettingUp);
10895
10896 // use appropriate locked media map (online or offline)
10897 MediumLockListMap lockedMediaOffline;
10898 MediumLockListMap *lockedMediaMap;
10899 if (aOnline)
10900 lockedMediaMap = &mData->mSession.mLockedMedia;
10901 else
10902 lockedMediaMap = &lockedMediaOffline;
10903
10904 try
10905 {
10906 if (!aOnline)
10907 {
10908 /* lock all attached hard disks early to detect "in use"
10909 * situations before deleting actual diffs */
10910 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10911 it != mMediaData->mAttachments.end();
10912 ++it)
10913 {
10914 MediumAttachment* pAtt = *it;
10915 if (pAtt->i_getType() == DeviceType_HardDisk)
10916 {
10917 Medium* pMedium = pAtt->i_getMedium();
10918 Assert(pMedium);
10919
10920 MediumLockList *pMediumLockList(new MediumLockList());
10921 alock.release();
10922 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10923 false /* fMediumLockWrite */,
10924 false /* fMediumLockWriteAll */,
10925 NULL,
10926 *pMediumLockList);
10927 alock.acquire();
10928
10929 if (FAILED(rc))
10930 {
10931 delete pMediumLockList;
10932 throw rc;
10933 }
10934
10935 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10936 if (FAILED(rc))
10937 throw rc;
10938 }
10939 }
10940
10941 if (FAILED(rc))
10942 throw rc;
10943 } // end of offline
10944
10945 /* Lock lists are now up to date and include implicitly created media */
10946
10947 /* Go through remembered attachments and delete all implicitly created
10948 * diffs and fix up the attachment information */
10949 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10950 MediaData::AttachmentList implicitAtts;
10951 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10952 it != mMediaData->mAttachments.end();
10953 ++it)
10954 {
10955 ComObjPtr<MediumAttachment> pAtt = *it;
10956 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10957 if (pMedium.isNull())
10958 continue;
10959
10960 // Implicit attachments go on the list for deletion and back references are removed.
10961 if (pAtt->i_isImplicit())
10962 {
10963 /* Deassociate and mark for deletion */
10964 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10965 rc = pMedium->i_removeBackReference(mData->mUuid);
10966 if (FAILED(rc))
10967 throw rc;
10968 implicitAtts.push_back(pAtt);
10969 continue;
10970 }
10971
10972 /* Was this medium attached before? */
10973 if (!i_findAttachment(oldAtts, pMedium))
10974 {
10975 /* no: de-associate */
10976 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10977 rc = pMedium->i_removeBackReference(mData->mUuid);
10978 if (FAILED(rc))
10979 throw rc;
10980 continue;
10981 }
10982 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10983 }
10984
10985 /* If there are implicit attachments to delete, throw away the lock
10986 * map contents (which will unlock all media) since the medium
10987 * attachments will be rolled back. Below we need to completely
10988 * recreate the lock map anyway since it is infinitely complex to
10989 * do this incrementally (would need reconstructing each attachment
10990 * change, which would be extremely hairy). */
10991 if (implicitAtts.size() != 0)
10992 {
10993 ErrorInfoKeeper eik;
10994
10995 HRESULT rc1 = lockedMediaMap->Clear();
10996 AssertComRC(rc1);
10997 }
10998
10999 /* rollback hard disk changes */
11000 mMediaData.rollback();
11001
11002 MultiResult mrc(S_OK);
11003
11004 // Delete unused implicit diffs.
11005 if (implicitAtts.size() != 0)
11006 {
11007 alock.release();
11008
11009 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11010 {
11011 // Remove medium associated with this attachment.
11012 ComObjPtr<MediumAttachment> pAtt = *it;
11013 Assert(pAtt);
11014 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11015 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11016 Assert(pMedium);
11017
11018 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11019 // continue on delete failure, just collect error messages
11020 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11021 pMedium->i_getLocationFull().c_str() ));
11022 mrc = rc;
11023 }
11024 // Clear the list of deleted implicit attachments now, while not
11025 // holding the lock, as it will ultimately trigger Medium::uninit()
11026 // calls which assume that the media tree lock isn't held.
11027 implicitAtts.clear();
11028
11029 alock.acquire();
11030
11031 /* if there is a VM recreate media lock map as mentioned above,
11032 * otherwise it is a waste of time and we leave things unlocked */
11033 if (aOnline)
11034 {
11035 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11036 /* must never be NULL, but better safe than sorry */
11037 if (!pMachine.isNull())
11038 {
11039 alock.release();
11040 rc = mData->mSession.mMachine->i_lockMedia();
11041 alock.acquire();
11042 if (FAILED(rc))
11043 throw rc;
11044 }
11045 }
11046 }
11047 }
11048 catch (HRESULT aRC) {rc = aRC;}
11049
11050 if (mData->mMachineState == MachineState_SettingUp)
11051 i_setMachineState(oldState);
11052
11053 /* unlock all hard disks we locked when there is no VM */
11054 if (!aOnline)
11055 {
11056 ErrorInfoKeeper eik;
11057
11058 HRESULT rc1 = lockedMediaMap->Clear();
11059 AssertComRC(rc1);
11060 }
11061
11062 return rc;
11063}
11064
11065
11066/**
11067 * Looks through the given list of media attachments for one with the given parameters
11068 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11069 * can be searched as well if needed.
11070 *
11071 * @param list
11072 * @param aControllerName
11073 * @param aControllerPort
11074 * @param aDevice
11075 * @return
11076 */
11077MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11078 IN_BSTR aControllerName,
11079 LONG aControllerPort,
11080 LONG aDevice)
11081{
11082 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11083 {
11084 MediumAttachment *pAttach = *it;
11085 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11086 return pAttach;
11087 }
11088
11089 return NULL;
11090}
11091
11092/**
11093 * Looks through the given list of media attachments for one with the given parameters
11094 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11095 * can be searched as well if needed.
11096 *
11097 * @param list
11098 * @param aControllerName
11099 * @param aControllerPort
11100 * @param aDevice
11101 * @return
11102 */
11103MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11104 ComObjPtr<Medium> pMedium)
11105{
11106 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11107 {
11108 MediumAttachment *pAttach = *it;
11109 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11110 if (pMediumThis == pMedium)
11111 return pAttach;
11112 }
11113
11114 return NULL;
11115}
11116
11117/**
11118 * Looks through the given list of media attachments for one with the given parameters
11119 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11120 * can be searched as well if needed.
11121 *
11122 * @param list
11123 * @param aControllerName
11124 * @param aControllerPort
11125 * @param aDevice
11126 * @return
11127 */
11128MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11129 Guid &id)
11130{
11131 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11132 {
11133 MediumAttachment *pAttach = *it;
11134 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11135 if (pMediumThis->i_getId() == id)
11136 return pAttach;
11137 }
11138
11139 return NULL;
11140}
11141
11142/**
11143 * Main implementation for Machine::DetachDevice. This also gets called
11144 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11145 *
11146 * @param pAttach Medium attachment to detach.
11147 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11148 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11149 * SnapshotMachine, and this must be its snapshot.
11150 * @return
11151 */
11152HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11153 AutoWriteLock &writeLock,
11154 Snapshot *pSnapshot)
11155{
11156 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11157 DeviceType_T mediumType = pAttach->i_getType();
11158
11159 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11160
11161 if (pAttach->i_isImplicit())
11162 {
11163 /* attempt to implicitly delete the implicitly created diff */
11164
11165 /// @todo move the implicit flag from MediumAttachment to Medium
11166 /// and forbid any hard disk operation when it is implicit. Or maybe
11167 /// a special media state for it to make it even more simple.
11168
11169 Assert(mMediaData.isBackedUp());
11170
11171 /* will release the lock before the potentially lengthy operation, so
11172 * protect with the special state */
11173 MachineState_T oldState = mData->mMachineState;
11174 i_setMachineState(MachineState_SettingUp);
11175
11176 writeLock.release();
11177
11178 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11179 true /*aWait*/);
11180
11181 writeLock.acquire();
11182
11183 i_setMachineState(oldState);
11184
11185 if (FAILED(rc)) return rc;
11186 }
11187
11188 i_setModified(IsModified_Storage);
11189 mMediaData.backup();
11190 mMediaData->mAttachments.remove(pAttach);
11191
11192 if (!oldmedium.isNull())
11193 {
11194 // if this is from a snapshot, do not defer detachment to commitMedia()
11195 if (pSnapshot)
11196 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11197 // else if non-hard disk media, do not defer detachment to commitMedia() either
11198 else if (mediumType != DeviceType_HardDisk)
11199 oldmedium->i_removeBackReference(mData->mUuid);
11200 }
11201
11202 return S_OK;
11203}
11204
11205/**
11206 * Goes thru all media of the given list and
11207 *
11208 * 1) calls i_detachDevice() on each of them for this machine and
11209 * 2) adds all Medium objects found in the process to the given list,
11210 * depending on cleanupMode.
11211 *
11212 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11213 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11214 * media to the list.
11215 *
11216 * This gets called from Machine::Unregister, both for the actual Machine and
11217 * the SnapshotMachine objects that might be found in the snapshots.
11218 *
11219 * Requires caller and locking. The machine lock must be passed in because it
11220 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11221 *
11222 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11223 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11224 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11225 * Full, then all media get added;
11226 * otherwise no media get added.
11227 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11228 * @return
11229 */
11230HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11231 Snapshot *pSnapshot,
11232 CleanupMode_T cleanupMode,
11233 MediaList &llMedia)
11234{
11235 Assert(isWriteLockOnCurrentThread());
11236
11237 HRESULT rc;
11238
11239 // make a temporary list because i_detachDevice invalidates iterators into
11240 // mMediaData->mAttachments
11241 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11242
11243 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11244 {
11245 ComObjPtr<MediumAttachment> &pAttach = *it;
11246 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11247
11248 if (!pMedium.isNull())
11249 {
11250 AutoCaller mac(pMedium);
11251 if (FAILED(mac.rc())) return mac.rc();
11252 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11253 DeviceType_T devType = pMedium->i_getDeviceType();
11254 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11255 && devType == DeviceType_HardDisk)
11256 || (cleanupMode == CleanupMode_Full)
11257 )
11258 {
11259 llMedia.push_back(pMedium);
11260 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11261 /* Not allowed to keep this lock as below we need the parent
11262 * medium lock, and the lock order is parent to child. */
11263 lock.release();
11264 /*
11265 * Search for medias which are not attached to any machine, but
11266 * in the chain to an attached disk. Mediums are only consided
11267 * if they are:
11268 * - have only one child
11269 * - no references to any machines
11270 * - are of normal medium type
11271 */
11272 while (!pParent.isNull())
11273 {
11274 AutoCaller mac1(pParent);
11275 if (FAILED(mac1.rc())) return mac1.rc();
11276 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11277 if (pParent->i_getChildren().size() == 1)
11278 {
11279 if ( pParent->i_getMachineBackRefCount() == 0
11280 && pParent->i_getType() == MediumType_Normal
11281 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11282 llMedia.push_back(pParent);
11283 }
11284 else
11285 break;
11286 pParent = pParent->i_getParent();
11287 }
11288 }
11289 }
11290
11291 // real machine: then we need to use the proper method
11292 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11293
11294 if (FAILED(rc))
11295 return rc;
11296 }
11297
11298 return S_OK;
11299}
11300
11301/**
11302 * Perform deferred hard disk detachments.
11303 *
11304 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11305 * backed up).
11306 *
11307 * If @a aOnline is @c true then this method will also unlock the old hard disks
11308 * for which the new implicit diffs were created and will lock these new diffs for
11309 * writing.
11310 *
11311 * @param aOnline Whether the VM was online prior to this operation.
11312 *
11313 * @note Locks this object for writing!
11314 */
11315void Machine::i_commitMedia(bool aOnline /*= false*/)
11316{
11317 AutoCaller autoCaller(this);
11318 AssertComRCReturnVoid(autoCaller.rc());
11319
11320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11321
11322 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11323
11324 HRESULT rc = S_OK;
11325
11326 /* no attach/detach operations -- nothing to do */
11327 if (!mMediaData.isBackedUp())
11328 return;
11329
11330 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11331 bool fMediaNeedsLocking = false;
11332
11333 /* enumerate new attachments */
11334 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11335 it != mMediaData->mAttachments.end();
11336 ++it)
11337 {
11338 MediumAttachment *pAttach = *it;
11339
11340 pAttach->i_commit();
11341
11342 Medium* pMedium = pAttach->i_getMedium();
11343 bool fImplicit = pAttach->i_isImplicit();
11344
11345 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11346 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11347 fImplicit));
11348
11349 /** @todo convert all this Machine-based voodoo to MediumAttachment
11350 * based commit logic. */
11351 if (fImplicit)
11352 {
11353 /* convert implicit attachment to normal */
11354 pAttach->i_setImplicit(false);
11355
11356 if ( aOnline
11357 && pMedium
11358 && pAttach->i_getType() == DeviceType_HardDisk
11359 )
11360 {
11361 /* update the appropriate lock list */
11362 MediumLockList *pMediumLockList;
11363 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11364 AssertComRC(rc);
11365 if (pMediumLockList)
11366 {
11367 /* unlock if there's a need to change the locking */
11368 if (!fMediaNeedsLocking)
11369 {
11370 rc = mData->mSession.mLockedMedia.Unlock();
11371 AssertComRC(rc);
11372 fMediaNeedsLocking = true;
11373 }
11374 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11375 AssertComRC(rc);
11376 rc = pMediumLockList->Append(pMedium, true);
11377 AssertComRC(rc);
11378 }
11379 }
11380
11381 continue;
11382 }
11383
11384 if (pMedium)
11385 {
11386 /* was this medium attached before? */
11387 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11388 {
11389 MediumAttachment *pOldAttach = *oldIt;
11390 if (pOldAttach->i_getMedium() == pMedium)
11391 {
11392 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11393
11394 /* yes: remove from old to avoid de-association */
11395 oldAtts.erase(oldIt);
11396 break;
11397 }
11398 }
11399 }
11400 }
11401
11402 /* enumerate remaining old attachments and de-associate from the
11403 * current machine state */
11404 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11405 {
11406 MediumAttachment *pAttach = *it;
11407 Medium* pMedium = pAttach->i_getMedium();
11408
11409 /* Detach only hard disks, since DVD/floppy media is detached
11410 * instantly in MountMedium. */
11411 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11412 {
11413 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11414
11415 /* now de-associate from the current machine state */
11416 rc = pMedium->i_removeBackReference(mData->mUuid);
11417 AssertComRC(rc);
11418
11419 if (aOnline)
11420 {
11421 /* unlock since medium is not used anymore */
11422 MediumLockList *pMediumLockList;
11423 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11424 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11425 {
11426 /* this happens for online snapshots, there the attachment
11427 * is changing, but only to a diff image created under
11428 * the old one, so there is no separate lock list */
11429 Assert(!pMediumLockList);
11430 }
11431 else
11432 {
11433 AssertComRC(rc);
11434 if (pMediumLockList)
11435 {
11436 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11437 AssertComRC(rc);
11438 }
11439 }
11440 }
11441 }
11442 }
11443
11444 /* take media locks again so that the locking state is consistent */
11445 if (fMediaNeedsLocking)
11446 {
11447 Assert(aOnline);
11448 rc = mData->mSession.mLockedMedia.Lock();
11449 AssertComRC(rc);
11450 }
11451
11452 /* commit the hard disk changes */
11453 mMediaData.commit();
11454
11455 if (i_isSessionMachine())
11456 {
11457 /*
11458 * Update the parent machine to point to the new owner.
11459 * This is necessary because the stored parent will point to the
11460 * session machine otherwise and cause crashes or errors later
11461 * when the session machine gets invalid.
11462 */
11463 /** @todo Change the MediumAttachment class to behave like any other
11464 * class in this regard by creating peer MediumAttachment
11465 * objects for session machines and share the data with the peer
11466 * machine.
11467 */
11468 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11469 it != mMediaData->mAttachments.end();
11470 ++it)
11471 (*it)->i_updateParentMachine(mPeer);
11472
11473 /* attach new data to the primary machine and reshare it */
11474 mPeer->mMediaData.attach(mMediaData);
11475 }
11476
11477 return;
11478}
11479
11480/**
11481 * Perform deferred deletion of implicitly created diffs.
11482 *
11483 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11484 * backed up).
11485 *
11486 * @note Locks this object for writing!
11487 */
11488void Machine::i_rollbackMedia()
11489{
11490 AutoCaller autoCaller(this);
11491 AssertComRCReturnVoid(autoCaller.rc());
11492
11493 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11494 LogFlowThisFunc(("Entering rollbackMedia\n"));
11495
11496 HRESULT rc = S_OK;
11497
11498 /* no attach/detach operations -- nothing to do */
11499 if (!mMediaData.isBackedUp())
11500 return;
11501
11502 /* enumerate new attachments */
11503 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11504 it != mMediaData->mAttachments.end();
11505 ++it)
11506 {
11507 MediumAttachment *pAttach = *it;
11508 /* Fix up the backrefs for DVD/floppy media. */
11509 if (pAttach->i_getType() != DeviceType_HardDisk)
11510 {
11511 Medium* pMedium = pAttach->i_getMedium();
11512 if (pMedium)
11513 {
11514 rc = pMedium->i_removeBackReference(mData->mUuid);
11515 AssertComRC(rc);
11516 }
11517 }
11518
11519 (*it)->i_rollback();
11520
11521 pAttach = *it;
11522 /* Fix up the backrefs for DVD/floppy media. */
11523 if (pAttach->i_getType() != DeviceType_HardDisk)
11524 {
11525 Medium* pMedium = pAttach->i_getMedium();
11526 if (pMedium)
11527 {
11528 rc = pMedium->i_addBackReference(mData->mUuid);
11529 AssertComRC(rc);
11530 }
11531 }
11532 }
11533
11534 /** @todo convert all this Machine-based voodoo to MediumAttachment
11535 * based rollback logic. */
11536 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11537
11538 return;
11539}
11540
11541/**
11542 * Returns true if the settings file is located in the directory named exactly
11543 * as the machine; this means, among other things, that the machine directory
11544 * should be auto-renamed.
11545 *
11546 * @param aSettingsDir if not NULL, the full machine settings file directory
11547 * name will be assigned there.
11548 *
11549 * @note Doesn't lock anything.
11550 * @note Not thread safe (must be called from this object's lock).
11551 */
11552bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11553{
11554 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11555 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11556 if (aSettingsDir)
11557 *aSettingsDir = strMachineDirName;
11558 strMachineDirName.stripPath(); // vmname
11559 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11560 strConfigFileOnly.stripPath() // vmname.vbox
11561 .stripSuffix(); // vmname
11562 /** @todo hack, make somehow use of ComposeMachineFilename */
11563 if (mUserData->s.fDirectoryIncludesUUID)
11564 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11565
11566 AssertReturn(!strMachineDirName.isEmpty(), false);
11567 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11568
11569 return strMachineDirName == strConfigFileOnly;
11570}
11571
11572/**
11573 * Discards all changes to machine settings.
11574 *
11575 * @param aNotify Whether to notify the direct session about changes or not.
11576 *
11577 * @note Locks objects for writing!
11578 */
11579void Machine::i_rollback(bool aNotify)
11580{
11581 AutoCaller autoCaller(this);
11582 AssertComRCReturn(autoCaller.rc(), (void)0);
11583
11584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11585
11586 if (!mStorageControllers.isNull())
11587 {
11588 if (mStorageControllers.isBackedUp())
11589 {
11590 /* unitialize all new devices (absent in the backed up list). */
11591 StorageControllerList::const_iterator it = mStorageControllers->begin();
11592 StorageControllerList *backedList = mStorageControllers.backedUpData();
11593 while (it != mStorageControllers->end())
11594 {
11595 if ( std::find(backedList->begin(), backedList->end(), *it)
11596 == backedList->end()
11597 )
11598 {
11599 (*it)->uninit();
11600 }
11601 ++it;
11602 }
11603
11604 /* restore the list */
11605 mStorageControllers.rollback();
11606 }
11607
11608 /* rollback any changes to devices after restoring the list */
11609 if (mData->flModifications & IsModified_Storage)
11610 {
11611 StorageControllerList::const_iterator it = mStorageControllers->begin();
11612 while (it != mStorageControllers->end())
11613 {
11614 (*it)->i_rollback();
11615 ++it;
11616 }
11617 }
11618 }
11619
11620 if (!mUSBControllers.isNull())
11621 {
11622 if (mUSBControllers.isBackedUp())
11623 {
11624 /* unitialize all new devices (absent in the backed up list). */
11625 USBControllerList::const_iterator it = mUSBControllers->begin();
11626 USBControllerList *backedList = mUSBControllers.backedUpData();
11627 while (it != mUSBControllers->end())
11628 {
11629 if ( std::find(backedList->begin(), backedList->end(), *it)
11630 == backedList->end()
11631 )
11632 {
11633 (*it)->uninit();
11634 }
11635 ++it;
11636 }
11637
11638 /* restore the list */
11639 mUSBControllers.rollback();
11640 }
11641
11642 /* rollback any changes to devices after restoring the list */
11643 if (mData->flModifications & IsModified_USB)
11644 {
11645 USBControllerList::const_iterator it = mUSBControllers->begin();
11646 while (it != mUSBControllers->end())
11647 {
11648 (*it)->i_rollback();
11649 ++it;
11650 }
11651 }
11652 }
11653
11654 mUserData.rollback();
11655
11656 mHWData.rollback();
11657
11658 if (mData->flModifications & IsModified_Storage)
11659 i_rollbackMedia();
11660
11661 if (mBIOSSettings)
11662 mBIOSSettings->i_rollback();
11663
11664 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11665 mVRDEServer->i_rollback();
11666
11667 if (mAudioAdapter)
11668 mAudioAdapter->i_rollback();
11669
11670 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11671 mUSBDeviceFilters->i_rollback();
11672
11673 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11674 mBandwidthControl->i_rollback();
11675
11676 if (!mHWData.isNull())
11677 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11678 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11679 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11680 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11681
11682 if (mData->flModifications & IsModified_NetworkAdapters)
11683 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11684 if ( mNetworkAdapters[slot]
11685 && mNetworkAdapters[slot]->i_isModified())
11686 {
11687 mNetworkAdapters[slot]->i_rollback();
11688 networkAdapters[slot] = mNetworkAdapters[slot];
11689 }
11690
11691 if (mData->flModifications & IsModified_SerialPorts)
11692 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11693 if ( mSerialPorts[slot]
11694 && mSerialPorts[slot]->i_isModified())
11695 {
11696 mSerialPorts[slot]->i_rollback();
11697 serialPorts[slot] = mSerialPorts[slot];
11698 }
11699
11700 if (mData->flModifications & IsModified_ParallelPorts)
11701 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11702 if ( mParallelPorts[slot]
11703 && mParallelPorts[slot]->i_isModified())
11704 {
11705 mParallelPorts[slot]->i_rollback();
11706 parallelPorts[slot] = mParallelPorts[slot];
11707 }
11708
11709 if (aNotify)
11710 {
11711 /* inform the direct session about changes */
11712
11713 ComObjPtr<Machine> that = this;
11714 uint32_t flModifications = mData->flModifications;
11715 alock.release();
11716
11717 if (flModifications & IsModified_SharedFolders)
11718 that->i_onSharedFolderChange();
11719
11720 if (flModifications & IsModified_VRDEServer)
11721 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11722 if (flModifications & IsModified_USB)
11723 that->i_onUSBControllerChange();
11724
11725 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11726 if (networkAdapters[slot])
11727 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11728 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11729 if (serialPorts[slot])
11730 that->i_onSerialPortChange(serialPorts[slot]);
11731 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11732 if (parallelPorts[slot])
11733 that->i_onParallelPortChange(parallelPorts[slot]);
11734
11735 if (flModifications & IsModified_Storage)
11736 that->i_onStorageControllerChange();
11737
11738#if 0
11739 if (flModifications & IsModified_BandwidthControl)
11740 that->onBandwidthControlChange();
11741#endif
11742 }
11743}
11744
11745/**
11746 * Commits all the changes to machine settings.
11747 *
11748 * Note that this operation is supposed to never fail.
11749 *
11750 * @note Locks this object and children for writing.
11751 */
11752void Machine::i_commit()
11753{
11754 AutoCaller autoCaller(this);
11755 AssertComRCReturnVoid(autoCaller.rc());
11756
11757 AutoCaller peerCaller(mPeer);
11758 AssertComRCReturnVoid(peerCaller.rc());
11759
11760 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11761
11762 /*
11763 * use safe commit to ensure Snapshot machines (that share mUserData)
11764 * will still refer to a valid memory location
11765 */
11766 mUserData.commitCopy();
11767
11768 mHWData.commit();
11769
11770 if (mMediaData.isBackedUp())
11771 i_commitMedia(Global::IsOnline(mData->mMachineState));
11772
11773 mBIOSSettings->i_commit();
11774 mVRDEServer->i_commit();
11775 mAudioAdapter->i_commit();
11776 mUSBDeviceFilters->i_commit();
11777 mBandwidthControl->i_commit();
11778
11779 /* Since mNetworkAdapters is a list which might have been changed (resized)
11780 * without using the Backupable<> template we need to handle the copying
11781 * of the list entries manually, including the creation of peers for the
11782 * new objects. */
11783 bool commitNetworkAdapters = false;
11784 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11785 if (mPeer)
11786 {
11787 /* commit everything, even the ones which will go away */
11788 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11789 mNetworkAdapters[slot]->i_commit();
11790 /* copy over the new entries, creating a peer and uninit the original */
11791 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11792 for (size_t slot = 0; slot < newSize; slot++)
11793 {
11794 /* look if this adapter has a peer device */
11795 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11796 if (!peer)
11797 {
11798 /* no peer means the adapter is a newly created one;
11799 * create a peer owning data this data share it with */
11800 peer.createObject();
11801 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11802 }
11803 mPeer->mNetworkAdapters[slot] = peer;
11804 }
11805 /* uninit any no longer needed network adapters */
11806 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11807 mNetworkAdapters[slot]->uninit();
11808 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11809 {
11810 if (mPeer->mNetworkAdapters[slot])
11811 mPeer->mNetworkAdapters[slot]->uninit();
11812 }
11813 /* Keep the original network adapter count until this point, so that
11814 * discarding a chipset type change will not lose settings. */
11815 mNetworkAdapters.resize(newSize);
11816 mPeer->mNetworkAdapters.resize(newSize);
11817 }
11818 else
11819 {
11820 /* we have no peer (our parent is the newly created machine);
11821 * just commit changes to the network adapters */
11822 commitNetworkAdapters = true;
11823 }
11824 if (commitNetworkAdapters)
11825 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11826 mNetworkAdapters[slot]->i_commit();
11827
11828 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11829 mSerialPorts[slot]->i_commit();
11830 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11831 mParallelPorts[slot]->i_commit();
11832
11833 bool commitStorageControllers = false;
11834
11835 if (mStorageControllers.isBackedUp())
11836 {
11837 mStorageControllers.commit();
11838
11839 if (mPeer)
11840 {
11841 /* Commit all changes to new controllers (this will reshare data with
11842 * peers for those who have peers) */
11843 StorageControllerList *newList = new StorageControllerList();
11844 StorageControllerList::const_iterator it = mStorageControllers->begin();
11845 while (it != mStorageControllers->end())
11846 {
11847 (*it)->i_commit();
11848
11849 /* look if this controller has a peer device */
11850 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11851 if (!peer)
11852 {
11853 /* no peer means the device is a newly created one;
11854 * create a peer owning data this device share it with */
11855 peer.createObject();
11856 peer->init(mPeer, *it, true /* aReshare */);
11857 }
11858 else
11859 {
11860 /* remove peer from the old list */
11861 mPeer->mStorageControllers->remove(peer);
11862 }
11863 /* and add it to the new list */
11864 newList->push_back(peer);
11865
11866 ++it;
11867 }
11868
11869 /* uninit old peer's controllers that are left */
11870 it = mPeer->mStorageControllers->begin();
11871 while (it != mPeer->mStorageControllers->end())
11872 {
11873 (*it)->uninit();
11874 ++it;
11875 }
11876
11877 /* attach new list of controllers to our peer */
11878 mPeer->mStorageControllers.attach(newList);
11879 }
11880 else
11881 {
11882 /* we have no peer (our parent is the newly created machine);
11883 * just commit changes to devices */
11884 commitStorageControllers = true;
11885 }
11886 }
11887 else
11888 {
11889 /* the list of controllers itself is not changed,
11890 * just commit changes to controllers themselves */
11891 commitStorageControllers = true;
11892 }
11893
11894 if (commitStorageControllers)
11895 {
11896 StorageControllerList::const_iterator it = mStorageControllers->begin();
11897 while (it != mStorageControllers->end())
11898 {
11899 (*it)->i_commit();
11900 ++it;
11901 }
11902 }
11903
11904 bool commitUSBControllers = false;
11905
11906 if (mUSBControllers.isBackedUp())
11907 {
11908 mUSBControllers.commit();
11909
11910 if (mPeer)
11911 {
11912 /* Commit all changes to new controllers (this will reshare data with
11913 * peers for those who have peers) */
11914 USBControllerList *newList = new USBControllerList();
11915 USBControllerList::const_iterator it = mUSBControllers->begin();
11916 while (it != mUSBControllers->end())
11917 {
11918 (*it)->i_commit();
11919
11920 /* look if this controller has a peer device */
11921 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11922 if (!peer)
11923 {
11924 /* no peer means the device is a newly created one;
11925 * create a peer owning data this device share it with */
11926 peer.createObject();
11927 peer->init(mPeer, *it, true /* aReshare */);
11928 }
11929 else
11930 {
11931 /* remove peer from the old list */
11932 mPeer->mUSBControllers->remove(peer);
11933 }
11934 /* and add it to the new list */
11935 newList->push_back(peer);
11936
11937 ++it;
11938 }
11939
11940 /* uninit old peer's controllers that are left */
11941 it = mPeer->mUSBControllers->begin();
11942 while (it != mPeer->mUSBControllers->end())
11943 {
11944 (*it)->uninit();
11945 ++it;
11946 }
11947
11948 /* attach new list of controllers to our peer */
11949 mPeer->mUSBControllers.attach(newList);
11950 }
11951 else
11952 {
11953 /* we have no peer (our parent is the newly created machine);
11954 * just commit changes to devices */
11955 commitUSBControllers = true;
11956 }
11957 }
11958 else
11959 {
11960 /* the list of controllers itself is not changed,
11961 * just commit changes to controllers themselves */
11962 commitUSBControllers = true;
11963 }
11964
11965 if (commitUSBControllers)
11966 {
11967 USBControllerList::const_iterator it = mUSBControllers->begin();
11968 while (it != mUSBControllers->end())
11969 {
11970 (*it)->i_commit();
11971 ++it;
11972 }
11973 }
11974
11975 if (i_isSessionMachine())
11976 {
11977 /* attach new data to the primary machine and reshare it */
11978 mPeer->mUserData.attach(mUserData);
11979 mPeer->mHWData.attach(mHWData);
11980 /* mMediaData is reshared by fixupMedia */
11981 // mPeer->mMediaData.attach(mMediaData);
11982 Assert(mPeer->mMediaData.data() == mMediaData.data());
11983 }
11984}
11985
11986/**
11987 * Copies all the hardware data from the given machine.
11988 *
11989 * Currently, only called when the VM is being restored from a snapshot. In
11990 * particular, this implies that the VM is not running during this method's
11991 * call.
11992 *
11993 * @note This method must be called from under this object's lock.
11994 *
11995 * @note This method doesn't call #commit(), so all data remains backed up and
11996 * unsaved.
11997 */
11998void Machine::i_copyFrom(Machine *aThat)
11999{
12000 AssertReturnVoid(!i_isSnapshotMachine());
12001 AssertReturnVoid(aThat->i_isSnapshotMachine());
12002
12003 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12004
12005 mHWData.assignCopy(aThat->mHWData);
12006
12007 // create copies of all shared folders (mHWData after attaching a copy
12008 // contains just references to original objects)
12009 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12010 it != mHWData->mSharedFolders.end();
12011 ++it)
12012 {
12013 ComObjPtr<SharedFolder> folder;
12014 folder.createObject();
12015 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12016 AssertComRC(rc);
12017 *it = folder;
12018 }
12019
12020 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12021 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12022 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12023 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12024 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12025
12026 /* create private copies of all controllers */
12027 mStorageControllers.backup();
12028 mStorageControllers->clear();
12029 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12030 it != aThat->mStorageControllers->end();
12031 ++it)
12032 {
12033 ComObjPtr<StorageController> ctrl;
12034 ctrl.createObject();
12035 ctrl->initCopy(this, *it);
12036 mStorageControllers->push_back(ctrl);
12037 }
12038
12039 /* create private copies of all USB controllers */
12040 mUSBControllers.backup();
12041 mUSBControllers->clear();
12042 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12043 it != aThat->mUSBControllers->end();
12044 ++it)
12045 {
12046 ComObjPtr<USBController> ctrl;
12047 ctrl.createObject();
12048 ctrl->initCopy(this, *it);
12049 mUSBControllers->push_back(ctrl);
12050 }
12051
12052 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12053 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12054 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12055 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12056 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12057 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12058 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12059}
12060
12061/**
12062 * Returns whether the given storage controller is hotplug capable.
12063 *
12064 * @returns true if the controller supports hotplugging
12065 * false otherwise.
12066 * @param enmCtrlType The controller type to check for.
12067 */
12068bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12069{
12070 ComPtr<ISystemProperties> systemProperties;
12071 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12072 if (FAILED(rc))
12073 return false;
12074
12075 BOOL aHotplugCapable = FALSE;
12076 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12077
12078 return RT_BOOL(aHotplugCapable);
12079}
12080
12081#ifdef VBOX_WITH_RESOURCE_USAGE_API
12082
12083void Machine::i_getDiskList(MediaList &list)
12084{
12085 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12086 it != mMediaData->mAttachments.end();
12087 ++it)
12088 {
12089 MediumAttachment* pAttach = *it;
12090 /* just in case */
12091 AssertStmt(pAttach, continue);
12092
12093 AutoCaller localAutoCallerA(pAttach);
12094 if (FAILED(localAutoCallerA.rc())) continue;
12095
12096 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12097
12098 if (pAttach->i_getType() == DeviceType_HardDisk)
12099 list.push_back(pAttach->i_getMedium());
12100 }
12101}
12102
12103void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12104{
12105 AssertReturnVoid(isWriteLockOnCurrentThread());
12106 AssertPtrReturnVoid(aCollector);
12107
12108 pm::CollectorHAL *hal = aCollector->getHAL();
12109 /* Create sub metrics */
12110 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12111 "Percentage of processor time spent in user mode by the VM process.");
12112 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12113 "Percentage of processor time spent in kernel mode by the VM process.");
12114 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12115 "Size of resident portion of VM process in memory.");
12116 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12117 "Actual size of all VM disks combined.");
12118 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12119 "Network receive rate.");
12120 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12121 "Network transmit rate.");
12122 /* Create and register base metrics */
12123 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12124 cpuLoadUser, cpuLoadKernel);
12125 aCollector->registerBaseMetric(cpuLoad);
12126 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12127 ramUsageUsed);
12128 aCollector->registerBaseMetric(ramUsage);
12129 MediaList disks;
12130 i_getDiskList(disks);
12131 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12132 diskUsageUsed);
12133 aCollector->registerBaseMetric(diskUsage);
12134
12135 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12136 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12137 new pm::AggregateAvg()));
12138 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12139 new pm::AggregateMin()));
12140 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12141 new pm::AggregateMax()));
12142 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12143 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12144 new pm::AggregateAvg()));
12145 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12146 new pm::AggregateMin()));
12147 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12148 new pm::AggregateMax()));
12149
12150 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12151 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12152 new pm::AggregateAvg()));
12153 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12154 new pm::AggregateMin()));
12155 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12156 new pm::AggregateMax()));
12157
12158 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12159 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12160 new pm::AggregateAvg()));
12161 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12162 new pm::AggregateMin()));
12163 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12164 new pm::AggregateMax()));
12165
12166
12167 /* Guest metrics collector */
12168 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12169 aCollector->registerGuest(mCollectorGuest);
12170 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12171 this, __PRETTY_FUNCTION__, mCollectorGuest));
12172
12173 /* Create sub metrics */
12174 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12175 "Percentage of processor time spent in user mode as seen by the guest.");
12176 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12177 "Percentage of processor time spent in kernel mode as seen by the guest.");
12178 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12179 "Percentage of processor time spent idling as seen by the guest.");
12180
12181 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12182 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12183 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12184 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12185 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12186 pm::SubMetric *guestMemCache = new pm::SubMetric(
12187 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12188
12189 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12190 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12191
12192 /* Create and register base metrics */
12193 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12194 machineNetRx, machineNetTx);
12195 aCollector->registerBaseMetric(machineNetRate);
12196
12197 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12198 guestLoadUser, guestLoadKernel, guestLoadIdle);
12199 aCollector->registerBaseMetric(guestCpuLoad);
12200
12201 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12202 guestMemTotal, guestMemFree,
12203 guestMemBalloon, guestMemShared,
12204 guestMemCache, guestPagedTotal);
12205 aCollector->registerBaseMetric(guestCpuMem);
12206
12207 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12208 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12209 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12210 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12211
12212 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12213 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12214 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12215 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12216
12217 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12218 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12219 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12220 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12221
12222 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12223 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12225 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12226
12227 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12228 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12230 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12231
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12233 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12236
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12241
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12246
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12251
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12256
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12261}
12262
12263void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12264{
12265 AssertReturnVoid(isWriteLockOnCurrentThread());
12266
12267 if (aCollector)
12268 {
12269 aCollector->unregisterMetricsFor(aMachine);
12270 aCollector->unregisterBaseMetricsFor(aMachine);
12271 }
12272}
12273
12274#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12275
12276
12277////////////////////////////////////////////////////////////////////////////////
12278
12279DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12280
12281HRESULT SessionMachine::FinalConstruct()
12282{
12283 LogFlowThisFunc(("\n"));
12284
12285 mClientToken = NULL;
12286
12287 return BaseFinalConstruct();
12288}
12289
12290void SessionMachine::FinalRelease()
12291{
12292 LogFlowThisFunc(("\n"));
12293
12294 Assert(!mClientToken);
12295 /* paranoia, should not hang around any more */
12296 if (mClientToken)
12297 {
12298 delete mClientToken;
12299 mClientToken = NULL;
12300 }
12301
12302 uninit(Uninit::Unexpected);
12303
12304 BaseFinalRelease();
12305}
12306
12307/**
12308 * @note Must be called only by Machine::LockMachine() from its own write lock.
12309 */
12310HRESULT SessionMachine::init(Machine *aMachine)
12311{
12312 LogFlowThisFuncEnter();
12313 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12314
12315 AssertReturn(aMachine, E_INVALIDARG);
12316
12317 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12318
12319 /* Enclose the state transition NotReady->InInit->Ready */
12320 AutoInitSpan autoInitSpan(this);
12321 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12322
12323 HRESULT rc = S_OK;
12324
12325 /* create the machine client token */
12326 try
12327 {
12328 mClientToken = new ClientToken(aMachine, this);
12329 if (!mClientToken->isReady())
12330 {
12331 delete mClientToken;
12332 mClientToken = NULL;
12333 rc = E_FAIL;
12334 }
12335 }
12336 catch (std::bad_alloc &)
12337 {
12338 rc = E_OUTOFMEMORY;
12339 }
12340 if (FAILED(rc))
12341 return rc;
12342
12343 /* memorize the peer Machine */
12344 unconst(mPeer) = aMachine;
12345 /* share the parent pointer */
12346 unconst(mParent) = aMachine->mParent;
12347
12348 /* take the pointers to data to share */
12349 mData.share(aMachine->mData);
12350 mSSData.share(aMachine->mSSData);
12351
12352 mUserData.share(aMachine->mUserData);
12353 mHWData.share(aMachine->mHWData);
12354 mMediaData.share(aMachine->mMediaData);
12355
12356 mStorageControllers.allocate();
12357 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12358 it != aMachine->mStorageControllers->end();
12359 ++it)
12360 {
12361 ComObjPtr<StorageController> ctl;
12362 ctl.createObject();
12363 ctl->init(this, *it);
12364 mStorageControllers->push_back(ctl);
12365 }
12366
12367 mUSBControllers.allocate();
12368 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12369 it != aMachine->mUSBControllers->end();
12370 ++it)
12371 {
12372 ComObjPtr<USBController> ctl;
12373 ctl.createObject();
12374 ctl->init(this, *it);
12375 mUSBControllers->push_back(ctl);
12376 }
12377
12378 unconst(mBIOSSettings).createObject();
12379 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12380 /* create another VRDEServer object that will be mutable */
12381 unconst(mVRDEServer).createObject();
12382 mVRDEServer->init(this, aMachine->mVRDEServer);
12383 /* create another audio adapter object that will be mutable */
12384 unconst(mAudioAdapter).createObject();
12385 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12386 /* create a list of serial ports that will be mutable */
12387 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12388 {
12389 unconst(mSerialPorts[slot]).createObject();
12390 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12391 }
12392 /* create a list of parallel ports that will be mutable */
12393 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12394 {
12395 unconst(mParallelPorts[slot]).createObject();
12396 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12397 }
12398
12399 /* create another USB device filters object that will be mutable */
12400 unconst(mUSBDeviceFilters).createObject();
12401 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12402
12403 /* create a list of network adapters that will be mutable */
12404 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12405 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12406 {
12407 unconst(mNetworkAdapters[slot]).createObject();
12408 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12409 }
12410
12411 /* create another bandwidth control object that will be mutable */
12412 unconst(mBandwidthControl).createObject();
12413 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12414
12415 /* default is to delete saved state on Saved -> PoweredOff transition */
12416 mRemoveSavedState = true;
12417
12418 /* Confirm a successful initialization when it's the case */
12419 autoInitSpan.setSucceeded();
12420
12421 miNATNetworksStarted = 0;
12422
12423 LogFlowThisFuncLeave();
12424 return rc;
12425}
12426
12427/**
12428 * Uninitializes this session object. If the reason is other than
12429 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12430 * or the client watcher code.
12431 *
12432 * @param aReason uninitialization reason
12433 *
12434 * @note Locks mParent + this object for writing.
12435 */
12436void SessionMachine::uninit(Uninit::Reason aReason)
12437{
12438 LogFlowThisFuncEnter();
12439 LogFlowThisFunc(("reason=%d\n", aReason));
12440
12441 /*
12442 * Strongly reference ourselves to prevent this object deletion after
12443 * mData->mSession.mMachine.setNull() below (which can release the last
12444 * reference and call the destructor). Important: this must be done before
12445 * accessing any members (and before AutoUninitSpan that does it as well).
12446 * This self reference will be released as the very last step on return.
12447 */
12448 ComObjPtr<SessionMachine> selfRef = this;
12449
12450 /* Enclose the state transition Ready->InUninit->NotReady */
12451 AutoUninitSpan autoUninitSpan(this);
12452 if (autoUninitSpan.uninitDone())
12453 {
12454 LogFlowThisFunc(("Already uninitialized\n"));
12455 LogFlowThisFuncLeave();
12456 return;
12457 }
12458
12459 if (autoUninitSpan.initFailed())
12460 {
12461 /* We've been called by init() because it's failed. It's not really
12462 * necessary (nor it's safe) to perform the regular uninit sequence
12463 * below, the following is enough.
12464 */
12465 LogFlowThisFunc(("Initialization failed.\n"));
12466 /* destroy the machine client token */
12467 if (mClientToken)
12468 {
12469 delete mClientToken;
12470 mClientToken = NULL;
12471 }
12472 uninitDataAndChildObjects();
12473 mData.free();
12474 unconst(mParent) = NULL;
12475 unconst(mPeer) = NULL;
12476 LogFlowThisFuncLeave();
12477 return;
12478 }
12479
12480 MachineState_T lastState;
12481 {
12482 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12483 lastState = mData->mMachineState;
12484 }
12485 NOREF(lastState);
12486
12487#ifdef VBOX_WITH_USB
12488 // release all captured USB devices, but do this before requesting the locks below
12489 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12490 {
12491 /* Console::captureUSBDevices() is called in the VM process only after
12492 * setting the machine state to Starting or Restoring.
12493 * Console::detachAllUSBDevices() will be called upon successful
12494 * termination. So, we need to release USB devices only if there was
12495 * an abnormal termination of a running VM.
12496 *
12497 * This is identical to SessionMachine::DetachAllUSBDevices except
12498 * for the aAbnormal argument. */
12499 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12500 AssertComRC(rc);
12501 NOREF(rc);
12502
12503 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12504 if (service)
12505 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12506 }
12507#endif /* VBOX_WITH_USB */
12508
12509 // we need to lock this object in uninit() because the lock is shared
12510 // with mPeer (as well as data we modify below). mParent lock is needed
12511 // by several calls to it, and USB needs host lock.
12512 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12513
12514#ifdef VBOX_WITH_RESOURCE_USAGE_API
12515 /*
12516 * It is safe to call Machine::i_unregisterMetrics() here because
12517 * PerformanceCollector::samplerCallback no longer accesses guest methods
12518 * holding the lock.
12519 */
12520 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12521 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12522 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12523 this, __PRETTY_FUNCTION__, mCollectorGuest));
12524 if (mCollectorGuest)
12525 {
12526 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12527 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12528 mCollectorGuest = NULL;
12529 }
12530#endif
12531
12532 if (aReason == Uninit::Abnormal)
12533 {
12534 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12535 Global::IsOnlineOrTransient(lastState)));
12536
12537 /* reset the state to Aborted */
12538 if (mData->mMachineState != MachineState_Aborted)
12539 i_setMachineState(MachineState_Aborted);
12540 }
12541
12542 // any machine settings modified?
12543 if (mData->flModifications)
12544 {
12545 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12546 i_rollback(false /* aNotify */);
12547 }
12548
12549 mData->mSession.mPID = NIL_RTPROCESS;
12550
12551 if (aReason == Uninit::Unexpected)
12552 {
12553 /* Uninitialization didn't come from #checkForDeath(), so tell the
12554 * client watcher thread to update the set of machines that have open
12555 * sessions. */
12556 mParent->i_updateClientWatcher();
12557 }
12558
12559 /* uninitialize all remote controls */
12560 if (mData->mSession.mRemoteControls.size())
12561 {
12562 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12563 mData->mSession.mRemoteControls.size()));
12564
12565 Data::Session::RemoteControlList::iterator it =
12566 mData->mSession.mRemoteControls.begin();
12567 while (it != mData->mSession.mRemoteControls.end())
12568 {
12569 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12570 HRESULT rc = (*it)->Uninitialize();
12571 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12572 if (FAILED(rc))
12573 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12574 ++it;
12575 }
12576 mData->mSession.mRemoteControls.clear();
12577 }
12578
12579 /* Remove all references to the NAT network service. The service will stop
12580 * if all references (also from other VMs) are removed. */
12581 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12582 {
12583 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12584 {
12585 NetworkAttachmentType_T type;
12586 HRESULT hrc;
12587
12588 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12589 if ( SUCCEEDED(hrc)
12590 && type == NetworkAttachmentType_NATNetwork)
12591 {
12592 Bstr name;
12593 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12594 if (SUCCEEDED(hrc))
12595 {
12596 multilock.release();
12597 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12598 mUserData->s.strName.c_str(), name.raw()));
12599 mParent->i_natNetworkRefDec(name.raw());
12600 multilock.acquire();
12601 }
12602 }
12603 }
12604 }
12605
12606 /*
12607 * An expected uninitialization can come only from #checkForDeath().
12608 * Otherwise it means that something's gone really wrong (for example,
12609 * the Session implementation has released the VirtualBox reference
12610 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12611 * etc). However, it's also possible, that the client releases the IPC
12612 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12613 * but the VirtualBox release event comes first to the server process.
12614 * This case is practically possible, so we should not assert on an
12615 * unexpected uninit, just log a warning.
12616 */
12617
12618 if ((aReason == Uninit::Unexpected))
12619 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12620
12621 if (aReason != Uninit::Normal)
12622 {
12623 mData->mSession.mDirectControl.setNull();
12624 }
12625 else
12626 {
12627 /* this must be null here (see #OnSessionEnd()) */
12628 Assert(mData->mSession.mDirectControl.isNull());
12629 Assert(mData->mSession.mState == SessionState_Unlocking);
12630 Assert(!mData->mSession.mProgress.isNull());
12631 }
12632 if (mData->mSession.mProgress)
12633 {
12634 if (aReason == Uninit::Normal)
12635 mData->mSession.mProgress->i_notifyComplete(S_OK);
12636 else
12637 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12638 COM_IIDOF(ISession),
12639 getComponentName(),
12640 tr("The VM session was aborted"));
12641 mData->mSession.mProgress.setNull();
12642 }
12643
12644 /* remove the association between the peer machine and this session machine */
12645 Assert( (SessionMachine*)mData->mSession.mMachine == this
12646 || aReason == Uninit::Unexpected);
12647
12648 /* reset the rest of session data */
12649 mData->mSession.mLockType = LockType_Null;
12650 mData->mSession.mMachine.setNull();
12651 mData->mSession.mState = SessionState_Unlocked;
12652 mData->mSession.mType.setNull();
12653
12654 /* destroy the machine client token before leaving the exclusive lock */
12655 if (mClientToken)
12656 {
12657 delete mClientToken;
12658 mClientToken = NULL;
12659 }
12660
12661 /* fire an event */
12662 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12663
12664 uninitDataAndChildObjects();
12665
12666 /* free the essential data structure last */
12667 mData.free();
12668
12669 /* release the exclusive lock before setting the below two to NULL */
12670 multilock.release();
12671
12672 unconst(mParent) = NULL;
12673 unconst(mPeer) = NULL;
12674
12675 LogFlowThisFuncLeave();
12676}
12677
12678// util::Lockable interface
12679////////////////////////////////////////////////////////////////////////////////
12680
12681/**
12682 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12683 * with the primary Machine instance (mPeer).
12684 */
12685RWLockHandle *SessionMachine::lockHandle() const
12686{
12687 AssertReturn(mPeer != NULL, NULL);
12688 return mPeer->lockHandle();
12689}
12690
12691// IInternalMachineControl methods
12692////////////////////////////////////////////////////////////////////////////////
12693
12694/**
12695 * Passes collected guest statistics to performance collector object
12696 */
12697HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12698 ULONG aCpuKernel, ULONG aCpuIdle,
12699 ULONG aMemTotal, ULONG aMemFree,
12700 ULONG aMemBalloon, ULONG aMemShared,
12701 ULONG aMemCache, ULONG aPageTotal,
12702 ULONG aAllocVMM, ULONG aFreeVMM,
12703 ULONG aBalloonedVMM, ULONG aSharedVMM,
12704 ULONG aVmNetRx, ULONG aVmNetTx)
12705{
12706#ifdef VBOX_WITH_RESOURCE_USAGE_API
12707 if (mCollectorGuest)
12708 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12709 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12710 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12711 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12712
12713 return S_OK;
12714#else
12715 NOREF(aValidStats);
12716 NOREF(aCpuUser);
12717 NOREF(aCpuKernel);
12718 NOREF(aCpuIdle);
12719 NOREF(aMemTotal);
12720 NOREF(aMemFree);
12721 NOREF(aMemBalloon);
12722 NOREF(aMemShared);
12723 NOREF(aMemCache);
12724 NOREF(aPageTotal);
12725 NOREF(aAllocVMM);
12726 NOREF(aFreeVMM);
12727 NOREF(aBalloonedVMM);
12728 NOREF(aSharedVMM);
12729 NOREF(aVmNetRx);
12730 NOREF(aVmNetTx);
12731 return E_NOTIMPL;
12732#endif
12733}
12734
12735////////////////////////////////////////////////////////////////////////////////
12736//
12737// SessionMachine task records
12738//
12739////////////////////////////////////////////////////////////////////////////////
12740
12741/**
12742 * Task record for saving the machine state.
12743 */
12744struct SessionMachine::SaveStateTask
12745 : public Machine::Task
12746{
12747 SaveStateTask(SessionMachine *m,
12748 Progress *p,
12749 const Utf8Str &t,
12750 Reason_T enmReason,
12751 const Utf8Str &strStateFilePath)
12752 : Task(m, p, t),
12753 m_enmReason(enmReason),
12754 m_strStateFilePath(strStateFilePath)
12755 {}
12756
12757 void handler()
12758 {
12759 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12760 }
12761
12762 Reason_T m_enmReason;
12763 Utf8Str m_strStateFilePath;
12764};
12765
12766/**
12767 * Task thread implementation for SessionMachine::SaveState(), called from
12768 * SessionMachine::taskHandler().
12769 *
12770 * @note Locks this object for writing.
12771 *
12772 * @param task
12773 * @return
12774 */
12775void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12776{
12777 LogFlowThisFuncEnter();
12778
12779 AutoCaller autoCaller(this);
12780 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12781 if (FAILED(autoCaller.rc()))
12782 {
12783 /* we might have been uninitialized because the session was accidentally
12784 * closed by the client, so don't assert */
12785 HRESULT rc = setError(E_FAIL,
12786 tr("The session has been accidentally closed"));
12787 task.m_pProgress->i_notifyComplete(rc);
12788 LogFlowThisFuncLeave();
12789 return;
12790 }
12791
12792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12793
12794 HRESULT rc = S_OK;
12795
12796 try
12797 {
12798 ComPtr<IInternalSessionControl> directControl;
12799 if (mData->mSession.mLockType == LockType_VM)
12800 directControl = mData->mSession.mDirectControl;
12801 if (directControl.isNull())
12802 throw setError(VBOX_E_INVALID_VM_STATE,
12803 tr("Trying to save state without a running VM"));
12804 alock.release();
12805 BOOL fSuspendedBySave;
12806 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12807 Assert(!fSuspendedBySave);
12808 alock.acquire();
12809
12810 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12811 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12812 throw E_FAIL);
12813
12814 if (SUCCEEDED(rc))
12815 {
12816 mSSData->strStateFilePath = task.m_strStateFilePath;
12817
12818 /* save all VM settings */
12819 rc = i_saveSettings(NULL);
12820 // no need to check whether VirtualBox.xml needs saving also since
12821 // we can't have a name change pending at this point
12822 }
12823 else
12824 {
12825 // On failure, set the state to the state we had at the beginning.
12826 i_setMachineState(task.m_machineStateBackup);
12827 i_updateMachineStateOnClient();
12828
12829 // Delete the saved state file (might have been already created).
12830 // No need to check whether this is shared with a snapshot here
12831 // because we certainly created a fresh saved state file here.
12832 RTFileDelete(task.m_strStateFilePath.c_str());
12833 }
12834 }
12835 catch (HRESULT aRC) { rc = aRC; }
12836
12837 task.m_pProgress->i_notifyComplete(rc);
12838
12839 LogFlowThisFuncLeave();
12840}
12841
12842/**
12843 * @note Locks this object for writing.
12844 */
12845HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12846{
12847 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12848}
12849
12850HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12851{
12852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12853
12854 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12855 if (FAILED(rc)) return rc;
12856
12857 if ( mData->mMachineState != MachineState_Running
12858 && mData->mMachineState != MachineState_Paused
12859 )
12860 return setError(VBOX_E_INVALID_VM_STATE,
12861 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12862 Global::stringifyMachineState(mData->mMachineState));
12863
12864 ComObjPtr<Progress> pProgress;
12865 pProgress.createObject();
12866 rc = pProgress->init(i_getVirtualBox(),
12867 static_cast<IMachine *>(this) /* aInitiator */,
12868 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12869 FALSE /* aCancelable */);
12870 if (FAILED(rc))
12871 return rc;
12872
12873 Utf8Str strStateFilePath;
12874 i_composeSavedStateFilename(strStateFilePath);
12875
12876 /* create and start the task on a separate thread (note that it will not
12877 * start working until we release alock) */
12878 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12879 rc = pTask->createThread();
12880 if (FAILED(rc))
12881 return rc;
12882
12883 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12884 i_setMachineState(MachineState_Saving);
12885 i_updateMachineStateOnClient();
12886
12887 pProgress.queryInterfaceTo(aProgress.asOutParam());
12888
12889 return S_OK;
12890}
12891
12892/**
12893 * @note Locks this object for writing.
12894 */
12895HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12896{
12897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12898
12899 HRESULT rc = i_checkStateDependency(MutableStateDep);
12900 if (FAILED(rc)) return rc;
12901
12902 if ( mData->mMachineState != MachineState_PoweredOff
12903 && mData->mMachineState != MachineState_Teleported
12904 && mData->mMachineState != MachineState_Aborted
12905 )
12906 return setError(VBOX_E_INVALID_VM_STATE,
12907 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12908 Global::stringifyMachineState(mData->mMachineState));
12909
12910 com::Utf8Str stateFilePathFull;
12911 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12912 if (RT_FAILURE(vrc))
12913 return setError(VBOX_E_FILE_ERROR,
12914 tr("Invalid saved state file path '%s' (%Rrc)"),
12915 aSavedStateFile.c_str(),
12916 vrc);
12917
12918 mSSData->strStateFilePath = stateFilePathFull;
12919
12920 /* The below i_setMachineState() will detect the state transition and will
12921 * update the settings file */
12922
12923 return i_setMachineState(MachineState_Saved);
12924}
12925
12926/**
12927 * @note Locks this object for writing.
12928 */
12929HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12930{
12931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12932
12933 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12934 if (FAILED(rc)) return rc;
12935
12936 if (mData->mMachineState != MachineState_Saved)
12937 return setError(VBOX_E_INVALID_VM_STATE,
12938 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12939 Global::stringifyMachineState(mData->mMachineState));
12940
12941 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12942
12943 /*
12944 * Saved -> PoweredOff transition will be detected in the SessionMachine
12945 * and properly handled.
12946 */
12947 rc = i_setMachineState(MachineState_PoweredOff);
12948 return rc;
12949}
12950
12951
12952/**
12953 * @note Locks the same as #i_setMachineState() does.
12954 */
12955HRESULT SessionMachine::updateState(MachineState_T aState)
12956{
12957 return i_setMachineState(aState);
12958}
12959
12960/**
12961 * @note Locks this object for writing.
12962 */
12963HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12964{
12965 IProgress* pProgress(aProgress);
12966
12967 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12968
12969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12970
12971 if (mData->mSession.mState != SessionState_Locked)
12972 return VBOX_E_INVALID_OBJECT_STATE;
12973
12974 if (!mData->mSession.mProgress.isNull())
12975 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12976
12977 /* If we didn't reference the NAT network service yet, add a reference to
12978 * force a start */
12979 if (miNATNetworksStarted < 1)
12980 {
12981 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12982 {
12983 NetworkAttachmentType_T type;
12984 HRESULT hrc;
12985 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12986 if ( SUCCEEDED(hrc)
12987 && type == NetworkAttachmentType_NATNetwork)
12988 {
12989 Bstr name;
12990 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12991 if (SUCCEEDED(hrc))
12992 {
12993 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12994 mUserData->s.strName.c_str(), name.raw()));
12995 mPeer->lockHandle()->unlockWrite();
12996 mParent->i_natNetworkRefInc(name.raw());
12997#ifdef RT_LOCK_STRICT
12998 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12999#else
13000 mPeer->lockHandle()->lockWrite();
13001#endif
13002 }
13003 }
13004 }
13005 miNATNetworksStarted++;
13006 }
13007
13008 LogFlowThisFunc(("returns S_OK.\n"));
13009 return S_OK;
13010}
13011
13012/**
13013 * @note Locks this object for writing.
13014 */
13015HRESULT SessionMachine::endPowerUp(LONG aResult)
13016{
13017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13018
13019 if (mData->mSession.mState != SessionState_Locked)
13020 return VBOX_E_INVALID_OBJECT_STATE;
13021
13022 /* Finalize the LaunchVMProcess progress object. */
13023 if (mData->mSession.mProgress)
13024 {
13025 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13026 mData->mSession.mProgress.setNull();
13027 }
13028
13029 if (SUCCEEDED((HRESULT)aResult))
13030 {
13031#ifdef VBOX_WITH_RESOURCE_USAGE_API
13032 /* The VM has been powered up successfully, so it makes sense
13033 * now to offer the performance metrics for a running machine
13034 * object. Doing it earlier wouldn't be safe. */
13035 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13036 mData->mSession.mPID);
13037#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13038 }
13039
13040 return S_OK;
13041}
13042
13043/**
13044 * @note Locks this object for writing.
13045 */
13046HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13047{
13048 LogFlowThisFuncEnter();
13049
13050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13051
13052 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13053 E_FAIL);
13054
13055 /* create a progress object to track operation completion */
13056 ComObjPtr<Progress> pProgress;
13057 pProgress.createObject();
13058 pProgress->init(i_getVirtualBox(),
13059 static_cast<IMachine *>(this) /* aInitiator */,
13060 Bstr(tr("Stopping the virtual machine")).raw(),
13061 FALSE /* aCancelable */);
13062
13063 /* fill in the console task data */
13064 mConsoleTaskData.mLastState = mData->mMachineState;
13065 mConsoleTaskData.mProgress = pProgress;
13066
13067 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13068 i_setMachineState(MachineState_Stopping);
13069
13070 pProgress.queryInterfaceTo(aProgress.asOutParam());
13071
13072 return S_OK;
13073}
13074
13075/**
13076 * @note Locks this object for writing.
13077 */
13078HRESULT SessionMachine::endPoweringDown(LONG aResult,
13079 const com::Utf8Str &aErrMsg)
13080{
13081 LogFlowThisFuncEnter();
13082
13083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13084
13085 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13086 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13087 && mConsoleTaskData.mLastState != MachineState_Null,
13088 E_FAIL);
13089
13090 /*
13091 * On failure, set the state to the state we had when BeginPoweringDown()
13092 * was called (this is expected by Console::PowerDown() and the associated
13093 * task). On success the VM process already changed the state to
13094 * MachineState_PoweredOff, so no need to do anything.
13095 */
13096 if (FAILED(aResult))
13097 i_setMachineState(mConsoleTaskData.mLastState);
13098
13099 /* notify the progress object about operation completion */
13100 Assert(mConsoleTaskData.mProgress);
13101 if (SUCCEEDED(aResult))
13102 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13103 else
13104 {
13105 if (aErrMsg.length())
13106 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13107 COM_IIDOF(ISession),
13108 getComponentName(),
13109 aErrMsg.c_str());
13110 else
13111 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13112 }
13113
13114 /* clear out the temporary saved state data */
13115 mConsoleTaskData.mLastState = MachineState_Null;
13116 mConsoleTaskData.mProgress.setNull();
13117
13118 LogFlowThisFuncLeave();
13119 return S_OK;
13120}
13121
13122
13123/**
13124 * Goes through the USB filters of the given machine to see if the given
13125 * device matches any filter or not.
13126 *
13127 * @note Locks the same as USBController::hasMatchingFilter() does.
13128 */
13129HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13130 BOOL *aMatched,
13131 ULONG *aMaskedInterfaces)
13132{
13133 LogFlowThisFunc(("\n"));
13134
13135#ifdef VBOX_WITH_USB
13136 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13137#else
13138 NOREF(aDevice);
13139 NOREF(aMaskedInterfaces);
13140 *aMatched = FALSE;
13141#endif
13142
13143 return S_OK;
13144}
13145
13146/**
13147 * @note Locks the same as Host::captureUSBDevice() does.
13148 */
13149HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13150{
13151 LogFlowThisFunc(("\n"));
13152
13153#ifdef VBOX_WITH_USB
13154 /* if captureDeviceForVM() fails, it must have set extended error info */
13155 clearError();
13156 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13157 if (FAILED(rc)) return rc;
13158
13159 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13160 AssertReturn(service, E_FAIL);
13161 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13162#else
13163 NOREF(aId);
13164 return E_NOTIMPL;
13165#endif
13166}
13167
13168/**
13169 * @note Locks the same as Host::detachUSBDevice() does.
13170 */
13171HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13172 BOOL aDone)
13173{
13174 LogFlowThisFunc(("\n"));
13175
13176#ifdef VBOX_WITH_USB
13177 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13178 AssertReturn(service, E_FAIL);
13179 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13180#else
13181 NOREF(aId);
13182 NOREF(aDone);
13183 return E_NOTIMPL;
13184#endif
13185}
13186
13187/**
13188 * Inserts all machine filters to the USB proxy service and then calls
13189 * Host::autoCaptureUSBDevices().
13190 *
13191 * Called by Console from the VM process upon VM startup.
13192 *
13193 * @note Locks what called methods lock.
13194 */
13195HRESULT SessionMachine::autoCaptureUSBDevices()
13196{
13197 LogFlowThisFunc(("\n"));
13198
13199#ifdef VBOX_WITH_USB
13200 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13201 AssertComRC(rc);
13202 NOREF(rc);
13203
13204 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13205 AssertReturn(service, E_FAIL);
13206 return service->autoCaptureDevicesForVM(this);
13207#else
13208 return S_OK;
13209#endif
13210}
13211
13212/**
13213 * Removes all machine filters from the USB proxy service and then calls
13214 * Host::detachAllUSBDevices().
13215 *
13216 * Called by Console from the VM process upon normal VM termination or by
13217 * SessionMachine::uninit() upon abnormal VM termination (from under the
13218 * Machine/SessionMachine lock).
13219 *
13220 * @note Locks what called methods lock.
13221 */
13222HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13223{
13224 LogFlowThisFunc(("\n"));
13225
13226#ifdef VBOX_WITH_USB
13227 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13228 AssertComRC(rc);
13229 NOREF(rc);
13230
13231 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13232 AssertReturn(service, E_FAIL);
13233 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13234#else
13235 NOREF(aDone);
13236 return S_OK;
13237#endif
13238}
13239
13240/**
13241 * @note Locks this object for writing.
13242 */
13243HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13244 ComPtr<IProgress> &aProgress)
13245{
13246 LogFlowThisFuncEnter();
13247
13248 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13249 /*
13250 * We don't assert below because it might happen that a non-direct session
13251 * informs us it is closed right after we've been uninitialized -- it's ok.
13252 */
13253
13254 /* get IInternalSessionControl interface */
13255 ComPtr<IInternalSessionControl> control(aSession);
13256
13257 ComAssertRet(!control.isNull(), E_INVALIDARG);
13258
13259 /* Creating a Progress object requires the VirtualBox lock, and
13260 * thus locking it here is required by the lock order rules. */
13261 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13262
13263 if (control == mData->mSession.mDirectControl)
13264 {
13265 /* The direct session is being normally closed by the client process
13266 * ----------------------------------------------------------------- */
13267
13268 /* go to the closing state (essential for all open*Session() calls and
13269 * for #checkForDeath()) */
13270 Assert(mData->mSession.mState == SessionState_Locked);
13271 mData->mSession.mState = SessionState_Unlocking;
13272
13273 /* set direct control to NULL to release the remote instance */
13274 mData->mSession.mDirectControl.setNull();
13275 LogFlowThisFunc(("Direct control is set to NULL\n"));
13276
13277 if (mData->mSession.mProgress)
13278 {
13279 /* finalize the progress, someone might wait if a frontend
13280 * closes the session before powering on the VM. */
13281 mData->mSession.mProgress->notifyComplete(E_FAIL,
13282 COM_IIDOF(ISession),
13283 getComponentName(),
13284 tr("The VM session was closed before any attempt to power it on"));
13285 mData->mSession.mProgress.setNull();
13286 }
13287
13288 /* Create the progress object the client will use to wait until
13289 * #checkForDeath() is called to uninitialize this session object after
13290 * it releases the IPC semaphore.
13291 * Note! Because we're "reusing" mProgress here, this must be a proxy
13292 * object just like for LaunchVMProcess. */
13293 Assert(mData->mSession.mProgress.isNull());
13294 ComObjPtr<ProgressProxy> progress;
13295 progress.createObject();
13296 ComPtr<IUnknown> pPeer(mPeer);
13297 progress->init(mParent, pPeer,
13298 Bstr(tr("Closing session")).raw(),
13299 FALSE /* aCancelable */);
13300 progress.queryInterfaceTo(aProgress.asOutParam());
13301 mData->mSession.mProgress = progress;
13302 }
13303 else
13304 {
13305 /* the remote session is being normally closed */
13306 Data::Session::RemoteControlList::iterator it =
13307 mData->mSession.mRemoteControls.begin();
13308 while (it != mData->mSession.mRemoteControls.end())
13309 {
13310 if (control == *it)
13311 break;
13312 ++it;
13313 }
13314 BOOL found = it != mData->mSession.mRemoteControls.end();
13315 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13316 E_INVALIDARG);
13317 // This MUST be erase(it), not remove(*it) as the latter triggers a
13318 // very nasty use after free due to the place where the value "lives".
13319 mData->mSession.mRemoteControls.erase(it);
13320 }
13321
13322 /* signal the client watcher thread, because the client is going away */
13323 mParent->i_updateClientWatcher();
13324
13325 LogFlowThisFuncLeave();
13326 return S_OK;
13327}
13328
13329HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13330 std::vector<com::Utf8Str> &aValues,
13331 std::vector<LONG64> &aTimestamps,
13332 std::vector<com::Utf8Str> &aFlags)
13333{
13334 LogFlowThisFunc(("\n"));
13335
13336#ifdef VBOX_WITH_GUEST_PROPS
13337 using namespace guestProp;
13338
13339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13340
13341 size_t cEntries = mHWData->mGuestProperties.size();
13342 aNames.resize(cEntries);
13343 aValues.resize(cEntries);
13344 aTimestamps.resize(cEntries);
13345 aFlags.resize(cEntries);
13346
13347 size_t i = 0;
13348 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13349 it != mHWData->mGuestProperties.end();
13350 ++it, ++i)
13351 {
13352 char szFlags[MAX_FLAGS_LEN + 1];
13353 aNames[i] = it->first;
13354 aValues[i] = it->second.strValue;
13355 aTimestamps[i] = it->second.mTimestamp;
13356
13357 /* If it is NULL, keep it NULL. */
13358 if (it->second.mFlags)
13359 {
13360 writeFlags(it->second.mFlags, szFlags);
13361 aFlags[i] = szFlags;
13362 }
13363 else
13364 aFlags[i] = "";
13365 }
13366 return S_OK;
13367#else
13368 ReturnComNotImplemented();
13369#endif
13370}
13371
13372HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13373 const com::Utf8Str &aValue,
13374 LONG64 aTimestamp,
13375 const com::Utf8Str &aFlags,
13376 BOOL *aNotify)
13377{
13378 LogFlowThisFunc(("\n"));
13379
13380#ifdef VBOX_WITH_GUEST_PROPS
13381 using namespace guestProp;
13382
13383 *aNotify = FALSE;
13384
13385 try
13386 {
13387 /*
13388 * Convert input up front.
13389 */
13390 uint32_t fFlags = NILFLAG;
13391 if (aFlags.length())
13392 {
13393 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13394 AssertRCReturn(vrc, E_INVALIDARG);
13395 }
13396
13397 /*
13398 * Now grab the object lock, validate the state and do the update.
13399 */
13400
13401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13402
13403 switch (mData->mMachineState)
13404 {
13405 case MachineState_Paused:
13406 case MachineState_Running:
13407 case MachineState_Teleporting:
13408 case MachineState_TeleportingPausedVM:
13409 case MachineState_OnlineSnapshotting:
13410 case MachineState_LiveSnapshotting:
13411 case MachineState_DeletingSnapshotOnline:
13412 case MachineState_DeletingSnapshotPaused:
13413 case MachineState_Saving:
13414 case MachineState_Stopping:
13415 break;
13416
13417 default:
13418 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13419 VBOX_E_INVALID_VM_STATE);
13420 }
13421
13422 i_setModified(IsModified_MachineData);
13423 mHWData.backup();
13424
13425 bool fDelete = !aValue.length();
13426 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13427 if (it != mHWData->mGuestProperties.end())
13428 {
13429 if (!fDelete)
13430 {
13431 it->second.strValue = aValue;
13432 it->second.mTimestamp = aTimestamp;
13433 it->second.mFlags = fFlags;
13434 }
13435 else
13436 mHWData->mGuestProperties.erase(it);
13437
13438 mData->mGuestPropertiesModified = TRUE;
13439 }
13440 else if (!fDelete)
13441 {
13442 HWData::GuestProperty prop;
13443 prop.strValue = aValue;
13444 prop.mTimestamp = aTimestamp;
13445 prop.mFlags = fFlags;
13446
13447 mHWData->mGuestProperties[aName] = prop;
13448 mData->mGuestPropertiesModified = TRUE;
13449 }
13450
13451 /*
13452 * Send a callback notification if appropriate
13453 */
13454 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13455 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13456 RTSTR_MAX,
13457 aName.c_str(),
13458 RTSTR_MAX, NULL)
13459 )
13460 {
13461 alock.release();
13462
13463 mParent->i_onGuestPropertyChange(mData->mUuid,
13464 Bstr(aName).raw(),
13465 Bstr(aValue).raw(),
13466 Bstr(aFlags).raw());
13467 *aNotify = TRUE;
13468 }
13469 }
13470 catch (...)
13471 {
13472 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13473 }
13474 return S_OK;
13475#else
13476 ReturnComNotImplemented();
13477#endif
13478}
13479
13480
13481HRESULT SessionMachine::lockMedia()
13482{
13483 AutoMultiWriteLock2 alock(this->lockHandle(),
13484 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13485
13486 AssertReturn( mData->mMachineState == MachineState_Starting
13487 || mData->mMachineState == MachineState_Restoring
13488 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13489
13490 clearError();
13491 alock.release();
13492 return i_lockMedia();
13493}
13494
13495HRESULT SessionMachine::unlockMedia()
13496{
13497 HRESULT hrc = i_unlockMedia();
13498 return hrc;
13499}
13500
13501HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13502 ComPtr<IMediumAttachment> &aNewAttachment)
13503{
13504 // request the host lock first, since might be calling Host methods for getting host drives;
13505 // next, protect the media tree all the while we're in here, as well as our member variables
13506 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13507 this->lockHandle(),
13508 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13509
13510 IMediumAttachment *iAttach = aAttachment;
13511 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13512
13513 Bstr ctrlName;
13514 LONG lPort;
13515 LONG lDevice;
13516 bool fTempEject;
13517 {
13518 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13519
13520 /* Need to query the details first, as the IMediumAttachment reference
13521 * might be to the original settings, which we are going to change. */
13522 ctrlName = pAttach->i_getControllerName();
13523 lPort = pAttach->i_getPort();
13524 lDevice = pAttach->i_getDevice();
13525 fTempEject = pAttach->i_getTempEject();
13526 }
13527
13528 if (!fTempEject)
13529 {
13530 /* Remember previously mounted medium. The medium before taking the
13531 * backup is not necessarily the same thing. */
13532 ComObjPtr<Medium> oldmedium;
13533 oldmedium = pAttach->i_getMedium();
13534
13535 i_setModified(IsModified_Storage);
13536 mMediaData.backup();
13537
13538 // The backup operation makes the pAttach reference point to the
13539 // old settings. Re-get the correct reference.
13540 pAttach = i_findAttachment(mMediaData->mAttachments,
13541 ctrlName.raw(),
13542 lPort,
13543 lDevice);
13544
13545 {
13546 AutoCaller autoAttachCaller(this);
13547 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13548
13549 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13550 if (!oldmedium.isNull())
13551 oldmedium->i_removeBackReference(mData->mUuid);
13552
13553 pAttach->i_updateMedium(NULL);
13554 pAttach->i_updateEjected();
13555 }
13556
13557 i_setModified(IsModified_Storage);
13558 }
13559 else
13560 {
13561 {
13562 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13563 pAttach->i_updateEjected();
13564 }
13565 }
13566
13567 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13568
13569 return S_OK;
13570}
13571
13572// public methods only for internal purposes
13573/////////////////////////////////////////////////////////////////////////////
13574
13575#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13576/**
13577 * Called from the client watcher thread to check for expected or unexpected
13578 * death of the client process that has a direct session to this machine.
13579 *
13580 * On Win32 and on OS/2, this method is called only when we've got the
13581 * mutex (i.e. the client has either died or terminated normally) so it always
13582 * returns @c true (the client is terminated, the session machine is
13583 * uninitialized).
13584 *
13585 * On other platforms, the method returns @c true if the client process has
13586 * terminated normally or abnormally and the session machine was uninitialized,
13587 * and @c false if the client process is still alive.
13588 *
13589 * @note Locks this object for writing.
13590 */
13591bool SessionMachine::i_checkForDeath()
13592{
13593 Uninit::Reason reason;
13594 bool terminated = false;
13595
13596 /* Enclose autoCaller with a block because calling uninit() from under it
13597 * will deadlock. */
13598 {
13599 AutoCaller autoCaller(this);
13600 if (!autoCaller.isOk())
13601 {
13602 /* return true if not ready, to cause the client watcher to exclude
13603 * the corresponding session from watching */
13604 LogFlowThisFunc(("Already uninitialized!\n"));
13605 return true;
13606 }
13607
13608 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13609
13610 /* Determine the reason of death: if the session state is Closing here,
13611 * everything is fine. Otherwise it means that the client did not call
13612 * OnSessionEnd() before it released the IPC semaphore. This may happen
13613 * either because the client process has abnormally terminated, or
13614 * because it simply forgot to call ISession::Close() before exiting. We
13615 * threat the latter also as an abnormal termination (see
13616 * Session::uninit() for details). */
13617 reason = mData->mSession.mState == SessionState_Unlocking ?
13618 Uninit::Normal :
13619 Uninit::Abnormal;
13620
13621 if (mClientToken)
13622 terminated = mClientToken->release();
13623 } /* AutoCaller block */
13624
13625 if (terminated)
13626 uninit(reason);
13627
13628 return terminated;
13629}
13630
13631void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13632{
13633 LogFlowThisFunc(("\n"));
13634
13635 strTokenId.setNull();
13636
13637 AutoCaller autoCaller(this);
13638 AssertComRCReturnVoid(autoCaller.rc());
13639
13640 Assert(mClientToken);
13641 if (mClientToken)
13642 mClientToken->getId(strTokenId);
13643}
13644#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13645IToken *SessionMachine::i_getToken()
13646{
13647 LogFlowThisFunc(("\n"));
13648
13649 AutoCaller autoCaller(this);
13650 AssertComRCReturn(autoCaller.rc(), NULL);
13651
13652 Assert(mClientToken);
13653 if (mClientToken)
13654 return mClientToken->getToken();
13655 else
13656 return NULL;
13657}
13658#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13659
13660Machine::ClientToken *SessionMachine::i_getClientToken()
13661{
13662 LogFlowThisFunc(("\n"));
13663
13664 AutoCaller autoCaller(this);
13665 AssertComRCReturn(autoCaller.rc(), NULL);
13666
13667 return mClientToken;
13668}
13669
13670
13671/**
13672 * @note Locks this object for reading.
13673 */
13674HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
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 if (mData->mSession.mLockType == LockType_VM)
13685 directControl = mData->mSession.mDirectControl;
13686 }
13687
13688 /* ignore notifications sent after #OnSessionEnd() is called */
13689 if (!directControl)
13690 return S_OK;
13691
13692 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13693}
13694
13695/**
13696 * @note Locks this object for reading.
13697 */
13698HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13699 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13700 IN_BSTR aGuestIp, LONG aGuestPort)
13701{
13702 LogFlowThisFunc(("\n"));
13703
13704 AutoCaller autoCaller(this);
13705 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13706
13707 ComPtr<IInternalSessionControl> directControl;
13708 {
13709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13710 if (mData->mSession.mLockType == LockType_VM)
13711 directControl = mData->mSession.mDirectControl;
13712 }
13713
13714 /* ignore notifications sent after #OnSessionEnd() is called */
13715 if (!directControl)
13716 return S_OK;
13717 /*
13718 * instead acting like callback we ask IVirtualBox deliver corresponding event
13719 */
13720
13721 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13722 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13723 return S_OK;
13724}
13725
13726/**
13727 * @note Locks this object for reading.
13728 */
13729HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13730{
13731 LogFlowThisFunc(("\n"));
13732
13733 AutoCaller autoCaller(this);
13734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13735
13736 ComPtr<IInternalSessionControl> directControl;
13737 {
13738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13739 if (mData->mSession.mLockType == LockType_VM)
13740 directControl = mData->mSession.mDirectControl;
13741 }
13742
13743 /* ignore notifications sent after #OnSessionEnd() is called */
13744 if (!directControl)
13745 return S_OK;
13746
13747 return directControl->OnSerialPortChange(serialPort);
13748}
13749
13750/**
13751 * @note Locks this object for reading.
13752 */
13753HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13754{
13755 LogFlowThisFunc(("\n"));
13756
13757 AutoCaller autoCaller(this);
13758 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13759
13760 ComPtr<IInternalSessionControl> directControl;
13761 {
13762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13763 if (mData->mSession.mLockType == LockType_VM)
13764 directControl = mData->mSession.mDirectControl;
13765 }
13766
13767 /* ignore notifications sent after #OnSessionEnd() is called */
13768 if (!directControl)
13769 return S_OK;
13770
13771 return directControl->OnParallelPortChange(parallelPort);
13772}
13773
13774/**
13775 * @note Locks this object for reading.
13776 */
13777HRESULT SessionMachine::i_onStorageControllerChange()
13778{
13779 LogFlowThisFunc(("\n"));
13780
13781 AutoCaller autoCaller(this);
13782 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13783
13784 ComPtr<IInternalSessionControl> directControl;
13785 {
13786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13787 if (mData->mSession.mLockType == LockType_VM)
13788 directControl = mData->mSession.mDirectControl;
13789 }
13790
13791 /* ignore notifications sent after #OnSessionEnd() is called */
13792 if (!directControl)
13793 return S_OK;
13794
13795 return directControl->OnStorageControllerChange();
13796}
13797
13798/**
13799 * @note Locks this object for reading.
13800 */
13801HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13802{
13803 LogFlowThisFunc(("\n"));
13804
13805 AutoCaller autoCaller(this);
13806 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13807
13808 ComPtr<IInternalSessionControl> directControl;
13809 {
13810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13811 if (mData->mSession.mLockType == LockType_VM)
13812 directControl = mData->mSession.mDirectControl;
13813 }
13814
13815 /* ignore notifications sent after #OnSessionEnd() is called */
13816 if (!directControl)
13817 return S_OK;
13818
13819 return directControl->OnMediumChange(aAttachment, aForce);
13820}
13821
13822/**
13823 * @note Locks this object for reading.
13824 */
13825HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829 AutoCaller autoCaller(this);
13830 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13831
13832 ComPtr<IInternalSessionControl> directControl;
13833 {
13834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13835 if (mData->mSession.mLockType == LockType_VM)
13836 directControl = mData->mSession.mDirectControl;
13837 }
13838
13839 /* ignore notifications sent after #OnSessionEnd() is called */
13840 if (!directControl)
13841 return S_OK;
13842
13843 return directControl->OnCPUChange(aCPU, aRemove);
13844}
13845
13846HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13847{
13848 LogFlowThisFunc(("\n"));
13849
13850 AutoCaller autoCaller(this);
13851 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13852
13853 ComPtr<IInternalSessionControl> directControl;
13854 {
13855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13856 if (mData->mSession.mLockType == LockType_VM)
13857 directControl = mData->mSession.mDirectControl;
13858 }
13859
13860 /* ignore notifications sent after #OnSessionEnd() is called */
13861 if (!directControl)
13862 return S_OK;
13863
13864 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13865}
13866
13867/**
13868 * @note Locks this object for reading.
13869 */
13870HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13871{
13872 LogFlowThisFunc(("\n"));
13873
13874 AutoCaller autoCaller(this);
13875 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13876
13877 ComPtr<IInternalSessionControl> directControl;
13878 {
13879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13880 if (mData->mSession.mLockType == LockType_VM)
13881 directControl = mData->mSession.mDirectControl;
13882 }
13883
13884 /* ignore notifications sent after #OnSessionEnd() is called */
13885 if (!directControl)
13886 return S_OK;
13887
13888 return directControl->OnVRDEServerChange(aRestart);
13889}
13890
13891/**
13892 * @note Locks this object for reading.
13893 */
13894HRESULT SessionMachine::i_onVideoCaptureChange()
13895{
13896 LogFlowThisFunc(("\n"));
13897
13898 AutoCaller autoCaller(this);
13899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13900
13901 ComPtr<IInternalSessionControl> directControl;
13902 {
13903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13904 if (mData->mSession.mLockType == LockType_VM)
13905 directControl = mData->mSession.mDirectControl;
13906 }
13907
13908 /* ignore notifications sent after #OnSessionEnd() is called */
13909 if (!directControl)
13910 return S_OK;
13911
13912 return directControl->OnVideoCaptureChange();
13913}
13914
13915/**
13916 * @note Locks this object for reading.
13917 */
13918HRESULT SessionMachine::i_onUSBControllerChange()
13919{
13920 LogFlowThisFunc(("\n"));
13921
13922 AutoCaller autoCaller(this);
13923 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13924
13925 ComPtr<IInternalSessionControl> directControl;
13926 {
13927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13928 if (mData->mSession.mLockType == LockType_VM)
13929 directControl = mData->mSession.mDirectControl;
13930 }
13931
13932 /* ignore notifications sent after #OnSessionEnd() is called */
13933 if (!directControl)
13934 return S_OK;
13935
13936 return directControl->OnUSBControllerChange();
13937}
13938
13939/**
13940 * @note Locks this object for reading.
13941 */
13942HRESULT SessionMachine::i_onSharedFolderChange()
13943{
13944 LogFlowThisFunc(("\n"));
13945
13946 AutoCaller autoCaller(this);
13947 AssertComRCReturnRC(autoCaller.rc());
13948
13949 ComPtr<IInternalSessionControl> directControl;
13950 {
13951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13952 if (mData->mSession.mLockType == LockType_VM)
13953 directControl = mData->mSession.mDirectControl;
13954 }
13955
13956 /* ignore notifications sent after #OnSessionEnd() is called */
13957 if (!directControl)
13958 return S_OK;
13959
13960 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13961}
13962
13963/**
13964 * @note Locks this object for reading.
13965 */
13966HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13967{
13968 LogFlowThisFunc(("\n"));
13969
13970 AutoCaller autoCaller(this);
13971 AssertComRCReturnRC(autoCaller.rc());
13972
13973 ComPtr<IInternalSessionControl> directControl;
13974 {
13975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13976 if (mData->mSession.mLockType == LockType_VM)
13977 directControl = mData->mSession.mDirectControl;
13978 }
13979
13980 /* ignore notifications sent after #OnSessionEnd() is called */
13981 if (!directControl)
13982 return S_OK;
13983
13984 return directControl->OnClipboardModeChange(aClipboardMode);
13985}
13986
13987/**
13988 * @note Locks this object for reading.
13989 */
13990HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13991{
13992 LogFlowThisFunc(("\n"));
13993
13994 AutoCaller autoCaller(this);
13995 AssertComRCReturnRC(autoCaller.rc());
13996
13997 ComPtr<IInternalSessionControl> directControl;
13998 {
13999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14000 if (mData->mSession.mLockType == LockType_VM)
14001 directControl = mData->mSession.mDirectControl;
14002 }
14003
14004 /* ignore notifications sent after #OnSessionEnd() is called */
14005 if (!directControl)
14006 return S_OK;
14007
14008 return directControl->OnDnDModeChange(aDnDMode);
14009}
14010
14011/**
14012 * @note Locks this object for reading.
14013 */
14014HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14015{
14016 LogFlowThisFunc(("\n"));
14017
14018 AutoCaller autoCaller(this);
14019 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14020
14021 ComPtr<IInternalSessionControl> directControl;
14022 {
14023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14024 if (mData->mSession.mLockType == LockType_VM)
14025 directControl = mData->mSession.mDirectControl;
14026 }
14027
14028 /* ignore notifications sent after #OnSessionEnd() is called */
14029 if (!directControl)
14030 return S_OK;
14031
14032 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14033}
14034
14035/**
14036 * @note Locks this object for reading.
14037 */
14038HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14039{
14040 LogFlowThisFunc(("\n"));
14041
14042 AutoCaller autoCaller(this);
14043 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14044
14045 ComPtr<IInternalSessionControl> directControl;
14046 {
14047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14048 if (mData->mSession.mLockType == LockType_VM)
14049 directControl = mData->mSession.mDirectControl;
14050 }
14051
14052 /* ignore notifications sent after #OnSessionEnd() is called */
14053 if (!directControl)
14054 return S_OK;
14055
14056 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14057}
14058
14059/**
14060 * Returns @c true if this machine's USB controller reports it has a matching
14061 * filter for the given USB device and @c false otherwise.
14062 *
14063 * @note locks this object for reading.
14064 */
14065bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14066{
14067 AutoCaller autoCaller(this);
14068 /* silently return if not ready -- this method may be called after the
14069 * direct machine session has been called */
14070 if (!autoCaller.isOk())
14071 return false;
14072
14073#ifdef VBOX_WITH_USB
14074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14075
14076 switch (mData->mMachineState)
14077 {
14078 case MachineState_Starting:
14079 case MachineState_Restoring:
14080 case MachineState_TeleportingIn:
14081 case MachineState_Paused:
14082 case MachineState_Running:
14083 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14084 * elsewhere... */
14085 alock.release();
14086 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14087 default: break;
14088 }
14089#else
14090 NOREF(aDevice);
14091 NOREF(aMaskedIfs);
14092#endif
14093 return false;
14094}
14095
14096/**
14097 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14098 */
14099HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14100 IVirtualBoxErrorInfo *aError,
14101 ULONG aMaskedIfs,
14102 const com::Utf8Str &aCaptureFilename)
14103{
14104 LogFlowThisFunc(("\n"));
14105
14106 AutoCaller autoCaller(this);
14107
14108 /* This notification may happen after the machine object has been
14109 * uninitialized (the session was closed), so don't assert. */
14110 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14111
14112 ComPtr<IInternalSessionControl> directControl;
14113 {
14114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14115 if (mData->mSession.mLockType == LockType_VM)
14116 directControl = mData->mSession.mDirectControl;
14117 }
14118
14119 /* fail on notifications sent after #OnSessionEnd() is called, it is
14120 * expected by the caller */
14121 if (!directControl)
14122 return E_FAIL;
14123
14124 /* No locks should be held at this point. */
14125 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14126 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14127
14128 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14129}
14130
14131/**
14132 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14133 */
14134HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14135 IVirtualBoxErrorInfo *aError)
14136{
14137 LogFlowThisFunc(("\n"));
14138
14139 AutoCaller autoCaller(this);
14140
14141 /* This notification may happen after the machine object has been
14142 * uninitialized (the session was closed), so don't assert. */
14143 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14144
14145 ComPtr<IInternalSessionControl> directControl;
14146 {
14147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14148 if (mData->mSession.mLockType == LockType_VM)
14149 directControl = mData->mSession.mDirectControl;
14150 }
14151
14152 /* fail on notifications sent after #OnSessionEnd() is called, it is
14153 * expected by the caller */
14154 if (!directControl)
14155 return E_FAIL;
14156
14157 /* No locks should be held at this point. */
14158 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14159 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14160
14161 return directControl->OnUSBDeviceDetach(aId, aError);
14162}
14163
14164// protected methods
14165/////////////////////////////////////////////////////////////////////////////
14166
14167/**
14168 * Deletes the given file if it is no longer in use by either the current machine state
14169 * (if the machine is "saved") or any of the machine's snapshots.
14170 *
14171 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14172 * but is different for each SnapshotMachine. When calling this, the order of calling this
14173 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14174 * is therefore critical. I know, it's all rather messy.
14175 *
14176 * @param strStateFile
14177 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14178 * the test for whether the saved state file is in use.
14179 */
14180void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14181 Snapshot *pSnapshotToIgnore)
14182{
14183 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14184 if ( (strStateFile.isNotEmpty())
14185 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14186 )
14187 // ... and it must also not be shared with other snapshots
14188 if ( !mData->mFirstSnapshot
14189 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14190 // this checks the SnapshotMachine's state file paths
14191 )
14192 RTFileDelete(strStateFile.c_str());
14193}
14194
14195/**
14196 * Locks the attached media.
14197 *
14198 * All attached hard disks are locked for writing and DVD/floppy are locked for
14199 * reading. Parents of attached hard disks (if any) are locked for reading.
14200 *
14201 * This method also performs accessibility check of all media it locks: if some
14202 * media is inaccessible, the method will return a failure and a bunch of
14203 * extended error info objects per each inaccessible medium.
14204 *
14205 * Note that this method is atomic: if it returns a success, all media are
14206 * locked as described above; on failure no media is locked at all (all
14207 * succeeded individual locks will be undone).
14208 *
14209 * The caller is responsible for doing the necessary state sanity checks.
14210 *
14211 * The locks made by this method must be undone by calling #unlockMedia() when
14212 * no more needed.
14213 */
14214HRESULT SessionMachine::i_lockMedia()
14215{
14216 AutoCaller autoCaller(this);
14217 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14218
14219 AutoMultiWriteLock2 alock(this->lockHandle(),
14220 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14221
14222 /* bail out if trying to lock things with already set up locking */
14223 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14224
14225 MultiResult mrc(S_OK);
14226
14227 /* Collect locking information for all medium objects attached to the VM. */
14228 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14229 it != mMediaData->mAttachments.end();
14230 ++it)
14231 {
14232 MediumAttachment* pAtt = *it;
14233 DeviceType_T devType = pAtt->i_getType();
14234 Medium *pMedium = pAtt->i_getMedium();
14235
14236 MediumLockList *pMediumLockList(new MediumLockList());
14237 // There can be attachments without a medium (floppy/dvd), and thus
14238 // it's impossible to create a medium lock list. It still makes sense
14239 // to have the empty medium lock list in the map in case a medium is
14240 // attached later.
14241 if (pMedium != NULL)
14242 {
14243 MediumType_T mediumType = pMedium->i_getType();
14244 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14245 || mediumType == MediumType_Shareable;
14246 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14247
14248 alock.release();
14249 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14250 !fIsReadOnlyLock /* fMediumLockWrite */,
14251 false /* fMediumLockWriteAll */,
14252 NULL,
14253 *pMediumLockList);
14254 alock.acquire();
14255 if (FAILED(mrc))
14256 {
14257 delete pMediumLockList;
14258 mData->mSession.mLockedMedia.Clear();
14259 break;
14260 }
14261 }
14262
14263 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14264 if (FAILED(rc))
14265 {
14266 mData->mSession.mLockedMedia.Clear();
14267 mrc = setError(rc,
14268 tr("Collecting locking information for all attached media failed"));
14269 break;
14270 }
14271 }
14272
14273 if (SUCCEEDED(mrc))
14274 {
14275 /* Now lock all media. If this fails, nothing is locked. */
14276 alock.release();
14277 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14278 alock.acquire();
14279 if (FAILED(rc))
14280 {
14281 mrc = setError(rc,
14282 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14283 }
14284 }
14285
14286 return mrc;
14287}
14288
14289/**
14290 * Undoes the locks made by by #lockMedia().
14291 */
14292HRESULT SessionMachine::i_unlockMedia()
14293{
14294 AutoCaller autoCaller(this);
14295 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14296
14297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14298
14299 /* we may be holding important error info on the current thread;
14300 * preserve it */
14301 ErrorInfoKeeper eik;
14302
14303 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14304 AssertComRC(rc);
14305 return rc;
14306}
14307
14308/**
14309 * Helper to change the machine state (reimplementation).
14310 *
14311 * @note Locks this object for writing.
14312 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14313 * it can cause crashes in random places due to unexpectedly committing
14314 * the current settings. The caller is responsible for that. The call
14315 * to saveStateSettings is fine, because this method does not commit.
14316 */
14317HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14318{
14319 LogFlowThisFuncEnter();
14320 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14321
14322 AutoCaller autoCaller(this);
14323 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14324
14325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14326
14327 MachineState_T oldMachineState = mData->mMachineState;
14328
14329 AssertMsgReturn(oldMachineState != aMachineState,
14330 ("oldMachineState=%s, aMachineState=%s\n",
14331 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14332 E_FAIL);
14333
14334 HRESULT rc = S_OK;
14335
14336 int stsFlags = 0;
14337 bool deleteSavedState = false;
14338
14339 /* detect some state transitions */
14340
14341 if ( ( oldMachineState == MachineState_Saved
14342 && aMachineState == MachineState_Restoring)
14343 || ( ( oldMachineState == MachineState_PoweredOff
14344 || oldMachineState == MachineState_Teleported
14345 || oldMachineState == MachineState_Aborted
14346 )
14347 && ( aMachineState == MachineState_TeleportingIn
14348 || aMachineState == MachineState_Starting
14349 )
14350 )
14351 )
14352 {
14353 /* The EMT thread is about to start */
14354
14355 /* Nothing to do here for now... */
14356
14357 /// @todo NEWMEDIA don't let mDVDDrive and other children
14358 /// change anything when in the Starting/Restoring state
14359 }
14360 else if ( ( oldMachineState == MachineState_Running
14361 || oldMachineState == MachineState_Paused
14362 || oldMachineState == MachineState_Teleporting
14363 || oldMachineState == MachineState_OnlineSnapshotting
14364 || oldMachineState == MachineState_LiveSnapshotting
14365 || oldMachineState == MachineState_Stuck
14366 || oldMachineState == MachineState_Starting
14367 || oldMachineState == MachineState_Stopping
14368 || oldMachineState == MachineState_Saving
14369 || oldMachineState == MachineState_Restoring
14370 || oldMachineState == MachineState_TeleportingPausedVM
14371 || oldMachineState == MachineState_TeleportingIn
14372 )
14373 && ( aMachineState == MachineState_PoweredOff
14374 || aMachineState == MachineState_Saved
14375 || aMachineState == MachineState_Teleported
14376 || aMachineState == MachineState_Aborted
14377 )
14378 )
14379 {
14380 /* The EMT thread has just stopped, unlock attached media. Note that as
14381 * opposed to locking that is done from Console, we do unlocking here
14382 * because the VM process may have aborted before having a chance to
14383 * properly unlock all media it locked. */
14384
14385 unlockMedia();
14386 }
14387
14388 if (oldMachineState == MachineState_Restoring)
14389 {
14390 if (aMachineState != MachineState_Saved)
14391 {
14392 /*
14393 * delete the saved state file once the machine has finished
14394 * restoring from it (note that Console sets the state from
14395 * Restoring to Saved if the VM couldn't restore successfully,
14396 * to give the user an ability to fix an error and retry --
14397 * we keep the saved state file in this case)
14398 */
14399 deleteSavedState = true;
14400 }
14401 }
14402 else if ( oldMachineState == MachineState_Saved
14403 && ( aMachineState == MachineState_PoweredOff
14404 || aMachineState == MachineState_Aborted
14405 || aMachineState == MachineState_Teleported
14406 )
14407 )
14408 {
14409 /*
14410 * delete the saved state after SessionMachine::ForgetSavedState() is called
14411 * or if the VM process (owning a direct VM session) crashed while the
14412 * VM was Saved
14413 */
14414
14415 /// @todo (dmik)
14416 // Not sure that deleting the saved state file just because of the
14417 // client death before it attempted to restore the VM is a good
14418 // thing. But when it crashes we need to go to the Aborted state
14419 // which cannot have the saved state file associated... The only
14420 // way to fix this is to make the Aborted condition not a VM state
14421 // but a bool flag: i.e., when a crash occurs, set it to true and
14422 // change the state to PoweredOff or Saved depending on the
14423 // saved state presence.
14424
14425 deleteSavedState = true;
14426 mData->mCurrentStateModified = TRUE;
14427 stsFlags |= SaveSTS_CurStateModified;
14428 }
14429
14430 if ( aMachineState == MachineState_Starting
14431 || aMachineState == MachineState_Restoring
14432 || aMachineState == MachineState_TeleportingIn
14433 )
14434 {
14435 /* set the current state modified flag to indicate that the current
14436 * state is no more identical to the state in the
14437 * current snapshot */
14438 if (!mData->mCurrentSnapshot.isNull())
14439 {
14440 mData->mCurrentStateModified = TRUE;
14441 stsFlags |= SaveSTS_CurStateModified;
14442 }
14443 }
14444
14445 if (deleteSavedState)
14446 {
14447 if (mRemoveSavedState)
14448 {
14449 Assert(!mSSData->strStateFilePath.isEmpty());
14450
14451 // it is safe to delete the saved state file if ...
14452 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14453 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14454 // ... none of the snapshots share the saved state file
14455 )
14456 RTFileDelete(mSSData->strStateFilePath.c_str());
14457 }
14458
14459 mSSData->strStateFilePath.setNull();
14460 stsFlags |= SaveSTS_StateFilePath;
14461 }
14462
14463 /* redirect to the underlying peer machine */
14464 mPeer->i_setMachineState(aMachineState);
14465
14466 if ( aMachineState == MachineState_PoweredOff
14467 || aMachineState == MachineState_Teleported
14468 || aMachineState == MachineState_Aborted
14469 || aMachineState == MachineState_Saved)
14470 {
14471 /* the machine has stopped execution
14472 * (or the saved state file was adopted) */
14473 stsFlags |= SaveSTS_StateTimeStamp;
14474 }
14475
14476 if ( ( oldMachineState == MachineState_PoweredOff
14477 || oldMachineState == MachineState_Aborted
14478 || oldMachineState == MachineState_Teleported
14479 )
14480 && aMachineState == MachineState_Saved)
14481 {
14482 /* the saved state file was adopted */
14483 Assert(!mSSData->strStateFilePath.isEmpty());
14484 stsFlags |= SaveSTS_StateFilePath;
14485 }
14486
14487#ifdef VBOX_WITH_GUEST_PROPS
14488 if ( aMachineState == MachineState_PoweredOff
14489 || aMachineState == MachineState_Aborted
14490 || aMachineState == MachineState_Teleported)
14491 {
14492 /* Make sure any transient guest properties get removed from the
14493 * property store on shutdown. */
14494 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14495
14496 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14497 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14498 while (it != llGuestProperties.end())
14499 {
14500 const settings::GuestProperty &prop = *it;
14501 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14502 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14503 {
14504 it = llGuestProperties.erase(it);
14505 fNeedsSaving = true;
14506 }
14507 else
14508 {
14509 ++it;
14510 }
14511 }
14512
14513 if (fNeedsSaving)
14514 {
14515 mData->mCurrentStateModified = TRUE;
14516 stsFlags |= SaveSTS_CurStateModified;
14517 }
14518 }
14519#endif /* VBOX_WITH_GUEST_PROPS */
14520
14521 rc = i_saveStateSettings(stsFlags);
14522
14523 if ( ( oldMachineState != MachineState_PoweredOff
14524 && oldMachineState != MachineState_Aborted
14525 && oldMachineState != MachineState_Teleported
14526 )
14527 && ( aMachineState == MachineState_PoweredOff
14528 || aMachineState == MachineState_Aborted
14529 || aMachineState == MachineState_Teleported
14530 )
14531 )
14532 {
14533 /* we've been shut down for any reason */
14534 /* no special action so far */
14535 }
14536
14537 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14538 LogFlowThisFuncLeave();
14539 return rc;
14540}
14541
14542/**
14543 * Sends the current machine state value to the VM process.
14544 *
14545 * @note Locks this object for reading, then calls a client process.
14546 */
14547HRESULT SessionMachine::i_updateMachineStateOnClient()
14548{
14549 AutoCaller autoCaller(this);
14550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14551
14552 ComPtr<IInternalSessionControl> directControl;
14553 {
14554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14555 AssertReturn(!!mData, E_FAIL);
14556 if (mData->mSession.mLockType == LockType_VM)
14557 directControl = mData->mSession.mDirectControl;
14558
14559 /* directControl may be already set to NULL here in #OnSessionEnd()
14560 * called too early by the direct session process while there is still
14561 * some operation (like deleting the snapshot) in progress. The client
14562 * process in this case is waiting inside Session::close() for the
14563 * "end session" process object to complete, while #uninit() called by
14564 * #checkForDeath() on the Watcher thread is waiting for the pending
14565 * operation to complete. For now, we accept this inconsistent behavior
14566 * and simply do nothing here. */
14567
14568 if (mData->mSession.mState == SessionState_Unlocking)
14569 return S_OK;
14570 }
14571
14572 /* ignore notifications sent after #OnSessionEnd() is called */
14573 if (!directControl)
14574 return S_OK;
14575
14576 return directControl->UpdateMachineState(mData->mMachineState);
14577}
14578
14579
14580/**
14581 * Static Machine method that can get passed to RTThreadCreate to
14582 * have a thread started for a Task. See Machine::Task.
14583 */
14584/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14585{
14586 AssertReturn(pvUser, VERR_INVALID_POINTER);
14587
14588 Task *pTask = static_cast<Task *>(pvUser);
14589 pTask->handler();
14590 /** @todo r=klaus it would be safer to update the progress object here,
14591 * as it avoids possible races due to scoping issues/tricks in the handler */
14592 // it's our responsibility to delete the task
14593 delete pTask;
14594
14595 return 0;
14596}
14597
14598/*static*/
14599HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14600{
14601 va_list args;
14602 va_start(args, pcszMsg);
14603 HRESULT rc = setErrorInternal(aResultCode,
14604 getStaticClassIID(),
14605 getStaticComponentName(),
14606 Utf8Str(pcszMsg, args),
14607 false /* aWarning */,
14608 true /* aLogIt */);
14609 va_end(args);
14610 return rc;
14611}
14612
14613
14614HRESULT Machine::updateState(MachineState_T aState)
14615{
14616 NOREF(aState);
14617 ReturnComNotImplemented();
14618}
14619
14620HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14621{
14622 NOREF(aProgress);
14623 ReturnComNotImplemented();
14624}
14625
14626HRESULT Machine::endPowerUp(LONG aResult)
14627{
14628 NOREF(aResult);
14629 ReturnComNotImplemented();
14630}
14631
14632HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14633{
14634 NOREF(aProgress);
14635 ReturnComNotImplemented();
14636}
14637
14638HRESULT Machine::endPoweringDown(LONG aResult,
14639 const com::Utf8Str &aErrMsg)
14640{
14641 NOREF(aResult);
14642 NOREF(aErrMsg);
14643 ReturnComNotImplemented();
14644}
14645
14646HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14647 BOOL *aMatched,
14648 ULONG *aMaskedInterfaces)
14649{
14650 NOREF(aDevice);
14651 NOREF(aMatched);
14652 NOREF(aMaskedInterfaces);
14653 ReturnComNotImplemented();
14654
14655}
14656
14657HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14658{
14659 NOREF(aId); NOREF(aCaptureFilename);
14660 ReturnComNotImplemented();
14661}
14662
14663HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14664 BOOL aDone)
14665{
14666 NOREF(aId);
14667 NOREF(aDone);
14668 ReturnComNotImplemented();
14669}
14670
14671HRESULT Machine::autoCaptureUSBDevices()
14672{
14673 ReturnComNotImplemented();
14674}
14675
14676HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14677{
14678 NOREF(aDone);
14679 ReturnComNotImplemented();
14680}
14681
14682HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14683 ComPtr<IProgress> &aProgress)
14684{
14685 NOREF(aSession);
14686 NOREF(aProgress);
14687 ReturnComNotImplemented();
14688}
14689
14690HRESULT Machine::finishOnlineMergeMedium()
14691{
14692 ReturnComNotImplemented();
14693}
14694
14695HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14696 std::vector<com::Utf8Str> &aValues,
14697 std::vector<LONG64> &aTimestamps,
14698 std::vector<com::Utf8Str> &aFlags)
14699{
14700 NOREF(aNames);
14701 NOREF(aValues);
14702 NOREF(aTimestamps);
14703 NOREF(aFlags);
14704 ReturnComNotImplemented();
14705}
14706
14707HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14708 const com::Utf8Str &aValue,
14709 LONG64 aTimestamp,
14710 const com::Utf8Str &aFlags,
14711 BOOL *aNotify)
14712{
14713 NOREF(aName);
14714 NOREF(aValue);
14715 NOREF(aTimestamp);
14716 NOREF(aFlags);
14717 NOREF(aNotify);
14718 ReturnComNotImplemented();
14719}
14720
14721HRESULT Machine::lockMedia()
14722{
14723 ReturnComNotImplemented();
14724}
14725
14726HRESULT Machine::unlockMedia()
14727{
14728 ReturnComNotImplemented();
14729}
14730
14731HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14732 ComPtr<IMediumAttachment> &aNewAttachment)
14733{
14734 NOREF(aAttachment);
14735 NOREF(aNewAttachment);
14736 ReturnComNotImplemented();
14737}
14738
14739HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14740 ULONG aCpuUser,
14741 ULONG aCpuKernel,
14742 ULONG aCpuIdle,
14743 ULONG aMemTotal,
14744 ULONG aMemFree,
14745 ULONG aMemBalloon,
14746 ULONG aMemShared,
14747 ULONG aMemCache,
14748 ULONG aPagedTotal,
14749 ULONG aMemAllocTotal,
14750 ULONG aMemFreeTotal,
14751 ULONG aMemBalloonTotal,
14752 ULONG aMemSharedTotal,
14753 ULONG aVmNetRx,
14754 ULONG aVmNetTx)
14755{
14756 NOREF(aValidStats);
14757 NOREF(aCpuUser);
14758 NOREF(aCpuKernel);
14759 NOREF(aCpuIdle);
14760 NOREF(aMemTotal);
14761 NOREF(aMemFree);
14762 NOREF(aMemBalloon);
14763 NOREF(aMemShared);
14764 NOREF(aMemCache);
14765 NOREF(aPagedTotal);
14766 NOREF(aMemAllocTotal);
14767 NOREF(aMemFreeTotal);
14768 NOREF(aMemBalloonTotal);
14769 NOREF(aMemSharedTotal);
14770 NOREF(aVmNetRx);
14771 NOREF(aVmNetTx);
14772 ReturnComNotImplemented();
14773}
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