VirtualBox

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

Last change on this file since 58650 was 58553, checked in by vboxsync, 9 years ago

Main/Machine: signal error on the poweroff progress object if the VM process aborted before finishing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 514.5 KB
Line 
1/* $Id: MachineImpl.cpp 58553 2015-11-03 16:40:57Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69#include <iprt/base64.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mHPETEnabled = false;
197 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
198 mCpuIdPortabilityLevel = 0;
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
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222}
223
224Machine::HWData::~HWData()
225{
226}
227
228/////////////////////////////////////////////////////////////////////////////
229// Machine::HDData structure
230/////////////////////////////////////////////////////////////////////////////
231
232Machine::MediaData::MediaData()
233{
234}
235
236Machine::MediaData::~MediaData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine() :
248#ifdef VBOX_WITH_RESOURCE_USAGE_API
249 mCollectorGuest(NULL),
250#endif
251 mPeer(NULL),
252 mParent(NULL),
253 mSerialPorts(),
254 mParallelPorts(),
255 uRegistryNeedsSaving(0)
256{}
257
258Machine::~Machine()
259{}
260
261HRESULT Machine::FinalConstruct()
262{
263 LogFlowThisFunc(("\n"));
264 return BaseFinalConstruct();
265}
266
267void Machine::FinalRelease()
268{
269 LogFlowThisFunc(("\n"));
270 uninit();
271 BaseFinalRelease();
272}
273
274/**
275 * Initializes a new machine instance; this init() variant creates a new, empty machine.
276 * This gets called from VirtualBox::CreateMachine().
277 *
278 * @param aParent Associated parent object
279 * @param strConfigFile Local file system path to the VM settings file (can
280 * be relative to the VirtualBox config directory).
281 * @param strName name for the machine
282 * @param llGroups list of groups for the machine
283 * @param aOsType OS Type of this machine or NULL.
284 * @param aId UUID for the new machine.
285 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 mUserData->s.llGroups = llGroups;
329
330 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
331 // the "name sync" flag determines whether the machine directory gets renamed along
332 // with the machine file; say so if the settings file name is the same as the
333 // settings file parent directory (machine directory)
334 mUserData->s.fNameSync = i_isInOwnDir();
335
336 // initialize the default snapshots folder
337 rc = COMSETTER(SnapshotFolder)(NULL);
338 AssertComRC(rc);
339
340 if (aOsType)
341 {
342 /* Store OS type */
343 mUserData->s.strOsType = aOsType->i_id();
344
345 /* Apply BIOS defaults */
346 mBIOSSettings->i_applyDefaults(aOsType);
347
348 /* Apply network adapters defaults */
349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
350 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
351
352 /* Apply serial port defaults */
353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
354 mSerialPorts[slot]->i_applyDefaults(aOsType);
355
356 /* Apply parallel port defaults */
357 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
358 mParallelPorts[slot]->i_applyDefaults();
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 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
767
768 /* rollback all changes */
769 i_rollback(false /* aNotify */);
770
771 // uninit media from this machine's media registry, or else
772 // reloading the settings will fail
773 mParent->i_unregisterMachineMedia(i_getId());
774
775 /* uninitialize the common part to make sure all data is reset to
776 * default (null) values */
777 uninitDataAndChildObjects();
778
779 rc = S_OK;
780 }
781
782 return rc;
783}
784
785/**
786 * Uninitializes the instance.
787 * Called either from FinalRelease() or by the parent when it gets destroyed.
788 *
789 * @note The caller of this method must make sure that this object
790 * a) doesn't have active callers on the current thread and b) is not locked
791 * by the current thread; otherwise uninit() will hang either a) due to
792 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
793 * a dead-lock caused by this thread waiting for all callers on the other
794 * threads are done but preventing them from doing so by holding a lock.
795 */
796void Machine::uninit()
797{
798 LogFlowThisFuncEnter();
799
800 Assert(!isWriteLockOnCurrentThread());
801
802 Assert(!uRegistryNeedsSaving);
803 if (uRegistryNeedsSaving)
804 {
805 AutoCaller autoCaller(this);
806 if (SUCCEEDED(autoCaller.rc()))
807 {
808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
809 i_saveSettings(NULL, Machine::SaveS_Force);
810 }
811 }
812
813 /* Enclose the state transition Ready->InUninit->NotReady */
814 AutoUninitSpan autoUninitSpan(this);
815 if (autoUninitSpan.uninitDone())
816 return;
817
818 Assert(!i_isSnapshotMachine());
819 Assert(!i_isSessionMachine());
820 Assert(!!mData);
821
822 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
823 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
824
825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
826
827 if (!mData->mSession.mMachine.isNull())
828 {
829 /* Theoretically, this can only happen if the VirtualBox server has been
830 * terminated while there were clients running that owned open direct
831 * sessions. Since in this case we are definitely called by
832 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
833 * won't happen on the client watcher thread (because it does
834 * VirtualBox::addCaller() for the duration of the
835 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
836 * cannot happen until the VirtualBox caller is released). This is
837 * important, because SessionMachine::uninit() cannot correctly operate
838 * after we return from this method (it expects the Machine instance is
839 * still valid). We'll call it ourselves below.
840 */
841 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
842 (SessionMachine*)mData->mSession.mMachine));
843
844 if (Global::IsOnlineOrTransient(mData->mMachineState))
845 {
846 Log1WarningThisFunc(("Setting state to Aborted!\n"));
847 /* set machine state using SessionMachine reimplementation */
848 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
849 }
850
851 /*
852 * Uninitialize SessionMachine using public uninit() to indicate
853 * an unexpected uninitialization.
854 */
855 mData->mSession.mMachine->uninit();
856 /* SessionMachine::uninit() must set mSession.mMachine to null */
857 Assert(mData->mSession.mMachine.isNull());
858 }
859
860 // uninit media from this machine's media registry, if they're still there
861 Guid uuidMachine(i_getId());
862
863 /* the lock is no more necessary (SessionMachine is uninitialized) */
864 alock.release();
865
866 /* XXX This will fail with
867 * "cannot be closed because it is still attached to 1 virtual machines"
868 * because at this point we did not call uninitDataAndChildObjects() yet
869 * and therefore also removeBackReference() for all these mediums was not called! */
870
871 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
872 mParent->i_unregisterMachineMedia(uuidMachine);
873
874 // has machine been modified?
875 if (mData->flModifications)
876 {
877 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
878 i_rollback(false /* aNotify */);
879 }
880
881 if (mData->mAccessible)
882 uninitDataAndChildObjects();
883
884 /* free the essential data structure last */
885 mData.free();
886
887 LogFlowThisFuncLeave();
888}
889
890// Wrapped IMachine properties
891/////////////////////////////////////////////////////////////////////////////
892HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
893{
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 aParent = pVirtualBox;
897
898 return S_OK;
899}
900
901
902HRESULT Machine::getAccessible(BOOL *aAccessible)
903{
904 /* In some cases (medium registry related), it is necessary to be able to
905 * go through the list of all machines. Happens when an inaccessible VM
906 * has a sensible medium registry. */
907 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
909
910 HRESULT rc = S_OK;
911
912 if (!mData->mAccessible)
913 {
914 /* try to initialize the VM once more if not accessible */
915
916 AutoReinitSpan autoReinitSpan(this);
917 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
918
919#ifdef DEBUG
920 LogFlowThisFunc(("Dumping media backreferences\n"));
921 mParent->i_dumpAllBackRefs();
922#endif
923
924 if (mData->pMachineConfigFile)
925 {
926 // reset the XML file to force loadSettings() (called from registeredInit())
927 // to parse it again; the file might have changed
928 delete mData->pMachineConfigFile;
929 mData->pMachineConfigFile = NULL;
930 }
931
932 rc = i_registeredInit();
933
934 if (SUCCEEDED(rc) && mData->mAccessible)
935 {
936 autoReinitSpan.setSucceeded();
937
938 /* make sure interesting parties will notice the accessibility
939 * state change */
940 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
941 mParent->i_onMachineDataChange(mData->mUuid);
942 }
943 }
944
945 if (SUCCEEDED(rc))
946 *aAccessible = mData->mAccessible;
947
948 LogFlowThisFuncLeave();
949
950 return rc;
951}
952
953HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
954{
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
958 {
959 /* return shortly */
960 aAccessError = NULL;
961 return S_OK;
962 }
963
964 HRESULT rc = S_OK;
965
966 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
967 rc = errorInfo.createObject();
968 if (SUCCEEDED(rc))
969 {
970 errorInfo->init(mData->mAccessError.getResultCode(),
971 mData->mAccessError.getInterfaceID().ref(),
972 Utf8Str(mData->mAccessError.getComponent()).c_str(),
973 Utf8Str(mData->mAccessError.getText()));
974 aAccessError = errorInfo;
975 }
976
977 return rc;
978}
979
980HRESULT Machine::getName(com::Utf8Str &aName)
981{
982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
983
984 aName = mUserData->s.strName;
985
986 return S_OK;
987}
988
989HRESULT Machine::setName(const com::Utf8Str &aName)
990{
991 // prohibit setting a UUID only as the machine name, or else it can
992 // never be found by findMachine()
993 Guid test(aName);
994
995 if (test.isValid())
996 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
997
998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 HRESULT rc = i_checkStateDependency(MutableStateDep);
1001 if (FAILED(rc)) return rc;
1002
1003 i_setModified(IsModified_MachineData);
1004 mUserData.backup();
1005 mUserData->s.strName = aName;
1006
1007 return S_OK;
1008}
1009
1010HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1011{
1012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 aDescription = mUserData->s.strDescription;
1015
1016 return S_OK;
1017}
1018
1019HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1020{
1021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 // this can be done in principle in any state as it doesn't affect the VM
1024 // significantly, but play safe by not messing around while complex
1025 // activities are going on
1026 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1027 if (FAILED(rc)) return rc;
1028
1029 i_setModified(IsModified_MachineData);
1030 mUserData.backup();
1031 mUserData->s.strDescription = aDescription;
1032
1033 return S_OK;
1034}
1035
1036HRESULT Machine::getId(com::Guid &aId)
1037{
1038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 aId = mData->mUuid;
1041
1042 return S_OK;
1043}
1044
1045HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1046{
1047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1048 aGroups.resize(mUserData->s.llGroups.size());
1049 size_t i = 0;
1050 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1051 it != mUserData->s.llGroups.end(); ++it, ++i)
1052 aGroups[i] = (*it);
1053
1054 return S_OK;
1055}
1056
1057HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1058{
1059 StringsList llGroups;
1060 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1061 if (FAILED(rc))
1062 return rc;
1063
1064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1065
1066 rc = i_checkStateDependency(MutableOrSavedStateDep);
1067 if (FAILED(rc)) return rc;
1068
1069 i_setModified(IsModified_MachineData);
1070 mUserData.backup();
1071 mUserData->s.llGroups = llGroups;
1072
1073 return S_OK;
1074}
1075
1076HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1077{
1078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 aOSTypeId = mUserData->s.strOsType;
1081
1082 return S_OK;
1083}
1084
1085HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1086{
1087 /* look up the object by Id to check it is valid */
1088 ComPtr<IGuestOSType> guestOSType;
1089 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1090 if (FAILED(rc)) return rc;
1091
1092 /* when setting, always use the "etalon" value for consistency -- lookup
1093 * by ID is case-insensitive and the input value may have different case */
1094 Bstr osTypeId;
1095 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1096 if (FAILED(rc)) return rc;
1097
1098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1099
1100 rc = i_checkStateDependency(MutableStateDep);
1101 if (FAILED(rc)) return rc;
1102
1103 i_setModified(IsModified_MachineData);
1104 mUserData.backup();
1105 mUserData->s.strOsType = osTypeId;
1106
1107 return S_OK;
1108}
1109
1110HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1111{
1112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 *aFirmwareType = mHWData->mFirmwareType;
1115
1116 return S_OK;
1117}
1118
1119HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1120{
1121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1122
1123 HRESULT rc = i_checkStateDependency(MutableStateDep);
1124 if (FAILED(rc)) return rc;
1125
1126 i_setModified(IsModified_MachineData);
1127 mHWData.backup();
1128 mHWData->mFirmwareType = aFirmwareType;
1129
1130 return S_OK;
1131}
1132
1133HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1134{
1135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1136
1137 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1138
1139 return S_OK;
1140}
1141
1142HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1143{
1144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 HRESULT rc = i_checkStateDependency(MutableStateDep);
1147 if (FAILED(rc)) return rc;
1148
1149 i_setModified(IsModified_MachineData);
1150 mHWData.backup();
1151 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1152
1153 return S_OK;
1154}
1155
1156HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1157{
1158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1159
1160 *aPointingHIDType = mHWData->mPointingHIDType;
1161
1162 return S_OK;
1163}
1164
1165HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1166{
1167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 HRESULT rc = i_checkStateDependency(MutableStateDep);
1170 if (FAILED(rc)) return rc;
1171
1172 i_setModified(IsModified_MachineData);
1173 mHWData.backup();
1174 mHWData->mPointingHIDType = aPointingHIDType;
1175
1176 return S_OK;
1177}
1178
1179HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1180{
1181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1182
1183 *aChipsetType = mHWData->mChipsetType;
1184
1185 return S_OK;
1186}
1187
1188HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1189{
1190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 HRESULT rc = i_checkStateDependency(MutableStateDep);
1193 if (FAILED(rc)) return rc;
1194
1195 if (aChipsetType != mHWData->mChipsetType)
1196 {
1197 i_setModified(IsModified_MachineData);
1198 mHWData.backup();
1199 mHWData->mChipsetType = aChipsetType;
1200
1201 // Resize network adapter array, to be finalized on commit/rollback.
1202 // We must not throw away entries yet, otherwise settings are lost
1203 // without a way to roll back.
1204 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1205 size_t oldCount = mNetworkAdapters.size();
1206 if (newCount > oldCount)
1207 {
1208 mNetworkAdapters.resize(newCount);
1209 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1210 {
1211 unconst(mNetworkAdapters[slot]).createObject();
1212 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1213 }
1214 }
1215 }
1216
1217 return S_OK;
1218}
1219
1220HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1221{
1222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1223
1224 aParavirtDebug = mHWData->mParavirtDebug;
1225 return S_OK;
1226}
1227
1228HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1229{
1230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1231
1232 HRESULT rc = i_checkStateDependency(MutableStateDep);
1233 if (FAILED(rc)) return rc;
1234
1235 /** @todo Parse/validate options? */
1236 if (aParavirtDebug != mHWData->mParavirtDebug)
1237 {
1238 i_setModified(IsModified_MachineData);
1239 mHWData.backup();
1240 mHWData->mParavirtDebug = aParavirtDebug;
1241 }
1242
1243 return S_OK;
1244}
1245
1246HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1247{
1248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 *aParavirtProvider = mHWData->mParavirtProvider;
1251
1252 return S_OK;
1253}
1254
1255HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1256{
1257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1258
1259 HRESULT rc = i_checkStateDependency(MutableStateDep);
1260 if (FAILED(rc)) return rc;
1261
1262 if (aParavirtProvider != mHWData->mParavirtProvider)
1263 {
1264 i_setModified(IsModified_MachineData);
1265 mHWData.backup();
1266 mHWData->mParavirtProvider = aParavirtProvider;
1267 }
1268
1269 return S_OK;
1270}
1271
1272HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1273{
1274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 *aParavirtProvider = mHWData->mParavirtProvider;
1277 switch (mHWData->mParavirtProvider)
1278 {
1279 case ParavirtProvider_None:
1280 case ParavirtProvider_HyperV:
1281 case ParavirtProvider_KVM:
1282 case ParavirtProvider_Minimal:
1283 break;
1284
1285 /* Resolve dynamic provider types to the effective types. */
1286 default:
1287 {
1288 ComPtr<IGuestOSType> ptrGuestOSType;
1289 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1290 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1291
1292 Bstr guestTypeFamilyId;
1293 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1294 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1295 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1296
1297 switch (mHWData->mParavirtProvider)
1298 {
1299 case ParavirtProvider_Legacy:
1300 {
1301 if (fOsXGuest)
1302 *aParavirtProvider = ParavirtProvider_Minimal;
1303 else
1304 *aParavirtProvider = ParavirtProvider_None;
1305 break;
1306 }
1307
1308 case ParavirtProvider_Default:
1309 {
1310 if (fOsXGuest)
1311 *aParavirtProvider = ParavirtProvider_Minimal;
1312 else if ( mUserData->s.strOsType == "Windows10"
1313 || mUserData->s.strOsType == "Windows10_64"
1314 || mUserData->s.strOsType == "Windows81"
1315 || mUserData->s.strOsType == "Windows81_64"
1316 || mUserData->s.strOsType == "Windows8"
1317 || mUserData->s.strOsType == "Windows8_64"
1318 || mUserData->s.strOsType == "Windows7"
1319 || mUserData->s.strOsType == "Windows7_64"
1320 || mUserData->s.strOsType == "WindowsVista"
1321 || mUserData->s.strOsType == "WindowsVista_64"
1322 || mUserData->s.strOsType == "Windows2012"
1323 || mUserData->s.strOsType == "Windows2012_64"
1324 || mUserData->s.strOsType == "Windows2008"
1325 || mUserData->s.strOsType == "Windows2008_64")
1326 {
1327 *aParavirtProvider = ParavirtProvider_HyperV;
1328 }
1329 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1330 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1331 || mUserData->s.strOsType == "Linux"
1332 || mUserData->s.strOsType == "Linux_64"
1333 || mUserData->s.strOsType == "ArchLinux"
1334 || mUserData->s.strOsType == "ArchLinux_64"
1335 || mUserData->s.strOsType == "Debian"
1336 || mUserData->s.strOsType == "Debian_64"
1337 || mUserData->s.strOsType == "Fedora"
1338 || mUserData->s.strOsType == "Fedora_64"
1339 || mUserData->s.strOsType == "Gentoo"
1340 || mUserData->s.strOsType == "Gentoo_64"
1341 || mUserData->s.strOsType == "Mandriva"
1342 || mUserData->s.strOsType == "Mandriva_64"
1343 || mUserData->s.strOsType == "OpenSUSE"
1344 || mUserData->s.strOsType == "OpenSUSE_64"
1345 || mUserData->s.strOsType == "Oracle"
1346 || mUserData->s.strOsType == "Oracle_64"
1347 || mUserData->s.strOsType == "RedHat"
1348 || mUserData->s.strOsType == "RedHat_64"
1349 || mUserData->s.strOsType == "Turbolinux"
1350 || mUserData->s.strOsType == "Turbolinux_64"
1351 || mUserData->s.strOsType == "Ubuntu"
1352 || mUserData->s.strOsType == "Ubuntu_64"
1353 || mUserData->s.strOsType == "Xandros"
1354 || mUserData->s.strOsType == "Xandros_64")
1355 {
1356 *aParavirtProvider = ParavirtProvider_KVM;
1357 }
1358 else
1359 *aParavirtProvider = ParavirtProvider_None;
1360 break;
1361 }
1362 }
1363 break;
1364 }
1365 }
1366
1367 Assert( *aParavirtProvider == ParavirtProvider_None
1368 || *aParavirtProvider == ParavirtProvider_Minimal
1369 || *aParavirtProvider == ParavirtProvider_HyperV
1370 || *aParavirtProvider == ParavirtProvider_KVM);
1371 return S_OK;
1372}
1373
1374HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1375{
1376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1377
1378 aHardwareVersion = mHWData->mHWVersion;
1379
1380 return S_OK;
1381}
1382
1383HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1384{
1385 /* check known version */
1386 Utf8Str hwVersion = aHardwareVersion;
1387 if ( hwVersion.compare("1") != 0
1388 && hwVersion.compare("2") != 0)
1389 return setError(E_INVALIDARG,
1390 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1391
1392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 HRESULT rc = i_checkStateDependency(MutableStateDep);
1395 if (FAILED(rc)) return rc;
1396
1397 i_setModified(IsModified_MachineData);
1398 mHWData.backup();
1399 mHWData->mHWVersion = aHardwareVersion;
1400
1401 return S_OK;
1402}
1403
1404HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1405{
1406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 if (!mHWData->mHardwareUUID.isZero())
1409 aHardwareUUID = mHWData->mHardwareUUID;
1410 else
1411 aHardwareUUID = mData->mUuid;
1412
1413 return S_OK;
1414}
1415
1416HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1417{
1418 if (!aHardwareUUID.isValid())
1419 return E_INVALIDARG;
1420
1421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1422
1423 HRESULT rc = i_checkStateDependency(MutableStateDep);
1424 if (FAILED(rc)) return rc;
1425
1426 i_setModified(IsModified_MachineData);
1427 mHWData.backup();
1428 if (aHardwareUUID == mData->mUuid)
1429 mHWData->mHardwareUUID.clear();
1430 else
1431 mHWData->mHardwareUUID = aHardwareUUID;
1432
1433 return S_OK;
1434}
1435
1436HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1437{
1438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 *aMemorySize = mHWData->mMemorySize;
1441
1442 return S_OK;
1443}
1444
1445HRESULT Machine::setMemorySize(ULONG aMemorySize)
1446{
1447 /* check RAM limits */
1448 if ( aMemorySize < MM_RAM_MIN_IN_MB
1449 || aMemorySize > MM_RAM_MAX_IN_MB
1450 )
1451 return setError(E_INVALIDARG,
1452 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1453 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1454
1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 HRESULT rc = i_checkStateDependency(MutableStateDep);
1458 if (FAILED(rc)) return rc;
1459
1460 i_setModified(IsModified_MachineData);
1461 mHWData.backup();
1462 mHWData->mMemorySize = aMemorySize;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1468{
1469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 *aCPUCount = mHWData->mCPUCount;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::setCPUCount(ULONG aCPUCount)
1477{
1478 /* check CPU limits */
1479 if ( aCPUCount < SchemaDefs::MinCPUCount
1480 || aCPUCount > SchemaDefs::MaxCPUCount
1481 )
1482 return setError(E_INVALIDARG,
1483 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1484 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1485
1486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1489 if (mHWData->mCPUHotPlugEnabled)
1490 {
1491 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1492 {
1493 if (mHWData->mCPUAttached[idx])
1494 return setError(E_INVALIDARG,
1495 tr("There is still a CPU attached to socket %lu."
1496 "Detach the CPU before removing the socket"),
1497 aCPUCount, idx+1);
1498 }
1499 }
1500
1501 HRESULT rc = i_checkStateDependency(MutableStateDep);
1502 if (FAILED(rc)) return rc;
1503
1504 i_setModified(IsModified_MachineData);
1505 mHWData.backup();
1506 mHWData->mCPUCount = aCPUCount;
1507
1508 return S_OK;
1509}
1510
1511HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1512{
1513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1514
1515 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1516
1517 return S_OK;
1518}
1519
1520HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1521{
1522 HRESULT rc = S_OK;
1523
1524 /* check throttle limits */
1525 if ( aCPUExecutionCap < 1
1526 || aCPUExecutionCap > 100
1527 )
1528 return setError(E_INVALIDARG,
1529 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1530 aCPUExecutionCap, 1, 100);
1531
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 alock.release();
1535 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1536 alock.acquire();
1537 if (FAILED(rc)) return rc;
1538
1539 i_setModified(IsModified_MachineData);
1540 mHWData.backup();
1541 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1542
1543 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1544 if (Global::IsOnline(mData->mMachineState))
1545 i_saveSettings(NULL);
1546
1547 return S_OK;
1548}
1549
1550HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1551{
1552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1555
1556 return S_OK;
1557}
1558
1559HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1560{
1561 HRESULT rc = S_OK;
1562
1563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1564
1565 rc = i_checkStateDependency(MutableStateDep);
1566 if (FAILED(rc)) return rc;
1567
1568 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1569 {
1570 if (aCPUHotPlugEnabled)
1571 {
1572 i_setModified(IsModified_MachineData);
1573 mHWData.backup();
1574
1575 /* Add the amount of CPUs currently attached */
1576 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1577 mHWData->mCPUAttached[i] = true;
1578 }
1579 else
1580 {
1581 /*
1582 * We can disable hotplug only if the amount of maximum CPUs is equal
1583 * to the amount of attached CPUs
1584 */
1585 unsigned cCpusAttached = 0;
1586 unsigned iHighestId = 0;
1587
1588 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1589 {
1590 if (mHWData->mCPUAttached[i])
1591 {
1592 cCpusAttached++;
1593 iHighestId = i;
1594 }
1595 }
1596
1597 if ( (cCpusAttached != mHWData->mCPUCount)
1598 || (iHighestId >= mHWData->mCPUCount))
1599 return setError(E_INVALIDARG,
1600 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1601
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604 }
1605 }
1606
1607 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1608
1609 return rc;
1610}
1611
1612HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1613{
1614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1615
1616 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1617
1618 return S_OK;
1619}
1620
1621HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1622{
1623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1626 if (SUCCEEDED(hrc))
1627 {
1628 i_setModified(IsModified_MachineData);
1629 mHWData.backup();
1630 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1631 }
1632 return hrc;
1633}
1634
1635HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1636{
1637#ifdef VBOX_WITH_USB_CARDREADER
1638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1639
1640 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1641
1642 return S_OK;
1643#else
1644 NOREF(aEmulatedUSBCardReaderEnabled);
1645 return E_NOTIMPL;
1646#endif
1647}
1648
1649HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1650{
1651#ifdef VBOX_WITH_USB_CARDREADER
1652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1653
1654 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1655 if (FAILED(rc)) return rc;
1656
1657 i_setModified(IsModified_MachineData);
1658 mHWData.backup();
1659 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1660
1661 return S_OK;
1662#else
1663 NOREF(aEmulatedUSBCardReaderEnabled);
1664 return E_NOTIMPL;
1665#endif
1666}
1667
1668HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1669{
1670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1671
1672 *aHPETEnabled = mHWData->mHPETEnabled;
1673
1674 return S_OK;
1675}
1676
1677HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1678{
1679 HRESULT rc = S_OK;
1680
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 rc = i_checkStateDependency(MutableStateDep);
1684 if (FAILED(rc)) return rc;
1685
1686 i_setModified(IsModified_MachineData);
1687 mHWData.backup();
1688
1689 mHWData->mHPETEnabled = aHPETEnabled;
1690
1691 return rc;
1692}
1693
1694HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1695{
1696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1697
1698 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1699 return S_OK;
1700}
1701
1702HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1703{
1704 HRESULT rc = S_OK;
1705
1706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1707
1708 i_setModified(IsModified_MachineData);
1709 mHWData.backup();
1710 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1711
1712 alock.release();
1713 rc = i_onVideoCaptureChange();
1714 alock.acquire();
1715 if (FAILED(rc))
1716 {
1717 /*
1718 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1719 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1720 * determine if it should start or stop capturing. Therefore we need to manually
1721 * undo change.
1722 */
1723 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1724 return rc;
1725 }
1726
1727 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1728 if (Global::IsOnline(mData->mMachineState))
1729 i_saveSettings(NULL);
1730
1731 return rc;
1732}
1733
1734HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1735{
1736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1737 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1738 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1739 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1740 return S_OK;
1741}
1742
1743HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1744{
1745 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1746 bool fChanged = false;
1747
1748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1749
1750 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1751 {
1752 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1753 {
1754 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1755 fChanged = true;
1756 }
1757 }
1758 if (fChanged)
1759 {
1760 alock.release();
1761 HRESULT rc = i_onVideoCaptureChange();
1762 alock.acquire();
1763 if (FAILED(rc)) return rc;
1764 i_setModified(IsModified_MachineData);
1765
1766 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1767 if (Global::IsOnline(mData->mMachineState))
1768 i_saveSettings(NULL);
1769 }
1770
1771 return S_OK;
1772}
1773
1774HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1775{
1776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1777 if (mHWData->mVideoCaptureFile.isEmpty())
1778 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1779 else
1780 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1781 return S_OK;
1782}
1783
1784HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1785{
1786 Utf8Str strFile(aVideoCaptureFile);
1787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1788
1789 if ( Global::IsOnline(mData->mMachineState)
1790 && mHWData->mVideoCaptureEnabled)
1791 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1792
1793 if (!RTPathStartsWithRoot(strFile.c_str()))
1794 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1795
1796 if (!strFile.isEmpty())
1797 {
1798 Utf8Str defaultFile;
1799 i_getDefaultVideoCaptureFile(defaultFile);
1800 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1801 strFile.setNull();
1802 }
1803
1804 i_setModified(IsModified_MachineData);
1805 mHWData.backup();
1806 mHWData->mVideoCaptureFile = strFile;
1807
1808 return S_OK;
1809}
1810
1811HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1812{
1813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1814 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1815 return S_OK;
1816}
1817
1818HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1819{
1820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1821
1822 if ( Global::IsOnline(mData->mMachineState)
1823 && mHWData->mVideoCaptureEnabled)
1824 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1825
1826 i_setModified(IsModified_MachineData);
1827 mHWData.backup();
1828 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1829
1830 return S_OK;
1831}
1832
1833HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1834{
1835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1836 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1837 return S_OK;
1838}
1839
1840HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1841{
1842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1843
1844 if ( Global::IsOnline(mData->mMachineState)
1845 && mHWData->mVideoCaptureEnabled)
1846 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1847
1848 i_setModified(IsModified_MachineData);
1849 mHWData.backup();
1850 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1851
1852 return S_OK;
1853}
1854
1855HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1856{
1857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1858 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1859 return S_OK;
1860}
1861
1862HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1863{
1864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 if ( Global::IsOnline(mData->mMachineState)
1867 && mHWData->mVideoCaptureEnabled)
1868 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1869
1870 i_setModified(IsModified_MachineData);
1871 mHWData.backup();
1872 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1873
1874 return S_OK;
1875}
1876
1877HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1878{
1879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1880 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1881 return S_OK;
1882}
1883
1884HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1885{
1886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1887
1888 if ( Global::IsOnline(mData->mMachineState)
1889 && mHWData->mVideoCaptureEnabled)
1890 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1891
1892 i_setModified(IsModified_MachineData);
1893 mHWData.backup();
1894 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1895
1896 return S_OK;
1897}
1898
1899HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1900{
1901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1902 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1903 return S_OK;
1904}
1905
1906HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1907{
1908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1909
1910 if ( Global::IsOnline(mData->mMachineState)
1911 && mHWData->mVideoCaptureEnabled)
1912 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1913
1914 i_setModified(IsModified_MachineData);
1915 mHWData.backup();
1916 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1917
1918 return S_OK;
1919}
1920
1921HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1922{
1923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1924 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1925 return S_OK;
1926}
1927
1928HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1929{
1930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1931
1932 if ( Global::IsOnline(mData->mMachineState)
1933 && mHWData->mVideoCaptureEnabled)
1934 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1935
1936 i_setModified(IsModified_MachineData);
1937 mHWData.backup();
1938 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1939
1940 return S_OK;
1941}
1942
1943HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1944{
1945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1946
1947 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1948 return S_OK;
1949}
1950
1951HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1952{
1953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1954
1955 if ( Global::IsOnline(mData->mMachineState)
1956 && mHWData->mVideoCaptureEnabled)
1957 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1958
1959 i_setModified(IsModified_MachineData);
1960 mHWData.backup();
1961 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1962
1963 return S_OK;
1964}
1965
1966HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1967{
1968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1969
1970 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1971
1972 return S_OK;
1973}
1974
1975HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1976{
1977 switch (aGraphicsControllerType)
1978 {
1979 case GraphicsControllerType_Null:
1980 case GraphicsControllerType_VBoxVGA:
1981#ifdef VBOX_WITH_VMSVGA
1982 case GraphicsControllerType_VMSVGA:
1983#endif
1984 break;
1985 default:
1986 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1987 }
1988
1989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 HRESULT rc = i_checkStateDependency(MutableStateDep);
1992 if (FAILED(rc)) return rc;
1993
1994 i_setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1997
1998 return S_OK;
1999}
2000
2001HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2002{
2003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2004
2005 *aVRAMSize = mHWData->mVRAMSize;
2006
2007 return S_OK;
2008}
2009
2010HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2011{
2012 /* check VRAM limits */
2013 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
2014 aVRAMSize > SchemaDefs::MaxGuestVRAM)
2015 return setError(E_INVALIDARG,
2016 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2017 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2018
2019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2020
2021 HRESULT rc = i_checkStateDependency(MutableStateDep);
2022 if (FAILED(rc)) return rc;
2023
2024 i_setModified(IsModified_MachineData);
2025 mHWData.backup();
2026 mHWData->mVRAMSize = aVRAMSize;
2027
2028 return S_OK;
2029}
2030
2031/** @todo this method should not be public */
2032HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2033{
2034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2035
2036 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2037
2038 return S_OK;
2039}
2040
2041/**
2042 * Set the memory balloon size.
2043 *
2044 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2045 * we have to make sure that we never call IGuest from here.
2046 */
2047HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2048{
2049 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2050#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2051 /* check limits */
2052 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2053 return setError(E_INVALIDARG,
2054 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2055 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2056
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 i_setModified(IsModified_MachineData);
2060 mHWData.backup();
2061 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2062
2063 return S_OK;
2064#else
2065 NOREF(aMemoryBalloonSize);
2066 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2067#endif
2068}
2069
2070HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2071{
2072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2073
2074 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2075 return S_OK;
2076}
2077
2078HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2079{
2080#ifdef VBOX_WITH_PAGE_SHARING
2081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2082
2083 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2087 return S_OK;
2088#else
2089 NOREF(aPageFusionEnabled);
2090 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2091#endif
2092}
2093
2094HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2095{
2096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2097
2098 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2099
2100 return S_OK;
2101}
2102
2103HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2104{
2105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2106
2107 HRESULT rc = i_checkStateDependency(MutableStateDep);
2108 if (FAILED(rc)) return rc;
2109
2110 /** @todo check validity! */
2111
2112 i_setModified(IsModified_MachineData);
2113 mHWData.backup();
2114 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2115
2116 return S_OK;
2117}
2118
2119
2120HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2121{
2122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2123
2124 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2125
2126 return S_OK;
2127}
2128
2129HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2130{
2131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2132
2133 HRESULT rc = i_checkStateDependency(MutableStateDep);
2134 if (FAILED(rc)) return rc;
2135
2136 /** @todo check validity! */
2137 i_setModified(IsModified_MachineData);
2138 mHWData.backup();
2139 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2140
2141 return S_OK;
2142}
2143
2144HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2145{
2146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2147
2148 *aMonitorCount = mHWData->mMonitorCount;
2149
2150 return S_OK;
2151}
2152
2153HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2154{
2155 /* make sure monitor count is a sensible number */
2156 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2157 return setError(E_INVALIDARG,
2158 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2159 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2160
2161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 HRESULT rc = i_checkStateDependency(MutableStateDep);
2164 if (FAILED(rc)) return rc;
2165
2166 i_setModified(IsModified_MachineData);
2167 mHWData.backup();
2168 mHWData->mMonitorCount = aMonitorCount;
2169
2170 return S_OK;
2171}
2172
2173HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2174{
2175 /* mBIOSSettings is constant during life time, no need to lock */
2176 aBIOSSettings = mBIOSSettings;
2177
2178 return S_OK;
2179}
2180
2181HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2182{
2183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2184
2185 switch (aProperty)
2186 {
2187 case CPUPropertyType_PAE:
2188 *aValue = mHWData->mPAEEnabled;
2189 break;
2190
2191 case CPUPropertyType_LongMode:
2192 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2193 *aValue = TRUE;
2194 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2195 *aValue = FALSE;
2196#if HC_ARCH_BITS == 64
2197 else
2198 *aValue = TRUE;
2199#else
2200 else
2201 {
2202 *aValue = FALSE;
2203
2204 ComPtr<IGuestOSType> ptrGuestOSType;
2205 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2206 if (SUCCEEDED(hrc2))
2207 {
2208 BOOL fIs64Bit = FALSE;
2209 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2210 if (SUCCEEDED(hrc2) && fIs64Bit)
2211 {
2212 ComObjPtr<Host> ptrHost = mParent->i_host();
2213 alock.release();
2214
2215 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2216 if (FAILED(hrc2))
2217 *aValue = FALSE;
2218 }
2219 }
2220 }
2221#endif
2222 break;
2223
2224 case CPUPropertyType_TripleFaultReset:
2225 *aValue = mHWData->mTripleFaultReset;
2226 break;
2227
2228 default:
2229 return E_INVALIDARG;
2230 }
2231 return S_OK;
2232}
2233
2234HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2235{
2236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2237
2238 HRESULT rc = i_checkStateDependency(MutableStateDep);
2239 if (FAILED(rc)) return rc;
2240
2241 switch (aProperty)
2242 {
2243 case CPUPropertyType_PAE:
2244 i_setModified(IsModified_MachineData);
2245 mHWData.backup();
2246 mHWData->mPAEEnabled = !!aValue;
2247 break;
2248
2249 case CPUPropertyType_LongMode:
2250 i_setModified(IsModified_MachineData);
2251 mHWData.backup();
2252 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2253 break;
2254
2255 case CPUPropertyType_TripleFaultReset:
2256 i_setModified(IsModified_MachineData);
2257 mHWData.backup();
2258 mHWData->mTripleFaultReset = !!aValue;
2259 break;
2260
2261 default:
2262 return E_INVALIDARG;
2263 }
2264 return S_OK;
2265}
2266
2267HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2268{
2269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2270
2271 switch(aId)
2272 {
2273 case 0x0:
2274 case 0x1:
2275 case 0x2:
2276 case 0x3:
2277 case 0x4:
2278 case 0x5:
2279 case 0x6:
2280 case 0x7:
2281 case 0x8:
2282 case 0x9:
2283 case 0xA:
2284 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2285 return E_INVALIDARG;
2286
2287 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2288 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2289 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2290 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2291 break;
2292
2293 case 0x80000000:
2294 case 0x80000001:
2295 case 0x80000002:
2296 case 0x80000003:
2297 case 0x80000004:
2298 case 0x80000005:
2299 case 0x80000006:
2300 case 0x80000007:
2301 case 0x80000008:
2302 case 0x80000009:
2303 case 0x8000000A:
2304 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2305 return E_INVALIDARG;
2306
2307 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2308 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2309 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2310 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2311 break;
2312
2313 default:
2314 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2315 }
2316 return S_OK;
2317}
2318
2319
2320HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2321{
2322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2323
2324 HRESULT rc = i_checkStateDependency(MutableStateDep);
2325 if (FAILED(rc)) return rc;
2326
2327 switch(aId)
2328 {
2329 case 0x0:
2330 case 0x1:
2331 case 0x2:
2332 case 0x3:
2333 case 0x4:
2334 case 0x5:
2335 case 0x6:
2336 case 0x7:
2337 case 0x8:
2338 case 0x9:
2339 case 0xA:
2340 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2341 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2342 i_setModified(IsModified_MachineData);
2343 mHWData.backup();
2344 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2345 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2346 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2347 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2348 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2349 break;
2350
2351 case 0x80000000:
2352 case 0x80000001:
2353 case 0x80000002:
2354 case 0x80000003:
2355 case 0x80000004:
2356 case 0x80000005:
2357 case 0x80000006:
2358 case 0x80000007:
2359 case 0x80000008:
2360 case 0x80000009:
2361 case 0x8000000A:
2362 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2363 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2364 i_setModified(IsModified_MachineData);
2365 mHWData.backup();
2366 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2367 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2368 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2369 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2370 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2371 break;
2372
2373 default:
2374 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2375 }
2376 return S_OK;
2377}
2378
2379HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2380{
2381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2382
2383 HRESULT rc = i_checkStateDependency(MutableStateDep);
2384 if (FAILED(rc)) return rc;
2385
2386 switch(aId)
2387 {
2388 case 0x0:
2389 case 0x1:
2390 case 0x2:
2391 case 0x3:
2392 case 0x4:
2393 case 0x5:
2394 case 0x6:
2395 case 0x7:
2396 case 0x8:
2397 case 0x9:
2398 case 0xA:
2399 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2400 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2401 i_setModified(IsModified_MachineData);
2402 mHWData.backup();
2403 /* Invalidate leaf. */
2404 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2405 break;
2406
2407 case 0x80000000:
2408 case 0x80000001:
2409 case 0x80000002:
2410 case 0x80000003:
2411 case 0x80000004:
2412 case 0x80000005:
2413 case 0x80000006:
2414 case 0x80000007:
2415 case 0x80000008:
2416 case 0x80000009:
2417 case 0x8000000A:
2418 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2419 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2420 i_setModified(IsModified_MachineData);
2421 mHWData.backup();
2422 /* Invalidate leaf. */
2423 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2424 break;
2425
2426 default:
2427 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2428 }
2429 return S_OK;
2430}
2431
2432HRESULT Machine::removeAllCPUIDLeaves()
2433{
2434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2435
2436 HRESULT rc = i_checkStateDependency(MutableStateDep);
2437 if (FAILED(rc)) return rc;
2438
2439 i_setModified(IsModified_MachineData);
2440 mHWData.backup();
2441
2442 /* Invalidate all standard leafs. */
2443 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2444 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2445
2446 /* Invalidate all extended leafs. */
2447 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2448 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2449
2450 return S_OK;
2451}
2452HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2453{
2454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2455
2456 switch(aProperty)
2457 {
2458 case HWVirtExPropertyType_Enabled:
2459 *aValue = mHWData->mHWVirtExEnabled;
2460 break;
2461
2462 case HWVirtExPropertyType_VPID:
2463 *aValue = mHWData->mHWVirtExVPIDEnabled;
2464 break;
2465
2466 case HWVirtExPropertyType_NestedPaging:
2467 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2468 break;
2469
2470 case HWVirtExPropertyType_UnrestrictedExecution:
2471 *aValue = mHWData->mHWVirtExUXEnabled;
2472 break;
2473
2474 case HWVirtExPropertyType_LargePages:
2475 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2476#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2477 *aValue = FALSE;
2478#endif
2479 break;
2480
2481 case HWVirtExPropertyType_Force:
2482 *aValue = mHWData->mHWVirtExForceEnabled;
2483 break;
2484
2485 default:
2486 return E_INVALIDARG;
2487 }
2488 return S_OK;
2489}
2490
2491HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2492{
2493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2494
2495 HRESULT rc = i_checkStateDependency(MutableStateDep);
2496 if (FAILED(rc)) return rc;
2497
2498 switch(aProperty)
2499 {
2500 case HWVirtExPropertyType_Enabled:
2501 i_setModified(IsModified_MachineData);
2502 mHWData.backup();
2503 mHWData->mHWVirtExEnabled = !!aValue;
2504 break;
2505
2506 case HWVirtExPropertyType_VPID:
2507 i_setModified(IsModified_MachineData);
2508 mHWData.backup();
2509 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2510 break;
2511
2512 case HWVirtExPropertyType_NestedPaging:
2513 i_setModified(IsModified_MachineData);
2514 mHWData.backup();
2515 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2516 break;
2517
2518 case HWVirtExPropertyType_UnrestrictedExecution:
2519 i_setModified(IsModified_MachineData);
2520 mHWData.backup();
2521 mHWData->mHWVirtExUXEnabled = !!aValue;
2522 break;
2523
2524 case HWVirtExPropertyType_LargePages:
2525 i_setModified(IsModified_MachineData);
2526 mHWData.backup();
2527 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2528 break;
2529
2530 case HWVirtExPropertyType_Force:
2531 i_setModified(IsModified_MachineData);
2532 mHWData.backup();
2533 mHWData->mHWVirtExForceEnabled = !!aValue;
2534 break;
2535
2536 default:
2537 return E_INVALIDARG;
2538 }
2539
2540 return S_OK;
2541}
2542
2543HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2544{
2545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2548
2549 return S_OK;
2550}
2551
2552HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2553{
2554 /* @todo (r=dmik):
2555 * 1. Allow to change the name of the snapshot folder containing snapshots
2556 * 2. Rename the folder on disk instead of just changing the property
2557 * value (to be smart and not to leave garbage). Note that it cannot be
2558 * done here because the change may be rolled back. Thus, the right
2559 * place is #saveSettings().
2560 */
2561
2562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 HRESULT rc = i_checkStateDependency(MutableStateDep);
2565 if (FAILED(rc)) return rc;
2566
2567 if (!mData->mCurrentSnapshot.isNull())
2568 return setError(E_FAIL,
2569 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2570
2571 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2572
2573 if (strSnapshotFolder.isEmpty())
2574 strSnapshotFolder = "Snapshots";
2575 int vrc = i_calculateFullPath(strSnapshotFolder,
2576 strSnapshotFolder);
2577 if (RT_FAILURE(vrc))
2578 return setError(E_FAIL,
2579 tr("Invalid snapshot folder '%s' (%Rrc)"),
2580 strSnapshotFolder.c_str(), vrc);
2581
2582 i_setModified(IsModified_MachineData);
2583 mUserData.backup();
2584
2585 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2586
2587 return S_OK;
2588}
2589
2590HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2591{
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594 aMediumAttachments.resize(mMediaData->mAttachments.size());
2595 size_t i = 0;
2596 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2597 it != mMediaData->mAttachments.end(); ++it, ++i)
2598 aMediumAttachments[i] = *it;
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 Assert(!!mVRDEServer);
2608
2609 aVRDEServer = mVRDEServer;
2610
2611 return S_OK;
2612}
2613
2614HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2615{
2616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2617
2618 aAudioAdapter = mAudioAdapter;
2619
2620 return S_OK;
2621}
2622
2623HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2624{
2625#ifdef VBOX_WITH_VUSB
2626 clearError();
2627 MultiResult rc(S_OK);
2628
2629# ifdef VBOX_WITH_USB
2630 rc = mParent->i_host()->i_checkUSBProxyService();
2631 if (FAILED(rc)) return rc;
2632# endif
2633
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 USBControllerList data = *mUSBControllers.data();
2637 aUSBControllers.resize(data.size());
2638 size_t i = 0;
2639 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2640 aUSBControllers[i] = *it;
2641
2642 return S_OK;
2643#else
2644 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2645 * extended error info to indicate that USB is simply not available
2646 * (w/o treating it as a failure), for example, as in OSE */
2647 NOREF(aUSBControllers);
2648 ReturnComNotImplemented();
2649#endif /* VBOX_WITH_VUSB */
2650}
2651
2652HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2653{
2654#ifdef VBOX_WITH_VUSB
2655 clearError();
2656 MultiResult rc(S_OK);
2657
2658# ifdef VBOX_WITH_USB
2659 rc = mParent->i_host()->i_checkUSBProxyService();
2660 if (FAILED(rc)) return rc;
2661# endif
2662
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 aUSBDeviceFilters = mUSBDeviceFilters;
2666 return rc;
2667#else
2668 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2669 * extended error info to indicate that USB is simply not available
2670 * (w/o treating it as a failure), for example, as in OSE */
2671 NOREF(aUSBDeviceFilters);
2672 ReturnComNotImplemented();
2673#endif /* VBOX_WITH_VUSB */
2674}
2675
2676HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2677{
2678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2679
2680 aSettingsFilePath = mData->m_strConfigFileFull;
2681
2682 return S_OK;
2683}
2684
2685HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2686{
2687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2688
2689 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2690 if (FAILED(rc)) return rc;
2691
2692 if (!mData->pMachineConfigFile->fileExists())
2693 // this is a new machine, and no config file exists yet:
2694 *aSettingsModified = TRUE;
2695 else
2696 *aSettingsModified = (mData->flModifications != 0);
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2702{
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 *aSessionState = mData->mSession.mState;
2706
2707 return S_OK;
2708}
2709
2710HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2711{
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 aSessionName = mData->mSession.mName;
2715
2716 return S_OK;
2717}
2718
2719HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2720{
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 *aSessionPID = mData->mSession.mPID;
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::getState(MachineState_T *aState)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 *aState = mData->mMachineState;
2733 Assert(mData->mMachineState != MachineState_Null);
2734
2735 return S_OK;
2736}
2737
2738HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2739{
2740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2741
2742 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 aStateFilePath = mSSData->strStateFilePath;
2752
2753 return S_OK;
2754}
2755
2756HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2757{
2758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 i_getLogFolder(aLogFolder);
2761
2762 return S_OK;
2763}
2764
2765HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2766{
2767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 aCurrentSnapshot = mData->mCurrentSnapshot;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2775{
2776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2779 ? 0
2780 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2781
2782 return S_OK;
2783}
2784
2785HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2786{
2787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2788
2789 /* Note: for machines with no snapshots, we always return FALSE
2790 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2791 * reasons :) */
2792
2793 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2794 ? FALSE
2795 : mData->mCurrentStateModified;
2796
2797 return S_OK;
2798}
2799
2800HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2801{
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 aSharedFolders.resize(mHWData->mSharedFolders.size());
2805 size_t i = 0;
2806 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2807 it != mHWData->mSharedFolders.end(); ++i, ++it)
2808 aSharedFolders[i] = *it;
2809
2810 return S_OK;
2811}
2812
2813HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2814{
2815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 *aClipboardMode = mHWData->mClipboardMode;
2818
2819 return S_OK;
2820}
2821
2822HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2823{
2824 HRESULT rc = S_OK;
2825
2826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 alock.release();
2829 rc = i_onClipboardModeChange(aClipboardMode);
2830 alock.acquire();
2831 if (FAILED(rc)) return rc;
2832
2833 i_setModified(IsModified_MachineData);
2834 mHWData.backup();
2835 mHWData->mClipboardMode = aClipboardMode;
2836
2837 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2838 if (Global::IsOnline(mData->mMachineState))
2839 i_saveSettings(NULL);
2840
2841 return S_OK;
2842}
2843
2844HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 *aDnDMode = mHWData->mDnDMode;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2854{
2855 HRESULT rc = S_OK;
2856
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 alock.release();
2860 rc = i_onDnDModeChange(aDnDMode);
2861
2862 alock.acquire();
2863 if (FAILED(rc)) return rc;
2864
2865 i_setModified(IsModified_MachineData);
2866 mHWData.backup();
2867 mHWData->mDnDMode = aDnDMode;
2868
2869 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2870 if (Global::IsOnline(mData->mMachineState))
2871 i_saveSettings(NULL);
2872
2873 return S_OK;
2874}
2875
2876HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2877{
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879 StorageControllerList data = *mStorageControllers.data();
2880 size_t i = 0;
2881 aStorageControllers.resize(data.size());
2882 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2883 aStorageControllers[i] = *it;
2884 return S_OK;
2885}
2886
2887HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2888{
2889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 *aEnabled = mUserData->s.fTeleporterEnabled;
2892
2893 return S_OK;
2894}
2895
2896HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2897{
2898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 /* Only allow it to be set to true when PoweredOff or Aborted.
2901 (Clearing it is always permitted.) */
2902 if ( aTeleporterEnabled
2903 && mData->mRegistered
2904 && ( !i_isSessionMachine()
2905 || ( mData->mMachineState != MachineState_PoweredOff
2906 && mData->mMachineState != MachineState_Teleported
2907 && mData->mMachineState != MachineState_Aborted
2908 )
2909 )
2910 )
2911 return setError(VBOX_E_INVALID_VM_STATE,
2912 tr("The machine is not powered off (state is %s)"),
2913 Global::stringifyMachineState(mData->mMachineState));
2914
2915 i_setModified(IsModified_MachineData);
2916 mUserData.backup();
2917 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2923{
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2927
2928 return S_OK;
2929}
2930
2931HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2932{
2933 if (aTeleporterPort >= _64K)
2934 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2935
2936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2939 if (FAILED(rc)) return rc;
2940
2941 i_setModified(IsModified_MachineData);
2942 mUserData.backup();
2943 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2949{
2950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2953
2954 return S_OK;
2955}
2956
2957HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2958{
2959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2962 if (FAILED(rc)) return rc;
2963
2964 i_setModified(IsModified_MachineData);
2965 mUserData.backup();
2966 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2972{
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2975
2976 return S_OK;
2977}
2978
2979HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2980{
2981 /*
2982 * Hash the password first.
2983 */
2984 com::Utf8Str aT = aTeleporterPassword;
2985
2986 if (!aT.isEmpty())
2987 {
2988 if (VBoxIsPasswordHashed(&aT))
2989 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2990 VBoxHashPassword(&aT);
2991 }
2992
2993 /*
2994 * Do the update.
2995 */
2996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2997 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2998 if (SUCCEEDED(hrc))
2999 {
3000 i_setModified(IsModified_MachineData);
3001 mUserData.backup();
3002 mUserData->s.strTeleporterPassword = aT;
3003 }
3004
3005 return hrc;
3006}
3007
3008HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3009{
3010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3013 return S_OK;
3014}
3015
3016HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3017{
3018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3019
3020 /* @todo deal with running state change. */
3021 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3022 if (FAILED(rc)) return rc;
3023
3024 i_setModified(IsModified_MachineData);
3025 mUserData.backup();
3026 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3027 return S_OK;
3028}
3029
3030HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3031{
3032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3033
3034 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3035 return S_OK;
3036}
3037
3038HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3039{
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 /* @todo deal with running state change. */
3043 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3044 if (FAILED(rc)) return rc;
3045
3046 i_setModified(IsModified_MachineData);
3047 mUserData.backup();
3048 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3049 return S_OK;
3050}
3051
3052HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3053{
3054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3057 return S_OK;
3058}
3059
3060HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3061{
3062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3063
3064 /* @todo deal with running state change. */
3065 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3066 if (FAILED(rc)) return rc;
3067
3068 i_setModified(IsModified_MachineData);
3069 mUserData.backup();
3070 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3071 return S_OK;
3072}
3073
3074HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3075{
3076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3077
3078 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3079
3080 return S_OK;
3081}
3082
3083HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3084{
3085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3086
3087 /* @todo deal with running state change. */
3088 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3089 if (FAILED(rc)) return rc;
3090
3091 i_setModified(IsModified_MachineData);
3092 mUserData.backup();
3093 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3094
3095 return S_OK;
3096}
3097
3098HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3099{
3100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3103 return S_OK;
3104}
3105
3106HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3107{
3108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3109
3110 /* @todo deal with running state change. */
3111 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3112 if (FAILED(rc)) return rc;
3113
3114 i_setModified(IsModified_MachineData);
3115 mUserData.backup();
3116 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3117 return S_OK;
3118}
3119
3120HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3121{
3122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3125
3126 return S_OK;
3127}
3128
3129HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3130{
3131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3132
3133 /* Only allow it to be set to true when PoweredOff or Aborted.
3134 (Clearing it is always permitted.) */
3135 if ( aRTCUseUTC
3136 && mData->mRegistered
3137 && ( !i_isSessionMachine()
3138 || ( mData->mMachineState != MachineState_PoweredOff
3139 && mData->mMachineState != MachineState_Teleported
3140 && mData->mMachineState != MachineState_Aborted
3141 )
3142 )
3143 )
3144 return setError(VBOX_E_INVALID_VM_STATE,
3145 tr("The machine is not powered off (state is %s)"),
3146 Global::stringifyMachineState(mData->mMachineState));
3147
3148 i_setModified(IsModified_MachineData);
3149 mUserData.backup();
3150 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3151
3152 return S_OK;
3153}
3154
3155HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3156{
3157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3158
3159 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3160
3161 return S_OK;
3162}
3163
3164HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3165{
3166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3167
3168 HRESULT rc = i_checkStateDependency(MutableStateDep);
3169 if (FAILED(rc)) return rc;
3170
3171 i_setModified(IsModified_MachineData);
3172 mHWData.backup();
3173 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3179{
3180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 *aIOCacheSize = mHWData->mIOCacheSize;
3183
3184 return S_OK;
3185}
3186
3187HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3188{
3189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3190
3191 HRESULT rc = i_checkStateDependency(MutableStateDep);
3192 if (FAILED(rc)) return rc;
3193
3194 i_setModified(IsModified_MachineData);
3195 mHWData.backup();
3196 mHWData->mIOCacheSize = aIOCacheSize;
3197
3198 return S_OK;
3199}
3200
3201
3202/**
3203 * @note Locks objects!
3204 */
3205HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3206 LockType_T aLockType)
3207{
3208 /* check the session state */
3209 SessionState_T state;
3210 HRESULT rc = aSession->COMGETTER(State)(&state);
3211 if (FAILED(rc)) return rc;
3212
3213 if (state != SessionState_Unlocked)
3214 return setError(VBOX_E_INVALID_OBJECT_STATE,
3215 tr("The given session is busy"));
3216
3217 // get the client's IInternalSessionControl interface
3218 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3219 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3220 E_INVALIDARG);
3221
3222 // session name (only used in some code paths)
3223 Utf8Str strSessionName;
3224
3225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3226
3227 if (!mData->mRegistered)
3228 return setError(E_UNEXPECTED,
3229 tr("The machine '%s' is not registered"),
3230 mUserData->s.strName.c_str());
3231
3232 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3233
3234 SessionState_T oldState = mData->mSession.mState;
3235 /* Hack: in case the session is closing and there is a progress object
3236 * which allows waiting for the session to be closed, take the opportunity
3237 * and do a limited wait (max. 1 second). This helps a lot when the system
3238 * is busy and thus session closing can take a little while. */
3239 if ( mData->mSession.mState == SessionState_Unlocking
3240 && mData->mSession.mProgress)
3241 {
3242 alock.release();
3243 mData->mSession.mProgress->WaitForCompletion(1000);
3244 alock.acquire();
3245 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3246 }
3247
3248 // try again now
3249 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3250 // (i.e. session machine exists)
3251 && (aLockType == LockType_Shared) // caller wants a shared link to the
3252 // existing session that holds the write lock:
3253 )
3254 {
3255 // OK, share the session... we are now dealing with three processes:
3256 // 1) VBoxSVC (where this code runs);
3257 // 2) process C: the caller's client process (who wants a shared session);
3258 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3259
3260 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3261 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3262 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3263 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3264 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3265
3266 /*
3267 * Release the lock before calling the client process. It's safe here
3268 * since the only thing to do after we get the lock again is to add
3269 * the remote control to the list (which doesn't directly influence
3270 * anything).
3271 */
3272 alock.release();
3273
3274 // get the console of the session holding the write lock (this is a remote call)
3275 ComPtr<IConsole> pConsoleW;
3276 if (mData->mSession.mLockType == LockType_VM)
3277 {
3278 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3279 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3280 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3281 if (FAILED(rc))
3282 // the failure may occur w/o any error info (from RPC), so provide one
3283 return setError(VBOX_E_VM_ERROR,
3284 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3285 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3286 }
3287
3288 // share the session machine and W's console with the caller's session
3289 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3290 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3291 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3292
3293 if (FAILED(rc))
3294 // the failure may occur w/o any error info (from RPC), so provide one
3295 return setError(VBOX_E_VM_ERROR,
3296 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3297 alock.acquire();
3298
3299 // need to revalidate the state after acquiring the lock again
3300 if (mData->mSession.mState != SessionState_Locked)
3301 {
3302 pSessionControl->Uninitialize();
3303 return setError(VBOX_E_INVALID_SESSION_STATE,
3304 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3305 mUserData->s.strName.c_str());
3306 }
3307
3308 // add the caller's session to the list
3309 mData->mSession.mRemoteControls.push_back(pSessionControl);
3310 }
3311 else if ( mData->mSession.mState == SessionState_Locked
3312 || mData->mSession.mState == SessionState_Unlocking
3313 )
3314 {
3315 // sharing not permitted, or machine still unlocking:
3316 return setError(VBOX_E_INVALID_OBJECT_STATE,
3317 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3318 mUserData->s.strName.c_str());
3319 }
3320 else
3321 {
3322 // machine is not locked: then write-lock the machine (create the session machine)
3323
3324 // must not be busy
3325 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3326
3327 // get the caller's session PID
3328 RTPROCESS pid = NIL_RTPROCESS;
3329 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3330 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3331 Assert(pid != NIL_RTPROCESS);
3332
3333 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3334
3335 if (fLaunchingVMProcess)
3336 {
3337 if (mData->mSession.mPID == NIL_RTPROCESS)
3338 {
3339 // two or more clients racing for a lock, the one which set the
3340 // session state to Spawning will win, the others will get an
3341 // error as we can't decide here if waiting a little would help
3342 // (only for shared locks this would avoid an error)
3343 return setError(VBOX_E_INVALID_OBJECT_STATE,
3344 tr("The machine '%s' already has a lock request pending"),
3345 mUserData->s.strName.c_str());
3346 }
3347
3348 // this machine is awaiting for a spawning session to be opened:
3349 // then the calling process must be the one that got started by
3350 // LaunchVMProcess()
3351
3352 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3353 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3354
3355#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3356 /* Hardened windows builds spawns three processes when a VM is
3357 launched, the 3rd one is the one that will end up here. */
3358 RTPROCESS ppid;
3359 int rc = RTProcQueryParent(pid, &ppid);
3360 if (RT_SUCCESS(rc))
3361 rc = RTProcQueryParent(ppid, &ppid);
3362 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3363 || rc == VERR_ACCESS_DENIED)
3364 {
3365 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3366 mData->mSession.mPID = pid;
3367 }
3368#endif
3369
3370 if (mData->mSession.mPID != pid)
3371 return setError(E_ACCESSDENIED,
3372 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3373 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3374 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3375 }
3376
3377 // create the mutable SessionMachine from the current machine
3378 ComObjPtr<SessionMachine> sessionMachine;
3379 sessionMachine.createObject();
3380 rc = sessionMachine->init(this);
3381 AssertComRC(rc);
3382
3383 /* NOTE: doing return from this function after this point but
3384 * before the end is forbidden since it may call SessionMachine::uninit()
3385 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3386 * lock while still holding the Machine lock in alock so that a deadlock
3387 * is possible due to the wrong lock order. */
3388
3389 if (SUCCEEDED(rc))
3390 {
3391 /*
3392 * Set the session state to Spawning to protect against subsequent
3393 * attempts to open a session and to unregister the machine after
3394 * we release the lock.
3395 */
3396 SessionState_T origState = mData->mSession.mState;
3397 mData->mSession.mState = SessionState_Spawning;
3398
3399#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3400 /* Get the client token ID to be passed to the client process */
3401 Utf8Str strTokenId;
3402 sessionMachine->i_getTokenId(strTokenId);
3403 Assert(!strTokenId.isEmpty());
3404#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3405 /* Get the client token to be passed to the client process */
3406 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3407 /* The token is now "owned" by pToken, fix refcount */
3408 if (!pToken.isNull())
3409 pToken->Release();
3410#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3411
3412 /*
3413 * Release the lock before calling the client process -- it will call
3414 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3415 * because the state is Spawning, so that LaunchVMProcess() and
3416 * LockMachine() calls will fail. This method, called before we
3417 * acquire the lock again, will fail because of the wrong PID.
3418 *
3419 * Note that mData->mSession.mRemoteControls accessed outside
3420 * the lock may not be modified when state is Spawning, so it's safe.
3421 */
3422 alock.release();
3423
3424 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3425#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3426 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3427#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3428 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3429 /* Now the token is owned by the client process. */
3430 pToken.setNull();
3431#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3432 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3433
3434 /* The failure may occur w/o any error info (from RPC), so provide one */
3435 if (FAILED(rc))
3436 setError(VBOX_E_VM_ERROR,
3437 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3438
3439 // get session name, either to remember or to compare against
3440 // the already known session name.
3441 {
3442 Bstr bstrSessionName;
3443 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3444 if (SUCCEEDED(rc2))
3445 strSessionName = bstrSessionName;
3446 }
3447
3448 if ( SUCCEEDED(rc)
3449 && fLaunchingVMProcess
3450 )
3451 {
3452 /* complete the remote session initialization */
3453
3454 /* get the console from the direct session */
3455 ComPtr<IConsole> console;
3456 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3457 ComAssertComRC(rc);
3458
3459 if (SUCCEEDED(rc) && !console)
3460 {
3461 ComAssert(!!console);
3462 rc = E_FAIL;
3463 }
3464
3465 /* assign machine & console to the remote session */
3466 if (SUCCEEDED(rc))
3467 {
3468 /*
3469 * after LaunchVMProcess(), the first and the only
3470 * entry in remoteControls is that remote session
3471 */
3472 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3473 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3474 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3475
3476 /* The failure may occur w/o any error info (from RPC), so provide one */
3477 if (FAILED(rc))
3478 setError(VBOX_E_VM_ERROR,
3479 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3480 }
3481
3482 if (FAILED(rc))
3483 pSessionControl->Uninitialize();
3484 }
3485
3486 /* acquire the lock again */
3487 alock.acquire();
3488
3489 /* Restore the session state */
3490 mData->mSession.mState = origState;
3491 }
3492
3493 // finalize spawning anyway (this is why we don't return on errors above)
3494 if (fLaunchingVMProcess)
3495 {
3496 Assert(mData->mSession.mName == strSessionName);
3497 /* Note that the progress object is finalized later */
3498 /** @todo Consider checking mData->mSession.mProgress for cancellation
3499 * around here. */
3500
3501 /* We don't reset mSession.mPID here because it is necessary for
3502 * SessionMachine::uninit() to reap the child process later. */
3503
3504 if (FAILED(rc))
3505 {
3506 /* Close the remote session, remove the remote control from the list
3507 * and reset session state to Closed (@note keep the code in sync
3508 * with the relevant part in checkForSpawnFailure()). */
3509
3510 Assert(mData->mSession.mRemoteControls.size() == 1);
3511 if (mData->mSession.mRemoteControls.size() == 1)
3512 {
3513 ErrorInfoKeeper eik;
3514 mData->mSession.mRemoteControls.front()->Uninitialize();
3515 }
3516
3517 mData->mSession.mRemoteControls.clear();
3518 mData->mSession.mState = SessionState_Unlocked;
3519 }
3520 }
3521 else
3522 {
3523 /* memorize PID of the directly opened session */
3524 if (SUCCEEDED(rc))
3525 mData->mSession.mPID = pid;
3526 }
3527
3528 if (SUCCEEDED(rc))
3529 {
3530 mData->mSession.mLockType = aLockType;
3531 /* memorize the direct session control and cache IUnknown for it */
3532 mData->mSession.mDirectControl = pSessionControl;
3533 mData->mSession.mState = SessionState_Locked;
3534 if (!fLaunchingVMProcess)
3535 mData->mSession.mName = strSessionName;
3536 /* associate the SessionMachine with this Machine */
3537 mData->mSession.mMachine = sessionMachine;
3538
3539 /* request an IUnknown pointer early from the remote party for later
3540 * identity checks (it will be internally cached within mDirectControl
3541 * at least on XPCOM) */
3542 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3543 NOREF(unk);
3544 }
3545
3546 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3547 * would break the lock order */
3548 alock.release();
3549
3550 /* uninitialize the created session machine on failure */
3551 if (FAILED(rc))
3552 sessionMachine->uninit();
3553 }
3554
3555 if (SUCCEEDED(rc))
3556 {
3557 /*
3558 * tell the client watcher thread to update the set of
3559 * machines that have open sessions
3560 */
3561 mParent->i_updateClientWatcher();
3562
3563 if (oldState != SessionState_Locked)
3564 /* fire an event */
3565 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3566 }
3567
3568 return rc;
3569}
3570
3571/**
3572 * @note Locks objects!
3573 */
3574HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3575 const com::Utf8Str &aName,
3576 const com::Utf8Str &aEnvironment,
3577 ComPtr<IProgress> &aProgress)
3578{
3579 Utf8Str strFrontend(aName);
3580 /* "emergencystop" doesn't need the session, so skip the checks/interface
3581 * retrieval. This code doesn't quite fit in here, but introducing a
3582 * special API method would be even more effort, and would require explicit
3583 * support by every API client. It's better to hide the feature a bit. */
3584 if (strFrontend != "emergencystop")
3585 CheckComArgNotNull(aSession);
3586
3587 HRESULT rc = S_OK;
3588 if (strFrontend.isEmpty())
3589 {
3590 Bstr bstrFrontend;
3591 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3592 if (FAILED(rc))
3593 return rc;
3594 strFrontend = bstrFrontend;
3595 if (strFrontend.isEmpty())
3596 {
3597 ComPtr<ISystemProperties> systemProperties;
3598 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3599 if (FAILED(rc))
3600 return rc;
3601 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3602 if (FAILED(rc))
3603 return rc;
3604 strFrontend = bstrFrontend;
3605 }
3606 /* paranoia - emergencystop is not a valid default */
3607 if (strFrontend == "emergencystop")
3608 strFrontend = Utf8Str::Empty;
3609 }
3610 /* default frontend: Qt GUI */
3611 if (strFrontend.isEmpty())
3612 strFrontend = "GUI/Qt";
3613
3614 if (strFrontend != "emergencystop")
3615 {
3616 /* check the session state */
3617 SessionState_T state;
3618 rc = aSession->COMGETTER(State)(&state);
3619 if (FAILED(rc))
3620 return rc;
3621
3622 if (state != SessionState_Unlocked)
3623 return setError(VBOX_E_INVALID_OBJECT_STATE,
3624 tr("The given session is busy"));
3625
3626 /* get the IInternalSessionControl interface */
3627 ComPtr<IInternalSessionControl> control(aSession);
3628 ComAssertMsgRet(!control.isNull(),
3629 ("No IInternalSessionControl interface"),
3630 E_INVALIDARG);
3631
3632 /* get the teleporter enable state for the progress object init. */
3633 BOOL fTeleporterEnabled;
3634 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3635 if (FAILED(rc))
3636 return rc;
3637
3638 /* create a progress object */
3639 ComObjPtr<ProgressProxy> progress;
3640 progress.createObject();
3641 rc = progress->init(mParent,
3642 static_cast<IMachine*>(this),
3643 Bstr(tr("Starting VM")).raw(),
3644 TRUE /* aCancelable */,
3645 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3646 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3647 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3648 2 /* uFirstOperationWeight */,
3649 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3650
3651 if (SUCCEEDED(rc))
3652 {
3653 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3654 if (SUCCEEDED(rc))
3655 {
3656 aProgress = progress;
3657
3658 /* signal the client watcher thread */
3659 mParent->i_updateClientWatcher();
3660
3661 /* fire an event */
3662 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3663 }
3664 }
3665 }
3666 else
3667 {
3668 /* no progress object - either instant success or failure */
3669 aProgress = NULL;
3670
3671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3672
3673 if (mData->mSession.mState != SessionState_Locked)
3674 return setError(VBOX_E_INVALID_OBJECT_STATE,
3675 tr("The machine '%s' is not locked by a session"),
3676 mUserData->s.strName.c_str());
3677
3678 /* must have a VM process associated - do not kill normal API clients
3679 * with an open session */
3680 if (!Global::IsOnline(mData->mMachineState))
3681 return setError(VBOX_E_INVALID_OBJECT_STATE,
3682 tr("The machine '%s' does not have a VM process"),
3683 mUserData->s.strName.c_str());
3684
3685 /* forcibly terminate the VM process */
3686 if (mData->mSession.mPID != NIL_RTPROCESS)
3687 RTProcTerminate(mData->mSession.mPID);
3688
3689 /* signal the client watcher thread, as most likely the client has
3690 * been terminated */
3691 mParent->i_updateClientWatcher();
3692 }
3693
3694 return rc;
3695}
3696
3697HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3698{
3699 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3700 return setError(E_INVALIDARG,
3701 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3702 aPosition, SchemaDefs::MaxBootPosition);
3703
3704 if (aDevice == DeviceType_USB)
3705 return setError(E_NOTIMPL,
3706 tr("Booting from USB device is currently not supported"));
3707
3708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3709
3710 HRESULT rc = i_checkStateDependency(MutableStateDep);
3711 if (FAILED(rc)) return rc;
3712
3713 i_setModified(IsModified_MachineData);
3714 mHWData.backup();
3715 mHWData->mBootOrder[aPosition - 1] = aDevice;
3716
3717 return S_OK;
3718}
3719
3720HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3721{
3722 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3723 return setError(E_INVALIDARG,
3724 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3725 aPosition, SchemaDefs::MaxBootPosition);
3726
3727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3728
3729 *aDevice = mHWData->mBootOrder[aPosition - 1];
3730
3731 return S_OK;
3732}
3733
3734HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3735 LONG aControllerPort,
3736 LONG aDevice,
3737 DeviceType_T aType,
3738 const ComPtr<IMedium> &aMedium)
3739{
3740 IMedium *aM = aMedium;
3741 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3742 aName.c_str(), aControllerPort, aDevice, aType, aM));
3743
3744 // request the host lock first, since might be calling Host methods for getting host drives;
3745 // next, protect the media tree all the while we're in here, as well as our member variables
3746 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3747 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3748
3749 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3750 if (FAILED(rc)) return rc;
3751
3752 /// @todo NEWMEDIA implicit machine registration
3753 if (!mData->mRegistered)
3754 return setError(VBOX_E_INVALID_OBJECT_STATE,
3755 tr("Cannot attach storage devices to an unregistered machine"));
3756
3757 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3758
3759 /* Check for an existing controller. */
3760 ComObjPtr<StorageController> ctl;
3761 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3762 if (FAILED(rc)) return rc;
3763
3764 StorageControllerType_T ctrlType;
3765 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3766 if (FAILED(rc))
3767 return setError(E_FAIL,
3768 tr("Could not get type of controller '%s'"),
3769 aName.c_str());
3770
3771 bool fSilent = false;
3772 Utf8Str strReconfig;
3773
3774 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3775 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3776 if ( mData->mMachineState == MachineState_Paused
3777 && strReconfig == "1")
3778 fSilent = true;
3779
3780 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3781 bool fHotplug = false;
3782 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3783 fHotplug = true;
3784
3785 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3786 return setError(VBOX_E_INVALID_VM_STATE,
3787 tr("Controller '%s' does not support hotplugging"),
3788 aName.c_str());
3789
3790 // check that the port and device are not out of range
3791 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3792 if (FAILED(rc)) return rc;
3793
3794 /* check if the device slot is already busy */
3795 MediumAttachment *pAttachTemp;
3796 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3797 Bstr(aName).raw(),
3798 aControllerPort,
3799 aDevice)))
3800 {
3801 Medium *pMedium = pAttachTemp->i_getMedium();
3802 if (pMedium)
3803 {
3804 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3805 return setError(VBOX_E_OBJECT_IN_USE,
3806 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3807 pMedium->i_getLocationFull().c_str(),
3808 aControllerPort,
3809 aDevice,
3810 aName.c_str());
3811 }
3812 else
3813 return setError(VBOX_E_OBJECT_IN_USE,
3814 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3815 aControllerPort, aDevice, aName.c_str());
3816 }
3817
3818 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3819 if (aMedium && medium.isNull())
3820 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3821
3822 AutoCaller mediumCaller(medium);
3823 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3824
3825 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3826
3827 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3828 && !medium.isNull()
3829 )
3830 return setError(VBOX_E_OBJECT_IN_USE,
3831 tr("Medium '%s' is already attached to this virtual machine"),
3832 medium->i_getLocationFull().c_str());
3833
3834 if (!medium.isNull())
3835 {
3836 MediumType_T mtype = medium->i_getType();
3837 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3838 // For DVDs it's not written to the config file, so needs no global config
3839 // version bump. For floppies it's a new attribute "type", which is ignored
3840 // by older VirtualBox version, so needs no global config version bump either.
3841 // For hard disks this type is not accepted.
3842 if (mtype == MediumType_MultiAttach)
3843 {
3844 // This type is new with VirtualBox 4.0 and therefore requires settings
3845 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3846 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3847 // two reasons: The medium type is a property of the media registry tree, which
3848 // can reside in the global config file (for pre-4.0 media); we would therefore
3849 // possibly need to bump the global config version. We don't want to do that though
3850 // because that might make downgrading to pre-4.0 impossible.
3851 // As a result, we can only use these two new types if the medium is NOT in the
3852 // global registry:
3853 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3854 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3855 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3856 )
3857 return setError(VBOX_E_INVALID_OBJECT_STATE,
3858 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3859 "to machines that were created with VirtualBox 4.0 or later"),
3860 medium->i_getLocationFull().c_str());
3861 }
3862 }
3863
3864 bool fIndirect = false;
3865 if (!medium.isNull())
3866 fIndirect = medium->i_isReadOnly();
3867 bool associate = true;
3868
3869 do
3870 {
3871 if ( aType == DeviceType_HardDisk
3872 && mMediaData.isBackedUp())
3873 {
3874 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3875
3876 /* check if the medium was attached to the VM before we started
3877 * changing attachments in which case the attachment just needs to
3878 * be restored */
3879 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3880 {
3881 AssertReturn(!fIndirect, E_FAIL);
3882
3883 /* see if it's the same bus/channel/device */
3884 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3885 {
3886 /* the simplest case: restore the whole attachment
3887 * and return, nothing else to do */
3888 mMediaData->mAttachments.push_back(pAttachTemp);
3889
3890 /* Reattach the medium to the VM. */
3891 if (fHotplug || fSilent)
3892 {
3893 mediumLock.release();
3894 treeLock.release();
3895 alock.release();
3896
3897 MediumLockList *pMediumLockList(new MediumLockList());
3898
3899 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3900 true /* fMediumLockWrite */,
3901 false /* fMediumLockWriteAll */,
3902 NULL,
3903 *pMediumLockList);
3904 alock.acquire();
3905 if (FAILED(rc))
3906 delete pMediumLockList;
3907 else
3908 {
3909 mData->mSession.mLockedMedia.Unlock();
3910 alock.release();
3911 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3912 mData->mSession.mLockedMedia.Lock();
3913 alock.acquire();
3914 }
3915 alock.release();
3916
3917 if (SUCCEEDED(rc))
3918 {
3919 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3920 /* Remove lock list in case of error. */
3921 if (FAILED(rc))
3922 {
3923 mData->mSession.mLockedMedia.Unlock();
3924 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3925 mData->mSession.mLockedMedia.Lock();
3926 }
3927 }
3928 }
3929
3930 return S_OK;
3931 }
3932
3933 /* bus/channel/device differ; we need a new attachment object,
3934 * but don't try to associate it again */
3935 associate = false;
3936 break;
3937 }
3938 }
3939
3940 /* go further only if the attachment is to be indirect */
3941 if (!fIndirect)
3942 break;
3943
3944 /* perform the so called smart attachment logic for indirect
3945 * attachments. Note that smart attachment is only applicable to base
3946 * hard disks. */
3947
3948 if (medium->i_getParent().isNull())
3949 {
3950 /* first, investigate the backup copy of the current hard disk
3951 * attachments to make it possible to re-attach existing diffs to
3952 * another device slot w/o losing their contents */
3953 if (mMediaData.isBackedUp())
3954 {
3955 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3956
3957 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3958 uint32_t foundLevel = 0;
3959
3960 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3961 {
3962 uint32_t level = 0;
3963 MediumAttachment *pAttach = *it;
3964 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3965 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3966 if (pMedium.isNull())
3967 continue;
3968
3969 if (pMedium->i_getBase(&level) == medium)
3970 {
3971 /* skip the hard disk if its currently attached (we
3972 * cannot attach the same hard disk twice) */
3973 if (i_findAttachment(mMediaData->mAttachments,
3974 pMedium))
3975 continue;
3976
3977 /* matched device, channel and bus (i.e. attached to the
3978 * same place) will win and immediately stop the search;
3979 * otherwise the attachment that has the youngest
3980 * descendant of medium will be used
3981 */
3982 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3983 {
3984 /* the simplest case: restore the whole attachment
3985 * and return, nothing else to do */
3986 mMediaData->mAttachments.push_back(*it);
3987
3988 /* Reattach the medium to the VM. */
3989 if (fHotplug || fSilent)
3990 {
3991 mediumLock.release();
3992 treeLock.release();
3993 alock.release();
3994
3995 MediumLockList *pMediumLockList(new MediumLockList());
3996
3997 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3998 true /* fMediumLockWrite */,
3999 false /* fMediumLockWriteAll */,
4000 NULL,
4001 *pMediumLockList);
4002 alock.acquire();
4003 if (FAILED(rc))
4004 delete pMediumLockList;
4005 else
4006 {
4007 mData->mSession.mLockedMedia.Unlock();
4008 alock.release();
4009 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4010 mData->mSession.mLockedMedia.Lock();
4011 alock.acquire();
4012 }
4013 alock.release();
4014
4015 if (SUCCEEDED(rc))
4016 {
4017 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4018 /* Remove lock list in case of error. */
4019 if (FAILED(rc))
4020 {
4021 mData->mSession.mLockedMedia.Unlock();
4022 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4023 mData->mSession.mLockedMedia.Lock();
4024 }
4025 }
4026 }
4027
4028 return S_OK;
4029 }
4030 else if ( foundIt == oldAtts.end()
4031 || level > foundLevel /* prefer younger */
4032 )
4033 {
4034 foundIt = it;
4035 foundLevel = level;
4036 }
4037 }
4038 }
4039
4040 if (foundIt != oldAtts.end())
4041 {
4042 /* use the previously attached hard disk */
4043 medium = (*foundIt)->i_getMedium();
4044 mediumCaller.attach(medium);
4045 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4046 mediumLock.attach(medium);
4047 /* not implicit, doesn't require association with this VM */
4048 fIndirect = false;
4049 associate = false;
4050 /* go right to the MediumAttachment creation */
4051 break;
4052 }
4053 }
4054
4055 /* must give up the medium lock and medium tree lock as below we
4056 * go over snapshots, which needs a lock with higher lock order. */
4057 mediumLock.release();
4058 treeLock.release();
4059
4060 /* then, search through snapshots for the best diff in the given
4061 * hard disk's chain to base the new diff on */
4062
4063 ComObjPtr<Medium> base;
4064 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4065 while (snap)
4066 {
4067 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4068
4069 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4070
4071 MediumAttachment *pAttachFound = NULL;
4072 uint32_t foundLevel = 0;
4073
4074 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4075 {
4076 MediumAttachment *pAttach = *it;
4077 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4078 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4079 if (pMedium.isNull())
4080 continue;
4081
4082 uint32_t level = 0;
4083 if (pMedium->i_getBase(&level) == medium)
4084 {
4085 /* matched device, channel and bus (i.e. attached to the
4086 * same place) will win and immediately stop the search;
4087 * otherwise the attachment that has the youngest
4088 * descendant of medium will be used
4089 */
4090 if ( pAttach->i_getDevice() == aDevice
4091 && pAttach->i_getPort() == aControllerPort
4092 && pAttach->i_getControllerName() == aName
4093 )
4094 {
4095 pAttachFound = pAttach;
4096 break;
4097 }
4098 else if ( !pAttachFound
4099 || level > foundLevel /* prefer younger */
4100 )
4101 {
4102 pAttachFound = pAttach;
4103 foundLevel = level;
4104 }
4105 }
4106 }
4107
4108 if (pAttachFound)
4109 {
4110 base = pAttachFound->i_getMedium();
4111 break;
4112 }
4113
4114 snap = snap->i_getParent();
4115 }
4116
4117 /* re-lock medium tree and the medium, as we need it below */
4118 treeLock.acquire();
4119 mediumLock.acquire();
4120
4121 /* found a suitable diff, use it as a base */
4122 if (!base.isNull())
4123 {
4124 medium = base;
4125 mediumCaller.attach(medium);
4126 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4127 mediumLock.attach(medium);
4128 }
4129 }
4130
4131 Utf8Str strFullSnapshotFolder;
4132 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4133
4134 ComObjPtr<Medium> diff;
4135 diff.createObject();
4136 // store this diff in the same registry as the parent
4137 Guid uuidRegistryParent;
4138 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4139 {
4140 // parent image has no registry: this can happen if we're attaching a new immutable
4141 // image that has not yet been attached (medium then points to the base and we're
4142 // creating the diff image for the immutable, and the parent is not yet registered);
4143 // put the parent in the machine registry then
4144 mediumLock.release();
4145 treeLock.release();
4146 alock.release();
4147 i_addMediumToRegistry(medium);
4148 alock.acquire();
4149 treeLock.acquire();
4150 mediumLock.acquire();
4151 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4152 }
4153 rc = diff->init(mParent,
4154 medium->i_getPreferredDiffFormat(),
4155 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4156 uuidRegistryParent,
4157 DeviceType_HardDisk);
4158 if (FAILED(rc)) return rc;
4159
4160 /* Apply the normal locking logic to the entire chain. */
4161 MediumLockList *pMediumLockList(new MediumLockList());
4162 mediumLock.release();
4163 treeLock.release();
4164 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4165 true /* fMediumLockWrite */,
4166 false /* fMediumLockWriteAll */,
4167 medium,
4168 *pMediumLockList);
4169 treeLock.acquire();
4170 mediumLock.acquire();
4171 if (SUCCEEDED(rc))
4172 {
4173 mediumLock.release();
4174 treeLock.release();
4175 rc = pMediumLockList->Lock();
4176 treeLock.acquire();
4177 mediumLock.acquire();
4178 if (FAILED(rc))
4179 setError(rc,
4180 tr("Could not lock medium when creating diff '%s'"),
4181 diff->i_getLocationFull().c_str());
4182 else
4183 {
4184 /* will release the lock before the potentially lengthy
4185 * operation, so protect with the special state */
4186 MachineState_T oldState = mData->mMachineState;
4187 i_setMachineState(MachineState_SettingUp);
4188
4189 mediumLock.release();
4190 treeLock.release();
4191 alock.release();
4192
4193 rc = medium->i_createDiffStorage(diff,
4194 medium->i_getPreferredDiffVariant(),
4195 pMediumLockList,
4196 NULL /* aProgress */,
4197 true /* aWait */);
4198
4199 alock.acquire();
4200 treeLock.acquire();
4201 mediumLock.acquire();
4202
4203 i_setMachineState(oldState);
4204 }
4205 }
4206
4207 /* Unlock the media and free the associated memory. */
4208 delete pMediumLockList;
4209
4210 if (FAILED(rc)) return rc;
4211
4212 /* use the created diff for the actual attachment */
4213 medium = diff;
4214 mediumCaller.attach(medium);
4215 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4216 mediumLock.attach(medium);
4217 }
4218 while (0);
4219
4220 ComObjPtr<MediumAttachment> attachment;
4221 attachment.createObject();
4222 rc = attachment->init(this,
4223 medium,
4224 aName,
4225 aControllerPort,
4226 aDevice,
4227 aType,
4228 fIndirect,
4229 false /* fPassthrough */,
4230 false /* fTempEject */,
4231 false /* fNonRotational */,
4232 false /* fDiscard */,
4233 fHotplug /* fHotPluggable */,
4234 Utf8Str::Empty);
4235 if (FAILED(rc)) return rc;
4236
4237 if (associate && !medium.isNull())
4238 {
4239 // as the last step, associate the medium to the VM
4240 rc = medium->i_addBackReference(mData->mUuid);
4241 // here we can fail because of Deleting, or being in process of creating a Diff
4242 if (FAILED(rc)) return rc;
4243
4244 mediumLock.release();
4245 treeLock.release();
4246 alock.release();
4247 i_addMediumToRegistry(medium);
4248 alock.acquire();
4249 treeLock.acquire();
4250 mediumLock.acquire();
4251 }
4252
4253 /* success: finally remember the attachment */
4254 i_setModified(IsModified_Storage);
4255 mMediaData.backup();
4256 mMediaData->mAttachments.push_back(attachment);
4257
4258 mediumLock.release();
4259 treeLock.release();
4260 alock.release();
4261
4262 if (fHotplug || fSilent)
4263 {
4264 if (!medium.isNull())
4265 {
4266 MediumLockList *pMediumLockList(new MediumLockList());
4267
4268 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4269 true /* fMediumLockWrite */,
4270 false /* fMediumLockWriteAll */,
4271 NULL,
4272 *pMediumLockList);
4273 alock.acquire();
4274 if (FAILED(rc))
4275 delete pMediumLockList;
4276 else
4277 {
4278 mData->mSession.mLockedMedia.Unlock();
4279 alock.release();
4280 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4281 mData->mSession.mLockedMedia.Lock();
4282 alock.acquire();
4283 }
4284 alock.release();
4285 }
4286
4287 if (SUCCEEDED(rc))
4288 {
4289 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4290 /* Remove lock list in case of error. */
4291 if (FAILED(rc))
4292 {
4293 mData->mSession.mLockedMedia.Unlock();
4294 mData->mSession.mLockedMedia.Remove(attachment);
4295 mData->mSession.mLockedMedia.Lock();
4296 }
4297 }
4298 }
4299
4300 /* Save modified registries, but skip this machine as it's the caller's
4301 * job to save its settings like all other settings changes. */
4302 mParent->i_unmarkRegistryModified(i_getId());
4303 mParent->i_saveModifiedRegistries();
4304
4305 return rc;
4306}
4307
4308HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4309 LONG aDevice)
4310{
4311 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4312 aName.c_str(), aControllerPort, aDevice));
4313
4314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4315
4316 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4317 if (FAILED(rc)) return rc;
4318
4319 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4320
4321 /* Check for an existing controller. */
4322 ComObjPtr<StorageController> ctl;
4323 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4324 if (FAILED(rc)) return rc;
4325
4326 StorageControllerType_T ctrlType;
4327 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4328 if (FAILED(rc))
4329 return setError(E_FAIL,
4330 tr("Could not get type of controller '%s'"),
4331 aName.c_str());
4332
4333 bool fSilent = false;
4334 Utf8Str strReconfig;
4335
4336 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4337 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4338 if ( mData->mMachineState == MachineState_Paused
4339 && strReconfig == "1")
4340 fSilent = true;
4341
4342 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4343 bool fHotplug = false;
4344 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4345 fHotplug = true;
4346
4347 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4348 return setError(VBOX_E_INVALID_VM_STATE,
4349 tr("Controller '%s' does not support hotplugging"),
4350 aName.c_str());
4351
4352 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4353 Bstr(aName).raw(),
4354 aControllerPort,
4355 aDevice);
4356 if (!pAttach)
4357 return setError(VBOX_E_OBJECT_NOT_FOUND,
4358 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4359 aDevice, aControllerPort, aName.c_str());
4360
4361 if (fHotplug && !pAttach->i_getHotPluggable())
4362 return setError(VBOX_E_NOT_SUPPORTED,
4363 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4364 aDevice, aControllerPort, aName.c_str());
4365
4366 /*
4367 * The VM has to detach the device before we delete any implicit diffs.
4368 * If this fails we can roll back without loosing data.
4369 */
4370 if (fHotplug || fSilent)
4371 {
4372 alock.release();
4373 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4374 alock.acquire();
4375 }
4376 if (FAILED(rc)) return rc;
4377
4378 /* If we are here everything went well and we can delete the implicit now. */
4379 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4380
4381 alock.release();
4382
4383 /* Save modified registries, but skip this machine as it's the caller's
4384 * job to save its settings like all other settings changes. */
4385 mParent->i_unmarkRegistryModified(i_getId());
4386 mParent->i_saveModifiedRegistries();
4387
4388 return rc;
4389}
4390
4391HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4392 LONG aDevice, BOOL aPassthrough)
4393{
4394 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4395 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4396
4397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4398
4399 HRESULT rc = i_checkStateDependency(MutableStateDep);
4400 if (FAILED(rc)) return rc;
4401
4402 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4403
4404 if (Global::IsOnlineOrTransient(mData->mMachineState))
4405 return setError(VBOX_E_INVALID_VM_STATE,
4406 tr("Invalid machine state: %s"),
4407 Global::stringifyMachineState(mData->mMachineState));
4408
4409 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4410 Bstr(aName).raw(),
4411 aControllerPort,
4412 aDevice);
4413 if (!pAttach)
4414 return setError(VBOX_E_OBJECT_NOT_FOUND,
4415 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4416 aDevice, aControllerPort, aName.c_str());
4417
4418
4419 i_setModified(IsModified_Storage);
4420 mMediaData.backup();
4421
4422 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4423
4424 if (pAttach->i_getType() != DeviceType_DVD)
4425 return setError(E_INVALIDARG,
4426 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4427 aDevice, aControllerPort, aName.c_str());
4428 pAttach->i_updatePassthrough(!!aPassthrough);
4429
4430 return S_OK;
4431}
4432
4433HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4434 LONG aDevice, BOOL aTemporaryEject)
4435{
4436
4437 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4438 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4439
4440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4441
4442 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4443 if (FAILED(rc)) return rc;
4444
4445 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4446 Bstr(aName).raw(),
4447 aControllerPort,
4448 aDevice);
4449 if (!pAttach)
4450 return setError(VBOX_E_OBJECT_NOT_FOUND,
4451 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4452 aDevice, aControllerPort, aName.c_str());
4453
4454
4455 i_setModified(IsModified_Storage);
4456 mMediaData.backup();
4457
4458 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4459
4460 if (pAttach->i_getType() != DeviceType_DVD)
4461 return setError(E_INVALIDARG,
4462 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4463 aDevice, aControllerPort, aName.c_str());
4464 pAttach->i_updateTempEject(!!aTemporaryEject);
4465
4466 return S_OK;
4467}
4468
4469HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4470 LONG aDevice, BOOL aNonRotational)
4471{
4472
4473 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4474 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4475
4476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4477
4478 HRESULT rc = i_checkStateDependency(MutableStateDep);
4479 if (FAILED(rc)) return rc;
4480
4481 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4482
4483 if (Global::IsOnlineOrTransient(mData->mMachineState))
4484 return setError(VBOX_E_INVALID_VM_STATE,
4485 tr("Invalid machine state: %s"),
4486 Global::stringifyMachineState(mData->mMachineState));
4487
4488 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4489 Bstr(aName).raw(),
4490 aControllerPort,
4491 aDevice);
4492 if (!pAttach)
4493 return setError(VBOX_E_OBJECT_NOT_FOUND,
4494 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4495 aDevice, aControllerPort, aName.c_str());
4496
4497
4498 i_setModified(IsModified_Storage);
4499 mMediaData.backup();
4500
4501 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4502
4503 if (pAttach->i_getType() != DeviceType_HardDisk)
4504 return setError(E_INVALIDARG,
4505 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"),
4506 aDevice, aControllerPort, aName.c_str());
4507 pAttach->i_updateNonRotational(!!aNonRotational);
4508
4509 return S_OK;
4510}
4511
4512HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4513 LONG aDevice, BOOL aDiscard)
4514{
4515
4516 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4517 aName.c_str(), aControllerPort, aDevice, aDiscard));
4518
4519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4520
4521 HRESULT rc = i_checkStateDependency(MutableStateDep);
4522 if (FAILED(rc)) return rc;
4523
4524 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4525
4526 if (Global::IsOnlineOrTransient(mData->mMachineState))
4527 return setError(VBOX_E_INVALID_VM_STATE,
4528 tr("Invalid machine state: %s"),
4529 Global::stringifyMachineState(mData->mMachineState));
4530
4531 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4532 Bstr(aName).raw(),
4533 aControllerPort,
4534 aDevice);
4535 if (!pAttach)
4536 return setError(VBOX_E_OBJECT_NOT_FOUND,
4537 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4538 aDevice, aControllerPort, aName.c_str());
4539
4540
4541 i_setModified(IsModified_Storage);
4542 mMediaData.backup();
4543
4544 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4545
4546 if (pAttach->i_getType() != DeviceType_HardDisk)
4547 return setError(E_INVALIDARG,
4548 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"),
4549 aDevice, aControllerPort, aName.c_str());
4550 pAttach->i_updateDiscard(!!aDiscard);
4551
4552 return S_OK;
4553}
4554
4555HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4556 LONG aDevice, BOOL aHotPluggable)
4557{
4558 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4559 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4560
4561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4562
4563 HRESULT rc = i_checkStateDependency(MutableStateDep);
4564 if (FAILED(rc)) return rc;
4565
4566 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4567
4568 if (Global::IsOnlineOrTransient(mData->mMachineState))
4569 return setError(VBOX_E_INVALID_VM_STATE,
4570 tr("Invalid machine state: %s"),
4571 Global::stringifyMachineState(mData->mMachineState));
4572
4573 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4574 Bstr(aName).raw(),
4575 aControllerPort,
4576 aDevice);
4577 if (!pAttach)
4578 return setError(VBOX_E_OBJECT_NOT_FOUND,
4579 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4580 aDevice, aControllerPort, aName.c_str());
4581
4582 /* Check for an existing controller. */
4583 ComObjPtr<StorageController> ctl;
4584 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4585 if (FAILED(rc)) return rc;
4586
4587 StorageControllerType_T ctrlType;
4588 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4589 if (FAILED(rc))
4590 return setError(E_FAIL,
4591 tr("Could not get type of controller '%s'"),
4592 aName.c_str());
4593
4594 if (!i_isControllerHotplugCapable(ctrlType))
4595 return setError(VBOX_E_NOT_SUPPORTED,
4596 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4597 aName.c_str());
4598
4599 i_setModified(IsModified_Storage);
4600 mMediaData.backup();
4601
4602 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4603
4604 if (pAttach->i_getType() == DeviceType_Floppy)
4605 return setError(E_INVALIDARG,
4606 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"),
4607 aDevice, aControllerPort, aName.c_str());
4608 pAttach->i_updateHotPluggable(!!aHotPluggable);
4609
4610 return S_OK;
4611}
4612
4613HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4614 LONG aDevice)
4615{
4616 int rc = S_OK;
4617 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4618 aName.c_str(), aControllerPort, aDevice));
4619
4620 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4621
4622 return rc;
4623}
4624
4625HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4626 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4627{
4628 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4629 aName.c_str(), aControllerPort, aDevice));
4630
4631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4634 if (FAILED(rc)) return rc;
4635
4636 if (Global::IsOnlineOrTransient(mData->mMachineState))
4637 return setError(VBOX_E_INVALID_VM_STATE,
4638 tr("Invalid machine state: %s"),
4639 Global::stringifyMachineState(mData->mMachineState));
4640
4641 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4642 Bstr(aName).raw(),
4643 aControllerPort,
4644 aDevice);
4645 if (!pAttach)
4646 return setError(VBOX_E_OBJECT_NOT_FOUND,
4647 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4648 aDevice, aControllerPort, aName.c_str());
4649
4650
4651 i_setModified(IsModified_Storage);
4652 mMediaData.backup();
4653
4654 IBandwidthGroup *iB = aBandwidthGroup;
4655 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4656 if (aBandwidthGroup && group.isNull())
4657 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4658
4659 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4660
4661 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4662 if (strBandwidthGroupOld.isNotEmpty())
4663 {
4664 /* Get the bandwidth group object and release it - this must not fail. */
4665 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4666 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4667 Assert(SUCCEEDED(rc));
4668
4669 pBandwidthGroupOld->i_release();
4670 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4671 }
4672
4673 if (!group.isNull())
4674 {
4675 group->i_reference();
4676 pAttach->i_updateBandwidthGroup(group->i_getName());
4677 }
4678
4679 return S_OK;
4680}
4681
4682HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4683 LONG aControllerPort,
4684 LONG aDevice,
4685 DeviceType_T aType)
4686{
4687 HRESULT rc = S_OK;
4688
4689 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4690 aName.c_str(), aControllerPort, aDevice, aType));
4691
4692 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4693
4694 return rc;
4695}
4696
4697
4698HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4699 LONG aControllerPort,
4700 LONG aDevice,
4701 BOOL aForce)
4702{
4703 int rc = S_OK;
4704 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4705 aName.c_str(), aControllerPort, aForce));
4706
4707 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4708
4709 return rc;
4710}
4711
4712HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4713 LONG aControllerPort,
4714 LONG aDevice,
4715 const ComPtr<IMedium> &aMedium,
4716 BOOL aForce)
4717{
4718 int rc = S_OK;
4719 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4720 aName.c_str(), aControllerPort, aDevice, aForce));
4721
4722 // request the host lock first, since might be calling Host methods for getting host drives;
4723 // next, protect the media tree all the while we're in here, as well as our member variables
4724 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4725 this->lockHandle(),
4726 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4727
4728 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4729 Bstr(aName).raw(),
4730 aControllerPort,
4731 aDevice);
4732 if (pAttach.isNull())
4733 return setError(VBOX_E_OBJECT_NOT_FOUND,
4734 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4735 aDevice, aControllerPort, aName.c_str());
4736
4737 /* Remember previously mounted medium. The medium before taking the
4738 * backup is not necessarily the same thing. */
4739 ComObjPtr<Medium> oldmedium;
4740 oldmedium = pAttach->i_getMedium();
4741
4742 IMedium *iM = aMedium;
4743 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4744 if (aMedium && pMedium.isNull())
4745 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4746
4747 AutoCaller mediumCaller(pMedium);
4748 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4749
4750 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4751 if (pMedium)
4752 {
4753 DeviceType_T mediumType = pAttach->i_getType();
4754 switch (mediumType)
4755 {
4756 case DeviceType_DVD:
4757 case DeviceType_Floppy:
4758 break;
4759
4760 default:
4761 return setError(VBOX_E_INVALID_OBJECT_STATE,
4762 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4763 aControllerPort,
4764 aDevice,
4765 aName.c_str());
4766 }
4767 }
4768
4769 i_setModified(IsModified_Storage);
4770 mMediaData.backup();
4771
4772 {
4773 // The backup operation makes the pAttach reference point to the
4774 // old settings. Re-get the correct reference.
4775 pAttach = i_findAttachment(mMediaData->mAttachments,
4776 Bstr(aName).raw(),
4777 aControllerPort,
4778 aDevice);
4779 if (!oldmedium.isNull())
4780 oldmedium->i_removeBackReference(mData->mUuid);
4781 if (!pMedium.isNull())
4782 {
4783 pMedium->i_addBackReference(mData->mUuid);
4784
4785 mediumLock.release();
4786 multiLock.release();
4787 i_addMediumToRegistry(pMedium);
4788 multiLock.acquire();
4789 mediumLock.acquire();
4790 }
4791
4792 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4793 pAttach->i_updateMedium(pMedium);
4794 }
4795
4796 i_setModified(IsModified_Storage);
4797
4798 mediumLock.release();
4799 multiLock.release();
4800 rc = i_onMediumChange(pAttach, aForce);
4801 multiLock.acquire();
4802 mediumLock.acquire();
4803
4804 /* On error roll back this change only. */
4805 if (FAILED(rc))
4806 {
4807 if (!pMedium.isNull())
4808 pMedium->i_removeBackReference(mData->mUuid);
4809 pAttach = i_findAttachment(mMediaData->mAttachments,
4810 Bstr(aName).raw(),
4811 aControllerPort,
4812 aDevice);
4813 /* If the attachment is gone in the meantime, bail out. */
4814 if (pAttach.isNull())
4815 return rc;
4816 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4817 if (!oldmedium.isNull())
4818 oldmedium->i_addBackReference(mData->mUuid);
4819 pAttach->i_updateMedium(oldmedium);
4820 }
4821
4822 mediumLock.release();
4823 multiLock.release();
4824
4825 /* Save modified registries, but skip this machine as it's the caller's
4826 * job to save its settings like all other settings changes. */
4827 mParent->i_unmarkRegistryModified(i_getId());
4828 mParent->i_saveModifiedRegistries();
4829
4830 return rc;
4831}
4832HRESULT Machine::getMedium(const com::Utf8Str &aName,
4833 LONG aControllerPort,
4834 LONG aDevice,
4835 ComPtr<IMedium> &aMedium)
4836{
4837 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4838 aName.c_str(), aControllerPort, aDevice));
4839
4840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4841
4842 aMedium = NULL;
4843
4844 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4845 Bstr(aName).raw(),
4846 aControllerPort,
4847 aDevice);
4848 if (pAttach.isNull())
4849 return setError(VBOX_E_OBJECT_NOT_FOUND,
4850 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4851 aDevice, aControllerPort, aName.c_str());
4852
4853 aMedium = pAttach->i_getMedium();
4854
4855 return S_OK;
4856}
4857
4858HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4859{
4860
4861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4862
4863 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4864
4865 return S_OK;
4866}
4867
4868HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4869{
4870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4871
4872 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4873
4874 return S_OK;
4875}
4876
4877HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4878{
4879 /* Do not assert if slot is out of range, just return the advertised
4880 status. testdriver/vbox.py triggers this in logVmInfo. */
4881 if (aSlot >= mNetworkAdapters.size())
4882 return setError(E_INVALIDARG,
4883 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4884 aSlot, mNetworkAdapters.size());
4885
4886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4887
4888 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4889
4890 return S_OK;
4891}
4892
4893HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4894{
4895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4896
4897 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4898 size_t i = 0;
4899 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4900 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4901 ++it, ++i)
4902 aKeys[i] = it->first;
4903
4904 return S_OK;
4905}
4906
4907 /**
4908 * @note Locks this object for reading.
4909 */
4910HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4911 com::Utf8Str &aValue)
4912{
4913 /* start with nothing found */
4914 aValue = "";
4915
4916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4917
4918 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4919 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4920 // found:
4921 aValue = it->second; // source is a Utf8Str
4922
4923 /* return the result to caller (may be empty) */
4924 return S_OK;
4925}
4926
4927 /**
4928 * @note Locks mParent for writing + this object for writing.
4929 */
4930HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4931{
4932 Utf8Str strOldValue; // empty
4933
4934 // locking note: we only hold the read lock briefly to look up the old value,
4935 // then release it and call the onExtraCanChange callbacks. There is a small
4936 // chance of a race insofar as the callback might be called twice if two callers
4937 // change the same key at the same time, but that's a much better solution
4938 // than the deadlock we had here before. The actual changing of the extradata
4939 // is then performed under the write lock and race-free.
4940
4941 // look up the old value first; if nothing has changed then we need not do anything
4942 {
4943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4944
4945 // For snapshots don't even think about allowing changes, extradata
4946 // is global for a machine, so there is nothing snapshot specific.
4947 if (i_isSnapshotMachine())
4948 return setError(VBOX_E_INVALID_VM_STATE,
4949 tr("Cannot set extradata for a snapshot"));
4950
4951 // check if the right IMachine instance is used
4952 if (mData->mRegistered && !i_isSessionMachine())
4953 return setError(VBOX_E_INVALID_VM_STATE,
4954 tr("Cannot set extradata for an immutable machine"));
4955
4956 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4957 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4958 strOldValue = it->second;
4959 }
4960
4961 bool fChanged;
4962 if ((fChanged = (strOldValue != aValue)))
4963 {
4964 // ask for permission from all listeners outside the locks;
4965 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4966 // lock to copy the list of callbacks to invoke
4967 Bstr error;
4968 Bstr bstrValue(aValue);
4969
4970 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4971 {
4972 const char *sep = error.isEmpty() ? "" : ": ";
4973 CBSTR err = error.raw();
4974 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4975 return setError(E_ACCESSDENIED,
4976 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4977 aKey.c_str(),
4978 aValue.c_str(),
4979 sep,
4980 err);
4981 }
4982
4983 // data is changing and change not vetoed: then write it out under the lock
4984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4985
4986 if (aValue.isEmpty())
4987 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4988 else
4989 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4990 // creates a new key if needed
4991
4992 bool fNeedsGlobalSaveSettings = false;
4993 // This saving of settings is tricky: there is no "old state" for the
4994 // extradata items at all (unlike all other settings), so the old/new
4995 // settings comparison would give a wrong result!
4996 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4997
4998 if (fNeedsGlobalSaveSettings)
4999 {
5000 // save the global settings; for that we should hold only the VirtualBox lock
5001 alock.release();
5002 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5003 mParent->i_saveSettings();
5004 }
5005 }
5006
5007 // fire notification outside the lock
5008 if (fChanged)
5009 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5010
5011 return S_OK;
5012}
5013
5014HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5015{
5016 aProgress = NULL;
5017 NOREF(aSettingsFilePath);
5018 ReturnComNotImplemented();
5019}
5020
5021HRESULT Machine::saveSettings()
5022{
5023 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5024
5025 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5026 if (FAILED(rc)) return rc;
5027
5028 /* the settings file path may never be null */
5029 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5030
5031 /* save all VM data excluding snapshots */
5032 bool fNeedsGlobalSaveSettings = false;
5033 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5034 mlock.release();
5035
5036 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5037 {
5038 // save the global settings; for that we should hold only the VirtualBox lock
5039 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5040 rc = mParent->i_saveSettings();
5041 }
5042
5043 return rc;
5044}
5045
5046
5047HRESULT Machine::discardSettings()
5048{
5049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5050
5051 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5052 if (FAILED(rc)) return rc;
5053
5054 /*
5055 * during this rollback, the session will be notified if data has
5056 * been actually changed
5057 */
5058 i_rollback(true /* aNotify */);
5059
5060 return S_OK;
5061}
5062
5063/** @note Locks objects! */
5064HRESULT Machine::unregister(AutoCaller &autoCaller,
5065 CleanupMode_T aCleanupMode,
5066 std::vector<ComPtr<IMedium> > &aMedia)
5067{
5068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5069
5070 Guid id(i_getId());
5071
5072 if (mData->mSession.mState != SessionState_Unlocked)
5073 return setError(VBOX_E_INVALID_OBJECT_STATE,
5074 tr("Cannot unregister the machine '%s' while it is locked"),
5075 mUserData->s.strName.c_str());
5076
5077 // wait for state dependents to drop to zero
5078 i_ensureNoStateDependencies();
5079
5080 if (!mData->mAccessible)
5081 {
5082 // inaccessible maschines can only be unregistered; uninitialize ourselves
5083 // here because currently there may be no unregistered that are inaccessible
5084 // (this state combination is not supported). Note releasing the caller and
5085 // leaving the lock before calling uninit()
5086 alock.release();
5087 autoCaller.release();
5088
5089 uninit();
5090
5091 mParent->i_unregisterMachine(this, id);
5092 // calls VirtualBox::i_saveSettings()
5093
5094 return S_OK;
5095 }
5096
5097 HRESULT rc = S_OK;
5098
5099 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5100 // discard saved state
5101 if (mData->mMachineState == MachineState_Saved)
5102 {
5103 // add the saved state file to the list of files the caller should delete
5104 Assert(!mSSData->strStateFilePath.isEmpty());
5105 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5106
5107 mSSData->strStateFilePath.setNull();
5108
5109 // unconditionally set the machine state to powered off, we now
5110 // know no session has locked the machine
5111 mData->mMachineState = MachineState_PoweredOff;
5112 }
5113
5114 size_t cSnapshots = 0;
5115 if (mData->mFirstSnapshot)
5116 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5117 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5118 // fail now before we start detaching media
5119 return setError(VBOX_E_INVALID_OBJECT_STATE,
5120 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5121 mUserData->s.strName.c_str(), cSnapshots);
5122
5123 // This list collects the medium objects from all medium attachments
5124 // which we will detach from the machine and its snapshots, in a specific
5125 // order which allows for closing all media without getting "media in use"
5126 // errors, simply by going through the list from the front to the back:
5127 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5128 // and must be closed before the parent media from the snapshots, or closing the parents
5129 // will fail because they still have children);
5130 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5131 // the root ("first") snapshot of the machine.
5132 MediaList llMedia;
5133
5134 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5135 && mMediaData->mAttachments.size()
5136 )
5137 {
5138 // we have media attachments: detach them all and add the Medium objects to our list
5139 if (aCleanupMode != CleanupMode_UnregisterOnly)
5140 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5141 else
5142 return setError(VBOX_E_INVALID_OBJECT_STATE,
5143 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5144 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5145 }
5146
5147 if (cSnapshots)
5148 {
5149 // add the media from the medium attachments of the snapshots to llMedia
5150 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5151 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5152 // into the children first
5153
5154 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5155 MachineState_T oldState = mData->mMachineState;
5156 mData->mMachineState = MachineState_DeletingSnapshot;
5157
5158 // make a copy of the first snapshot so the refcount does not drop to 0
5159 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5160 // because of the AutoCaller voodoo)
5161 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5162
5163 // GO!
5164 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5165
5166 mData->mMachineState = oldState;
5167 }
5168
5169 if (FAILED(rc))
5170 {
5171 i_rollbackMedia();
5172 return rc;
5173 }
5174
5175 // commit all the media changes made above
5176 i_commitMedia();
5177
5178 mData->mRegistered = false;
5179
5180 // machine lock no longer needed
5181 alock.release();
5182
5183 // return media to caller
5184 size_t i = 0;
5185 aMedia.resize(llMedia.size());
5186 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5187 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5188
5189 mParent->i_unregisterMachine(this, id);
5190 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5191
5192 return S_OK;
5193}
5194
5195/**
5196 * Task record for deleting a machine config.
5197 */
5198struct Machine::DeleteConfigTask
5199 : public Machine::Task
5200{
5201 DeleteConfigTask(Machine *m,
5202 Progress *p,
5203 const Utf8Str &t,
5204 const RTCList<ComPtr<IMedium> > &llMediums,
5205 const StringsList &llFilesToDelete)
5206 : Task(m, p, t),
5207 m_llMediums(llMediums),
5208 m_llFilesToDelete(llFilesToDelete)
5209 {}
5210
5211 void handler()
5212 {
5213 m_pMachine->i_deleteConfigHandler(*this);
5214 }
5215
5216 RTCList<ComPtr<IMedium> > m_llMediums;
5217 StringsList m_llFilesToDelete;
5218};
5219
5220/**
5221 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5222 * SessionMachine::taskHandler().
5223 *
5224 * @note Locks this object for writing.
5225 *
5226 * @param task
5227 * @return
5228 */
5229void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5230{
5231 LogFlowThisFuncEnter();
5232
5233 AutoCaller autoCaller(this);
5234 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5235 if (FAILED(autoCaller.rc()))
5236 {
5237 /* we might have been uninitialized because the session was accidentally
5238 * closed by the client, so don't assert */
5239 HRESULT rc = setError(E_FAIL,
5240 tr("The session has been accidentally closed"));
5241 task.m_pProgress->i_notifyComplete(rc);
5242 LogFlowThisFuncLeave();
5243 return;
5244 }
5245
5246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5247
5248 HRESULT rc = S_OK;
5249
5250 try
5251 {
5252 ULONG uLogHistoryCount = 3;
5253 ComPtr<ISystemProperties> systemProperties;
5254 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5255 if (FAILED(rc)) throw rc;
5256
5257 if (!systemProperties.isNull())
5258 {
5259 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5260 if (FAILED(rc)) throw rc;
5261 }
5262
5263 MachineState_T oldState = mData->mMachineState;
5264 i_setMachineState(MachineState_SettingUp);
5265 alock.release();
5266 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5267 {
5268 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5269 {
5270 AutoCaller mac(pMedium);
5271 if (FAILED(mac.rc())) throw mac.rc();
5272 Utf8Str strLocation = pMedium->i_getLocationFull();
5273 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5274 if (FAILED(rc)) throw rc;
5275 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5276 }
5277 if (pMedium->i_isMediumFormatFile())
5278 {
5279 ComPtr<IProgress> pProgress2;
5280 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5281 if (FAILED(rc)) throw rc;
5282 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5283 if (FAILED(rc)) throw rc;
5284 /* Check the result of the asynchronous process. */
5285 LONG iRc;
5286 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5287 if (FAILED(rc)) throw rc;
5288 /* If the thread of the progress object has an error, then
5289 * retrieve the error info from there, or it'll be lost. */
5290 if (FAILED(iRc))
5291 throw setError(ProgressErrorInfo(pProgress2));
5292 }
5293
5294 /* Close the medium, deliberately without checking the return
5295 * code, and without leaving any trace in the error info, as
5296 * a failure here is a very minor issue, which shouldn't happen
5297 * as above we even managed to delete the medium. */
5298 {
5299 ErrorInfoKeeper eik;
5300 pMedium->Close();
5301 }
5302 }
5303 i_setMachineState(oldState);
5304 alock.acquire();
5305
5306 // delete the files pushed on the task list by Machine::Delete()
5307 // (this includes saved states of the machine and snapshots and
5308 // medium storage files from the IMedium list passed in, and the
5309 // machine XML file)
5310 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5311 while (it != task.m_llFilesToDelete.end())
5312 {
5313 const Utf8Str &strFile = *it;
5314 LogFunc(("Deleting file %s\n", strFile.c_str()));
5315 int vrc = RTFileDelete(strFile.c_str());
5316 if (RT_FAILURE(vrc))
5317 throw setError(VBOX_E_IPRT_ERROR,
5318 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5319
5320 ++it;
5321 if (it == task.m_llFilesToDelete.end())
5322 {
5323 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5324 if (FAILED(rc)) throw rc;
5325 break;
5326 }
5327
5328 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5329 if (FAILED(rc)) throw rc;
5330 }
5331
5332 /* delete the settings only when the file actually exists */
5333 if (mData->pMachineConfigFile->fileExists())
5334 {
5335 /* Delete any backup or uncommitted XML files. Ignore failures.
5336 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5337 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5338 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5339 RTFileDelete(otherXml.c_str());
5340 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5341 RTFileDelete(otherXml.c_str());
5342
5343 /* delete the Logs folder, nothing important should be left
5344 * there (we don't check for errors because the user might have
5345 * some private files there that we don't want to delete) */
5346 Utf8Str logFolder;
5347 getLogFolder(logFolder);
5348 Assert(logFolder.length());
5349 if (RTDirExists(logFolder.c_str()))
5350 {
5351 /* Delete all VBox.log[.N] files from the Logs folder
5352 * (this must be in sync with the rotation logic in
5353 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5354 * files that may have been created by the GUI. */
5355 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5356 logFolder.c_str(), RTPATH_DELIMITER);
5357 RTFileDelete(log.c_str());
5358 log = Utf8StrFmt("%s%cVBox.png",
5359 logFolder.c_str(), RTPATH_DELIMITER);
5360 RTFileDelete(log.c_str());
5361 for (int i = uLogHistoryCount; i > 0; i--)
5362 {
5363 log = Utf8StrFmt("%s%cVBox.log.%d",
5364 logFolder.c_str(), RTPATH_DELIMITER, i);
5365 RTFileDelete(log.c_str());
5366 log = Utf8StrFmt("%s%cVBox.png.%d",
5367 logFolder.c_str(), RTPATH_DELIMITER, i);
5368 RTFileDelete(log.c_str());
5369 }
5370#if defined(RT_OS_WINDOWS)
5371 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5372 RTFileDelete(log.c_str());
5373 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5374 RTFileDelete(log.c_str());
5375#endif
5376
5377 RTDirRemove(logFolder.c_str());
5378 }
5379
5380 /* delete the Snapshots folder, nothing important should be left
5381 * there (we don't check for errors because the user might have
5382 * some private files there that we don't want to delete) */
5383 Utf8Str strFullSnapshotFolder;
5384 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5385 Assert(!strFullSnapshotFolder.isEmpty());
5386 if (RTDirExists(strFullSnapshotFolder.c_str()))
5387 RTDirRemove(strFullSnapshotFolder.c_str());
5388
5389 // delete the directory that contains the settings file, but only
5390 // if it matches the VM name
5391 Utf8Str settingsDir;
5392 if (i_isInOwnDir(&settingsDir))
5393 RTDirRemove(settingsDir.c_str());
5394 }
5395
5396 alock.release();
5397
5398 mParent->i_saveModifiedRegistries();
5399 }
5400 catch (HRESULT aRC) { rc = aRC; }
5401
5402 task.m_pProgress->i_notifyComplete(rc);
5403
5404 LogFlowThisFuncLeave();
5405}
5406
5407HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5408{
5409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5410
5411 HRESULT rc = i_checkStateDependency(MutableStateDep);
5412 if (FAILED(rc)) return rc;
5413
5414 if (mData->mRegistered)
5415 return setError(VBOX_E_INVALID_VM_STATE,
5416 tr("Cannot delete settings of a registered machine"));
5417
5418 // collect files to delete
5419 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5420 if (mData->pMachineConfigFile->fileExists())
5421 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5422
5423 RTCList<ComPtr<IMedium> > llMediums;
5424 for (size_t i = 0; i < aMedia.size(); ++i)
5425 {
5426 IMedium *pIMedium(aMedia[i]);
5427 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5428 if (pMedium.isNull())
5429 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5430 SafeArray<BSTR> ids;
5431 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5432 if (FAILED(rc)) return rc;
5433 /* At this point the medium should not have any back references
5434 * anymore. If it has it is attached to another VM and *must* not
5435 * deleted. */
5436 if (ids.size() < 1)
5437 llMediums.append(pMedium);
5438 }
5439
5440 ComObjPtr<Progress> pProgress;
5441 pProgress.createObject();
5442 rc = pProgress->init(i_getVirtualBox(),
5443 static_cast<IMachine*>(this) /* aInitiator */,
5444 Bstr(tr("Deleting files")).raw(),
5445 true /* fCancellable */,
5446 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5447 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5448 if (FAILED(rc))
5449 return rc;
5450
5451 /* create and start the task on a separate thread (note that it will not
5452 * start working until we release alock) */
5453 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5454 rc = pTask->createThread();
5455 if (FAILED(rc))
5456 return rc;
5457
5458 pProgress.queryInterfaceTo(aProgress.asOutParam());
5459
5460 LogFlowFuncLeave();
5461
5462 return S_OK;
5463}
5464
5465HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5466{
5467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5468
5469 ComObjPtr<Snapshot> pSnapshot;
5470 HRESULT rc;
5471
5472 if (aNameOrId.isEmpty())
5473 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5474 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5475 else
5476 {
5477 Guid uuid(aNameOrId);
5478 if (uuid.isValid())
5479 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5480 else
5481 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5482 }
5483 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5484
5485 return rc;
5486}
5487
5488HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5489{
5490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5491
5492 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5493 if (FAILED(rc)) return rc;
5494
5495 ComObjPtr<SharedFolder> sharedFolder;
5496 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5497 if (SUCCEEDED(rc))
5498 return setError(VBOX_E_OBJECT_IN_USE,
5499 tr("Shared folder named '%s' already exists"),
5500 aName.c_str());
5501
5502 sharedFolder.createObject();
5503 rc = sharedFolder->init(i_getMachine(),
5504 aName,
5505 aHostPath,
5506 !!aWritable,
5507 !!aAutomount,
5508 true /* fFailOnError */);
5509 if (FAILED(rc)) return rc;
5510
5511 i_setModified(IsModified_SharedFolders);
5512 mHWData.backup();
5513 mHWData->mSharedFolders.push_back(sharedFolder);
5514
5515 /* inform the direct session if any */
5516 alock.release();
5517 i_onSharedFolderChange();
5518
5519 return S_OK;
5520}
5521
5522HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5523{
5524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5525
5526 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5527 if (FAILED(rc)) return rc;
5528
5529 ComObjPtr<SharedFolder> sharedFolder;
5530 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5531 if (FAILED(rc)) return rc;
5532
5533 i_setModified(IsModified_SharedFolders);
5534 mHWData.backup();
5535 mHWData->mSharedFolders.remove(sharedFolder);
5536
5537 /* inform the direct session if any */
5538 alock.release();
5539 i_onSharedFolderChange();
5540
5541 return S_OK;
5542}
5543
5544HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5545{
5546 /* start with No */
5547 *aCanShow = FALSE;
5548
5549 ComPtr<IInternalSessionControl> directControl;
5550 {
5551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5552
5553 if (mData->mSession.mState != SessionState_Locked)
5554 return setError(VBOX_E_INVALID_VM_STATE,
5555 tr("Machine is not locked for session (session state: %s)"),
5556 Global::stringifySessionState(mData->mSession.mState));
5557
5558 if (mData->mSession.mLockType == LockType_VM)
5559 directControl = mData->mSession.mDirectControl;
5560 }
5561
5562 /* ignore calls made after #OnSessionEnd() is called */
5563 if (!directControl)
5564 return S_OK;
5565
5566 LONG64 dummy;
5567 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5568}
5569
5570HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5571{
5572 ComPtr<IInternalSessionControl> directControl;
5573 {
5574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5575
5576 if (mData->mSession.mState != SessionState_Locked)
5577 return setError(E_FAIL,
5578 tr("Machine is not locked for session (session state: %s)"),
5579 Global::stringifySessionState(mData->mSession.mState));
5580
5581 if (mData->mSession.mLockType == LockType_VM)
5582 directControl = mData->mSession.mDirectControl;
5583 }
5584
5585 /* ignore calls made after #OnSessionEnd() is called */
5586 if (!directControl)
5587 return S_OK;
5588
5589 BOOL dummy;
5590 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5591}
5592
5593#ifdef VBOX_WITH_GUEST_PROPS
5594/**
5595 * Look up a guest property in VBoxSVC's internal structures.
5596 */
5597HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5598 com::Utf8Str &aValue,
5599 LONG64 *aTimestamp,
5600 com::Utf8Str &aFlags) const
5601{
5602 using namespace guestProp;
5603
5604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5605 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5606
5607 if (it != mHWData->mGuestProperties.end())
5608 {
5609 char szFlags[MAX_FLAGS_LEN + 1];
5610 aValue = it->second.strValue;
5611 *aTimestamp = it->second.mTimestamp;
5612 writeFlags(it->second.mFlags, szFlags);
5613 aFlags = Utf8Str(szFlags);
5614 }
5615
5616 return S_OK;
5617}
5618
5619/**
5620 * Query the VM that a guest property belongs to for the property.
5621 * @returns E_ACCESSDENIED if the VM process is not available or not
5622 * currently handling queries and the lookup should then be done in
5623 * VBoxSVC.
5624 */
5625HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5626 com::Utf8Str &aValue,
5627 LONG64 *aTimestamp,
5628 com::Utf8Str &aFlags) const
5629{
5630 HRESULT rc = S_OK;
5631 BSTR bValue = NULL;
5632 BSTR bFlags = NULL;
5633
5634 ComPtr<IInternalSessionControl> directControl;
5635 {
5636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5637 if (mData->mSession.mLockType == LockType_VM)
5638 directControl = mData->mSession.mDirectControl;
5639 }
5640
5641 /* ignore calls made after #OnSessionEnd() is called */
5642 if (!directControl)
5643 rc = E_ACCESSDENIED;
5644 else
5645 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5646 0 /* accessMode */,
5647 &bValue, aTimestamp, &bFlags);
5648
5649 aValue = bValue;
5650 aFlags = bFlags;
5651
5652 return rc;
5653}
5654#endif // VBOX_WITH_GUEST_PROPS
5655
5656HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5657 com::Utf8Str &aValue,
5658 LONG64 *aTimestamp,
5659 com::Utf8Str &aFlags)
5660{
5661#ifndef VBOX_WITH_GUEST_PROPS
5662 ReturnComNotImplemented();
5663#else // VBOX_WITH_GUEST_PROPS
5664
5665 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5666
5667 if (rc == E_ACCESSDENIED)
5668 /* The VM is not running or the service is not (yet) accessible */
5669 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5670 return rc;
5671#endif // VBOX_WITH_GUEST_PROPS
5672}
5673
5674HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5675{
5676 LONG64 dummyTimestamp;
5677 com::Utf8Str dummyFlags;
5678 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5679 return rc;
5680
5681}
5682HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5683{
5684 com::Utf8Str dummyFlags;
5685 com::Utf8Str dummyValue;
5686 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5687 return rc;
5688}
5689
5690#ifdef VBOX_WITH_GUEST_PROPS
5691/**
5692 * Set a guest property in VBoxSVC's internal structures.
5693 */
5694HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5695 const com::Utf8Str &aFlags, bool fDelete)
5696{
5697 using namespace guestProp;
5698
5699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5700 HRESULT rc = S_OK;
5701
5702 rc = i_checkStateDependency(MutableOrSavedStateDep);
5703 if (FAILED(rc)) return rc;
5704
5705 try
5706 {
5707 uint32_t fFlags = NILFLAG;
5708 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5709 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5710
5711 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5712 if (it == mHWData->mGuestProperties.end())
5713 {
5714 if (!fDelete)
5715 {
5716 i_setModified(IsModified_MachineData);
5717 mHWData.backupEx();
5718
5719 RTTIMESPEC time;
5720 HWData::GuestProperty prop;
5721 prop.strValue = Bstr(aValue).raw();
5722 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5723 prop.mFlags = fFlags;
5724 mHWData->mGuestProperties[aName] = prop;
5725 }
5726 }
5727 else
5728 {
5729 if (it->second.mFlags & (RDONLYHOST))
5730 {
5731 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5732 }
5733 else
5734 {
5735 i_setModified(IsModified_MachineData);
5736 mHWData.backupEx();
5737
5738 /* The backupEx() operation invalidates our iterator,
5739 * so get a new one. */
5740 it = mHWData->mGuestProperties.find(aName);
5741 Assert(it != mHWData->mGuestProperties.end());
5742
5743 if (!fDelete)
5744 {
5745 RTTIMESPEC time;
5746 it->second.strValue = aValue;
5747 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5748 it->second.mFlags = fFlags;
5749 }
5750 else
5751 mHWData->mGuestProperties.erase(it);
5752 }
5753 }
5754
5755 if (SUCCEEDED(rc))
5756 {
5757 alock.release();
5758
5759 mParent->i_onGuestPropertyChange(mData->mUuid,
5760 Bstr(aName).raw(),
5761 Bstr(aValue).raw(),
5762 Bstr(aFlags).raw());
5763 }
5764 }
5765 catch (std::bad_alloc &)
5766 {
5767 rc = E_OUTOFMEMORY;
5768 }
5769
5770 return rc;
5771}
5772
5773/**
5774 * Set a property on the VM that that property belongs to.
5775 * @returns E_ACCESSDENIED if the VM process is not available or not
5776 * currently handling queries and the setting should then be done in
5777 * VBoxSVC.
5778 */
5779HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5780 const com::Utf8Str &aFlags, bool fDelete)
5781{
5782 HRESULT rc;
5783
5784 try
5785 {
5786 ComPtr<IInternalSessionControl> directControl;
5787 {
5788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5789 if (mData->mSession.mLockType == LockType_VM)
5790 directControl = mData->mSession.mDirectControl;
5791 }
5792
5793 BSTR dummy = NULL; /* will not be changed (setter) */
5794 LONG64 dummy64;
5795 if (!directControl)
5796 rc = E_ACCESSDENIED;
5797 else
5798 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5799 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5800 fDelete? 2: 1 /* accessMode */,
5801 &dummy, &dummy64, &dummy);
5802 }
5803 catch (std::bad_alloc &)
5804 {
5805 rc = E_OUTOFMEMORY;
5806 }
5807
5808 return rc;
5809}
5810#endif // VBOX_WITH_GUEST_PROPS
5811
5812HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5813 const com::Utf8Str &aFlags)
5814{
5815#ifndef VBOX_WITH_GUEST_PROPS
5816 ReturnComNotImplemented();
5817#else // VBOX_WITH_GUEST_PROPS
5818 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5819 if (rc == E_ACCESSDENIED)
5820 /* The VM is not running or the service is not (yet) accessible */
5821 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5822 return rc;
5823#endif // VBOX_WITH_GUEST_PROPS
5824}
5825
5826HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5827{
5828 return setGuestProperty(aProperty, aValue, "");
5829}
5830
5831HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5832{
5833#ifndef VBOX_WITH_GUEST_PROPS
5834 ReturnComNotImplemented();
5835#else // VBOX_WITH_GUEST_PROPS
5836 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5837 if (rc == E_ACCESSDENIED)
5838 /* The VM is not running or the service is not (yet) accessible */
5839 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5840 return rc;
5841#endif // VBOX_WITH_GUEST_PROPS
5842}
5843
5844#ifdef VBOX_WITH_GUEST_PROPS
5845/**
5846 * Enumerate the guest properties in VBoxSVC's internal structures.
5847 */
5848HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5849 std::vector<com::Utf8Str> &aNames,
5850 std::vector<com::Utf8Str> &aValues,
5851 std::vector<LONG64> &aTimestamps,
5852 std::vector<com::Utf8Str> &aFlags)
5853{
5854 using namespace guestProp;
5855
5856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5857 Utf8Str strPatterns(aPatterns);
5858
5859 HWData::GuestPropertyMap propMap;
5860
5861 /*
5862 * Look for matching patterns and build up a list.
5863 */
5864 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5865 while (it != mHWData->mGuestProperties.end())
5866 {
5867 if ( strPatterns.isEmpty()
5868 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5869 RTSTR_MAX,
5870 it->first.c_str(),
5871 RTSTR_MAX,
5872 NULL)
5873 )
5874 propMap.insert(*it);
5875 ++it;
5876 }
5877
5878 alock.release();
5879
5880 /*
5881 * And build up the arrays for returning the property information.
5882 */
5883 size_t cEntries = propMap.size();
5884
5885 aNames.resize(cEntries);
5886 aValues.resize(cEntries);
5887 aTimestamps.resize(cEntries);
5888 aFlags.resize(cEntries);
5889
5890 char szFlags[MAX_FLAGS_LEN + 1];
5891 size_t i= 0;
5892 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5893 {
5894 aNames[i] = it->first;
5895 aValues[i] = it->second.strValue;
5896 aTimestamps[i] = it->second.mTimestamp;
5897 writeFlags(it->second.mFlags, szFlags);
5898 aFlags[i] = Utf8Str(szFlags);
5899 }
5900
5901 return S_OK;
5902}
5903
5904/**
5905 * Enumerate the properties managed by a VM.
5906 * @returns E_ACCESSDENIED if the VM process is not available or not
5907 * currently handling queries and the setting should then be done in
5908 * VBoxSVC.
5909 */
5910HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5911 std::vector<com::Utf8Str> &aNames,
5912 std::vector<com::Utf8Str> &aValues,
5913 std::vector<LONG64> &aTimestamps,
5914 std::vector<com::Utf8Str> &aFlags)
5915{
5916 HRESULT rc;
5917 ComPtr<IInternalSessionControl> directControl;
5918 {
5919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5920 if (mData->mSession.mLockType == LockType_VM)
5921 directControl = mData->mSession.mDirectControl;
5922 }
5923
5924 com::SafeArray<BSTR> bNames;
5925 com::SafeArray<BSTR> bValues;
5926 com::SafeArray<LONG64> bTimestamps;
5927 com::SafeArray<BSTR> bFlags;
5928
5929 if (!directControl)
5930 rc = E_ACCESSDENIED;
5931 else
5932 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5933 ComSafeArrayAsOutParam(bNames),
5934 ComSafeArrayAsOutParam(bValues),
5935 ComSafeArrayAsOutParam(bTimestamps),
5936 ComSafeArrayAsOutParam(bFlags));
5937 size_t i;
5938 aNames.resize(bNames.size());
5939 for (i = 0; i < bNames.size(); ++i)
5940 aNames[i] = Utf8Str(bNames[i]);
5941 aValues.resize(bValues.size());
5942 for (i = 0; i < bValues.size(); ++i)
5943 aValues[i] = Utf8Str(bValues[i]);
5944 aTimestamps.resize(bTimestamps.size());
5945 for (i = 0; i < bTimestamps.size(); ++i)
5946 aTimestamps[i] = bTimestamps[i];
5947 aFlags.resize(bFlags.size());
5948 for (i = 0; i < bFlags.size(); ++i)
5949 aFlags[i] = Utf8Str(bFlags[i]);
5950
5951 return rc;
5952}
5953#endif // VBOX_WITH_GUEST_PROPS
5954HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5955 std::vector<com::Utf8Str> &aNames,
5956 std::vector<com::Utf8Str> &aValues,
5957 std::vector<LONG64> &aTimestamps,
5958 std::vector<com::Utf8Str> &aFlags)
5959{
5960#ifndef VBOX_WITH_GUEST_PROPS
5961 ReturnComNotImplemented();
5962#else // VBOX_WITH_GUEST_PROPS
5963
5964 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5965
5966 if (rc == E_ACCESSDENIED)
5967 /* The VM is not running or the service is not (yet) accessible */
5968 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5969 return rc;
5970#endif // VBOX_WITH_GUEST_PROPS
5971}
5972
5973HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5974 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5975{
5976 MediaData::AttachmentList atts;
5977
5978 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5979 if (FAILED(rc)) return rc;
5980
5981 size_t i = 0;
5982 aMediumAttachments.resize(atts.size());
5983 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5984 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5985
5986 return S_OK;
5987}
5988
5989HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5990 LONG aControllerPort,
5991 LONG aDevice,
5992 ComPtr<IMediumAttachment> &aAttachment)
5993{
5994 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5995 aName.c_str(), aControllerPort, aDevice));
5996
5997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5998
5999 aAttachment = NULL;
6000
6001 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6002 Bstr(aName).raw(),
6003 aControllerPort,
6004 aDevice);
6005 if (pAttach.isNull())
6006 return setError(VBOX_E_OBJECT_NOT_FOUND,
6007 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6008 aDevice, aControllerPort, aName.c_str());
6009
6010 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6011
6012 return S_OK;
6013}
6014
6015
6016HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6017 StorageBus_T aConnectionType,
6018 ComPtr<IStorageController> &aController)
6019{
6020 if ( (aConnectionType <= StorageBus_Null)
6021 || (aConnectionType > StorageBus_USB))
6022 return setError(E_INVALIDARG,
6023 tr("Invalid connection type: %d"),
6024 aConnectionType);
6025
6026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6027
6028 HRESULT rc = i_checkStateDependency(MutableStateDep);
6029 if (FAILED(rc)) return rc;
6030
6031 /* try to find one with the name first. */
6032 ComObjPtr<StorageController> ctrl;
6033
6034 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6035 if (SUCCEEDED(rc))
6036 return setError(VBOX_E_OBJECT_IN_USE,
6037 tr("Storage controller named '%s' already exists"),
6038 aName.c_str());
6039
6040 ctrl.createObject();
6041
6042 /* get a new instance number for the storage controller */
6043 ULONG ulInstance = 0;
6044 bool fBootable = true;
6045 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6046 it != mStorageControllers->end();
6047 ++it)
6048 {
6049 if ((*it)->i_getStorageBus() == aConnectionType)
6050 {
6051 ULONG ulCurInst = (*it)->i_getInstance();
6052
6053 if (ulCurInst >= ulInstance)
6054 ulInstance = ulCurInst + 1;
6055
6056 /* Only one controller of each type can be marked as bootable. */
6057 if ((*it)->i_getBootable())
6058 fBootable = false;
6059 }
6060 }
6061
6062 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6063 if (FAILED(rc)) return rc;
6064
6065 i_setModified(IsModified_Storage);
6066 mStorageControllers.backup();
6067 mStorageControllers->push_back(ctrl);
6068
6069 ctrl.queryInterfaceTo(aController.asOutParam());
6070
6071 /* inform the direct session if any */
6072 alock.release();
6073 i_onStorageControllerChange();
6074
6075 return S_OK;
6076}
6077
6078HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6079 ComPtr<IStorageController> &aStorageController)
6080{
6081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6082
6083 ComObjPtr<StorageController> ctrl;
6084
6085 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6086 if (SUCCEEDED(rc))
6087 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6088
6089 return rc;
6090}
6091
6092HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6093 ULONG aInstance,
6094 ComPtr<IStorageController> &aStorageController)
6095{
6096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6097
6098 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6099 it != mStorageControllers->end();
6100 ++it)
6101 {
6102 if ( (*it)->i_getStorageBus() == aConnectionType
6103 && (*it)->i_getInstance() == aInstance)
6104 {
6105 (*it).queryInterfaceTo(aStorageController.asOutParam());
6106 return S_OK;
6107 }
6108 }
6109
6110 return setError(VBOX_E_OBJECT_NOT_FOUND,
6111 tr("Could not find a storage controller with instance number '%lu'"),
6112 aInstance);
6113}
6114
6115HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6116{
6117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6118
6119 HRESULT rc = i_checkStateDependency(MutableStateDep);
6120 if (FAILED(rc)) return rc;
6121
6122 ComObjPtr<StorageController> ctrl;
6123
6124 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6125 if (SUCCEEDED(rc))
6126 {
6127 /* Ensure that only one controller of each type is marked as bootable. */
6128 if (aBootable == TRUE)
6129 {
6130 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6131 it != mStorageControllers->end();
6132 ++it)
6133 {
6134 ComObjPtr<StorageController> aCtrl = (*it);
6135
6136 if ( (aCtrl->i_getName() != aName)
6137 && aCtrl->i_getBootable() == TRUE
6138 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6139 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6140 {
6141 aCtrl->i_setBootable(FALSE);
6142 break;
6143 }
6144 }
6145 }
6146
6147 if (SUCCEEDED(rc))
6148 {
6149 ctrl->i_setBootable(aBootable);
6150 i_setModified(IsModified_Storage);
6151 }
6152 }
6153
6154 if (SUCCEEDED(rc))
6155 {
6156 /* inform the direct session if any */
6157 alock.release();
6158 i_onStorageControllerChange();
6159 }
6160
6161 return rc;
6162}
6163
6164HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6165{
6166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6167
6168 HRESULT rc = i_checkStateDependency(MutableStateDep);
6169 if (FAILED(rc)) return rc;
6170
6171 ComObjPtr<StorageController> ctrl;
6172 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6173 if (FAILED(rc)) return rc;
6174
6175 {
6176 /* find all attached devices to the appropriate storage controller and detach them all */
6177 // make a temporary list because detachDevice invalidates iterators into
6178 // mMediaData->mAttachments
6179 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6180
6181 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6182 it != llAttachments2.end();
6183 ++it)
6184 {
6185 MediumAttachment *pAttachTemp = *it;
6186
6187 AutoCaller localAutoCaller(pAttachTemp);
6188 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6189
6190 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6191
6192 if (pAttachTemp->i_getControllerName() == aName)
6193 {
6194 rc = i_detachDevice(pAttachTemp, alock, NULL);
6195 if (FAILED(rc)) return rc;
6196 }
6197 }
6198 }
6199
6200 /* We can remove it now. */
6201 i_setModified(IsModified_Storage);
6202 mStorageControllers.backup();
6203
6204 ctrl->i_unshare();
6205
6206 mStorageControllers->remove(ctrl);
6207
6208 /* inform the direct session if any */
6209 alock.release();
6210 i_onStorageControllerChange();
6211
6212 return S_OK;
6213}
6214
6215HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6216 ComPtr<IUSBController> &aController)
6217{
6218 if ( (aType <= USBControllerType_Null)
6219 || (aType >= USBControllerType_Last))
6220 return setError(E_INVALIDARG,
6221 tr("Invalid USB controller type: %d"),
6222 aType);
6223
6224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 HRESULT rc = i_checkStateDependency(MutableStateDep);
6227 if (FAILED(rc)) return rc;
6228
6229 /* try to find one with the same type first. */
6230 ComObjPtr<USBController> ctrl;
6231
6232 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6233 if (SUCCEEDED(rc))
6234 return setError(VBOX_E_OBJECT_IN_USE,
6235 tr("USB controller named '%s' already exists"),
6236 aName.c_str());
6237
6238 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6239 ULONG maxInstances;
6240 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6241 if (FAILED(rc))
6242 return rc;
6243
6244 ULONG cInstances = i_getUSBControllerCountByType(aType);
6245 if (cInstances >= maxInstances)
6246 return setError(E_INVALIDARG,
6247 tr("Too many USB controllers of this type"));
6248
6249 ctrl.createObject();
6250
6251 rc = ctrl->init(this, aName, aType);
6252 if (FAILED(rc)) return rc;
6253
6254 i_setModified(IsModified_USB);
6255 mUSBControllers.backup();
6256 mUSBControllers->push_back(ctrl);
6257
6258 ctrl.queryInterfaceTo(aController.asOutParam());
6259
6260 /* inform the direct session if any */
6261 alock.release();
6262 i_onUSBControllerChange();
6263
6264 return S_OK;
6265}
6266
6267HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6268{
6269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6270
6271 ComObjPtr<USBController> ctrl;
6272
6273 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6274 if (SUCCEEDED(rc))
6275 ctrl.queryInterfaceTo(aController.asOutParam());
6276
6277 return rc;
6278}
6279
6280HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6281 ULONG *aControllers)
6282{
6283 if ( (aType <= USBControllerType_Null)
6284 || (aType >= USBControllerType_Last))
6285 return setError(E_INVALIDARG,
6286 tr("Invalid USB controller type: %d"),
6287 aType);
6288
6289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6290
6291 ComObjPtr<USBController> ctrl;
6292
6293 *aControllers = i_getUSBControllerCountByType(aType);
6294
6295 return S_OK;
6296}
6297
6298HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6299{
6300
6301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6302
6303 HRESULT rc = i_checkStateDependency(MutableStateDep);
6304 if (FAILED(rc)) return rc;
6305
6306 ComObjPtr<USBController> ctrl;
6307 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6308 if (FAILED(rc)) return rc;
6309
6310 i_setModified(IsModified_USB);
6311 mUSBControllers.backup();
6312
6313 ctrl->i_unshare();
6314
6315 mUSBControllers->remove(ctrl);
6316
6317 /* inform the direct session if any */
6318 alock.release();
6319 i_onUSBControllerChange();
6320
6321 return S_OK;
6322}
6323
6324HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6325 ULONG *aOriginX,
6326 ULONG *aOriginY,
6327 ULONG *aWidth,
6328 ULONG *aHeight,
6329 BOOL *aEnabled)
6330{
6331 uint32_t u32OriginX= 0;
6332 uint32_t u32OriginY= 0;
6333 uint32_t u32Width = 0;
6334 uint32_t u32Height = 0;
6335 uint16_t u16Flags = 0;
6336
6337 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6338 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6339 if (RT_FAILURE(vrc))
6340 {
6341#ifdef RT_OS_WINDOWS
6342 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6343 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6344 * So just assign fEnable to TRUE again.
6345 * The right fix would be to change GUI API wrappers to make sure that parameters
6346 * are changed only if API succeeds.
6347 */
6348 *aEnabled = TRUE;
6349#endif
6350 return setError(VBOX_E_IPRT_ERROR,
6351 tr("Saved guest size is not available (%Rrc)"),
6352 vrc);
6353 }
6354
6355 *aOriginX = u32OriginX;
6356 *aOriginY = u32OriginY;
6357 *aWidth = u32Width;
6358 *aHeight = u32Height;
6359 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6360
6361 return S_OK;
6362}
6363
6364HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6365 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6366{
6367 if (aScreenId != 0)
6368 return E_NOTIMPL;
6369
6370 if ( aBitmapFormat != BitmapFormat_BGR0
6371 && aBitmapFormat != BitmapFormat_BGRA
6372 && aBitmapFormat != BitmapFormat_RGBA
6373 && aBitmapFormat != BitmapFormat_PNG)
6374 return setError(E_NOTIMPL,
6375 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6376
6377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 uint8_t *pu8Data = NULL;
6380 uint32_t cbData = 0;
6381 uint32_t u32Width = 0;
6382 uint32_t u32Height = 0;
6383
6384 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6385
6386 if (RT_FAILURE(vrc))
6387 return setError(VBOX_E_IPRT_ERROR,
6388 tr("Saved thumbnail data is not available (%Rrc)"),
6389 vrc);
6390
6391 HRESULT hr = S_OK;
6392
6393 *aWidth = u32Width;
6394 *aHeight = u32Height;
6395
6396 if (cbData > 0)
6397 {
6398 /* Convert pixels to the format expected by the API caller. */
6399 if (aBitmapFormat == BitmapFormat_BGR0)
6400 {
6401 /* [0] B, [1] G, [2] R, [3] 0. */
6402 aData.resize(cbData);
6403 memcpy(&aData.front(), pu8Data, cbData);
6404 }
6405 else if (aBitmapFormat == BitmapFormat_BGRA)
6406 {
6407 /* [0] B, [1] G, [2] R, [3] A. */
6408 aData.resize(cbData);
6409 for (uint32_t i = 0; i < cbData; i += 4)
6410 {
6411 aData[i] = pu8Data[i];
6412 aData[i + 1] = pu8Data[i + 1];
6413 aData[i + 2] = pu8Data[i + 2];
6414 aData[i + 3] = 0xff;
6415 }
6416 }
6417 else if (aBitmapFormat == BitmapFormat_RGBA)
6418 {
6419 /* [0] R, [1] G, [2] B, [3] A. */
6420 aData.resize(cbData);
6421 for (uint32_t i = 0; i < cbData; i += 4)
6422 {
6423 aData[i] = pu8Data[i + 2];
6424 aData[i + 1] = pu8Data[i + 1];
6425 aData[i + 2] = pu8Data[i];
6426 aData[i + 3] = 0xff;
6427 }
6428 }
6429 else if (aBitmapFormat == BitmapFormat_PNG)
6430 {
6431 uint8_t *pu8PNG = NULL;
6432 uint32_t cbPNG = 0;
6433 uint32_t cxPNG = 0;
6434 uint32_t cyPNG = 0;
6435
6436 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6437
6438 if (RT_SUCCESS(vrc))
6439 {
6440 aData.resize(cbPNG);
6441 if (cbPNG)
6442 memcpy(&aData.front(), pu8PNG, cbPNG);
6443 }
6444 else
6445 hr = setError(VBOX_E_IPRT_ERROR,
6446 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6447 vrc);
6448
6449 RTMemFree(pu8PNG);
6450 }
6451 }
6452
6453 freeSavedDisplayScreenshot(pu8Data);
6454
6455 return hr;
6456}
6457
6458HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6459 ULONG *aWidth,
6460 ULONG *aHeight,
6461 std::vector<BitmapFormat_T> &aBitmapFormats)
6462{
6463 if (aScreenId != 0)
6464 return E_NOTIMPL;
6465
6466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6467
6468 uint8_t *pu8Data = NULL;
6469 uint32_t cbData = 0;
6470 uint32_t u32Width = 0;
6471 uint32_t u32Height = 0;
6472
6473 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6474
6475 if (RT_FAILURE(vrc))
6476 return setError(VBOX_E_IPRT_ERROR,
6477 tr("Saved screenshot data is not available (%Rrc)"),
6478 vrc);
6479
6480 *aWidth = u32Width;
6481 *aHeight = u32Height;
6482 aBitmapFormats.resize(1);
6483 aBitmapFormats[0] = BitmapFormat_PNG;
6484
6485 freeSavedDisplayScreenshot(pu8Data);
6486
6487 return S_OK;
6488}
6489
6490HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6491 BitmapFormat_T aBitmapFormat,
6492 ULONG *aWidth,
6493 ULONG *aHeight,
6494 std::vector<BYTE> &aData)
6495{
6496 if (aScreenId != 0)
6497 return E_NOTIMPL;
6498
6499 if (aBitmapFormat != BitmapFormat_PNG)
6500 return E_NOTIMPL;
6501
6502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6503
6504 uint8_t *pu8Data = NULL;
6505 uint32_t cbData = 0;
6506 uint32_t u32Width = 0;
6507 uint32_t u32Height = 0;
6508
6509 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6510
6511 if (RT_FAILURE(vrc))
6512 return setError(VBOX_E_IPRT_ERROR,
6513 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6514 vrc);
6515
6516 *aWidth = u32Width;
6517 *aHeight = u32Height;
6518
6519 aData.resize(cbData);
6520 if (cbData)
6521 memcpy(&aData.front(), pu8Data, cbData);
6522
6523 freeSavedDisplayScreenshot(pu8Data);
6524
6525 return S_OK;
6526}
6527
6528HRESULT Machine::hotPlugCPU(ULONG aCpu)
6529{
6530 HRESULT rc = S_OK;
6531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6532
6533 if (!mHWData->mCPUHotPlugEnabled)
6534 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6535
6536 if (aCpu >= mHWData->mCPUCount)
6537 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6538
6539 if (mHWData->mCPUAttached[aCpu])
6540 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6541
6542 alock.release();
6543 rc = i_onCPUChange(aCpu, false);
6544 alock.acquire();
6545 if (FAILED(rc)) return rc;
6546
6547 i_setModified(IsModified_MachineData);
6548 mHWData.backup();
6549 mHWData->mCPUAttached[aCpu] = true;
6550
6551 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6552 if (Global::IsOnline(mData->mMachineState))
6553 i_saveSettings(NULL);
6554
6555 return S_OK;
6556}
6557
6558HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6559{
6560 HRESULT rc = S_OK;
6561
6562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6563
6564 if (!mHWData->mCPUHotPlugEnabled)
6565 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6566
6567 if (aCpu >= SchemaDefs::MaxCPUCount)
6568 return setError(E_INVALIDARG,
6569 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6570 SchemaDefs::MaxCPUCount);
6571
6572 if (!mHWData->mCPUAttached[aCpu])
6573 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6574
6575 /* CPU 0 can't be detached */
6576 if (aCpu == 0)
6577 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6578
6579 alock.release();
6580 rc = i_onCPUChange(aCpu, true);
6581 alock.acquire();
6582 if (FAILED(rc)) return rc;
6583
6584 i_setModified(IsModified_MachineData);
6585 mHWData.backup();
6586 mHWData->mCPUAttached[aCpu] = false;
6587
6588 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6589 if (Global::IsOnline(mData->mMachineState))
6590 i_saveSettings(NULL);
6591
6592 return S_OK;
6593}
6594
6595HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6596{
6597 *aAttached = false;
6598
6599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6600
6601 /* If hotplug is enabled the CPU is always enabled. */
6602 if (!mHWData->mCPUHotPlugEnabled)
6603 {
6604 if (aCpu < mHWData->mCPUCount)
6605 *aAttached = true;
6606 }
6607 else
6608 {
6609 if (aCpu < SchemaDefs::MaxCPUCount)
6610 *aAttached = mHWData->mCPUAttached[aCpu];
6611 }
6612
6613 return S_OK;
6614}
6615
6616HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6617{
6618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6619
6620 Utf8Str log = i_getLogFilename(aIdx);
6621 if (!RTFileExists(log.c_str()))
6622 log.setNull();
6623 aFilename = log;
6624
6625 return S_OK;
6626}
6627
6628HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6629{
6630 if (aSize < 0)
6631 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6632
6633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6634
6635 HRESULT rc = S_OK;
6636 Utf8Str log = i_getLogFilename(aIdx);
6637
6638 /* do not unnecessarily hold the lock while doing something which does
6639 * not need the lock and potentially takes a long time. */
6640 alock.release();
6641
6642 /* Limit the chunk size to 32K for now, as that gives better performance
6643 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6644 * One byte expands to approx. 25 bytes of breathtaking XML. */
6645 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6646 aData.resize(cbData);
6647
6648 RTFILE LogFile;
6649 int vrc = RTFileOpen(&LogFile, log.c_str(),
6650 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6651 if (RT_SUCCESS(vrc))
6652 {
6653 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6654 if (RT_SUCCESS(vrc))
6655 aData.resize(cbData);
6656 else
6657 rc = setError(VBOX_E_IPRT_ERROR,
6658 tr("Could not read log file '%s' (%Rrc)"),
6659 log.c_str(), vrc);
6660 RTFileClose(LogFile);
6661 }
6662 else
6663 rc = setError(VBOX_E_IPRT_ERROR,
6664 tr("Could not open log file '%s' (%Rrc)"),
6665 log.c_str(), vrc);
6666
6667 if (FAILED(rc))
6668 aData.resize(0);
6669
6670 return rc;
6671}
6672
6673
6674/**
6675 * Currently this method doesn't attach device to the running VM,
6676 * just makes sure it's plugged on next VM start.
6677 */
6678HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6679{
6680 // lock scope
6681 {
6682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 HRESULT rc = i_checkStateDependency(MutableStateDep);
6685 if (FAILED(rc)) return rc;
6686
6687 ChipsetType_T aChipset = ChipsetType_PIIX3;
6688 COMGETTER(ChipsetType)(&aChipset);
6689
6690 if (aChipset != ChipsetType_ICH9)
6691 {
6692 return setError(E_INVALIDARG,
6693 tr("Host PCI attachment only supported with ICH9 chipset"));
6694 }
6695
6696 // check if device with this host PCI address already attached
6697 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6698 it != mHWData->mPCIDeviceAssignments.end();
6699 ++it)
6700 {
6701 LONG iHostAddress = -1;
6702 ComPtr<PCIDeviceAttachment> pAttach;
6703 pAttach = *it;
6704 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6705 if (iHostAddress == aHostAddress)
6706 return setError(E_INVALIDARG,
6707 tr("Device with host PCI address already attached to this VM"));
6708 }
6709
6710 ComObjPtr<PCIDeviceAttachment> pda;
6711 char name[32];
6712
6713 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6714 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6715 Bstr bname(name);
6716 pda.createObject();
6717 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6718 i_setModified(IsModified_MachineData);
6719 mHWData.backup();
6720 mHWData->mPCIDeviceAssignments.push_back(pda);
6721 }
6722
6723 return S_OK;
6724}
6725
6726/**
6727 * Currently this method doesn't detach device from the running VM,
6728 * just makes sure it's not plugged on next VM start.
6729 */
6730HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6731{
6732 ComObjPtr<PCIDeviceAttachment> pAttach;
6733 bool fRemoved = false;
6734 HRESULT rc;
6735
6736 // lock scope
6737 {
6738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6739
6740 rc = i_checkStateDependency(MutableStateDep);
6741 if (FAILED(rc)) return rc;
6742
6743 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6744 it != mHWData->mPCIDeviceAssignments.end();
6745 ++it)
6746 {
6747 LONG iHostAddress = -1;
6748 pAttach = *it;
6749 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6750 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6751 {
6752 i_setModified(IsModified_MachineData);
6753 mHWData.backup();
6754 mHWData->mPCIDeviceAssignments.remove(pAttach);
6755 fRemoved = true;
6756 break;
6757 }
6758 }
6759 }
6760
6761
6762 /* Fire event outside of the lock */
6763 if (fRemoved)
6764 {
6765 Assert(!pAttach.isNull());
6766 ComPtr<IEventSource> es;
6767 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6768 Assert(SUCCEEDED(rc));
6769 Bstr mid;
6770 rc = this->COMGETTER(Id)(mid.asOutParam());
6771 Assert(SUCCEEDED(rc));
6772 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6773 }
6774
6775 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6776 tr("No host PCI device %08x attached"),
6777 aHostAddress
6778 );
6779}
6780
6781HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6782{
6783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6784
6785 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6786
6787 size_t i = 0;
6788 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6789 it != mHWData->mPCIDeviceAssignments.end();
6790 ++i, ++it)
6791 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6792
6793 return S_OK;
6794}
6795
6796HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6797{
6798 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6799
6800 return S_OK;
6801}
6802
6803HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6804{
6805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6806
6807 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6808
6809 return S_OK;
6810}
6811
6812HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6813{
6814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6815 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6816 if (SUCCEEDED(hrc))
6817 {
6818 hrc = mHWData.backupEx();
6819 if (SUCCEEDED(hrc))
6820 {
6821 i_setModified(IsModified_MachineData);
6822 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6823 }
6824 }
6825 return hrc;
6826}
6827
6828HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6829{
6830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6831 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6832 return S_OK;
6833}
6834
6835HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6836{
6837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6838 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6839 if (SUCCEEDED(hrc))
6840 {
6841 hrc = mHWData.backupEx();
6842 if (SUCCEEDED(hrc))
6843 {
6844 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6845 if (SUCCEEDED(hrc))
6846 i_setModified(IsModified_MachineData);
6847 }
6848 }
6849 return hrc;
6850}
6851
6852HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6853{
6854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6855
6856 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6857
6858 return S_OK;
6859}
6860
6861HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6862{
6863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6864 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6865 if (SUCCEEDED(hrc))
6866 {
6867 hrc = mHWData.backupEx();
6868 if (SUCCEEDED(hrc))
6869 {
6870 i_setModified(IsModified_MachineData);
6871 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6872 }
6873 }
6874 return hrc;
6875}
6876
6877HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6878{
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880
6881 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6882
6883 return S_OK;
6884}
6885
6886HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6887{
6888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6889
6890 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6891 if ( SUCCEEDED(hrc)
6892 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6893 {
6894 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6895 int vrc;
6896
6897 if (aAutostartEnabled)
6898 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6899 else
6900 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6901
6902 if (RT_SUCCESS(vrc))
6903 {
6904 hrc = mHWData.backupEx();
6905 if (SUCCEEDED(hrc))
6906 {
6907 i_setModified(IsModified_MachineData);
6908 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6909 }
6910 }
6911 else if (vrc == VERR_NOT_SUPPORTED)
6912 hrc = setError(VBOX_E_NOT_SUPPORTED,
6913 tr("The VM autostart feature is not supported on this platform"));
6914 else if (vrc == VERR_PATH_NOT_FOUND)
6915 hrc = setError(E_FAIL,
6916 tr("The path to the autostart database is not set"));
6917 else
6918 hrc = setError(E_UNEXPECTED,
6919 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6920 aAutostartEnabled ? "Adding" : "Removing",
6921 mUserData->s.strName.c_str(), vrc);
6922 }
6923 return hrc;
6924}
6925
6926HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6927{
6928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6929
6930 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6931
6932 return S_OK;
6933}
6934
6935HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6936{
6937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6938 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6939 if (SUCCEEDED(hrc))
6940 {
6941 hrc = mHWData.backupEx();
6942 if (SUCCEEDED(hrc))
6943 {
6944 i_setModified(IsModified_MachineData);
6945 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6946 }
6947 }
6948 return hrc;
6949}
6950
6951HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6952{
6953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6954
6955 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6956
6957 return S_OK;
6958}
6959
6960HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6961{
6962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6963 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6964 if ( SUCCEEDED(hrc)
6965 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6966 {
6967 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6968 int vrc;
6969
6970 if (aAutostopType != AutostopType_Disabled)
6971 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6972 else
6973 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6974
6975 if (RT_SUCCESS(vrc))
6976 {
6977 hrc = mHWData.backupEx();
6978 if (SUCCEEDED(hrc))
6979 {
6980 i_setModified(IsModified_MachineData);
6981 mHWData->mAutostart.enmAutostopType = aAutostopType;
6982 }
6983 }
6984 else if (vrc == VERR_NOT_SUPPORTED)
6985 hrc = setError(VBOX_E_NOT_SUPPORTED,
6986 tr("The VM autostop feature is not supported on this platform"));
6987 else if (vrc == VERR_PATH_NOT_FOUND)
6988 hrc = setError(E_FAIL,
6989 tr("The path to the autostart database is not set"));
6990 else
6991 hrc = setError(E_UNEXPECTED,
6992 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6993 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6994 mUserData->s.strName.c_str(), vrc);
6995 }
6996 return hrc;
6997}
6998
6999HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7000{
7001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7002
7003 aDefaultFrontend = mHWData->mDefaultFrontend;
7004
7005 return S_OK;
7006}
7007
7008HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7009{
7010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7011 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7012 if (SUCCEEDED(hrc))
7013 {
7014 hrc = mHWData.backupEx();
7015 if (SUCCEEDED(hrc))
7016 {
7017 i_setModified(IsModified_MachineData);
7018 mHWData->mDefaultFrontend = aDefaultFrontend;
7019 }
7020 }
7021 return hrc;
7022}
7023
7024HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7025{
7026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7027 size_t cbIcon = mUserData->mIcon.size();
7028 aIcon.resize(cbIcon);
7029 if (cbIcon)
7030 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7031 return S_OK;
7032}
7033
7034HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7035{
7036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7037 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7038 if (SUCCEEDED(hrc))
7039 {
7040 i_setModified(IsModified_MachineData);
7041 mUserData.backup();
7042 size_t cbIcon = aIcon.size();
7043 mUserData->mIcon.resize(cbIcon);
7044 if (cbIcon)
7045 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7046 }
7047 return hrc;
7048}
7049
7050HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7051{
7052#ifdef VBOX_WITH_USB
7053 *aUSBProxyAvailable = true;
7054#else
7055 *aUSBProxyAvailable = false;
7056#endif
7057 return S_OK;
7058}
7059
7060HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7061{
7062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7063
7064 aVMProcessPriority = mUserData->s.strVMPriority;
7065
7066 return S_OK;
7067}
7068
7069HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7070{
7071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7072 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7073 if (SUCCEEDED(hrc))
7074 {
7075 /** @todo r=klaus: currently this is marked as not implemented, as
7076 * the code for setting the priority of the process is not there
7077 * (neither when starting the VM nor at runtime). */
7078 ReturnComNotImplemented();
7079 hrc = mUserData.backupEx();
7080 if (SUCCEEDED(hrc))
7081 {
7082 i_setModified(IsModified_MachineData);
7083 mUserData->s.strVMPriority = aVMProcessPriority;
7084 }
7085 }
7086 return hrc;
7087}
7088
7089HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7090 ComPtr<IProgress> &aProgress)
7091{
7092 ComObjPtr<Progress> pP;
7093 Progress *ppP = pP;
7094 IProgress *iP = static_cast<IProgress *>(ppP);
7095 IProgress **pProgress = &iP;
7096
7097 IMachine *pTarget = aTarget;
7098
7099 /* Convert the options. */
7100 RTCList<CloneOptions_T> optList;
7101 if (aOptions.size())
7102 for (size_t i = 0; i < aOptions.size(); ++i)
7103 optList.append(aOptions[i]);
7104
7105 if (optList.contains(CloneOptions_Link))
7106 {
7107 if (!i_isSnapshotMachine())
7108 return setError(E_INVALIDARG,
7109 tr("Linked clone can only be created from a snapshot"));
7110 if (aMode != CloneMode_MachineState)
7111 return setError(E_INVALIDARG,
7112 tr("Linked clone can only be created for a single machine state"));
7113 }
7114 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7115
7116 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7117
7118 HRESULT rc = pWorker->start(pProgress);
7119
7120 pP = static_cast<Progress *>(*pProgress);
7121 pP.queryInterfaceTo(aProgress.asOutParam());
7122
7123 return rc;
7124
7125}
7126
7127HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7128{
7129 NOREF(aProgress);
7130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7131
7132 // This check should always fail.
7133 HRESULT rc = i_checkStateDependency(MutableStateDep);
7134 if (FAILED(rc)) return rc;
7135
7136 AssertFailedReturn(E_NOTIMPL);
7137}
7138
7139HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7140{
7141 NOREF(aSavedStateFile);
7142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7143
7144 // This check should always fail.
7145 HRESULT rc = i_checkStateDependency(MutableStateDep);
7146 if (FAILED(rc)) return rc;
7147
7148 AssertFailedReturn(E_NOTIMPL);
7149}
7150
7151HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7152{
7153 NOREF(aFRemoveFile);
7154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7155
7156 // This check should always fail.
7157 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7158 if (FAILED(rc)) return rc;
7159
7160 AssertFailedReturn(E_NOTIMPL);
7161}
7162
7163// public methods for internal purposes
7164/////////////////////////////////////////////////////////////////////////////
7165
7166/**
7167 * Adds the given IsModified_* flag to the dirty flags of the machine.
7168 * This must be called either during i_loadSettings or under the machine write lock.
7169 * @param fl
7170 */
7171void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7172{
7173 mData->flModifications |= fl;
7174 if (fAllowStateModification && i_isStateModificationAllowed())
7175 mData->mCurrentStateModified = true;
7176}
7177
7178/**
7179 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7180 * care of the write locking.
7181 *
7182 * @param fModifications The flag to add.
7183 */
7184void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7185{
7186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7187 i_setModified(fModification, fAllowStateModification);
7188}
7189
7190/**
7191 * Saves the registry entry of this machine to the given configuration node.
7192 *
7193 * @param aEntryNode Node to save the registry entry to.
7194 *
7195 * @note locks this object for reading.
7196 */
7197HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7198{
7199 AutoLimitedCaller autoCaller(this);
7200 AssertComRCReturnRC(autoCaller.rc());
7201
7202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7203
7204 data.uuid = mData->mUuid;
7205 data.strSettingsFile = mData->m_strConfigFile;
7206
7207 return S_OK;
7208}
7209
7210/**
7211 * Calculates the absolute path of the given path taking the directory of the
7212 * machine settings file as the current directory.
7213 *
7214 * @param aPath Path to calculate the absolute path for.
7215 * @param aResult Where to put the result (used only on success, can be the
7216 * same Utf8Str instance as passed in @a aPath).
7217 * @return IPRT result.
7218 *
7219 * @note Locks this object for reading.
7220 */
7221int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7222{
7223 AutoCaller autoCaller(this);
7224 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7225
7226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7227
7228 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7229
7230 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7231
7232 strSettingsDir.stripFilename();
7233 char folder[RTPATH_MAX];
7234 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7235 if (RT_SUCCESS(vrc))
7236 aResult = folder;
7237
7238 return vrc;
7239}
7240
7241/**
7242 * Copies strSource to strTarget, making it relative to the machine folder
7243 * if it is a subdirectory thereof, or simply copying it otherwise.
7244 *
7245 * @param strSource Path to evaluate and copy.
7246 * @param strTarget Buffer to receive target path.
7247 *
7248 * @note Locks this object for reading.
7249 */
7250void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7251 Utf8Str &strTarget)
7252{
7253 AutoCaller autoCaller(this);
7254 AssertComRCReturn(autoCaller.rc(), (void)0);
7255
7256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7257
7258 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7259 // use strTarget as a temporary buffer to hold the machine settings dir
7260 strTarget = mData->m_strConfigFileFull;
7261 strTarget.stripFilename();
7262 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7263 {
7264 // is relative: then append what's left
7265 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7266 // for empty paths (only possible for subdirs) use "." to avoid
7267 // triggering default settings for not present config attributes.
7268 if (strTarget.isEmpty())
7269 strTarget = ".";
7270 }
7271 else
7272 // is not relative: then overwrite
7273 strTarget = strSource;
7274}
7275
7276/**
7277 * Returns the full path to the machine's log folder in the
7278 * \a aLogFolder argument.
7279 */
7280void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7281{
7282 AutoCaller autoCaller(this);
7283 AssertComRCReturnVoid(autoCaller.rc());
7284
7285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7286
7287 char szTmp[RTPATH_MAX];
7288 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7289 if (RT_SUCCESS(vrc))
7290 {
7291 if (szTmp[0] && !mUserData.isNull())
7292 {
7293 char szTmp2[RTPATH_MAX];
7294 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7295 if (RT_SUCCESS(vrc))
7296 aLogFolder = BstrFmt("%s%c%s",
7297 szTmp2,
7298 RTPATH_DELIMITER,
7299 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7300 }
7301 else
7302 vrc = VERR_PATH_IS_RELATIVE;
7303 }
7304
7305 if (RT_FAILURE(vrc))
7306 {
7307 // fallback if VBOX_USER_LOGHOME is not set or invalid
7308 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7309 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7310 aLogFolder.append(RTPATH_DELIMITER);
7311 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7312 }
7313}
7314
7315/**
7316 * Returns the full path to the machine's log file for an given index.
7317 */
7318Utf8Str Machine::i_getLogFilename(ULONG idx)
7319{
7320 Utf8Str logFolder;
7321 getLogFolder(logFolder);
7322 Assert(logFolder.length());
7323
7324 Utf8Str log;
7325 if (idx == 0)
7326 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7327#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7328 else if (idx == 1)
7329 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7330 else
7331 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7332#else
7333 else
7334 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7335#endif
7336 return log;
7337}
7338
7339/**
7340 * Returns the full path to the machine's hardened log file.
7341 */
7342Utf8Str Machine::i_getHardeningLogFilename(void)
7343{
7344 Utf8Str strFilename;
7345 getLogFolder(strFilename);
7346 Assert(strFilename.length());
7347 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7348 return strFilename;
7349}
7350
7351
7352/**
7353 * Composes a unique saved state filename based on the current system time. The filename is
7354 * granular to the second so this will work so long as no more than one snapshot is taken on
7355 * a machine per second.
7356 *
7357 * Before version 4.1, we used this formula for saved state files:
7358 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7359 * which no longer works because saved state files can now be shared between the saved state of the
7360 * "saved" machine and an online snapshot, and the following would cause problems:
7361 * 1) save machine
7362 * 2) create online snapshot from that machine state --> reusing saved state file
7363 * 3) save machine again --> filename would be reused, breaking the online snapshot
7364 *
7365 * So instead we now use a timestamp.
7366 *
7367 * @param str
7368 */
7369
7370void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7371{
7372 AutoCaller autoCaller(this);
7373 AssertComRCReturnVoid(autoCaller.rc());
7374
7375 {
7376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7377 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7378 }
7379
7380 RTTIMESPEC ts;
7381 RTTimeNow(&ts);
7382 RTTIME time;
7383 RTTimeExplode(&time, &ts);
7384
7385 strStateFilePath += RTPATH_DELIMITER;
7386 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7387 time.i32Year, time.u8Month, time.u8MonthDay,
7388 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7389}
7390
7391/**
7392 * Returns the full path to the default video capture file.
7393 */
7394void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7395{
7396 AutoCaller autoCaller(this);
7397 AssertComRCReturnVoid(autoCaller.rc());
7398
7399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7400
7401 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7402 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7403 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7404}
7405
7406/**
7407 * Returns whether at least one USB controller is present for the VM.
7408 */
7409bool Machine::i_isUSBControllerPresent()
7410{
7411 AutoCaller autoCaller(this);
7412 AssertComRCReturn(autoCaller.rc(), false);
7413
7414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7415
7416 return (mUSBControllers->size() > 0);
7417}
7418
7419/**
7420 * @note Locks this object for writing, calls the client process
7421 * (inside the lock).
7422 */
7423HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7424 const Utf8Str &strFrontend,
7425 const Utf8Str &strEnvironment,
7426 ProgressProxy *aProgress)
7427{
7428 LogFlowThisFuncEnter();
7429
7430 AssertReturn(aControl, E_FAIL);
7431 AssertReturn(aProgress, E_FAIL);
7432 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7433
7434 AutoCaller autoCaller(this);
7435 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7436
7437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7438
7439 if (!mData->mRegistered)
7440 return setError(E_UNEXPECTED,
7441 tr("The machine '%s' is not registered"),
7442 mUserData->s.strName.c_str());
7443
7444 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7445
7446 /* The process started when launching a VM with separate UI/VM processes is always
7447 * the UI process, i.e. needs special handling as it won't claim the session. */
7448 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7449
7450 if (fSeparate)
7451 {
7452 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7453 return setError(VBOX_E_INVALID_OBJECT_STATE,
7454 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7455 mUserData->s.strName.c_str());
7456 }
7457 else
7458 {
7459 if ( mData->mSession.mState == SessionState_Locked
7460 || mData->mSession.mState == SessionState_Spawning
7461 || mData->mSession.mState == SessionState_Unlocking)
7462 return setError(VBOX_E_INVALID_OBJECT_STATE,
7463 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7464 mUserData->s.strName.c_str());
7465
7466 /* may not be busy */
7467 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7468 }
7469
7470 /* get the path to the executable */
7471 char szPath[RTPATH_MAX];
7472 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7473 size_t cchBufLeft = strlen(szPath);
7474 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7475 szPath[cchBufLeft] = 0;
7476 char *pszNamePart = szPath + cchBufLeft;
7477 cchBufLeft = sizeof(szPath) - cchBufLeft;
7478
7479 int vrc = VINF_SUCCESS;
7480 RTPROCESS pid = NIL_RTPROCESS;
7481
7482 RTENV env = RTENV_DEFAULT;
7483
7484 if (!strEnvironment.isEmpty())
7485 {
7486 char *newEnvStr = NULL;
7487
7488 do
7489 {
7490 /* clone the current environment */
7491 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7492 AssertRCBreakStmt(vrc2, vrc = vrc2);
7493
7494 newEnvStr = RTStrDup(strEnvironment.c_str());
7495 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7496
7497 /* put new variables to the environment
7498 * (ignore empty variable names here since RTEnv API
7499 * intentionally doesn't do that) */
7500 char *var = newEnvStr;
7501 for (char *p = newEnvStr; *p; ++p)
7502 {
7503 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7504 {
7505 *p = '\0';
7506 if (*var)
7507 {
7508 char *val = strchr(var, '=');
7509 if (val)
7510 {
7511 *val++ = '\0';
7512 vrc2 = RTEnvSetEx(env, var, val);
7513 }
7514 else
7515 vrc2 = RTEnvUnsetEx(env, var);
7516 if (RT_FAILURE(vrc2))
7517 break;
7518 }
7519 var = p + 1;
7520 }
7521 }
7522 if (RT_SUCCESS(vrc2) && *var)
7523 vrc2 = RTEnvPutEx(env, var);
7524
7525 AssertRCBreakStmt(vrc2, vrc = vrc2);
7526 }
7527 while (0);
7528
7529 if (newEnvStr != NULL)
7530 RTStrFree(newEnvStr);
7531 }
7532
7533 /* Hardening logging */
7534#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7535 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7536 {
7537 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7538 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7539 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7540 {
7541 Utf8Str strStartupLogDir = strHardeningLogFile;
7542 strStartupLogDir.stripFilename();
7543 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7544 file without stripping the file. */
7545 }
7546 strSupHardeningLogArg.append(strHardeningLogFile);
7547
7548 /* Remove legacy log filename to avoid confusion. */
7549 Utf8Str strOldStartupLogFile;
7550 getLogFolder(strOldStartupLogFile);
7551 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7552 RTFileDelete(strOldStartupLogFile.c_str());
7553 }
7554 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7555#else
7556 const char *pszSupHardeningLogArg = NULL;
7557#endif
7558
7559 Utf8Str strCanonicalName;
7560
7561#ifdef VBOX_WITH_QTGUI
7562 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7563 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7564 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7565 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7566 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7567 {
7568 strCanonicalName = "GUI/Qt";
7569# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7570 /* Modify the base path so that we don't need to use ".." below. */
7571 RTPathStripTrailingSlash(szPath);
7572 RTPathStripFilename(szPath);
7573 cchBufLeft = strlen(szPath);
7574 pszNamePart = szPath + cchBufLeft;
7575 cchBufLeft = sizeof(szPath) - cchBufLeft;
7576
7577# define OSX_APP_NAME "VirtualBoxVM"
7578# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7579
7580 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7581 if ( strAppOverride.contains(".")
7582 || strAppOverride.contains("/")
7583 || strAppOverride.contains("\\")
7584 || strAppOverride.contains(":"))
7585 strAppOverride.setNull();
7586 Utf8Str strAppPath;
7587 if (!strAppOverride.isEmpty())
7588 {
7589 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7590 Utf8Str strFullPath(szPath);
7591 strFullPath.append(strAppPath);
7592 /* there is a race, but people using this deserve the failure */
7593 if (!RTFileExists(strFullPath.c_str()))
7594 strAppOverride.setNull();
7595 }
7596 if (strAppOverride.isEmpty())
7597 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7598 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7599 strcpy(pszNamePart, strAppPath.c_str());
7600# else
7601 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7602 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7603 strcpy(pszNamePart, s_szVirtualBox_exe);
7604# endif
7605
7606 Utf8Str idStr = mData->mUuid.toString();
7607 const char *apszArgs[] =
7608 {
7609 szPath,
7610 "--comment", mUserData->s.strName.c_str(),
7611 "--startvm", idStr.c_str(),
7612 "--no-startvm-errormsgbox",
7613 NULL, /* For "--separate". */
7614 NULL, /* For "--sup-startup-log". */
7615 NULL
7616 };
7617 unsigned iArg = 6;
7618 if (fSeparate)
7619 apszArgs[iArg++] = "--separate";
7620 apszArgs[iArg++] = pszSupHardeningLogArg;
7621
7622 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7623 }
7624#else /* !VBOX_WITH_QTGUI */
7625 if (0)
7626 ;
7627#endif /* VBOX_WITH_QTGUI */
7628
7629 else
7630
7631#ifdef VBOX_WITH_VBOXSDL
7632 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7633 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7634 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7635 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7636 {
7637 strCanonicalName = "GUI/SDL";
7638 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7639 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7640 strcpy(pszNamePart, s_szVBoxSDL_exe);
7641
7642 Utf8Str idStr = mData->mUuid.toString();
7643 const char *apszArgs[] =
7644 {
7645 szPath,
7646 "--comment", mUserData->s.strName.c_str(),
7647 "--startvm", idStr.c_str(),
7648 NULL, /* For "--separate". */
7649 NULL, /* For "--sup-startup-log". */
7650 NULL
7651 };
7652 unsigned iArg = 5;
7653 if (fSeparate)
7654 apszArgs[iArg++] = "--separate";
7655 apszArgs[iArg++] = pszSupHardeningLogArg;
7656
7657 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7658 }
7659#else /* !VBOX_WITH_VBOXSDL */
7660 if (0)
7661 ;
7662#endif /* !VBOX_WITH_VBOXSDL */
7663
7664 else
7665
7666#ifdef VBOX_WITH_HEADLESS
7667 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7668 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7669 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7670 )
7671 {
7672 strCanonicalName = "headless";
7673 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7674 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7675 * and a VM works even if the server has not been installed.
7676 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7677 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7678 * differently in 4.0 and 3.x.
7679 */
7680 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7681 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7682 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7683
7684 Utf8Str idStr = mData->mUuid.toString();
7685 const char *apszArgs[] =
7686 {
7687 szPath,
7688 "--comment", mUserData->s.strName.c_str(),
7689 "--startvm", idStr.c_str(),
7690 "--vrde", "config",
7691 NULL, /* For "--capture". */
7692 NULL, /* For "--sup-startup-log". */
7693 NULL
7694 };
7695 unsigned iArg = 7;
7696 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7697 apszArgs[iArg++] = "--capture";
7698 apszArgs[iArg++] = pszSupHardeningLogArg;
7699
7700# ifdef RT_OS_WINDOWS
7701 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7702# else
7703 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7704# endif
7705 }
7706#else /* !VBOX_WITH_HEADLESS */
7707 if (0)
7708 ;
7709#endif /* !VBOX_WITH_HEADLESS */
7710 else
7711 {
7712 RTEnvDestroy(env);
7713 return setError(E_INVALIDARG,
7714 tr("Invalid frontend name: '%s'"),
7715 strFrontend.c_str());
7716 }
7717
7718 RTEnvDestroy(env);
7719
7720 if (RT_FAILURE(vrc))
7721 return setError(VBOX_E_IPRT_ERROR,
7722 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7723 mUserData->s.strName.c_str(), vrc);
7724
7725 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7726
7727 if (!fSeparate)
7728 {
7729 /*
7730 * Note that we don't release the lock here before calling the client,
7731 * because it doesn't need to call us back if called with a NULL argument.
7732 * Releasing the lock here is dangerous because we didn't prepare the
7733 * launch data yet, but the client we've just started may happen to be
7734 * too fast and call LockMachine() that will fail (because of PID, etc.),
7735 * so that the Machine will never get out of the Spawning session state.
7736 */
7737
7738 /* inform the session that it will be a remote one */
7739 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7740#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7741 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7742#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7743 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7744#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7745 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7746
7747 if (FAILED(rc))
7748 {
7749 /* restore the session state */
7750 mData->mSession.mState = SessionState_Unlocked;
7751 alock.release();
7752 mParent->i_addProcessToReap(pid);
7753 /* The failure may occur w/o any error info (from RPC), so provide one */
7754 return setError(VBOX_E_VM_ERROR,
7755 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7756 }
7757
7758 /* attach launch data to the machine */
7759 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7760 mData->mSession.mRemoteControls.push_back(aControl);
7761 mData->mSession.mProgress = aProgress;
7762 mData->mSession.mPID = pid;
7763 mData->mSession.mState = SessionState_Spawning;
7764 Assert(strCanonicalName.isNotEmpty());
7765 mData->mSession.mName = strCanonicalName;
7766 }
7767 else
7768 {
7769 /* For separate UI process we declare the launch as completed instantly, as the
7770 * actual headless VM start may or may not come. No point in remembering anything
7771 * yet, as what matters for us is when the headless VM gets started. */
7772 aProgress->i_notifyComplete(S_OK);
7773 }
7774
7775 alock.release();
7776 mParent->i_addProcessToReap(pid);
7777
7778 LogFlowThisFuncLeave();
7779 return S_OK;
7780}
7781
7782/**
7783 * Returns @c true if the given session machine instance has an open direct
7784 * session (and optionally also for direct sessions which are closing) and
7785 * returns the session control machine instance if so.
7786 *
7787 * Note that when the method returns @c false, the arguments remain unchanged.
7788 *
7789 * @param aMachine Session machine object.
7790 * @param aControl Direct session control object (optional).
7791 * @param aRequireVM If true then only allow VM sessions.
7792 * @param aAllowClosing If true then additionally a session which is currently
7793 * being closed will also be allowed.
7794 *
7795 * @note locks this object for reading.
7796 */
7797bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7798 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7799 bool aRequireVM /*= false*/,
7800 bool aAllowClosing /*= false*/)
7801{
7802 AutoLimitedCaller autoCaller(this);
7803 AssertComRCReturn(autoCaller.rc(), false);
7804
7805 /* just return false for inaccessible machines */
7806 if (getObjectState().getState() != ObjectState::Ready)
7807 return false;
7808
7809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7810
7811 if ( ( mData->mSession.mState == SessionState_Locked
7812 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7813 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7814 )
7815 {
7816 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7817
7818 aMachine = mData->mSession.mMachine;
7819
7820 if (aControl != NULL)
7821 *aControl = mData->mSession.mDirectControl;
7822
7823 return true;
7824 }
7825
7826 return false;
7827}
7828
7829/**
7830 * Returns @c true if the given machine has an spawning direct session.
7831 *
7832 * @note locks this object for reading.
7833 */
7834bool Machine::i_isSessionSpawning()
7835{
7836 AutoLimitedCaller autoCaller(this);
7837 AssertComRCReturn(autoCaller.rc(), false);
7838
7839 /* just return false for inaccessible machines */
7840 if (getObjectState().getState() != ObjectState::Ready)
7841 return false;
7842
7843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7844
7845 if (mData->mSession.mState == SessionState_Spawning)
7846 return true;
7847
7848 return false;
7849}
7850
7851/**
7852 * Called from the client watcher thread to check for unexpected client process
7853 * death during Session_Spawning state (e.g. before it successfully opened a
7854 * direct session).
7855 *
7856 * On Win32 and on OS/2, this method is called only when we've got the
7857 * direct client's process termination notification, so it always returns @c
7858 * true.
7859 *
7860 * On other platforms, this method returns @c true if the client process is
7861 * terminated and @c false if it's still alive.
7862 *
7863 * @note Locks this object for writing.
7864 */
7865bool Machine::i_checkForSpawnFailure()
7866{
7867 AutoCaller autoCaller(this);
7868 if (!autoCaller.isOk())
7869 {
7870 /* nothing to do */
7871 LogFlowThisFunc(("Already uninitialized!\n"));
7872 return true;
7873 }
7874
7875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7876
7877 if (mData->mSession.mState != SessionState_Spawning)
7878 {
7879 /* nothing to do */
7880 LogFlowThisFunc(("Not spawning any more!\n"));
7881 return true;
7882 }
7883
7884 HRESULT rc = S_OK;
7885
7886 /* PID not yet initialized, skip check. */
7887 if (mData->mSession.mPID == NIL_RTPROCESS)
7888 return false;
7889
7890 RTPROCSTATUS status;
7891 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7892
7893 if (vrc != VERR_PROCESS_RUNNING)
7894 {
7895 Utf8Str strExtraInfo;
7896
7897#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7898 /* If the startup logfile exists and is of non-zero length, tell the
7899 user to look there for more details to encourage them to attach it
7900 when reporting startup issues. */
7901 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7902 uint64_t cbStartupLogFile = 0;
7903 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7904 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7905 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7906#endif
7907
7908 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7909 rc = setError(E_FAIL,
7910 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7911 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7912 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7913 rc = setError(E_FAIL,
7914 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7915 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7916 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7917 rc = setError(E_FAIL,
7918 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7919 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7920 else
7921 rc = setError(E_FAIL,
7922 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7923 i_getName().c_str(), vrc, strExtraInfo.c_str());
7924 }
7925
7926 if (FAILED(rc))
7927 {
7928 /* Close the remote session, remove the remote control from the list
7929 * and reset session state to Closed (@note keep the code in sync with
7930 * the relevant part in LockMachine()). */
7931
7932 Assert(mData->mSession.mRemoteControls.size() == 1);
7933 if (mData->mSession.mRemoteControls.size() == 1)
7934 {
7935 ErrorInfoKeeper eik;
7936 mData->mSession.mRemoteControls.front()->Uninitialize();
7937 }
7938
7939 mData->mSession.mRemoteControls.clear();
7940 mData->mSession.mState = SessionState_Unlocked;
7941
7942 /* finalize the progress after setting the state */
7943 if (!mData->mSession.mProgress.isNull())
7944 {
7945 mData->mSession.mProgress->notifyComplete(rc);
7946 mData->mSession.mProgress.setNull();
7947 }
7948
7949 mData->mSession.mPID = NIL_RTPROCESS;
7950
7951 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7952 return true;
7953 }
7954
7955 return false;
7956}
7957
7958/**
7959 * Checks whether the machine can be registered. If so, commits and saves
7960 * all settings.
7961 *
7962 * @note Must be called from mParent's write lock. Locks this object and
7963 * children for writing.
7964 */
7965HRESULT Machine::i_prepareRegister()
7966{
7967 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7968
7969 AutoLimitedCaller autoCaller(this);
7970 AssertComRCReturnRC(autoCaller.rc());
7971
7972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7973
7974 /* wait for state dependents to drop to zero */
7975 i_ensureNoStateDependencies();
7976
7977 if (!mData->mAccessible)
7978 return setError(VBOX_E_INVALID_OBJECT_STATE,
7979 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7980 mUserData->s.strName.c_str(),
7981 mData->mUuid.toString().c_str());
7982
7983 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7984
7985 if (mData->mRegistered)
7986 return setError(VBOX_E_INVALID_OBJECT_STATE,
7987 tr("The machine '%s' with UUID {%s} is already registered"),
7988 mUserData->s.strName.c_str(),
7989 mData->mUuid.toString().c_str());
7990
7991 HRESULT rc = S_OK;
7992
7993 // Ensure the settings are saved. If we are going to be registered and
7994 // no config file exists yet, create it by calling i_saveSettings() too.
7995 if ( (mData->flModifications)
7996 || (!mData->pMachineConfigFile->fileExists())
7997 )
7998 {
7999 rc = i_saveSettings(NULL);
8000 // no need to check whether VirtualBox.xml needs saving too since
8001 // we can't have a machine XML file rename pending
8002 if (FAILED(rc)) return rc;
8003 }
8004
8005 /* more config checking goes here */
8006
8007 if (SUCCEEDED(rc))
8008 {
8009 /* we may have had implicit modifications we want to fix on success */
8010 i_commit();
8011
8012 mData->mRegistered = true;
8013 }
8014 else
8015 {
8016 /* we may have had implicit modifications we want to cancel on failure*/
8017 i_rollback(false /* aNotify */);
8018 }
8019
8020 return rc;
8021}
8022
8023/**
8024 * Increases the number of objects dependent on the machine state or on the
8025 * registered state. Guarantees that these two states will not change at least
8026 * until #releaseStateDependency() is called.
8027 *
8028 * Depending on the @a aDepType value, additional state checks may be made.
8029 * These checks will set extended error info on failure. See
8030 * #checkStateDependency() for more info.
8031 *
8032 * If this method returns a failure, the dependency is not added and the caller
8033 * is not allowed to rely on any particular machine state or registration state
8034 * value and may return the failed result code to the upper level.
8035 *
8036 * @param aDepType Dependency type to add.
8037 * @param aState Current machine state (NULL if not interested).
8038 * @param aRegistered Current registered state (NULL if not interested).
8039 *
8040 * @note Locks this object for writing.
8041 */
8042HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8043 MachineState_T *aState /* = NULL */,
8044 BOOL *aRegistered /* = NULL */)
8045{
8046 AutoCaller autoCaller(this);
8047 AssertComRCReturnRC(autoCaller.rc());
8048
8049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8050
8051 HRESULT rc = i_checkStateDependency(aDepType);
8052 if (FAILED(rc)) return rc;
8053
8054 {
8055 if (mData->mMachineStateChangePending != 0)
8056 {
8057 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8058 * drop to zero so don't add more. It may make sense to wait a bit
8059 * and retry before reporting an error (since the pending state
8060 * transition should be really quick) but let's just assert for
8061 * now to see if it ever happens on practice. */
8062
8063 AssertFailed();
8064
8065 return setError(E_ACCESSDENIED,
8066 tr("Machine state change is in progress. Please retry the operation later."));
8067 }
8068
8069 ++mData->mMachineStateDeps;
8070 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8071 }
8072
8073 if (aState)
8074 *aState = mData->mMachineState;
8075 if (aRegistered)
8076 *aRegistered = mData->mRegistered;
8077
8078 return S_OK;
8079}
8080
8081/**
8082 * Decreases the number of objects dependent on the machine state.
8083 * Must always complete the #addStateDependency() call after the state
8084 * dependency is no more necessary.
8085 */
8086void Machine::i_releaseStateDependency()
8087{
8088 AutoCaller autoCaller(this);
8089 AssertComRCReturnVoid(autoCaller.rc());
8090
8091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8092
8093 /* releaseStateDependency() w/o addStateDependency()? */
8094 AssertReturnVoid(mData->mMachineStateDeps != 0);
8095 -- mData->mMachineStateDeps;
8096
8097 if (mData->mMachineStateDeps == 0)
8098 {
8099 /* inform i_ensureNoStateDependencies() that there are no more deps */
8100 if (mData->mMachineStateChangePending != 0)
8101 {
8102 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8103 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8104 }
8105 }
8106}
8107
8108Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8109{
8110 /* start with nothing found */
8111 Utf8Str strResult("");
8112
8113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8114
8115 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8116 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8117 // found:
8118 strResult = it->second; // source is a Utf8Str
8119
8120 return strResult;
8121}
8122
8123// protected methods
8124/////////////////////////////////////////////////////////////////////////////
8125
8126/**
8127 * Performs machine state checks based on the @a aDepType value. If a check
8128 * fails, this method will set extended error info, otherwise it will return
8129 * S_OK. It is supposed, that on failure, the caller will immediately return
8130 * the return value of this method to the upper level.
8131 *
8132 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8133 *
8134 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8135 * current state of this machine object allows to change settings of the
8136 * machine (i.e. the machine is not registered, or registered but not running
8137 * and not saved). It is useful to call this method from Machine setters
8138 * before performing any change.
8139 *
8140 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8141 * as for MutableStateDep except that if the machine is saved, S_OK is also
8142 * returned. This is useful in setters which allow changing machine
8143 * properties when it is in the saved state.
8144 *
8145 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8146 * if the current state of this machine object allows to change runtime
8147 * changeable settings of the machine (i.e. the machine is not registered, or
8148 * registered but either running or not running and not saved). It is useful
8149 * to call this method from Machine setters before performing any changes to
8150 * runtime changeable settings.
8151 *
8152 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8153 * the same as for MutableOrRunningStateDep except that if the machine is
8154 * saved, S_OK is also returned. This is useful in setters which allow
8155 * changing runtime and saved state changeable machine properties.
8156 *
8157 * @param aDepType Dependency type to check.
8158 *
8159 * @note Non Machine based classes should use #addStateDependency() and
8160 * #releaseStateDependency() methods or the smart AutoStateDependency
8161 * template.
8162 *
8163 * @note This method must be called from under this object's read or write
8164 * lock.
8165 */
8166HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8167{
8168 switch (aDepType)
8169 {
8170 case AnyStateDep:
8171 {
8172 break;
8173 }
8174 case MutableStateDep:
8175 {
8176 if ( mData->mRegistered
8177 && ( !i_isSessionMachine()
8178 || ( mData->mMachineState != MachineState_Aborted
8179 && mData->mMachineState != MachineState_Teleported
8180 && mData->mMachineState != MachineState_PoweredOff
8181 )
8182 )
8183 )
8184 return setError(VBOX_E_INVALID_VM_STATE,
8185 tr("The machine is not mutable (state is %s)"),
8186 Global::stringifyMachineState(mData->mMachineState));
8187 break;
8188 }
8189 case MutableOrSavedStateDep:
8190 {
8191 if ( mData->mRegistered
8192 && ( !i_isSessionMachine()
8193 || ( mData->mMachineState != MachineState_Aborted
8194 && mData->mMachineState != MachineState_Teleported
8195 && mData->mMachineState != MachineState_Saved
8196 && mData->mMachineState != MachineState_PoweredOff
8197 )
8198 )
8199 )
8200 return setError(VBOX_E_INVALID_VM_STATE,
8201 tr("The machine is not mutable or saved (state is %s)"),
8202 Global::stringifyMachineState(mData->mMachineState));
8203 break;
8204 }
8205 case MutableOrRunningStateDep:
8206 {
8207 if ( mData->mRegistered
8208 && ( !i_isSessionMachine()
8209 || ( mData->mMachineState != MachineState_Aborted
8210 && mData->mMachineState != MachineState_Teleported
8211 && mData->mMachineState != MachineState_PoweredOff
8212 && !Global::IsOnline(mData->mMachineState)
8213 )
8214 )
8215 )
8216 return setError(VBOX_E_INVALID_VM_STATE,
8217 tr("The machine is not mutable or running (state is %s)"),
8218 Global::stringifyMachineState(mData->mMachineState));
8219 break;
8220 }
8221 case MutableOrSavedOrRunningStateDep:
8222 {
8223 if ( mData->mRegistered
8224 && ( !i_isSessionMachine()
8225 || ( mData->mMachineState != MachineState_Aborted
8226 && mData->mMachineState != MachineState_Teleported
8227 && mData->mMachineState != MachineState_Saved
8228 && mData->mMachineState != MachineState_PoweredOff
8229 && !Global::IsOnline(mData->mMachineState)
8230 )
8231 )
8232 )
8233 return setError(VBOX_E_INVALID_VM_STATE,
8234 tr("The machine is not mutable, saved or running (state is %s)"),
8235 Global::stringifyMachineState(mData->mMachineState));
8236 break;
8237 }
8238 }
8239
8240 return S_OK;
8241}
8242
8243/**
8244 * Helper to initialize all associated child objects and allocate data
8245 * structures.
8246 *
8247 * This method must be called as a part of the object's initialization procedure
8248 * (usually done in the #init() method).
8249 *
8250 * @note Must be called only from #init() or from #registeredInit().
8251 */
8252HRESULT Machine::initDataAndChildObjects()
8253{
8254 AutoCaller autoCaller(this);
8255 AssertComRCReturnRC(autoCaller.rc());
8256 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8257 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8258
8259 AssertReturn(!mData->mAccessible, E_FAIL);
8260
8261 /* allocate data structures */
8262 mSSData.allocate();
8263 mUserData.allocate();
8264 mHWData.allocate();
8265 mMediaData.allocate();
8266 mStorageControllers.allocate();
8267 mUSBControllers.allocate();
8268
8269 /* initialize mOSTypeId */
8270 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8271
8272 /* create associated BIOS settings object */
8273 unconst(mBIOSSettings).createObject();
8274 mBIOSSettings->init(this);
8275
8276 /* create an associated VRDE object (default is disabled) */
8277 unconst(mVRDEServer).createObject();
8278 mVRDEServer->init(this);
8279
8280 /* create associated serial port objects */
8281 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8282 {
8283 unconst(mSerialPorts[slot]).createObject();
8284 mSerialPorts[slot]->init(this, slot);
8285 }
8286
8287 /* create associated parallel port objects */
8288 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8289 {
8290 unconst(mParallelPorts[slot]).createObject();
8291 mParallelPorts[slot]->init(this, slot);
8292 }
8293
8294 /* create the audio adapter object (always present, default is disabled) */
8295 unconst(mAudioAdapter).createObject();
8296 mAudioAdapter->init(this);
8297
8298 /* create the USB device filters object (always present) */
8299 unconst(mUSBDeviceFilters).createObject();
8300 mUSBDeviceFilters->init(this);
8301
8302 /* create associated network adapter objects */
8303 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8304 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8305 {
8306 unconst(mNetworkAdapters[slot]).createObject();
8307 mNetworkAdapters[slot]->init(this, slot);
8308 }
8309
8310 /* create the bandwidth control */
8311 unconst(mBandwidthControl).createObject();
8312 mBandwidthControl->init(this);
8313
8314 return S_OK;
8315}
8316
8317/**
8318 * Helper to uninitialize all associated child objects and to free all data
8319 * structures.
8320 *
8321 * This method must be called as a part of the object's uninitialization
8322 * procedure (usually done in the #uninit() method).
8323 *
8324 * @note Must be called only from #uninit() or from #registeredInit().
8325 */
8326void Machine::uninitDataAndChildObjects()
8327{
8328 AutoCaller autoCaller(this);
8329 AssertComRCReturnVoid(autoCaller.rc());
8330 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8331 || getObjectState().getState() == ObjectState::Limited);
8332
8333 /* tell all our other child objects we've been uninitialized */
8334 if (mBandwidthControl)
8335 {
8336 mBandwidthControl->uninit();
8337 unconst(mBandwidthControl).setNull();
8338 }
8339
8340 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8341 {
8342 if (mNetworkAdapters[slot])
8343 {
8344 mNetworkAdapters[slot]->uninit();
8345 unconst(mNetworkAdapters[slot]).setNull();
8346 }
8347 }
8348
8349 if (mUSBDeviceFilters)
8350 {
8351 mUSBDeviceFilters->uninit();
8352 unconst(mUSBDeviceFilters).setNull();
8353 }
8354
8355 if (mAudioAdapter)
8356 {
8357 mAudioAdapter->uninit();
8358 unconst(mAudioAdapter).setNull();
8359 }
8360
8361 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8362 {
8363 if (mParallelPorts[slot])
8364 {
8365 mParallelPorts[slot]->uninit();
8366 unconst(mParallelPorts[slot]).setNull();
8367 }
8368 }
8369
8370 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8371 {
8372 if (mSerialPorts[slot])
8373 {
8374 mSerialPorts[slot]->uninit();
8375 unconst(mSerialPorts[slot]).setNull();
8376 }
8377 }
8378
8379 if (mVRDEServer)
8380 {
8381 mVRDEServer->uninit();
8382 unconst(mVRDEServer).setNull();
8383 }
8384
8385 if (mBIOSSettings)
8386 {
8387 mBIOSSettings->uninit();
8388 unconst(mBIOSSettings).setNull();
8389 }
8390
8391 /* Deassociate media (only when a real Machine or a SnapshotMachine
8392 * instance is uninitialized; SessionMachine instances refer to real
8393 * Machine media). This is necessary for a clean re-initialization of
8394 * the VM after successfully re-checking the accessibility state. Note
8395 * that in case of normal Machine or SnapshotMachine uninitialization (as
8396 * a result of unregistering or deleting the snapshot), outdated media
8397 * attachments will already be uninitialized and deleted, so this
8398 * code will not affect them. */
8399 if ( !!mMediaData
8400 && (!i_isSessionMachine())
8401 )
8402 {
8403 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8404 it != mMediaData->mAttachments.end();
8405 ++it)
8406 {
8407 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8408 if (pMedium.isNull())
8409 continue;
8410 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8411 AssertComRC(rc);
8412 }
8413 }
8414
8415 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8416 {
8417 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8418 if (mData->mFirstSnapshot)
8419 {
8420 // snapshots tree is protected by machine write lock; strictly
8421 // this isn't necessary here since we're deleting the entire
8422 // machine, but otherwise we assert in Snapshot::uninit()
8423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8424 mData->mFirstSnapshot->uninit();
8425 mData->mFirstSnapshot.setNull();
8426 }
8427
8428 mData->mCurrentSnapshot.setNull();
8429 }
8430
8431 /* free data structures (the essential mData structure is not freed here
8432 * since it may be still in use) */
8433 mMediaData.free();
8434 mStorageControllers.free();
8435 mUSBControllers.free();
8436 mHWData.free();
8437 mUserData.free();
8438 mSSData.free();
8439}
8440
8441/**
8442 * Returns a pointer to the Machine object for this machine that acts like a
8443 * parent for complex machine data objects such as shared folders, etc.
8444 *
8445 * For primary Machine objects and for SnapshotMachine objects, returns this
8446 * object's pointer itself. For SessionMachine objects, returns the peer
8447 * (primary) machine pointer.
8448 */
8449Machine* Machine::i_getMachine()
8450{
8451 if (i_isSessionMachine())
8452 return (Machine*)mPeer;
8453 return this;
8454}
8455
8456/**
8457 * Makes sure that there are no machine state dependents. If necessary, waits
8458 * for the number of dependents to drop to zero.
8459 *
8460 * Make sure this method is called from under this object's write lock to
8461 * guarantee that no new dependents may be added when this method returns
8462 * control to the caller.
8463 *
8464 * @note Locks this object for writing. The lock will be released while waiting
8465 * (if necessary).
8466 *
8467 * @warning To be used only in methods that change the machine state!
8468 */
8469void Machine::i_ensureNoStateDependencies()
8470{
8471 AssertReturnVoid(isWriteLockOnCurrentThread());
8472
8473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8474
8475 /* Wait for all state dependents if necessary */
8476 if (mData->mMachineStateDeps != 0)
8477 {
8478 /* lazy semaphore creation */
8479 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8480 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8481
8482 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8483 mData->mMachineStateDeps));
8484
8485 ++mData->mMachineStateChangePending;
8486
8487 /* reset the semaphore before waiting, the last dependent will signal
8488 * it */
8489 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8490
8491 alock.release();
8492
8493 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8494
8495 alock.acquire();
8496
8497 -- mData->mMachineStateChangePending;
8498 }
8499}
8500
8501/**
8502 * Changes the machine state and informs callbacks.
8503 *
8504 * This method is not intended to fail so it either returns S_OK or asserts (and
8505 * returns a failure).
8506 *
8507 * @note Locks this object for writing.
8508 */
8509HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8510{
8511 LogFlowThisFuncEnter();
8512 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8513 Assert(aMachineState != MachineState_Null);
8514
8515 AutoCaller autoCaller(this);
8516 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8517
8518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8519
8520 /* wait for state dependents to drop to zero */
8521 i_ensureNoStateDependencies();
8522
8523 MachineState_T const enmOldState = mData->mMachineState;
8524 if (enmOldState != aMachineState)
8525 {
8526 mData->mMachineState = aMachineState;
8527 RTTimeNow(&mData->mLastStateChange);
8528
8529#ifdef VBOX_WITH_DTRACE_R3_MAIN
8530 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8531#endif
8532 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8533 }
8534
8535 LogFlowThisFuncLeave();
8536 return S_OK;
8537}
8538
8539/**
8540 * Searches for a shared folder with the given logical name
8541 * in the collection of shared folders.
8542 *
8543 * @param aName logical name of the shared folder
8544 * @param aSharedFolder where to return the found object
8545 * @param aSetError whether to set the error info if the folder is
8546 * not found
8547 * @return
8548 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8549 *
8550 * @note
8551 * must be called from under the object's lock!
8552 */
8553HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8554 ComObjPtr<SharedFolder> &aSharedFolder,
8555 bool aSetError /* = false */)
8556{
8557 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8558 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8559 it != mHWData->mSharedFolders.end();
8560 ++it)
8561 {
8562 SharedFolder *pSF = *it;
8563 AutoCaller autoCaller(pSF);
8564 if (pSF->i_getName() == aName)
8565 {
8566 aSharedFolder = pSF;
8567 rc = S_OK;
8568 break;
8569 }
8570 }
8571
8572 if (aSetError && FAILED(rc))
8573 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8574
8575 return rc;
8576}
8577
8578/**
8579 * Initializes all machine instance data from the given settings structures
8580 * from XML. The exception is the machine UUID which needs special handling
8581 * depending on the caller's use case, so the caller needs to set that herself.
8582 *
8583 * This gets called in several contexts during machine initialization:
8584 *
8585 * -- When machine XML exists on disk already and needs to be loaded into memory,
8586 * for example, from registeredInit() to load all registered machines on
8587 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8588 * attached to the machine should be part of some media registry already.
8589 *
8590 * -- During OVF import, when a machine config has been constructed from an
8591 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8592 * ensure that the media listed as attachments in the config (which have
8593 * been imported from the OVF) receive the correct registry ID.
8594 *
8595 * -- During VM cloning.
8596 *
8597 * @param config Machine settings from XML.
8598 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8599 * for each attached medium in the config.
8600 * @return
8601 */
8602HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8603 const Guid *puuidRegistry)
8604{
8605 // copy name, description, OS type, teleporter, UTC etc.
8606 mUserData->s = config.machineUserData;
8607
8608 // Decode the Icon overide data from config userdata and set onto Machine.
8609 #define DECODE_STR_MAX _1M
8610 const char* pszStr = config.machineUserData.ovIcon.c_str();
8611 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8612 if (cbOut > DECODE_STR_MAX)
8613 return setError(E_FAIL,
8614 tr("Icon Data too long.'%d' > '%d'"),
8615 cbOut,
8616 DECODE_STR_MAX);
8617 mUserData->mIcon.resize(cbOut);
8618 int vrc = VINF_SUCCESS;
8619 if (cbOut)
8620 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8621 if (RT_FAILURE(vrc))
8622 {
8623 mUserData->mIcon.resize(0);
8624 return setError(E_FAIL,
8625 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8626 pszStr,
8627 vrc);
8628 }
8629
8630 // look up the object by Id to check it is valid
8631 ComPtr<IGuestOSType> guestOSType;
8632 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8633 guestOSType.asOutParam());
8634 if (FAILED(rc)) return rc;
8635
8636 // stateFile (optional)
8637 if (config.strStateFile.isEmpty())
8638 mSSData->strStateFilePath.setNull();
8639 else
8640 {
8641 Utf8Str stateFilePathFull(config.strStateFile);
8642 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8643 if (RT_FAILURE(vrc))
8644 return setError(E_FAIL,
8645 tr("Invalid saved state file path '%s' (%Rrc)"),
8646 config.strStateFile.c_str(),
8647 vrc);
8648 mSSData->strStateFilePath = stateFilePathFull;
8649 }
8650
8651 // snapshot folder needs special processing so set it again
8652 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8653 if (FAILED(rc)) return rc;
8654
8655 /* Copy the extra data items (Not in any case config is already the same as
8656 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8657 * make sure the extra data map is copied). */
8658 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8659
8660 /* currentStateModified (optional, default is true) */
8661 mData->mCurrentStateModified = config.fCurrentStateModified;
8662
8663 mData->mLastStateChange = config.timeLastStateChange;
8664
8665 /*
8666 * note: all mUserData members must be assigned prior this point because
8667 * we need to commit changes in order to let mUserData be shared by all
8668 * snapshot machine instances.
8669 */
8670 mUserData.commitCopy();
8671
8672 // machine registry, if present (must be loaded before snapshots)
8673 if (config.canHaveOwnMediaRegistry())
8674 {
8675 // determine machine folder
8676 Utf8Str strMachineFolder = i_getSettingsFileFull();
8677 strMachineFolder.stripFilename();
8678 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8679 config.mediaRegistry,
8680 strMachineFolder);
8681 if (FAILED(rc)) return rc;
8682 }
8683
8684 /* Snapshot node (optional) */
8685 size_t cRootSnapshots;
8686 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8687 {
8688 // there must be only one root snapshot
8689 Assert(cRootSnapshots == 1);
8690
8691 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8692
8693 rc = i_loadSnapshot(snap,
8694 config.uuidCurrentSnapshot,
8695 NULL); // no parent == first snapshot
8696 if (FAILED(rc)) return rc;
8697 }
8698
8699 // hardware data
8700 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8701 if (FAILED(rc)) return rc;
8702
8703 // load storage controllers
8704 rc = i_loadStorageControllers(config.storageMachine,
8705 puuidRegistry,
8706 NULL /* puuidSnapshot */);
8707 if (FAILED(rc)) return rc;
8708
8709 /*
8710 * NOTE: the assignment below must be the last thing to do,
8711 * otherwise it will be not possible to change the settings
8712 * somewhere in the code above because all setters will be
8713 * blocked by i_checkStateDependency(MutableStateDep).
8714 */
8715
8716 /* set the machine state to Aborted or Saved when appropriate */
8717 if (config.fAborted)
8718 {
8719 mSSData->strStateFilePath.setNull();
8720
8721 /* no need to use i_setMachineState() during init() */
8722 mData->mMachineState = MachineState_Aborted;
8723 }
8724 else if (!mSSData->strStateFilePath.isEmpty())
8725 {
8726 /* no need to use i_setMachineState() during init() */
8727 mData->mMachineState = MachineState_Saved;
8728 }
8729
8730 // after loading settings, we are no longer different from the XML on disk
8731 mData->flModifications = 0;
8732
8733 return S_OK;
8734}
8735
8736/**
8737 * Recursively loads all snapshots starting from the given.
8738 *
8739 * @param aNode <Snapshot> node.
8740 * @param aCurSnapshotId Current snapshot ID from the settings file.
8741 * @param aParentSnapshot Parent snapshot.
8742 */
8743HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8744 const Guid &aCurSnapshotId,
8745 Snapshot *aParentSnapshot)
8746{
8747 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8748 AssertReturn(!i_isSessionMachine(), E_FAIL);
8749
8750 HRESULT rc = S_OK;
8751
8752 Utf8Str strStateFile;
8753 if (!data.strStateFile.isEmpty())
8754 {
8755 /* optional */
8756 strStateFile = data.strStateFile;
8757 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8758 if (RT_FAILURE(vrc))
8759 return setError(E_FAIL,
8760 tr("Invalid saved state file path '%s' (%Rrc)"),
8761 strStateFile.c_str(),
8762 vrc);
8763 }
8764
8765 /* create a snapshot machine object */
8766 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8767 pSnapshotMachine.createObject();
8768 rc = pSnapshotMachine->initFromSettings(this,
8769 data.hardware,
8770 &data.debugging,
8771 &data.autostart,
8772 data.storage,
8773 data.uuid.ref(),
8774 strStateFile);
8775 if (FAILED(rc)) return rc;
8776
8777 /* create a snapshot object */
8778 ComObjPtr<Snapshot> pSnapshot;
8779 pSnapshot.createObject();
8780 /* initialize the snapshot */
8781 rc = pSnapshot->init(mParent, // VirtualBox object
8782 data.uuid,
8783 data.strName,
8784 data.strDescription,
8785 data.timestamp,
8786 pSnapshotMachine,
8787 aParentSnapshot);
8788 if (FAILED(rc)) return rc;
8789
8790 /* memorize the first snapshot if necessary */
8791 if (!mData->mFirstSnapshot)
8792 mData->mFirstSnapshot = pSnapshot;
8793
8794 /* memorize the current snapshot when appropriate */
8795 if ( !mData->mCurrentSnapshot
8796 && pSnapshot->i_getId() == aCurSnapshotId
8797 )
8798 mData->mCurrentSnapshot = pSnapshot;
8799
8800 // now create the children
8801 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8802 it != data.llChildSnapshots.end();
8803 ++it)
8804 {
8805 const settings::Snapshot &childData = *it;
8806 // recurse
8807 rc = i_loadSnapshot(childData,
8808 aCurSnapshotId,
8809 pSnapshot); // parent = the one we created above
8810 if (FAILED(rc)) return rc;
8811 }
8812
8813 return rc;
8814}
8815
8816/**
8817 * Loads settings into mHWData.
8818 *
8819 * @param data Reference to the hardware settings.
8820 * @param pDbg Pointer to the debugging settings.
8821 * @param pAutostart Pointer to the autostart settings.
8822 */
8823HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8824 const settings::Autostart *pAutostart)
8825{
8826 AssertReturn(!i_isSessionMachine(), E_FAIL);
8827
8828 HRESULT rc = S_OK;
8829
8830 try
8831 {
8832 /* The hardware version attribute (optional). */
8833 mHWData->mHWVersion = data.strVersion;
8834 mHWData->mHardwareUUID = data.uuid;
8835
8836 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8837 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8838 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8839 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8840 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8841 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8842 mHWData->mPAEEnabled = data.fPAE;
8843 mHWData->mLongMode = data.enmLongMode;
8844 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8845 mHWData->mCPUCount = data.cCPUs;
8846 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8847 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8848 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8849
8850 // cpu
8851 if (mHWData->mCPUHotPlugEnabled)
8852 {
8853 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8854 it != data.llCpus.end();
8855 ++it)
8856 {
8857 const settings::Cpu &cpu = *it;
8858
8859 mHWData->mCPUAttached[cpu.ulId] = true;
8860 }
8861 }
8862
8863 // cpuid leafs
8864 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8865 it != data.llCpuIdLeafs.end();
8866 ++it)
8867 {
8868 const settings::CpuIdLeaf &leaf = *it;
8869
8870 switch (leaf.ulId)
8871 {
8872 case 0x0:
8873 case 0x1:
8874 case 0x2:
8875 case 0x3:
8876 case 0x4:
8877 case 0x5:
8878 case 0x6:
8879 case 0x7:
8880 case 0x8:
8881 case 0x9:
8882 case 0xA:
8883 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8884 break;
8885
8886 case 0x80000000:
8887 case 0x80000001:
8888 case 0x80000002:
8889 case 0x80000003:
8890 case 0x80000004:
8891 case 0x80000005:
8892 case 0x80000006:
8893 case 0x80000007:
8894 case 0x80000008:
8895 case 0x80000009:
8896 case 0x8000000A:
8897 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8898 break;
8899
8900 default:
8901 /* just ignore */
8902 break;
8903 }
8904 }
8905
8906 mHWData->mMemorySize = data.ulMemorySizeMB;
8907 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8908
8909 // boot order
8910 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8911 {
8912 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8913 if (it == data.mapBootOrder.end())
8914 mHWData->mBootOrder[i] = DeviceType_Null;
8915 else
8916 mHWData->mBootOrder[i] = it->second;
8917 }
8918
8919 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8920 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8921 mHWData->mMonitorCount = data.cMonitors;
8922 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8923 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8924 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8925 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8926 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8927 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8928 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8929 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8930 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8931 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8932 if (!data.strVideoCaptureFile.isEmpty())
8933 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8934 else
8935 mHWData->mVideoCaptureFile.setNull();
8936 mHWData->mFirmwareType = data.firmwareType;
8937 mHWData->mPointingHIDType = data.pointingHIDType;
8938 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8939 mHWData->mChipsetType = data.chipsetType;
8940 mHWData->mParavirtProvider = data.paravirtProvider;
8941 mHWData->mParavirtDebug = data.strParavirtDebug;
8942 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8943 mHWData->mHPETEnabled = data.fHPETEnabled;
8944
8945 /* VRDEServer */
8946 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8947 if (FAILED(rc)) return rc;
8948
8949 /* BIOS */
8950 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8951 if (FAILED(rc)) return rc;
8952
8953 // Bandwidth control (must come before network adapters)
8954 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8955 if (FAILED(rc)) return rc;
8956
8957 /* Shared folders */
8958 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8959 it != data.usbSettings.llUSBControllers.end();
8960 ++it)
8961 {
8962 const settings::USBController &settingsCtrl = *it;
8963 ComObjPtr<USBController> newCtrl;
8964
8965 newCtrl.createObject();
8966 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8967 mUSBControllers->push_back(newCtrl);
8968 }
8969
8970 /* USB device filters */
8971 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8972 if (FAILED(rc)) return rc;
8973
8974 // network adapters
8975 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8976 size_t oldCount = mNetworkAdapters.size();
8977 if (newCount > oldCount)
8978 {
8979 mNetworkAdapters.resize(newCount);
8980 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8981 {
8982 unconst(mNetworkAdapters[slot]).createObject();
8983 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8984 }
8985 }
8986 else if (newCount < oldCount)
8987 mNetworkAdapters.resize(newCount);
8988 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8989 it != data.llNetworkAdapters.end();
8990 ++it)
8991 {
8992 const settings::NetworkAdapter &nic = *it;
8993
8994 /* slot unicity is guaranteed by XML Schema */
8995 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8996 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8997 if (FAILED(rc)) return rc;
8998 }
8999
9000 // serial ports
9001 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9002 it != data.llSerialPorts.end();
9003 ++it)
9004 {
9005 const settings::SerialPort &s = *it;
9006
9007 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9008 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9009 if (FAILED(rc)) return rc;
9010 }
9011
9012 // parallel ports (optional)
9013 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9014 it != data.llParallelPorts.end();
9015 ++it)
9016 {
9017 const settings::ParallelPort &p = *it;
9018
9019 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9020 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9021 if (FAILED(rc)) return rc;
9022 }
9023
9024 /* AudioAdapter */
9025 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9026 if (FAILED(rc)) return rc;
9027
9028 /* Shared folders */
9029 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9030 it != data.llSharedFolders.end();
9031 ++it)
9032 {
9033 const settings::SharedFolder &sf = *it;
9034
9035 ComObjPtr<SharedFolder> sharedFolder;
9036 /* Check for double entries. Not allowed! */
9037 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9038 if (SUCCEEDED(rc))
9039 return setError(VBOX_E_OBJECT_IN_USE,
9040 tr("Shared folder named '%s' already exists"),
9041 sf.strName.c_str());
9042
9043 /* Create the new shared folder. Don't break on error. This will be
9044 * reported when the machine starts. */
9045 sharedFolder.createObject();
9046 rc = sharedFolder->init(i_getMachine(),
9047 sf.strName,
9048 sf.strHostPath,
9049 RT_BOOL(sf.fWritable),
9050 RT_BOOL(sf.fAutoMount),
9051 false /* fFailOnError */);
9052 if (FAILED(rc)) return rc;
9053 mHWData->mSharedFolders.push_back(sharedFolder);
9054 }
9055
9056 // Clipboard
9057 mHWData->mClipboardMode = data.clipboardMode;
9058
9059 // drag'n'drop
9060 mHWData->mDnDMode = data.dndMode;
9061
9062 // guest settings
9063 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9064
9065 // IO settings
9066 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9067 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9068
9069 // Host PCI devices
9070 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9071 it != data.pciAttachments.end();
9072 ++it)
9073 {
9074 const settings::HostPCIDeviceAttachment &hpda = *it;
9075 ComObjPtr<PCIDeviceAttachment> pda;
9076
9077 pda.createObject();
9078 pda->i_loadSettings(this, hpda);
9079 mHWData->mPCIDeviceAssignments.push_back(pda);
9080 }
9081
9082 /*
9083 * (The following isn't really real hardware, but it lives in HWData
9084 * for reasons of convenience.)
9085 */
9086
9087#ifdef VBOX_WITH_GUEST_PROPS
9088 /* Guest properties (optional) */
9089
9090 /* Only load transient guest properties for configs which have saved
9091 * state, because there shouldn't be any for powered off VMs. The same
9092 * logic applies for snapshots, as offline snapshots shouldn't have
9093 * any such properties. They confuse the code in various places.
9094 * Note: can't rely on the machine state, as it isn't set yet. */
9095 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9096 /* apologies for the hacky unconst() usage, but this needs hacking
9097 * actually inconsistent settings into consistency, otherwise there
9098 * will be some corner cases where the inconsistency survives
9099 * surprisingly long without getting fixed, especially for snapshots
9100 * as there are no config changes. */
9101 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9102 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9103 it != llGuestProperties.end();
9104 /*nothing*/)
9105 {
9106 const settings::GuestProperty &prop = *it;
9107 uint32_t fFlags = guestProp::NILFLAG;
9108 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9109 if ( fSkipTransientGuestProperties
9110 && ( fFlags & guestProp::TRANSIENT
9111 || fFlags & guestProp::TRANSRESET))
9112 {
9113 it = llGuestProperties.erase(it);
9114 continue;
9115 }
9116 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9117 mHWData->mGuestProperties[prop.strName] = property;
9118 ++it;
9119 }
9120#endif /* VBOX_WITH_GUEST_PROPS defined */
9121
9122 rc = i_loadDebugging(pDbg);
9123 if (FAILED(rc))
9124 return rc;
9125
9126 mHWData->mAutostart = *pAutostart;
9127
9128 /* default frontend */
9129 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9130 }
9131 catch(std::bad_alloc &)
9132 {
9133 return E_OUTOFMEMORY;
9134 }
9135
9136 AssertComRC(rc);
9137 return rc;
9138}
9139
9140/**
9141 * Called from Machine::loadHardware() to load the debugging settings of the
9142 * machine.
9143 *
9144 * @param pDbg Pointer to the settings.
9145 */
9146HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9147{
9148 mHWData->mDebugging = *pDbg;
9149 /* no more processing currently required, this will probably change. */
9150 return S_OK;
9151}
9152
9153/**
9154 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9155 *
9156 * @param data
9157 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9158 * @param puuidSnapshot
9159 * @return
9160 */
9161HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9162 const Guid *puuidRegistry,
9163 const Guid *puuidSnapshot)
9164{
9165 AssertReturn(!i_isSessionMachine(), E_FAIL);
9166
9167 HRESULT rc = S_OK;
9168
9169 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9170 it != data.llStorageControllers.end();
9171 ++it)
9172 {
9173 const settings::StorageController &ctlData = *it;
9174
9175 ComObjPtr<StorageController> pCtl;
9176 /* Try to find one with the name first. */
9177 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9178 if (SUCCEEDED(rc))
9179 return setError(VBOX_E_OBJECT_IN_USE,
9180 tr("Storage controller named '%s' already exists"),
9181 ctlData.strName.c_str());
9182
9183 pCtl.createObject();
9184 rc = pCtl->init(this,
9185 ctlData.strName,
9186 ctlData.storageBus,
9187 ctlData.ulInstance,
9188 ctlData.fBootable);
9189 if (FAILED(rc)) return rc;
9190
9191 mStorageControllers->push_back(pCtl);
9192
9193 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9194 if (FAILED(rc)) return rc;
9195
9196 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9197 if (FAILED(rc)) return rc;
9198
9199 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9200 if (FAILED(rc)) return rc;
9201
9202 /* Load the attached devices now. */
9203 rc = i_loadStorageDevices(pCtl,
9204 ctlData,
9205 puuidRegistry,
9206 puuidSnapshot);
9207 if (FAILED(rc)) return rc;
9208 }
9209
9210 return S_OK;
9211}
9212
9213/**
9214 * Called from i_loadStorageControllers for a controller's devices.
9215 *
9216 * @param aStorageController
9217 * @param data
9218 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9219 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9220 * @return
9221 */
9222HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9223 const settings::StorageController &data,
9224 const Guid *puuidRegistry,
9225 const Guid *puuidSnapshot)
9226{
9227 HRESULT rc = S_OK;
9228
9229 /* paranoia: detect duplicate attachments */
9230 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9231 it != data.llAttachedDevices.end();
9232 ++it)
9233 {
9234 const settings::AttachedDevice &ad = *it;
9235
9236 for (settings::AttachedDevicesList::const_iterator it2 = it;
9237 it2 != data.llAttachedDevices.end();
9238 ++it2)
9239 {
9240 if (it == it2)
9241 continue;
9242
9243 const settings::AttachedDevice &ad2 = *it2;
9244
9245 if ( ad.lPort == ad2.lPort
9246 && ad.lDevice == ad2.lDevice)
9247 {
9248 return setError(E_FAIL,
9249 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9250 aStorageController->i_getName().c_str(),
9251 ad.lPort,
9252 ad.lDevice,
9253 mUserData->s.strName.c_str());
9254 }
9255 }
9256 }
9257
9258 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9259 it != data.llAttachedDevices.end();
9260 ++it)
9261 {
9262 const settings::AttachedDevice &dev = *it;
9263 ComObjPtr<Medium> medium;
9264
9265 switch (dev.deviceType)
9266 {
9267 case DeviceType_Floppy:
9268 case DeviceType_DVD:
9269 if (dev.strHostDriveSrc.isNotEmpty())
9270 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9271 false /* fRefresh */, medium);
9272 else
9273 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9274 dev.uuid,
9275 false /* fRefresh */,
9276 false /* aSetError */,
9277 medium);
9278 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9279 // This is not an error. The host drive or UUID might have vanished, so just go
9280 // ahead without this removeable medium attachment
9281 rc = S_OK;
9282 break;
9283
9284 case DeviceType_HardDisk:
9285 {
9286 /* find a hard disk by UUID */
9287 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9288 if (FAILED(rc))
9289 {
9290 if (i_isSnapshotMachine())
9291 {
9292 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9293 // so the user knows that the bad disk is in a snapshot somewhere
9294 com::ErrorInfo info;
9295 return setError(E_FAIL,
9296 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9297 puuidSnapshot->raw(),
9298 info.getText().raw());
9299 }
9300 else
9301 return rc;
9302 }
9303
9304 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9305
9306 if (medium->i_getType() == MediumType_Immutable)
9307 {
9308 if (i_isSnapshotMachine())
9309 return setError(E_FAIL,
9310 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9311 "of the virtual machine '%s' ('%s')"),
9312 medium->i_getLocationFull().c_str(),
9313 dev.uuid.raw(),
9314 puuidSnapshot->raw(),
9315 mUserData->s.strName.c_str(),
9316 mData->m_strConfigFileFull.c_str());
9317
9318 return setError(E_FAIL,
9319 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9320 medium->i_getLocationFull().c_str(),
9321 dev.uuid.raw(),
9322 mUserData->s.strName.c_str(),
9323 mData->m_strConfigFileFull.c_str());
9324 }
9325
9326 if (medium->i_getType() == MediumType_MultiAttach)
9327 {
9328 if (i_isSnapshotMachine())
9329 return setError(E_FAIL,
9330 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9331 "of the virtual machine '%s' ('%s')"),
9332 medium->i_getLocationFull().c_str(),
9333 dev.uuid.raw(),
9334 puuidSnapshot->raw(),
9335 mUserData->s.strName.c_str(),
9336 mData->m_strConfigFileFull.c_str());
9337
9338 return setError(E_FAIL,
9339 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9340 medium->i_getLocationFull().c_str(),
9341 dev.uuid.raw(),
9342 mUserData->s.strName.c_str(),
9343 mData->m_strConfigFileFull.c_str());
9344 }
9345
9346 if ( !i_isSnapshotMachine()
9347 && medium->i_getChildren().size() != 0
9348 )
9349 return setError(E_FAIL,
9350 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9351 "because it has %d differencing child hard disks"),
9352 medium->i_getLocationFull().c_str(),
9353 dev.uuid.raw(),
9354 mUserData->s.strName.c_str(),
9355 mData->m_strConfigFileFull.c_str(),
9356 medium->i_getChildren().size());
9357
9358 if (i_findAttachment(mMediaData->mAttachments,
9359 medium))
9360 return setError(E_FAIL,
9361 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9362 medium->i_getLocationFull().c_str(),
9363 dev.uuid.raw(),
9364 mUserData->s.strName.c_str(),
9365 mData->m_strConfigFileFull.c_str());
9366
9367 break;
9368 }
9369
9370 default:
9371 return setError(E_FAIL,
9372 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9373 medium->i_getLocationFull().c_str(),
9374 mUserData->s.strName.c_str(),
9375 mData->m_strConfigFileFull.c_str());
9376 }
9377
9378 if (FAILED(rc))
9379 break;
9380
9381 /* Bandwidth groups are loaded at this point. */
9382 ComObjPtr<BandwidthGroup> pBwGroup;
9383
9384 if (!dev.strBwGroup.isEmpty())
9385 {
9386 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9387 if (FAILED(rc))
9388 return setError(E_FAIL,
9389 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9390 medium->i_getLocationFull().c_str(),
9391 dev.strBwGroup.c_str(),
9392 mUserData->s.strName.c_str(),
9393 mData->m_strConfigFileFull.c_str());
9394 pBwGroup->i_reference();
9395 }
9396
9397 const Bstr controllerName = aStorageController->i_getName();
9398 ComObjPtr<MediumAttachment> pAttachment;
9399 pAttachment.createObject();
9400 rc = pAttachment->init(this,
9401 medium,
9402 controllerName,
9403 dev.lPort,
9404 dev.lDevice,
9405 dev.deviceType,
9406 false,
9407 dev.fPassThrough,
9408 dev.fTempEject,
9409 dev.fNonRotational,
9410 dev.fDiscard,
9411 dev.fHotPluggable,
9412 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9413 if (FAILED(rc)) break;
9414
9415 /* associate the medium with this machine and snapshot */
9416 if (!medium.isNull())
9417 {
9418 AutoCaller medCaller(medium);
9419 if (FAILED(medCaller.rc())) return medCaller.rc();
9420 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9421
9422 if (i_isSnapshotMachine())
9423 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9424 else
9425 rc = medium->i_addBackReference(mData->mUuid);
9426 /* If the medium->addBackReference fails it sets an appropriate
9427 * error message, so no need to do any guesswork here. */
9428
9429 if (puuidRegistry)
9430 // caller wants registry ID to be set on all attached media (OVF import case)
9431 medium->i_addRegistry(*puuidRegistry);
9432 }
9433
9434 if (FAILED(rc))
9435 break;
9436
9437 /* back up mMediaData to let registeredInit() properly rollback on failure
9438 * (= limited accessibility) */
9439 i_setModified(IsModified_Storage);
9440 mMediaData.backup();
9441 mMediaData->mAttachments.push_back(pAttachment);
9442 }
9443
9444 return rc;
9445}
9446
9447/**
9448 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9449 *
9450 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9451 * @param aSnapshot where to return the found snapshot
9452 * @param aSetError true to set extended error info on failure
9453 */
9454HRESULT Machine::i_findSnapshotById(const Guid &aId,
9455 ComObjPtr<Snapshot> &aSnapshot,
9456 bool aSetError /* = false */)
9457{
9458 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9459
9460 if (!mData->mFirstSnapshot)
9461 {
9462 if (aSetError)
9463 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9464 return E_FAIL;
9465 }
9466
9467 if (aId.isZero())
9468 aSnapshot = mData->mFirstSnapshot;
9469 else
9470 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9471
9472 if (!aSnapshot)
9473 {
9474 if (aSetError)
9475 return setError(E_FAIL,
9476 tr("Could not find a snapshot with UUID {%s}"),
9477 aId.toString().c_str());
9478 return E_FAIL;
9479 }
9480
9481 return S_OK;
9482}
9483
9484/**
9485 * Returns the snapshot with the given name or fails of no such snapshot.
9486 *
9487 * @param aName snapshot name to find
9488 * @param aSnapshot where to return the found snapshot
9489 * @param aSetError true to set extended error info on failure
9490 */
9491HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9492 ComObjPtr<Snapshot> &aSnapshot,
9493 bool aSetError /* = false */)
9494{
9495 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9496
9497 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9498
9499 if (!mData->mFirstSnapshot)
9500 {
9501 if (aSetError)
9502 return setError(VBOX_E_OBJECT_NOT_FOUND,
9503 tr("This machine does not have any snapshots"));
9504 return VBOX_E_OBJECT_NOT_FOUND;
9505 }
9506
9507 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9508
9509 if (!aSnapshot)
9510 {
9511 if (aSetError)
9512 return setError(VBOX_E_OBJECT_NOT_FOUND,
9513 tr("Could not find a snapshot named '%s'"), strName.c_str());
9514 return VBOX_E_OBJECT_NOT_FOUND;
9515 }
9516
9517 return S_OK;
9518}
9519
9520/**
9521 * Returns a storage controller object with the given name.
9522 *
9523 * @param aName storage controller name to find
9524 * @param aStorageController where to return the found storage controller
9525 * @param aSetError true to set extended error info on failure
9526 */
9527HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9528 ComObjPtr<StorageController> &aStorageController,
9529 bool aSetError /* = false */)
9530{
9531 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9532
9533 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9534 it != mStorageControllers->end();
9535 ++it)
9536 {
9537 if ((*it)->i_getName() == aName)
9538 {
9539 aStorageController = (*it);
9540 return S_OK;
9541 }
9542 }
9543
9544 if (aSetError)
9545 return setError(VBOX_E_OBJECT_NOT_FOUND,
9546 tr("Could not find a storage controller named '%s'"),
9547 aName.c_str());
9548 return VBOX_E_OBJECT_NOT_FOUND;
9549}
9550
9551/**
9552 * Returns a USB controller object with the given name.
9553 *
9554 * @param aName USB controller name to find
9555 * @param aUSBController where to return the found USB controller
9556 * @param aSetError true to set extended error info on failure
9557 */
9558HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9559 ComObjPtr<USBController> &aUSBController,
9560 bool aSetError /* = false */)
9561{
9562 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9563
9564 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9565 it != mUSBControllers->end();
9566 ++it)
9567 {
9568 if ((*it)->i_getName() == aName)
9569 {
9570 aUSBController = (*it);
9571 return S_OK;
9572 }
9573 }
9574
9575 if (aSetError)
9576 return setError(VBOX_E_OBJECT_NOT_FOUND,
9577 tr("Could not find a storage controller named '%s'"),
9578 aName.c_str());
9579 return VBOX_E_OBJECT_NOT_FOUND;
9580}
9581
9582/**
9583 * Returns the number of USB controller instance of the given type.
9584 *
9585 * @param enmType USB controller type.
9586 */
9587ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9588{
9589 ULONG cCtrls = 0;
9590
9591 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9592 it != mUSBControllers->end();
9593 ++it)
9594 {
9595 if ((*it)->i_getControllerType() == enmType)
9596 cCtrls++;
9597 }
9598
9599 return cCtrls;
9600}
9601
9602HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9603 MediaData::AttachmentList &atts)
9604{
9605 AutoCaller autoCaller(this);
9606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9607
9608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9609
9610 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9611 it != mMediaData->mAttachments.end();
9612 ++it)
9613 {
9614 const ComObjPtr<MediumAttachment> &pAtt = *it;
9615 // should never happen, but deal with NULL pointers in the list.
9616 AssertStmt(!pAtt.isNull(), continue);
9617
9618 // getControllerName() needs caller+read lock
9619 AutoCaller autoAttCaller(pAtt);
9620 if (FAILED(autoAttCaller.rc()))
9621 {
9622 atts.clear();
9623 return autoAttCaller.rc();
9624 }
9625 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9626
9627 if (pAtt->i_getControllerName() == aName)
9628 atts.push_back(pAtt);
9629 }
9630
9631 return S_OK;
9632}
9633
9634
9635/**
9636 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9637 * file if the machine name was changed and about creating a new settings file
9638 * if this is a new machine.
9639 *
9640 * @note Must be never called directly but only from #saveSettings().
9641 */
9642HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9643{
9644 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9645
9646 HRESULT rc = S_OK;
9647
9648 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9649
9650 /// @todo need to handle primary group change, too
9651
9652 /* attempt to rename the settings file if machine name is changed */
9653 if ( mUserData->s.fNameSync
9654 && mUserData.isBackedUp()
9655 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9656 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9657 )
9658 {
9659 bool dirRenamed = false;
9660 bool fileRenamed = false;
9661
9662 Utf8Str configFile, newConfigFile;
9663 Utf8Str configFilePrev, newConfigFilePrev;
9664 Utf8Str configDir, newConfigDir;
9665
9666 do
9667 {
9668 int vrc = VINF_SUCCESS;
9669
9670 Utf8Str name = mUserData.backedUpData()->s.strName;
9671 Utf8Str newName = mUserData->s.strName;
9672 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9673 if (group == "/")
9674 group.setNull();
9675 Utf8Str newGroup = mUserData->s.llGroups.front();
9676 if (newGroup == "/")
9677 newGroup.setNull();
9678
9679 configFile = mData->m_strConfigFileFull;
9680
9681 /* first, rename the directory if it matches the group and machine name */
9682 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9683 group.c_str(), RTPATH_DELIMITER, name.c_str());
9684 /** @todo hack, make somehow use of ComposeMachineFilename */
9685 if (mUserData->s.fDirectoryIncludesUUID)
9686 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9687 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9688 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9689 /** @todo hack, make somehow use of ComposeMachineFilename */
9690 if (mUserData->s.fDirectoryIncludesUUID)
9691 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9692 configDir = configFile;
9693 configDir.stripFilename();
9694 newConfigDir = configDir;
9695 if ( configDir.length() >= groupPlusName.length()
9696 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9697 groupPlusName.c_str()))
9698 {
9699 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9700 Utf8Str newConfigBaseDir(newConfigDir);
9701 newConfigDir.append(newGroupPlusName);
9702 /* consistency: use \ if appropriate on the platform */
9703 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9704 /* new dir and old dir cannot be equal here because of 'if'
9705 * above and because name != newName */
9706 Assert(configDir != newConfigDir);
9707 if (!fSettingsFileIsNew)
9708 {
9709 /* perform real rename only if the machine is not new */
9710 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9711 if ( vrc == VERR_FILE_NOT_FOUND
9712 || vrc == VERR_PATH_NOT_FOUND)
9713 {
9714 /* create the parent directory, then retry renaming */
9715 Utf8Str parent(newConfigDir);
9716 parent.stripFilename();
9717 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9718 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9719 }
9720 if (RT_FAILURE(vrc))
9721 {
9722 rc = setError(E_FAIL,
9723 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9724 configDir.c_str(),
9725 newConfigDir.c_str(),
9726 vrc);
9727 break;
9728 }
9729 /* delete subdirectories which are no longer needed */
9730 Utf8Str dir(configDir);
9731 dir.stripFilename();
9732 while (dir != newConfigBaseDir && dir != ".")
9733 {
9734 vrc = RTDirRemove(dir.c_str());
9735 if (RT_FAILURE(vrc))
9736 break;
9737 dir.stripFilename();
9738 }
9739 dirRenamed = true;
9740 }
9741 }
9742
9743 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9744 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9745
9746 /* then try to rename the settings file itself */
9747 if (newConfigFile != configFile)
9748 {
9749 /* get the path to old settings file in renamed directory */
9750 configFile = Utf8StrFmt("%s%c%s",
9751 newConfigDir.c_str(),
9752 RTPATH_DELIMITER,
9753 RTPathFilename(configFile.c_str()));
9754 if (!fSettingsFileIsNew)
9755 {
9756 /* perform real rename only if the machine is not new */
9757 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9758 if (RT_FAILURE(vrc))
9759 {
9760 rc = setError(E_FAIL,
9761 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9762 configFile.c_str(),
9763 newConfigFile.c_str(),
9764 vrc);
9765 break;
9766 }
9767 fileRenamed = true;
9768 configFilePrev = configFile;
9769 configFilePrev += "-prev";
9770 newConfigFilePrev = newConfigFile;
9771 newConfigFilePrev += "-prev";
9772 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9773 }
9774 }
9775
9776 // update m_strConfigFileFull amd mConfigFile
9777 mData->m_strConfigFileFull = newConfigFile;
9778 // compute the relative path too
9779 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9780
9781 // store the old and new so that VirtualBox::i_saveSettings() can update
9782 // the media registry
9783 if ( mData->mRegistered
9784 && (configDir != newConfigDir || configFile != newConfigFile))
9785 {
9786 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9787
9788 if (pfNeedsGlobalSaveSettings)
9789 *pfNeedsGlobalSaveSettings = true;
9790 }
9791
9792 // in the saved state file path, replace the old directory with the new directory
9793 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9794 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9795
9796 // and do the same thing for the saved state file paths of all the online snapshots
9797 if (mData->mFirstSnapshot)
9798 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9799 newConfigDir.c_str());
9800 }
9801 while (0);
9802
9803 if (FAILED(rc))
9804 {
9805 /* silently try to rename everything back */
9806 if (fileRenamed)
9807 {
9808 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9809 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9810 }
9811 if (dirRenamed)
9812 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9813 }
9814
9815 if (FAILED(rc)) return rc;
9816 }
9817
9818 if (fSettingsFileIsNew)
9819 {
9820 /* create a virgin config file */
9821 int vrc = VINF_SUCCESS;
9822
9823 /* ensure the settings directory exists */
9824 Utf8Str path(mData->m_strConfigFileFull);
9825 path.stripFilename();
9826 if (!RTDirExists(path.c_str()))
9827 {
9828 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9829 if (RT_FAILURE(vrc))
9830 {
9831 return setError(E_FAIL,
9832 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9833 path.c_str(),
9834 vrc);
9835 }
9836 }
9837
9838 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9839 path = Utf8Str(mData->m_strConfigFileFull);
9840 RTFILE f = NIL_RTFILE;
9841 vrc = RTFileOpen(&f, path.c_str(),
9842 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9843 if (RT_FAILURE(vrc))
9844 return setError(E_FAIL,
9845 tr("Could not create the settings file '%s' (%Rrc)"),
9846 path.c_str(),
9847 vrc);
9848 RTFileClose(f);
9849 }
9850
9851 return rc;
9852}
9853
9854/**
9855 * Saves and commits machine data, user data and hardware data.
9856 *
9857 * Note that on failure, the data remains uncommitted.
9858 *
9859 * @a aFlags may combine the following flags:
9860 *
9861 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9862 * Used when saving settings after an operation that makes them 100%
9863 * correspond to the settings from the current snapshot.
9864 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9865 * #isReallyModified() returns false. This is necessary for cases when we
9866 * change machine data directly, not through the backup()/commit() mechanism.
9867 * - SaveS_Force: settings will be saved without doing a deep compare of the
9868 * settings structures. This is used when this is called because snapshots
9869 * have changed to avoid the overhead of the deep compare.
9870 *
9871 * @note Must be called from under this object's write lock. Locks children for
9872 * writing.
9873 *
9874 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9875 * initialized to false and that will be set to true by this function if
9876 * the caller must invoke VirtualBox::i_saveSettings() because the global
9877 * settings have changed. This will happen if a machine rename has been
9878 * saved and the global machine and media registries will therefore need
9879 * updating.
9880 */
9881HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9882 int aFlags /*= 0*/)
9883{
9884 LogFlowThisFuncEnter();
9885
9886 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9887
9888 /* make sure child objects are unable to modify the settings while we are
9889 * saving them */
9890 i_ensureNoStateDependencies();
9891
9892 AssertReturn(!i_isSnapshotMachine(),
9893 E_FAIL);
9894
9895 HRESULT rc = S_OK;
9896 bool fNeedsWrite = false;
9897
9898 /* First, prepare to save settings. It will care about renaming the
9899 * settings directory and file if the machine name was changed and about
9900 * creating a new settings file if this is a new machine. */
9901 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9902 if (FAILED(rc)) return rc;
9903
9904 // keep a pointer to the current settings structures
9905 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9906 settings::MachineConfigFile *pNewConfig = NULL;
9907
9908 try
9909 {
9910 // make a fresh one to have everyone write stuff into
9911 pNewConfig = new settings::MachineConfigFile(NULL);
9912 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9913
9914 // now go and copy all the settings data from COM to the settings structures
9915 // (this calles i_saveSettings() on all the COM objects in the machine)
9916 i_copyMachineDataToSettings(*pNewConfig);
9917
9918 if (aFlags & SaveS_ResetCurStateModified)
9919 {
9920 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9921 mData->mCurrentStateModified = FALSE;
9922 fNeedsWrite = true; // always, no need to compare
9923 }
9924 else if (aFlags & SaveS_Force)
9925 {
9926 fNeedsWrite = true; // always, no need to compare
9927 }
9928 else
9929 {
9930 if (!mData->mCurrentStateModified)
9931 {
9932 // do a deep compare of the settings that we just saved with the settings
9933 // previously stored in the config file; this invokes MachineConfigFile::operator==
9934 // which does a deep compare of all the settings, which is expensive but less expensive
9935 // than writing out XML in vain
9936 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9937
9938 // could still be modified if any settings changed
9939 mData->mCurrentStateModified = fAnySettingsChanged;
9940
9941 fNeedsWrite = fAnySettingsChanged;
9942 }
9943 else
9944 fNeedsWrite = true;
9945 }
9946
9947 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9948
9949 if (fNeedsWrite)
9950 // now spit it all out!
9951 pNewConfig->write(mData->m_strConfigFileFull);
9952
9953 mData->pMachineConfigFile = pNewConfig;
9954 delete pOldConfig;
9955 i_commit();
9956
9957 // after saving settings, we are no longer different from the XML on disk
9958 mData->flModifications = 0;
9959 }
9960 catch (HRESULT err)
9961 {
9962 // we assume that error info is set by the thrower
9963 rc = err;
9964
9965 // restore old config
9966 delete pNewConfig;
9967 mData->pMachineConfigFile = pOldConfig;
9968 }
9969 catch (...)
9970 {
9971 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9972 }
9973
9974 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9975 {
9976 /* Fire the data change event, even on failure (since we've already
9977 * committed all data). This is done only for SessionMachines because
9978 * mutable Machine instances are always not registered (i.e. private
9979 * to the client process that creates them) and thus don't need to
9980 * inform callbacks. */
9981 if (i_isSessionMachine())
9982 mParent->i_onMachineDataChange(mData->mUuid);
9983 }
9984
9985 LogFlowThisFunc(("rc=%08X\n", rc));
9986 LogFlowThisFuncLeave();
9987 return rc;
9988}
9989
9990/**
9991 * Implementation for saving the machine settings into the given
9992 * settings::MachineConfigFile instance. This copies machine extradata
9993 * from the previous machine config file in the instance data, if any.
9994 *
9995 * This gets called from two locations:
9996 *
9997 * -- Machine::i_saveSettings(), during the regular XML writing;
9998 *
9999 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10000 * exported to OVF and we write the VirtualBox proprietary XML
10001 * into a <vbox:Machine> tag.
10002 *
10003 * This routine fills all the fields in there, including snapshots, *except*
10004 * for the following:
10005 *
10006 * -- fCurrentStateModified. There is some special logic associated with that.
10007 *
10008 * The caller can then call MachineConfigFile::write() or do something else
10009 * with it.
10010 *
10011 * Caller must hold the machine lock!
10012 *
10013 * This throws XML errors and HRESULT, so the caller must have a catch block!
10014 */
10015void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10016{
10017 // deep copy extradata
10018 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10019
10020 config.uuid = mData->mUuid;
10021
10022 // copy name, description, OS type, teleport, UTC etc.
10023 config.machineUserData = mUserData->s;
10024
10025 // Encode the Icon Override data from Machine and store on config userdata.
10026 std::vector<BYTE> iconByte;
10027 getIcon(iconByte);
10028 ssize_t cbData = iconByte.size();
10029 if (cbData > 0)
10030 {
10031 ssize_t cchOut = RTBase64EncodedLength(cbData);
10032 Utf8Str strIconData;
10033 strIconData.reserve(cchOut+1);
10034 int vrc = RTBase64Encode(&iconByte.front(), cbData,
10035 strIconData.mutableRaw(), strIconData.capacity(),
10036 NULL);
10037 if (RT_FAILURE(vrc))
10038 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10039 strIconData.jolt();
10040 config.machineUserData.ovIcon = strIconData;
10041 }
10042 else
10043 config.machineUserData.ovIcon.setNull();
10044
10045 if ( mData->mMachineState == MachineState_Saved
10046 || mData->mMachineState == MachineState_Restoring
10047 // when doing certain snapshot operations we may or may not have
10048 // a saved state in the current state, so keep everything as is
10049 || ( ( mData->mMachineState == MachineState_Snapshotting
10050 || mData->mMachineState == MachineState_DeletingSnapshot
10051 || mData->mMachineState == MachineState_RestoringSnapshot)
10052 && (!mSSData->strStateFilePath.isEmpty())
10053 )
10054 )
10055 {
10056 Assert(!mSSData->strStateFilePath.isEmpty());
10057 /* try to make the file name relative to the settings file dir */
10058 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10059 }
10060 else
10061 {
10062 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10063 config.strStateFile.setNull();
10064 }
10065
10066 if (mData->mCurrentSnapshot)
10067 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10068 else
10069 config.uuidCurrentSnapshot.clear();
10070
10071 config.timeLastStateChange = mData->mLastStateChange;
10072 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10073 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10074
10075 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10076 if (FAILED(rc)) throw rc;
10077
10078 rc = i_saveStorageControllers(config.storageMachine);
10079 if (FAILED(rc)) throw rc;
10080
10081 // save machine's media registry if this is VirtualBox 4.0 or later
10082 if (config.canHaveOwnMediaRegistry())
10083 {
10084 // determine machine folder
10085 Utf8Str strMachineFolder = i_getSettingsFileFull();
10086 strMachineFolder.stripFilename();
10087 mParent->i_saveMediaRegistry(config.mediaRegistry,
10088 i_getId(), // only media with registry ID == machine UUID
10089 strMachineFolder);
10090 // this throws HRESULT
10091 }
10092
10093 // save snapshots
10094 rc = i_saveAllSnapshots(config);
10095 if (FAILED(rc)) throw rc;
10096}
10097
10098/**
10099 * Saves all snapshots of the machine into the given machine config file. Called
10100 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10101 * @param config
10102 * @return
10103 */
10104HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10105{
10106 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10107
10108 HRESULT rc = S_OK;
10109
10110 try
10111 {
10112 config.llFirstSnapshot.clear();
10113
10114 if (mData->mFirstSnapshot)
10115 {
10116 // the settings use a list for "the first snapshot"
10117 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10118
10119 // get reference to the snapshot on the list and work on that
10120 // element straight in the list to avoid excessive copying later
10121 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10122 if (FAILED(rc)) throw rc;
10123 }
10124
10125// if (mType == IsSessionMachine)
10126// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10127
10128 }
10129 catch (HRESULT err)
10130 {
10131 /* we assume that error info is set by the thrower */
10132 rc = err;
10133 }
10134 catch (...)
10135 {
10136 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10137 }
10138
10139 return rc;
10140}
10141
10142/**
10143 * Saves the VM hardware configuration. It is assumed that the
10144 * given node is empty.
10145 *
10146 * @param data Reference to the settings object for the hardware config.
10147 * @param pDbg Pointer to the settings object for the debugging config
10148 * which happens to live in mHWData.
10149 * @param pAutostart Pointer to the settings object for the autostart config
10150 * which happens to live in mHWData.
10151 */
10152HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10153 settings::Autostart *pAutostart)
10154{
10155 HRESULT rc = S_OK;
10156
10157 try
10158 {
10159 /* The hardware version attribute (optional).
10160 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10161 if ( mHWData->mHWVersion == "1"
10162 && mSSData->strStateFilePath.isEmpty()
10163 )
10164 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10165 other point needs to be found where this can be done. */
10166
10167 data.strVersion = mHWData->mHWVersion;
10168 data.uuid = mHWData->mHardwareUUID;
10169
10170 // CPU
10171 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10172 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10173 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10174 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10175 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10176 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10177 data.fPAE = !!mHWData->mPAEEnabled;
10178 data.enmLongMode = mHWData->mLongMode;
10179 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10180 data.cCPUs = mHWData->mCPUCount;
10181 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10182 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10183 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10184
10185 data.llCpus.clear();
10186 if (data.fCpuHotPlug)
10187 {
10188 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10189 {
10190 if (mHWData->mCPUAttached[idx])
10191 {
10192 settings::Cpu cpu;
10193 cpu.ulId = idx;
10194 data.llCpus.push_back(cpu);
10195 }
10196 }
10197 }
10198
10199 /* Standard and Extended CPUID leafs. */
10200 data.llCpuIdLeafs.clear();
10201 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10202 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10203 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10204 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10205 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10206 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10207
10208 // memory
10209 data.ulMemorySizeMB = mHWData->mMemorySize;
10210 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10211
10212 // firmware
10213 data.firmwareType = mHWData->mFirmwareType;
10214
10215 // HID
10216 data.pointingHIDType = mHWData->mPointingHIDType;
10217 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10218
10219 // chipset
10220 data.chipsetType = mHWData->mChipsetType;
10221
10222 // paravirt
10223 data.paravirtProvider = mHWData->mParavirtProvider;
10224 data.strParavirtDebug = mHWData->mParavirtDebug;
10225
10226 // emulated USB card reader
10227 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10228
10229 // HPET
10230 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10231
10232 // boot order
10233 data.mapBootOrder.clear();
10234 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10235 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10236
10237 // display
10238 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10239 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10240 data.cMonitors = mHWData->mMonitorCount;
10241 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10242 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10243 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10244 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10245 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10246 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10247 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10248 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10249 {
10250 if (mHWData->maVideoCaptureScreens[i])
10251 ASMBitSet(&data.u64VideoCaptureScreens, i);
10252 else
10253 ASMBitClear(&data.u64VideoCaptureScreens, i);
10254 }
10255 /* store relative video capture file if possible */
10256 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10257
10258 /* VRDEServer settings (optional) */
10259 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10260 if (FAILED(rc)) throw rc;
10261
10262 /* BIOS (required) */
10263 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10264 if (FAILED(rc)) throw rc;
10265
10266 /* USB Controller (required) */
10267 data.usbSettings.llUSBControllers.clear();
10268 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10269 {
10270 ComObjPtr<USBController> ctrl = *it;
10271 settings::USBController settingsCtrl;
10272
10273 settingsCtrl.strName = ctrl->i_getName();
10274 settingsCtrl.enmType = ctrl->i_getControllerType();
10275
10276 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10277 }
10278
10279 /* USB device filters (required) */
10280 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10281 if (FAILED(rc)) throw rc;
10282
10283 /* Network adapters (required) */
10284 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10285 data.llNetworkAdapters.clear();
10286 /* Write out only the nominal number of network adapters for this
10287 * chipset type. Since Machine::commit() hasn't been called there
10288 * may be extra NIC settings in the vector. */
10289 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10290 {
10291 settings::NetworkAdapter nic;
10292 nic.ulSlot = (uint32_t)slot;
10293 /* paranoia check... must not be NULL, but must not crash either. */
10294 if (mNetworkAdapters[slot])
10295 {
10296 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10297 if (FAILED(rc)) throw rc;
10298
10299 data.llNetworkAdapters.push_back(nic);
10300 }
10301 }
10302
10303 /* Serial ports */
10304 data.llSerialPorts.clear();
10305 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10306 {
10307 settings::SerialPort s;
10308 s.ulSlot = slot;
10309 rc = mSerialPorts[slot]->i_saveSettings(s);
10310 if (FAILED(rc)) return rc;
10311
10312 data.llSerialPorts.push_back(s);
10313 }
10314
10315 /* Parallel ports */
10316 data.llParallelPorts.clear();
10317 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10318 {
10319 settings::ParallelPort p;
10320 p.ulSlot = slot;
10321 rc = mParallelPorts[slot]->i_saveSettings(p);
10322 if (FAILED(rc)) return rc;
10323
10324 data.llParallelPorts.push_back(p);
10325 }
10326
10327 /* Audio adapter */
10328 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10329 if (FAILED(rc)) return rc;
10330
10331 /* Shared folders */
10332 data.llSharedFolders.clear();
10333 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10334 it != mHWData->mSharedFolders.end();
10335 ++it)
10336 {
10337 SharedFolder *pSF = *it;
10338 AutoCaller sfCaller(pSF);
10339 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10340 settings::SharedFolder sf;
10341 sf.strName = pSF->i_getName();
10342 sf.strHostPath = pSF->i_getHostPath();
10343 sf.fWritable = !!pSF->i_isWritable();
10344 sf.fAutoMount = !!pSF->i_isAutoMounted();
10345
10346 data.llSharedFolders.push_back(sf);
10347 }
10348
10349 // clipboard
10350 data.clipboardMode = mHWData->mClipboardMode;
10351
10352 // drag'n'drop
10353 data.dndMode = mHWData->mDnDMode;
10354
10355 /* Guest */
10356 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10357
10358 // IO settings
10359 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10360 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10361
10362 /* BandwidthControl (required) */
10363 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10364 if (FAILED(rc)) throw rc;
10365
10366 /* Host PCI devices */
10367 data.pciAttachments.clear();
10368 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10369 it != mHWData->mPCIDeviceAssignments.end();
10370 ++it)
10371 {
10372 ComObjPtr<PCIDeviceAttachment> pda = *it;
10373 settings::HostPCIDeviceAttachment hpda;
10374
10375 rc = pda->i_saveSettings(hpda);
10376 if (FAILED(rc)) throw rc;
10377
10378 data.pciAttachments.push_back(hpda);
10379 }
10380
10381
10382 // guest properties
10383 data.llGuestProperties.clear();
10384#ifdef VBOX_WITH_GUEST_PROPS
10385 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10386 it != mHWData->mGuestProperties.end();
10387 ++it)
10388 {
10389 HWData::GuestProperty property = it->second;
10390
10391 /* Remove transient guest properties at shutdown unless we
10392 * are saving state. Note that restoring snapshot intentionally
10393 * keeps them, they will be removed if appropriate once the final
10394 * machine state is set (as crashes etc. need to work). */
10395 if ( ( mData->mMachineState == MachineState_PoweredOff
10396 || mData->mMachineState == MachineState_Aborted
10397 || mData->mMachineState == MachineState_Teleported)
10398 && ( property.mFlags & guestProp::TRANSIENT
10399 || property.mFlags & guestProp::TRANSRESET))
10400 continue;
10401 settings::GuestProperty prop;
10402 prop.strName = it->first;
10403 prop.strValue = property.strValue;
10404 prop.timestamp = property.mTimestamp;
10405 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10406 guestProp::writeFlags(property.mFlags, szFlags);
10407 prop.strFlags = szFlags;
10408
10409 data.llGuestProperties.push_back(prop);
10410 }
10411
10412 /* I presume this doesn't require a backup(). */
10413 mData->mGuestPropertiesModified = FALSE;
10414#endif /* VBOX_WITH_GUEST_PROPS defined */
10415
10416 *pDbg = mHWData->mDebugging;
10417 *pAutostart = mHWData->mAutostart;
10418
10419 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10420 }
10421 catch(std::bad_alloc &)
10422 {
10423 return E_OUTOFMEMORY;
10424 }
10425
10426 AssertComRC(rc);
10427 return rc;
10428}
10429
10430/**
10431 * Saves the storage controller configuration.
10432 *
10433 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10434 */
10435HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10436{
10437 data.llStorageControllers.clear();
10438
10439 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10440 it != mStorageControllers->end();
10441 ++it)
10442 {
10443 HRESULT rc;
10444 ComObjPtr<StorageController> pCtl = *it;
10445
10446 settings::StorageController ctl;
10447 ctl.strName = pCtl->i_getName();
10448 ctl.controllerType = pCtl->i_getControllerType();
10449 ctl.storageBus = pCtl->i_getStorageBus();
10450 ctl.ulInstance = pCtl->i_getInstance();
10451 ctl.fBootable = pCtl->i_getBootable();
10452
10453 /* Save the port count. */
10454 ULONG portCount;
10455 rc = pCtl->COMGETTER(PortCount)(&portCount);
10456 ComAssertComRCRet(rc, rc);
10457 ctl.ulPortCount = portCount;
10458
10459 /* Save fUseHostIOCache */
10460 BOOL fUseHostIOCache;
10461 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10462 ComAssertComRCRet(rc, rc);
10463 ctl.fUseHostIOCache = !!fUseHostIOCache;
10464
10465 /* save the devices now. */
10466 rc = i_saveStorageDevices(pCtl, ctl);
10467 ComAssertComRCRet(rc, rc);
10468
10469 data.llStorageControllers.push_back(ctl);
10470 }
10471
10472 return S_OK;
10473}
10474
10475/**
10476 * Saves the hard disk configuration.
10477 */
10478HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10479 settings::StorageController &data)
10480{
10481 MediaData::AttachmentList atts;
10482
10483 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10484 if (FAILED(rc)) return rc;
10485
10486 data.llAttachedDevices.clear();
10487 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10488 it != atts.end();
10489 ++it)
10490 {
10491 settings::AttachedDevice dev;
10492 IMediumAttachment *iA = *it;
10493 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10494 Medium *pMedium = pAttach->i_getMedium();
10495
10496 dev.deviceType = pAttach->i_getType();
10497 dev.lPort = pAttach->i_getPort();
10498 dev.lDevice = pAttach->i_getDevice();
10499 dev.fPassThrough = pAttach->i_getPassthrough();
10500 dev.fHotPluggable = pAttach->i_getHotPluggable();
10501 if (pMedium)
10502 {
10503 if (pMedium->i_isHostDrive())
10504 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10505 else
10506 dev.uuid = pMedium->i_getId();
10507 dev.fTempEject = pAttach->i_getTempEject();
10508 dev.fNonRotational = pAttach->i_getNonRotational();
10509 dev.fDiscard = pAttach->i_getDiscard();
10510 }
10511
10512 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10513
10514 data.llAttachedDevices.push_back(dev);
10515 }
10516
10517 return S_OK;
10518}
10519
10520/**
10521 * Saves machine state settings as defined by aFlags
10522 * (SaveSTS_* values).
10523 *
10524 * @param aFlags Combination of SaveSTS_* flags.
10525 *
10526 * @note Locks objects for writing.
10527 */
10528HRESULT Machine::i_saveStateSettings(int aFlags)
10529{
10530 if (aFlags == 0)
10531 return S_OK;
10532
10533 AutoCaller autoCaller(this);
10534 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10535
10536 /* This object's write lock is also necessary to serialize file access
10537 * (prevent concurrent reads and writes) */
10538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10539
10540 HRESULT rc = S_OK;
10541
10542 Assert(mData->pMachineConfigFile);
10543
10544 try
10545 {
10546 if (aFlags & SaveSTS_CurStateModified)
10547 mData->pMachineConfigFile->fCurrentStateModified = true;
10548
10549 if (aFlags & SaveSTS_StateFilePath)
10550 {
10551 if (!mSSData->strStateFilePath.isEmpty())
10552 /* try to make the file name relative to the settings file dir */
10553 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10554 else
10555 mData->pMachineConfigFile->strStateFile.setNull();
10556 }
10557
10558 if (aFlags & SaveSTS_StateTimeStamp)
10559 {
10560 Assert( mData->mMachineState != MachineState_Aborted
10561 || mSSData->strStateFilePath.isEmpty());
10562
10563 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10564
10565 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10566//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10567 }
10568
10569 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10570 }
10571 catch (...)
10572 {
10573 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10574 }
10575
10576 return rc;
10577}
10578
10579/**
10580 * Ensures that the given medium is added to a media registry. If this machine
10581 * was created with 4.0 or later, then the machine registry is used. Otherwise
10582 * the global VirtualBox media registry is used.
10583 *
10584 * Caller must NOT hold machine lock, media tree or any medium locks!
10585 *
10586 * @param pMedium
10587 */
10588void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10589{
10590 /* Paranoia checks: do not hold machine or media tree locks. */
10591 AssertReturnVoid(!isWriteLockOnCurrentThread());
10592 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10593
10594 ComObjPtr<Medium> pBase;
10595 {
10596 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10597 pBase = pMedium->i_getBase();
10598 }
10599
10600 /* Paranoia checks: do not hold medium locks. */
10601 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10602 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10603
10604 // decide which medium registry to use now that the medium is attached:
10605 Guid uuid;
10606 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10607 // machine XML is VirtualBox 4.0 or higher:
10608 uuid = i_getId(); // machine UUID
10609 else
10610 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10611
10612 if (pMedium->i_addRegistry(uuid))
10613 mParent->i_markRegistryModified(uuid);
10614
10615 /* For more complex hard disk structures it can happen that the base
10616 * medium isn't yet associated with any medium registry. Do that now. */
10617 if (pMedium != pBase)
10618 {
10619 /* Tree lock needed by Medium::addRegistry when recursing. */
10620 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10621 if (pBase->i_addRegistryRecursive(uuid))
10622 {
10623 treeLock.release();
10624 mParent->i_markRegistryModified(uuid);
10625 }
10626 }
10627}
10628
10629/**
10630 * Creates differencing hard disks for all normal hard disks attached to this
10631 * machine and a new set of attachments to refer to created disks.
10632 *
10633 * Used when taking a snapshot or when deleting the current state. Gets called
10634 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10635 *
10636 * This method assumes that mMediaData contains the original hard disk attachments
10637 * it needs to create diffs for. On success, these attachments will be replaced
10638 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10639 * called to delete created diffs which will also rollback mMediaData and restore
10640 * whatever was backed up before calling this method.
10641 *
10642 * Attachments with non-normal hard disks are left as is.
10643 *
10644 * If @a aOnline is @c false then the original hard disks that require implicit
10645 * diffs will be locked for reading. Otherwise it is assumed that they are
10646 * already locked for writing (when the VM was started). Note that in the latter
10647 * case it is responsibility of the caller to lock the newly created diffs for
10648 * writing if this method succeeds.
10649 *
10650 * @param aProgress Progress object to run (must contain at least as
10651 * many operations left as the number of hard disks
10652 * attached).
10653 * @param aOnline Whether the VM was online prior to this operation.
10654 *
10655 * @note The progress object is not marked as completed, neither on success nor
10656 * on failure. This is a responsibility of the caller.
10657 *
10658 * @note Locks this object and the media tree for writing.
10659 */
10660HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10661 ULONG aWeight,
10662 bool aOnline)
10663{
10664 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10665
10666 AutoCaller autoCaller(this);
10667 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10668
10669 AutoMultiWriteLock2 alock(this->lockHandle(),
10670 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10671
10672 /* must be in a protective state because we release the lock below */
10673 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10674 || mData->mMachineState == MachineState_OnlineSnapshotting
10675 || mData->mMachineState == MachineState_LiveSnapshotting
10676 || mData->mMachineState == MachineState_RestoringSnapshot
10677 || mData->mMachineState == MachineState_DeletingSnapshot
10678 , E_FAIL);
10679
10680 HRESULT rc = S_OK;
10681
10682 // use appropriate locked media map (online or offline)
10683 MediumLockListMap lockedMediaOffline;
10684 MediumLockListMap *lockedMediaMap;
10685 if (aOnline)
10686 lockedMediaMap = &mData->mSession.mLockedMedia;
10687 else
10688 lockedMediaMap = &lockedMediaOffline;
10689
10690 try
10691 {
10692 if (!aOnline)
10693 {
10694 /* lock all attached hard disks early to detect "in use"
10695 * situations before creating actual diffs */
10696 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10697 it != mMediaData->mAttachments.end();
10698 ++it)
10699 {
10700 MediumAttachment* pAtt = *it;
10701 if (pAtt->i_getType() == DeviceType_HardDisk)
10702 {
10703 Medium* pMedium = pAtt->i_getMedium();
10704 Assert(pMedium);
10705
10706 MediumLockList *pMediumLockList(new MediumLockList());
10707 alock.release();
10708 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10709 false /* fMediumLockWrite */,
10710 false /* fMediumLockWriteAll */,
10711 NULL,
10712 *pMediumLockList);
10713 alock.acquire();
10714 if (FAILED(rc))
10715 {
10716 delete pMediumLockList;
10717 throw rc;
10718 }
10719 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10720 if (FAILED(rc))
10721 {
10722 throw setError(rc,
10723 tr("Collecting locking information for all attached media failed"));
10724 }
10725 }
10726 }
10727
10728 /* Now lock all media. If this fails, nothing is locked. */
10729 alock.release();
10730 rc = lockedMediaMap->Lock();
10731 alock.acquire();
10732 if (FAILED(rc))
10733 {
10734 throw setError(rc,
10735 tr("Locking of attached media failed"));
10736 }
10737 }
10738
10739 /* remember the current list (note that we don't use backup() since
10740 * mMediaData may be already backed up) */
10741 MediaData::AttachmentList atts = mMediaData->mAttachments;
10742
10743 /* start from scratch */
10744 mMediaData->mAttachments.clear();
10745
10746 /* go through remembered attachments and create diffs for normal hard
10747 * disks and attach them */
10748 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10749 it != atts.end();
10750 ++it)
10751 {
10752 MediumAttachment* pAtt = *it;
10753
10754 DeviceType_T devType = pAtt->i_getType();
10755 Medium* pMedium = pAtt->i_getMedium();
10756
10757 if ( devType != DeviceType_HardDisk
10758 || pMedium == NULL
10759 || pMedium->i_getType() != MediumType_Normal)
10760 {
10761 /* copy the attachment as is */
10762
10763 /** @todo the progress object created in SessionMachine::TakeSnaphot
10764 * only expects operations for hard disks. Later other
10765 * device types need to show up in the progress as well. */
10766 if (devType == DeviceType_HardDisk)
10767 {
10768 if (pMedium == NULL)
10769 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10770 aWeight); // weight
10771 else
10772 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10773 pMedium->i_getBase()->i_getName().c_str()).raw(),
10774 aWeight); // weight
10775 }
10776
10777 mMediaData->mAttachments.push_back(pAtt);
10778 continue;
10779 }
10780
10781 /* need a diff */
10782 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10783 pMedium->i_getBase()->i_getName().c_str()).raw(),
10784 aWeight); // weight
10785
10786 Utf8Str strFullSnapshotFolder;
10787 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10788
10789 ComObjPtr<Medium> diff;
10790 diff.createObject();
10791 // store the diff in the same registry as the parent
10792 // (this cannot fail here because we can't create implicit diffs for
10793 // unregistered images)
10794 Guid uuidRegistryParent;
10795 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10796 Assert(fInRegistry); NOREF(fInRegistry);
10797 rc = diff->init(mParent,
10798 pMedium->i_getPreferredDiffFormat(),
10799 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10800 uuidRegistryParent,
10801 DeviceType_HardDisk);
10802 if (FAILED(rc)) throw rc;
10803
10804 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10805 * the push_back? Looks like we're going to release medium with the
10806 * wrong kind of lock (general issue with if we fail anywhere at all)
10807 * and an orphaned VDI in the snapshots folder. */
10808
10809 /* update the appropriate lock list */
10810 MediumLockList *pMediumLockList;
10811 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10812 AssertComRCThrowRC(rc);
10813 if (aOnline)
10814 {
10815 alock.release();
10816 /* The currently attached medium will be read-only, change
10817 * the lock type to read. */
10818 rc = pMediumLockList->Update(pMedium, false);
10819 alock.acquire();
10820 AssertComRCThrowRC(rc);
10821 }
10822
10823 /* release the locks before the potentially lengthy operation */
10824 alock.release();
10825 rc = pMedium->i_createDiffStorage(diff,
10826 pMedium->i_getPreferredDiffVariant(),
10827 pMediumLockList,
10828 NULL /* aProgress */,
10829 true /* aWait */);
10830 alock.acquire();
10831 if (FAILED(rc)) throw rc;
10832
10833 /* actual lock list update is done in Medium::commitMedia */
10834
10835 rc = diff->i_addBackReference(mData->mUuid);
10836 AssertComRCThrowRC(rc);
10837
10838 /* add a new attachment */
10839 ComObjPtr<MediumAttachment> attachment;
10840 attachment.createObject();
10841 rc = attachment->init(this,
10842 diff,
10843 pAtt->i_getControllerName(),
10844 pAtt->i_getPort(),
10845 pAtt->i_getDevice(),
10846 DeviceType_HardDisk,
10847 true /* aImplicit */,
10848 false /* aPassthrough */,
10849 false /* aTempEject */,
10850 pAtt->i_getNonRotational(),
10851 pAtt->i_getDiscard(),
10852 pAtt->i_getHotPluggable(),
10853 pAtt->i_getBandwidthGroup());
10854 if (FAILED(rc)) throw rc;
10855
10856 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10857 AssertComRCThrowRC(rc);
10858 mMediaData->mAttachments.push_back(attachment);
10859 }
10860 }
10861 catch (HRESULT aRC) { rc = aRC; }
10862
10863 /* unlock all hard disks we locked when there is no VM */
10864 if (!aOnline)
10865 {
10866 ErrorInfoKeeper eik;
10867
10868 HRESULT rc1 = lockedMediaMap->Clear();
10869 AssertComRC(rc1);
10870 }
10871
10872 return rc;
10873}
10874
10875/**
10876 * Deletes implicit differencing hard disks created either by
10877 * #i_createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10878 *
10879 * Note that to delete hard disks created by #AttachDevice() this method is
10880 * called from #fixupMedia() when the changes are rolled back.
10881 *
10882 * @note Locks this object and the media tree for writing.
10883 */
10884HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10885{
10886 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10887
10888 AutoCaller autoCaller(this);
10889 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10890
10891 AutoMultiWriteLock2 alock(this->lockHandle(),
10892 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10893
10894 /* We absolutely must have backed up state. */
10895 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10896
10897 /* Check if there are any implicitly created diff images. */
10898 bool fImplicitDiffs = false;
10899 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10900 it != mMediaData->mAttachments.end();
10901 ++it)
10902 {
10903 const ComObjPtr<MediumAttachment> &pAtt = *it;
10904 if (pAtt->i_isImplicit())
10905 {
10906 fImplicitDiffs = true;
10907 break;
10908 }
10909 }
10910 /* If there is nothing to do, leave early. This saves lots of image locking
10911 * effort. It also avoids a MachineStateChanged event without real reason.
10912 * This is important e.g. when loading a VM config, because there should be
10913 * no events. Otherwise API clients can become thoroughly confused for
10914 * inaccessible VMs (the code for loading VM configs uses this method for
10915 * cleanup if the config makes no sense), as they take such events as an
10916 * indication that the VM is alive, and they would force the VM config to
10917 * be reread, leading to an endless loop. */
10918 if (!fImplicitDiffs)
10919 return S_OK;
10920
10921 HRESULT rc = S_OK;
10922 MachineState_T oldState = mData->mMachineState;
10923
10924 /* will release the lock before the potentially lengthy operation,
10925 * so protect with the special state (unless already protected) */
10926 if ( oldState != MachineState_Snapshotting
10927 && oldState != MachineState_OnlineSnapshotting
10928 && oldState != MachineState_LiveSnapshotting
10929 && oldState != MachineState_RestoringSnapshot
10930 && oldState != MachineState_DeletingSnapshot
10931 && oldState != MachineState_DeletingSnapshotOnline
10932 && oldState != MachineState_DeletingSnapshotPaused
10933 )
10934 i_setMachineState(MachineState_SettingUp);
10935
10936 // use appropriate locked media map (online or offline)
10937 MediumLockListMap lockedMediaOffline;
10938 MediumLockListMap *lockedMediaMap;
10939 if (aOnline)
10940 lockedMediaMap = &mData->mSession.mLockedMedia;
10941 else
10942 lockedMediaMap = &lockedMediaOffline;
10943
10944 try
10945 {
10946 if (!aOnline)
10947 {
10948 /* lock all attached hard disks early to detect "in use"
10949 * situations before deleting actual diffs */
10950 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10951 it != mMediaData->mAttachments.end();
10952 ++it)
10953 {
10954 MediumAttachment* pAtt = *it;
10955 if (pAtt->i_getType() == DeviceType_HardDisk)
10956 {
10957 Medium* pMedium = pAtt->i_getMedium();
10958 Assert(pMedium);
10959
10960 MediumLockList *pMediumLockList(new MediumLockList());
10961 alock.release();
10962 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10963 false /* fMediumLockWrite */,
10964 false /* fMediumLockWriteAll */,
10965 NULL,
10966 *pMediumLockList);
10967 alock.acquire();
10968
10969 if (FAILED(rc))
10970 {
10971 delete pMediumLockList;
10972 throw rc;
10973 }
10974
10975 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10976 if (FAILED(rc))
10977 throw rc;
10978 }
10979 }
10980
10981 if (FAILED(rc))
10982 throw rc;
10983 } // end of offline
10984
10985 /* Lock lists are now up to date and include implicitly created media */
10986
10987 /* Go through remembered attachments and delete all implicitly created
10988 * diffs and fix up the attachment information */
10989 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10990 MediaData::AttachmentList implicitAtts;
10991 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10992 it != mMediaData->mAttachments.end();
10993 ++it)
10994 {
10995 ComObjPtr<MediumAttachment> pAtt = *it;
10996 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10997 if (pMedium.isNull())
10998 continue;
10999
11000 // Implicit attachments go on the list for deletion and back references are removed.
11001 if (pAtt->i_isImplicit())
11002 {
11003 /* Deassociate and mark for deletion */
11004 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11005 rc = pMedium->i_removeBackReference(mData->mUuid);
11006 if (FAILED(rc))
11007 throw rc;
11008 implicitAtts.push_back(pAtt);
11009 continue;
11010 }
11011
11012 /* Was this medium attached before? */
11013 if (!i_findAttachment(oldAtts, pMedium))
11014 {
11015 /* no: de-associate */
11016 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11017 rc = pMedium->i_removeBackReference(mData->mUuid);
11018 if (FAILED(rc))
11019 throw rc;
11020 continue;
11021 }
11022 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11023 }
11024
11025 /* If there are implicit attachments to delete, throw away the lock
11026 * map contents (which will unlock all media) since the medium
11027 * attachments will be rolled back. Below we need to completely
11028 * recreate the lock map anyway since it is infinitely complex to
11029 * do this incrementally (would need reconstructing each attachment
11030 * change, which would be extremely hairy). */
11031 if (implicitAtts.size() != 0)
11032 {
11033 ErrorInfoKeeper eik;
11034
11035 HRESULT rc1 = lockedMediaMap->Clear();
11036 AssertComRC(rc1);
11037 }
11038
11039 /* rollback hard disk changes */
11040 mMediaData.rollback();
11041
11042 MultiResult mrc(S_OK);
11043
11044 // Delete unused implicit diffs.
11045 if (implicitAtts.size() != 0)
11046 {
11047 alock.release();
11048
11049 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11050 {
11051 // Remove medium associated with this attachment.
11052 ComObjPtr<MediumAttachment> pAtt = *it;
11053 Assert(pAtt);
11054 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11055 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11056 Assert(pMedium);
11057
11058 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11059 // continue on delete failure, just collect error messages
11060 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11061 pMedium->i_getLocationFull().c_str() ));
11062 mrc = rc;
11063 }
11064 // Clear the list of deleted implicit attachments now, while not
11065 // holding the lock, as it will ultimately trigger Medium::uninit()
11066 // calls which assume that the media tree lock isn't held.
11067 implicitAtts.clear();
11068
11069 alock.acquire();
11070
11071 /* if there is a VM recreate media lock map as mentioned above,
11072 * otherwise it is a waste of time and we leave things unlocked */
11073 if (aOnline)
11074 {
11075 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11076 /* must never be NULL, but better safe than sorry */
11077 if (!pMachine.isNull())
11078 {
11079 alock.release();
11080 rc = mData->mSession.mMachine->i_lockMedia();
11081 alock.acquire();
11082 if (FAILED(rc))
11083 throw rc;
11084 }
11085 }
11086 }
11087 }
11088 catch (HRESULT aRC) {rc = aRC;}
11089
11090 if (mData->mMachineState == MachineState_SettingUp)
11091 i_setMachineState(oldState);
11092
11093 /* unlock all hard disks we locked when there is no VM */
11094 if (!aOnline)
11095 {
11096 ErrorInfoKeeper eik;
11097
11098 HRESULT rc1 = lockedMediaMap->Clear();
11099 AssertComRC(rc1);
11100 }
11101
11102 return rc;
11103}
11104
11105
11106/**
11107 * Looks through the given list of media attachments for one with the given parameters
11108 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11109 * can be searched as well if needed.
11110 *
11111 * @param list
11112 * @param aControllerName
11113 * @param aControllerPort
11114 * @param aDevice
11115 * @return
11116 */
11117MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11118 const Utf8Str &aControllerName,
11119 LONG aControllerPort,
11120 LONG aDevice)
11121{
11122 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11123 {
11124 MediumAttachment *pAttach = *it;
11125 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11126 return pAttach;
11127 }
11128
11129 return NULL;
11130}
11131
11132/**
11133 * Looks through the given list of media attachments for one with the given parameters
11134 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11135 * can be searched as well if needed.
11136 *
11137 * @param list
11138 * @param aControllerName
11139 * @param aControllerPort
11140 * @param aDevice
11141 * @return
11142 */
11143MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11144 ComObjPtr<Medium> pMedium)
11145{
11146 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11147 {
11148 MediumAttachment *pAttach = *it;
11149 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11150 if (pMediumThis == pMedium)
11151 return pAttach;
11152 }
11153
11154 return NULL;
11155}
11156
11157/**
11158 * Looks through the given list of media attachments for one with the given parameters
11159 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11160 * can be searched as well if needed.
11161 *
11162 * @param list
11163 * @param aControllerName
11164 * @param aControllerPort
11165 * @param aDevice
11166 * @return
11167 */
11168MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11169 Guid &id)
11170{
11171 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11172 {
11173 MediumAttachment *pAttach = *it;
11174 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11175 if (pMediumThis->i_getId() == id)
11176 return pAttach;
11177 }
11178
11179 return NULL;
11180}
11181
11182/**
11183 * Main implementation for Machine::DetachDevice. This also gets called
11184 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11185 *
11186 * @param pAttach Medium attachment to detach.
11187 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11188 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11189 * SnapshotMachine, and this must be its snapshot.
11190 * @return
11191 */
11192HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11193 AutoWriteLock &writeLock,
11194 Snapshot *pSnapshot)
11195{
11196 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11197 DeviceType_T mediumType = pAttach->i_getType();
11198
11199 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11200
11201 if (pAttach->i_isImplicit())
11202 {
11203 /* attempt to implicitly delete the implicitly created diff */
11204
11205 /// @todo move the implicit flag from MediumAttachment to Medium
11206 /// and forbid any hard disk operation when it is implicit. Or maybe
11207 /// a special media state for it to make it even more simple.
11208
11209 Assert(mMediaData.isBackedUp());
11210
11211 /* will release the lock before the potentially lengthy operation, so
11212 * protect with the special state */
11213 MachineState_T oldState = mData->mMachineState;
11214 i_setMachineState(MachineState_SettingUp);
11215
11216 writeLock.release();
11217
11218 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11219 true /*aWait*/);
11220
11221 writeLock.acquire();
11222
11223 i_setMachineState(oldState);
11224
11225 if (FAILED(rc)) return rc;
11226 }
11227
11228 i_setModified(IsModified_Storage);
11229 mMediaData.backup();
11230 mMediaData->mAttachments.remove(pAttach);
11231
11232 if (!oldmedium.isNull())
11233 {
11234 // if this is from a snapshot, do not defer detachment to commitMedia()
11235 if (pSnapshot)
11236 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11237 // else if non-hard disk media, do not defer detachment to commitMedia() either
11238 else if (mediumType != DeviceType_HardDisk)
11239 oldmedium->i_removeBackReference(mData->mUuid);
11240 }
11241
11242 return S_OK;
11243}
11244
11245/**
11246 * Goes thru all media of the given list and
11247 *
11248 * 1) calls i_detachDevice() on each of them for this machine and
11249 * 2) adds all Medium objects found in the process to the given list,
11250 * depending on cleanupMode.
11251 *
11252 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11253 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11254 * media to the list.
11255 *
11256 * This gets called from Machine::Unregister, both for the actual Machine and
11257 * the SnapshotMachine objects that might be found in the snapshots.
11258 *
11259 * Requires caller and locking. The machine lock must be passed in because it
11260 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11261 *
11262 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11263 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11264 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11265 * Full, then all media get added;
11266 * otherwise no media get added.
11267 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11268 * @return
11269 */
11270HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11271 Snapshot *pSnapshot,
11272 CleanupMode_T cleanupMode,
11273 MediaList &llMedia)
11274{
11275 Assert(isWriteLockOnCurrentThread());
11276
11277 HRESULT rc;
11278
11279 // make a temporary list because i_detachDevice invalidates iterators into
11280 // mMediaData->mAttachments
11281 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11282
11283 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11284 {
11285 ComObjPtr<MediumAttachment> &pAttach = *it;
11286 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11287
11288 if (!pMedium.isNull())
11289 {
11290 AutoCaller mac(pMedium);
11291 if (FAILED(mac.rc())) return mac.rc();
11292 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11293 DeviceType_T devType = pMedium->i_getDeviceType();
11294 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11295 && devType == DeviceType_HardDisk)
11296 || (cleanupMode == CleanupMode_Full)
11297 )
11298 {
11299 llMedia.push_back(pMedium);
11300 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11301 /* Not allowed to keep this lock as below we need the parent
11302 * medium lock, and the lock order is parent to child. */
11303 lock.release();
11304 /*
11305 * Search for medias which are not attached to any machine, but
11306 * in the chain to an attached disk. Mediums are only consided
11307 * if they are:
11308 * - have only one child
11309 * - no references to any machines
11310 * - are of normal medium type
11311 */
11312 while (!pParent.isNull())
11313 {
11314 AutoCaller mac1(pParent);
11315 if (FAILED(mac1.rc())) return mac1.rc();
11316 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11317 if (pParent->i_getChildren().size() == 1)
11318 {
11319 if ( pParent->i_getMachineBackRefCount() == 0
11320 && pParent->i_getType() == MediumType_Normal
11321 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11322 llMedia.push_back(pParent);
11323 }
11324 else
11325 break;
11326 pParent = pParent->i_getParent();
11327 }
11328 }
11329 }
11330
11331 // real machine: then we need to use the proper method
11332 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11333
11334 if (FAILED(rc))
11335 return rc;
11336 }
11337
11338 return S_OK;
11339}
11340
11341/**
11342 * Perform deferred hard disk detachments.
11343 *
11344 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11345 * backed up).
11346 *
11347 * If @a aOnline is @c true then this method will also unlock the old hard disks
11348 * for which the new implicit diffs were created and will lock these new diffs for
11349 * writing.
11350 *
11351 * @param aOnline Whether the VM was online prior to this operation.
11352 *
11353 * @note Locks this object for writing!
11354 */
11355void Machine::i_commitMedia(bool aOnline /*= false*/)
11356{
11357 AutoCaller autoCaller(this);
11358 AssertComRCReturnVoid(autoCaller.rc());
11359
11360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11361
11362 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11363
11364 HRESULT rc = S_OK;
11365
11366 /* no attach/detach operations -- nothing to do */
11367 if (!mMediaData.isBackedUp())
11368 return;
11369
11370 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11371 bool fMediaNeedsLocking = false;
11372
11373 /* enumerate new attachments */
11374 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11375 it != mMediaData->mAttachments.end();
11376 ++it)
11377 {
11378 MediumAttachment *pAttach = *it;
11379
11380 pAttach->i_commit();
11381
11382 Medium* pMedium = pAttach->i_getMedium();
11383 bool fImplicit = pAttach->i_isImplicit();
11384
11385 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11386 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11387 fImplicit));
11388
11389 /** @todo convert all this Machine-based voodoo to MediumAttachment
11390 * based commit logic. */
11391 if (fImplicit)
11392 {
11393 /* convert implicit attachment to normal */
11394 pAttach->i_setImplicit(false);
11395
11396 if ( aOnline
11397 && pMedium
11398 && pAttach->i_getType() == DeviceType_HardDisk
11399 )
11400 {
11401 /* update the appropriate lock list */
11402 MediumLockList *pMediumLockList;
11403 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11404 AssertComRC(rc);
11405 if (pMediumLockList)
11406 {
11407 /* unlock if there's a need to change the locking */
11408 if (!fMediaNeedsLocking)
11409 {
11410 rc = mData->mSession.mLockedMedia.Unlock();
11411 AssertComRC(rc);
11412 fMediaNeedsLocking = true;
11413 }
11414 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11415 AssertComRC(rc);
11416 rc = pMediumLockList->Append(pMedium, true);
11417 AssertComRC(rc);
11418 }
11419 }
11420
11421 continue;
11422 }
11423
11424 if (pMedium)
11425 {
11426 /* was this medium attached before? */
11427 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11428 {
11429 MediumAttachment *pOldAttach = *oldIt;
11430 if (pOldAttach->i_getMedium() == pMedium)
11431 {
11432 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11433
11434 /* yes: remove from old to avoid de-association */
11435 oldAtts.erase(oldIt);
11436 break;
11437 }
11438 }
11439 }
11440 }
11441
11442 /* enumerate remaining old attachments and de-associate from the
11443 * current machine state */
11444 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11445 {
11446 MediumAttachment *pAttach = *it;
11447 Medium* pMedium = pAttach->i_getMedium();
11448
11449 /* Detach only hard disks, since DVD/floppy media is detached
11450 * instantly in MountMedium. */
11451 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11452 {
11453 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11454
11455 /* now de-associate from the current machine state */
11456 rc = pMedium->i_removeBackReference(mData->mUuid);
11457 AssertComRC(rc);
11458
11459 if (aOnline)
11460 {
11461 /* unlock since medium is not used anymore */
11462 MediumLockList *pMediumLockList;
11463 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11464 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11465 {
11466 /* this happens for online snapshots, there the attachment
11467 * is changing, but only to a diff image created under
11468 * the old one, so there is no separate lock list */
11469 Assert(!pMediumLockList);
11470 }
11471 else
11472 {
11473 AssertComRC(rc);
11474 if (pMediumLockList)
11475 {
11476 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11477 AssertComRC(rc);
11478 }
11479 }
11480 }
11481 }
11482 }
11483
11484 /* take media locks again so that the locking state is consistent */
11485 if (fMediaNeedsLocking)
11486 {
11487 Assert(aOnline);
11488 rc = mData->mSession.mLockedMedia.Lock();
11489 AssertComRC(rc);
11490 }
11491
11492 /* commit the hard disk changes */
11493 mMediaData.commit();
11494
11495 if (i_isSessionMachine())
11496 {
11497 /*
11498 * Update the parent machine to point to the new owner.
11499 * This is necessary because the stored parent will point to the
11500 * session machine otherwise and cause crashes or errors later
11501 * when the session machine gets invalid.
11502 */
11503 /** @todo Change the MediumAttachment class to behave like any other
11504 * class in this regard by creating peer MediumAttachment
11505 * objects for session machines and share the data with the peer
11506 * machine.
11507 */
11508 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11509 it != mMediaData->mAttachments.end();
11510 ++it)
11511 (*it)->i_updateParentMachine(mPeer);
11512
11513 /* attach new data to the primary machine and reshare it */
11514 mPeer->mMediaData.attach(mMediaData);
11515 }
11516
11517 return;
11518}
11519
11520/**
11521 * Perform deferred deletion of implicitly created diffs.
11522 *
11523 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11524 * backed up).
11525 *
11526 * @note Locks this object for writing!
11527 */
11528void Machine::i_rollbackMedia()
11529{
11530 AutoCaller autoCaller(this);
11531 AssertComRCReturnVoid(autoCaller.rc());
11532
11533 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11534 LogFlowThisFunc(("Entering rollbackMedia\n"));
11535
11536 HRESULT rc = S_OK;
11537
11538 /* no attach/detach operations -- nothing to do */
11539 if (!mMediaData.isBackedUp())
11540 return;
11541
11542 /* enumerate new attachments */
11543 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11544 it != mMediaData->mAttachments.end();
11545 ++it)
11546 {
11547 MediumAttachment *pAttach = *it;
11548 /* Fix up the backrefs for DVD/floppy media. */
11549 if (pAttach->i_getType() != DeviceType_HardDisk)
11550 {
11551 Medium* pMedium = pAttach->i_getMedium();
11552 if (pMedium)
11553 {
11554 rc = pMedium->i_removeBackReference(mData->mUuid);
11555 AssertComRC(rc);
11556 }
11557 }
11558
11559 (*it)->i_rollback();
11560
11561 pAttach = *it;
11562 /* Fix up the backrefs for DVD/floppy media. */
11563 if (pAttach->i_getType() != DeviceType_HardDisk)
11564 {
11565 Medium* pMedium = pAttach->i_getMedium();
11566 if (pMedium)
11567 {
11568 rc = pMedium->i_addBackReference(mData->mUuid);
11569 AssertComRC(rc);
11570 }
11571 }
11572 }
11573
11574 /** @todo convert all this Machine-based voodoo to MediumAttachment
11575 * based rollback logic. */
11576 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11577
11578 return;
11579}
11580
11581/**
11582 * Returns true if the settings file is located in the directory named exactly
11583 * as the machine; this means, among other things, that the machine directory
11584 * should be auto-renamed.
11585 *
11586 * @param aSettingsDir if not NULL, the full machine settings file directory
11587 * name will be assigned there.
11588 *
11589 * @note Doesn't lock anything.
11590 * @note Not thread safe (must be called from this object's lock).
11591 */
11592bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11593{
11594 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11595 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11596 if (aSettingsDir)
11597 *aSettingsDir = strMachineDirName;
11598 strMachineDirName.stripPath(); // vmname
11599 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11600 strConfigFileOnly.stripPath() // vmname.vbox
11601 .stripSuffix(); // vmname
11602 /** @todo hack, make somehow use of ComposeMachineFilename */
11603 if (mUserData->s.fDirectoryIncludesUUID)
11604 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11605
11606 AssertReturn(!strMachineDirName.isEmpty(), false);
11607 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11608
11609 return strMachineDirName == strConfigFileOnly;
11610}
11611
11612/**
11613 * Discards all changes to machine settings.
11614 *
11615 * @param aNotify Whether to notify the direct session about changes or not.
11616 *
11617 * @note Locks objects for writing!
11618 */
11619void Machine::i_rollback(bool aNotify)
11620{
11621 AutoCaller autoCaller(this);
11622 AssertComRCReturn(autoCaller.rc(), (void)0);
11623
11624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11625
11626 if (!mStorageControllers.isNull())
11627 {
11628 if (mStorageControllers.isBackedUp())
11629 {
11630 /* unitialize all new devices (absent in the backed up list). */
11631 StorageControllerList::const_iterator it = mStorageControllers->begin();
11632 StorageControllerList *backedList = mStorageControllers.backedUpData();
11633 while (it != mStorageControllers->end())
11634 {
11635 if ( std::find(backedList->begin(), backedList->end(), *it)
11636 == backedList->end()
11637 )
11638 {
11639 (*it)->uninit();
11640 }
11641 ++it;
11642 }
11643
11644 /* restore the list */
11645 mStorageControllers.rollback();
11646 }
11647
11648 /* rollback any changes to devices after restoring the list */
11649 if (mData->flModifications & IsModified_Storage)
11650 {
11651 StorageControllerList::const_iterator it = mStorageControllers->begin();
11652 while (it != mStorageControllers->end())
11653 {
11654 (*it)->i_rollback();
11655 ++it;
11656 }
11657 }
11658 }
11659
11660 if (!mUSBControllers.isNull())
11661 {
11662 if (mUSBControllers.isBackedUp())
11663 {
11664 /* unitialize all new devices (absent in the backed up list). */
11665 USBControllerList::const_iterator it = mUSBControllers->begin();
11666 USBControllerList *backedList = mUSBControllers.backedUpData();
11667 while (it != mUSBControllers->end())
11668 {
11669 if ( std::find(backedList->begin(), backedList->end(), *it)
11670 == backedList->end()
11671 )
11672 {
11673 (*it)->uninit();
11674 }
11675 ++it;
11676 }
11677
11678 /* restore the list */
11679 mUSBControllers.rollback();
11680 }
11681
11682 /* rollback any changes to devices after restoring the list */
11683 if (mData->flModifications & IsModified_USB)
11684 {
11685 USBControllerList::const_iterator it = mUSBControllers->begin();
11686 while (it != mUSBControllers->end())
11687 {
11688 (*it)->i_rollback();
11689 ++it;
11690 }
11691 }
11692 }
11693
11694 mUserData.rollback();
11695
11696 mHWData.rollback();
11697
11698 if (mData->flModifications & IsModified_Storage)
11699 i_rollbackMedia();
11700
11701 if (mBIOSSettings)
11702 mBIOSSettings->i_rollback();
11703
11704 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11705 mVRDEServer->i_rollback();
11706
11707 if (mAudioAdapter)
11708 mAudioAdapter->i_rollback();
11709
11710 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11711 mUSBDeviceFilters->i_rollback();
11712
11713 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11714 mBandwidthControl->i_rollback();
11715
11716 if (!mHWData.isNull())
11717 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11718 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11719 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11720 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11721
11722 if (mData->flModifications & IsModified_NetworkAdapters)
11723 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11724 if ( mNetworkAdapters[slot]
11725 && mNetworkAdapters[slot]->i_isModified())
11726 {
11727 mNetworkAdapters[slot]->i_rollback();
11728 networkAdapters[slot] = mNetworkAdapters[slot];
11729 }
11730
11731 if (mData->flModifications & IsModified_SerialPorts)
11732 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11733 if ( mSerialPorts[slot]
11734 && mSerialPorts[slot]->i_isModified())
11735 {
11736 mSerialPorts[slot]->i_rollback();
11737 serialPorts[slot] = mSerialPorts[slot];
11738 }
11739
11740 if (mData->flModifications & IsModified_ParallelPorts)
11741 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11742 if ( mParallelPorts[slot]
11743 && mParallelPorts[slot]->i_isModified())
11744 {
11745 mParallelPorts[slot]->i_rollback();
11746 parallelPorts[slot] = mParallelPorts[slot];
11747 }
11748
11749 if (aNotify)
11750 {
11751 /* inform the direct session about changes */
11752
11753 ComObjPtr<Machine> that = this;
11754 uint32_t flModifications = mData->flModifications;
11755 alock.release();
11756
11757 if (flModifications & IsModified_SharedFolders)
11758 that->i_onSharedFolderChange();
11759
11760 if (flModifications & IsModified_VRDEServer)
11761 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11762 if (flModifications & IsModified_USB)
11763 that->i_onUSBControllerChange();
11764
11765 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11766 if (networkAdapters[slot])
11767 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11768 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11769 if (serialPorts[slot])
11770 that->i_onSerialPortChange(serialPorts[slot]);
11771 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11772 if (parallelPorts[slot])
11773 that->i_onParallelPortChange(parallelPorts[slot]);
11774
11775 if (flModifications & IsModified_Storage)
11776 that->i_onStorageControllerChange();
11777
11778#if 0
11779 if (flModifications & IsModified_BandwidthControl)
11780 that->onBandwidthControlChange();
11781#endif
11782 }
11783}
11784
11785/**
11786 * Commits all the changes to machine settings.
11787 *
11788 * Note that this operation is supposed to never fail.
11789 *
11790 * @note Locks this object and children for writing.
11791 */
11792void Machine::i_commit()
11793{
11794 AutoCaller autoCaller(this);
11795 AssertComRCReturnVoid(autoCaller.rc());
11796
11797 AutoCaller peerCaller(mPeer);
11798 AssertComRCReturnVoid(peerCaller.rc());
11799
11800 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11801
11802 /*
11803 * use safe commit to ensure Snapshot machines (that share mUserData)
11804 * will still refer to a valid memory location
11805 */
11806 mUserData.commitCopy();
11807
11808 mHWData.commit();
11809
11810 if (mMediaData.isBackedUp())
11811 i_commitMedia(Global::IsOnline(mData->mMachineState));
11812
11813 mBIOSSettings->i_commit();
11814 mVRDEServer->i_commit();
11815 mAudioAdapter->i_commit();
11816 mUSBDeviceFilters->i_commit();
11817 mBandwidthControl->i_commit();
11818
11819 /* Since mNetworkAdapters is a list which might have been changed (resized)
11820 * without using the Backupable<> template we need to handle the copying
11821 * of the list entries manually, including the creation of peers for the
11822 * new objects. */
11823 bool commitNetworkAdapters = false;
11824 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11825 if (mPeer)
11826 {
11827 /* commit everything, even the ones which will go away */
11828 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11829 mNetworkAdapters[slot]->i_commit();
11830 /* copy over the new entries, creating a peer and uninit the original */
11831 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11832 for (size_t slot = 0; slot < newSize; slot++)
11833 {
11834 /* look if this adapter has a peer device */
11835 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11836 if (!peer)
11837 {
11838 /* no peer means the adapter is a newly created one;
11839 * create a peer owning data this data share it with */
11840 peer.createObject();
11841 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11842 }
11843 mPeer->mNetworkAdapters[slot] = peer;
11844 }
11845 /* uninit any no longer needed network adapters */
11846 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11847 mNetworkAdapters[slot]->uninit();
11848 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11849 {
11850 if (mPeer->mNetworkAdapters[slot])
11851 mPeer->mNetworkAdapters[slot]->uninit();
11852 }
11853 /* Keep the original network adapter count until this point, so that
11854 * discarding a chipset type change will not lose settings. */
11855 mNetworkAdapters.resize(newSize);
11856 mPeer->mNetworkAdapters.resize(newSize);
11857 }
11858 else
11859 {
11860 /* we have no peer (our parent is the newly created machine);
11861 * just commit changes to the network adapters */
11862 commitNetworkAdapters = true;
11863 }
11864 if (commitNetworkAdapters)
11865 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11866 mNetworkAdapters[slot]->i_commit();
11867
11868 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11869 mSerialPorts[slot]->i_commit();
11870 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11871 mParallelPorts[slot]->i_commit();
11872
11873 bool commitStorageControllers = false;
11874
11875 if (mStorageControllers.isBackedUp())
11876 {
11877 mStorageControllers.commit();
11878
11879 if (mPeer)
11880 {
11881 /* Commit all changes to new controllers (this will reshare data with
11882 * peers for those who have peers) */
11883 StorageControllerList *newList = new StorageControllerList();
11884 StorageControllerList::const_iterator it = mStorageControllers->begin();
11885 while (it != mStorageControllers->end())
11886 {
11887 (*it)->i_commit();
11888
11889 /* look if this controller has a peer device */
11890 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11891 if (!peer)
11892 {
11893 /* no peer means the device is a newly created one;
11894 * create a peer owning data this device share it with */
11895 peer.createObject();
11896 peer->init(mPeer, *it, true /* aReshare */);
11897 }
11898 else
11899 {
11900 /* remove peer from the old list */
11901 mPeer->mStorageControllers->remove(peer);
11902 }
11903 /* and add it to the new list */
11904 newList->push_back(peer);
11905
11906 ++it;
11907 }
11908
11909 /* uninit old peer's controllers that are left */
11910 it = mPeer->mStorageControllers->begin();
11911 while (it != mPeer->mStorageControllers->end())
11912 {
11913 (*it)->uninit();
11914 ++it;
11915 }
11916
11917 /* attach new list of controllers to our peer */
11918 mPeer->mStorageControllers.attach(newList);
11919 }
11920 else
11921 {
11922 /* we have no peer (our parent is the newly created machine);
11923 * just commit changes to devices */
11924 commitStorageControllers = true;
11925 }
11926 }
11927 else
11928 {
11929 /* the list of controllers itself is not changed,
11930 * just commit changes to controllers themselves */
11931 commitStorageControllers = true;
11932 }
11933
11934 if (commitStorageControllers)
11935 {
11936 StorageControllerList::const_iterator it = mStorageControllers->begin();
11937 while (it != mStorageControllers->end())
11938 {
11939 (*it)->i_commit();
11940 ++it;
11941 }
11942 }
11943
11944 bool commitUSBControllers = false;
11945
11946 if (mUSBControllers.isBackedUp())
11947 {
11948 mUSBControllers.commit();
11949
11950 if (mPeer)
11951 {
11952 /* Commit all changes to new controllers (this will reshare data with
11953 * peers for those who have peers) */
11954 USBControllerList *newList = new USBControllerList();
11955 USBControllerList::const_iterator it = mUSBControllers->begin();
11956 while (it != mUSBControllers->end())
11957 {
11958 (*it)->i_commit();
11959
11960 /* look if this controller has a peer device */
11961 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11962 if (!peer)
11963 {
11964 /* no peer means the device is a newly created one;
11965 * create a peer owning data this device share it with */
11966 peer.createObject();
11967 peer->init(mPeer, *it, true /* aReshare */);
11968 }
11969 else
11970 {
11971 /* remove peer from the old list */
11972 mPeer->mUSBControllers->remove(peer);
11973 }
11974 /* and add it to the new list */
11975 newList->push_back(peer);
11976
11977 ++it;
11978 }
11979
11980 /* uninit old peer's controllers that are left */
11981 it = mPeer->mUSBControllers->begin();
11982 while (it != mPeer->mUSBControllers->end())
11983 {
11984 (*it)->uninit();
11985 ++it;
11986 }
11987
11988 /* attach new list of controllers to our peer */
11989 mPeer->mUSBControllers.attach(newList);
11990 }
11991 else
11992 {
11993 /* we have no peer (our parent is the newly created machine);
11994 * just commit changes to devices */
11995 commitUSBControllers = true;
11996 }
11997 }
11998 else
11999 {
12000 /* the list of controllers itself is not changed,
12001 * just commit changes to controllers themselves */
12002 commitUSBControllers = true;
12003 }
12004
12005 if (commitUSBControllers)
12006 {
12007 USBControllerList::const_iterator it = mUSBControllers->begin();
12008 while (it != mUSBControllers->end())
12009 {
12010 (*it)->i_commit();
12011 ++it;
12012 }
12013 }
12014
12015 if (i_isSessionMachine())
12016 {
12017 /* attach new data to the primary machine and reshare it */
12018 mPeer->mUserData.attach(mUserData);
12019 mPeer->mHWData.attach(mHWData);
12020 /* mMediaData is reshared by fixupMedia */
12021 // mPeer->mMediaData.attach(mMediaData);
12022 Assert(mPeer->mMediaData.data() == mMediaData.data());
12023 }
12024}
12025
12026/**
12027 * Copies all the hardware data from the given machine.
12028 *
12029 * Currently, only called when the VM is being restored from a snapshot. In
12030 * particular, this implies that the VM is not running during this method's
12031 * call.
12032 *
12033 * @note This method must be called from under this object's lock.
12034 *
12035 * @note This method doesn't call #commit(), so all data remains backed up and
12036 * unsaved.
12037 */
12038void Machine::i_copyFrom(Machine *aThat)
12039{
12040 AssertReturnVoid(!i_isSnapshotMachine());
12041 AssertReturnVoid(aThat->i_isSnapshotMachine());
12042
12043 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12044
12045 mHWData.assignCopy(aThat->mHWData);
12046
12047 // create copies of all shared folders (mHWData after attaching a copy
12048 // contains just references to original objects)
12049 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12050 it != mHWData->mSharedFolders.end();
12051 ++it)
12052 {
12053 ComObjPtr<SharedFolder> folder;
12054 folder.createObject();
12055 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12056 AssertComRC(rc);
12057 *it = folder;
12058 }
12059
12060 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12061 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12062 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12063 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12064 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12065
12066 /* create private copies of all controllers */
12067 mStorageControllers.backup();
12068 mStorageControllers->clear();
12069 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12070 it != aThat->mStorageControllers->end();
12071 ++it)
12072 {
12073 ComObjPtr<StorageController> ctrl;
12074 ctrl.createObject();
12075 ctrl->initCopy(this, *it);
12076 mStorageControllers->push_back(ctrl);
12077 }
12078
12079 /* create private copies of all USB controllers */
12080 mUSBControllers.backup();
12081 mUSBControllers->clear();
12082 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12083 it != aThat->mUSBControllers->end();
12084 ++it)
12085 {
12086 ComObjPtr<USBController> ctrl;
12087 ctrl.createObject();
12088 ctrl->initCopy(this, *it);
12089 mUSBControllers->push_back(ctrl);
12090 }
12091
12092 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12093 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12094 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12095 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12096 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12097 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12098 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12099}
12100
12101/**
12102 * Returns whether the given storage controller is hotplug capable.
12103 *
12104 * @returns true if the controller supports hotplugging
12105 * false otherwise.
12106 * @param enmCtrlType The controller type to check for.
12107 */
12108bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12109{
12110 ComPtr<ISystemProperties> systemProperties;
12111 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12112 if (FAILED(rc))
12113 return false;
12114
12115 BOOL aHotplugCapable = FALSE;
12116 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12117
12118 return RT_BOOL(aHotplugCapable);
12119}
12120
12121#ifdef VBOX_WITH_RESOURCE_USAGE_API
12122
12123void Machine::i_getDiskList(MediaList &list)
12124{
12125 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12126 it != mMediaData->mAttachments.end();
12127 ++it)
12128 {
12129 MediumAttachment* pAttach = *it;
12130 /* just in case */
12131 AssertStmt(pAttach, continue);
12132
12133 AutoCaller localAutoCallerA(pAttach);
12134 if (FAILED(localAutoCallerA.rc())) continue;
12135
12136 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12137
12138 if (pAttach->i_getType() == DeviceType_HardDisk)
12139 list.push_back(pAttach->i_getMedium());
12140 }
12141}
12142
12143void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12144{
12145 AssertReturnVoid(isWriteLockOnCurrentThread());
12146 AssertPtrReturnVoid(aCollector);
12147
12148 pm::CollectorHAL *hal = aCollector->getHAL();
12149 /* Create sub metrics */
12150 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12151 "Percentage of processor time spent in user mode by the VM process.");
12152 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12153 "Percentage of processor time spent in kernel mode by the VM process.");
12154 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12155 "Size of resident portion of VM process in memory.");
12156 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12157 "Actual size of all VM disks combined.");
12158 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12159 "Network receive rate.");
12160 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12161 "Network transmit rate.");
12162 /* Create and register base metrics */
12163 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12164 cpuLoadUser, cpuLoadKernel);
12165 aCollector->registerBaseMetric(cpuLoad);
12166 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12167 ramUsageUsed);
12168 aCollector->registerBaseMetric(ramUsage);
12169 MediaList disks;
12170 i_getDiskList(disks);
12171 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12172 diskUsageUsed);
12173 aCollector->registerBaseMetric(diskUsage);
12174
12175 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12176 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12177 new pm::AggregateAvg()));
12178 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12179 new pm::AggregateMin()));
12180 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12181 new pm::AggregateMax()));
12182 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12183 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12184 new pm::AggregateAvg()));
12185 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12186 new pm::AggregateMin()));
12187 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12188 new pm::AggregateMax()));
12189
12190 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12191 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12192 new pm::AggregateAvg()));
12193 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12194 new pm::AggregateMin()));
12195 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12196 new pm::AggregateMax()));
12197
12198 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12199 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12200 new pm::AggregateAvg()));
12201 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12202 new pm::AggregateMin()));
12203 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12204 new pm::AggregateMax()));
12205
12206
12207 /* Guest metrics collector */
12208 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12209 aCollector->registerGuest(mCollectorGuest);
12210 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12211
12212 /* Create sub metrics */
12213 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12214 "Percentage of processor time spent in user mode as seen by the guest.");
12215 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12216 "Percentage of processor time spent in kernel mode as seen by the guest.");
12217 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12218 "Percentage of processor time spent idling as seen by the guest.");
12219
12220 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12221 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12222 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12223 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12224 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12225 pm::SubMetric *guestMemCache = new pm::SubMetric(
12226 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12227
12228 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12229 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12230
12231 /* Create and register base metrics */
12232 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12233 machineNetRx, machineNetTx);
12234 aCollector->registerBaseMetric(machineNetRate);
12235
12236 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12237 guestLoadUser, guestLoadKernel, guestLoadIdle);
12238 aCollector->registerBaseMetric(guestCpuLoad);
12239
12240 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12241 guestMemTotal, guestMemFree,
12242 guestMemBalloon, guestMemShared,
12243 guestMemCache, guestPagedTotal);
12244 aCollector->registerBaseMetric(guestCpuMem);
12245
12246 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12247 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12248 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12249 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12250
12251 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12252 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12253 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12254 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12255
12256 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12257 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12258 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12260
12261 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12262 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12263 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12264 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12265
12266 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12267 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12268 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12269 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12270
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12272 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12275
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12280
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12284 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12285
12286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12287 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12288 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12289 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12290
12291 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12293 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12294 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12295
12296 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12297 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12298 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12299 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12300}
12301
12302void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12303{
12304 AssertReturnVoid(isWriteLockOnCurrentThread());
12305
12306 if (aCollector)
12307 {
12308 aCollector->unregisterMetricsFor(aMachine);
12309 aCollector->unregisterBaseMetricsFor(aMachine);
12310 }
12311}
12312
12313#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12314
12315
12316////////////////////////////////////////////////////////////////////////////////
12317
12318DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12319
12320HRESULT SessionMachine::FinalConstruct()
12321{
12322 LogFlowThisFunc(("\n"));
12323
12324 mClientToken = NULL;
12325
12326 return BaseFinalConstruct();
12327}
12328
12329void SessionMachine::FinalRelease()
12330{
12331 LogFlowThisFunc(("\n"));
12332
12333 Assert(!mClientToken);
12334 /* paranoia, should not hang around any more */
12335 if (mClientToken)
12336 {
12337 delete mClientToken;
12338 mClientToken = NULL;
12339 }
12340
12341 uninit(Uninit::Unexpected);
12342
12343 BaseFinalRelease();
12344}
12345
12346/**
12347 * @note Must be called only by Machine::LockMachine() from its own write lock.
12348 */
12349HRESULT SessionMachine::init(Machine *aMachine)
12350{
12351 LogFlowThisFuncEnter();
12352 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12353
12354 AssertReturn(aMachine, E_INVALIDARG);
12355
12356 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12357
12358 /* Enclose the state transition NotReady->InInit->Ready */
12359 AutoInitSpan autoInitSpan(this);
12360 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12361
12362 HRESULT rc = S_OK;
12363
12364 RT_ZERO(mAuthLibCtx);
12365
12366 /* create the machine client token */
12367 try
12368 {
12369 mClientToken = new ClientToken(aMachine, this);
12370 if (!mClientToken->isReady())
12371 {
12372 delete mClientToken;
12373 mClientToken = NULL;
12374 rc = E_FAIL;
12375 }
12376 }
12377 catch (std::bad_alloc &)
12378 {
12379 rc = E_OUTOFMEMORY;
12380 }
12381 if (FAILED(rc))
12382 return rc;
12383
12384 /* memorize the peer Machine */
12385 unconst(mPeer) = aMachine;
12386 /* share the parent pointer */
12387 unconst(mParent) = aMachine->mParent;
12388
12389 /* take the pointers to data to share */
12390 mData.share(aMachine->mData);
12391 mSSData.share(aMachine->mSSData);
12392
12393 mUserData.share(aMachine->mUserData);
12394 mHWData.share(aMachine->mHWData);
12395 mMediaData.share(aMachine->mMediaData);
12396
12397 mStorageControllers.allocate();
12398 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12399 it != aMachine->mStorageControllers->end();
12400 ++it)
12401 {
12402 ComObjPtr<StorageController> ctl;
12403 ctl.createObject();
12404 ctl->init(this, *it);
12405 mStorageControllers->push_back(ctl);
12406 }
12407
12408 mUSBControllers.allocate();
12409 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12410 it != aMachine->mUSBControllers->end();
12411 ++it)
12412 {
12413 ComObjPtr<USBController> ctl;
12414 ctl.createObject();
12415 ctl->init(this, *it);
12416 mUSBControllers->push_back(ctl);
12417 }
12418
12419 unconst(mBIOSSettings).createObject();
12420 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12421 /* create another VRDEServer object that will be mutable */
12422 unconst(mVRDEServer).createObject();
12423 mVRDEServer->init(this, aMachine->mVRDEServer);
12424 /* create another audio adapter object that will be mutable */
12425 unconst(mAudioAdapter).createObject();
12426 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12427 /* create a list of serial ports that will be mutable */
12428 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12429 {
12430 unconst(mSerialPorts[slot]).createObject();
12431 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12432 }
12433 /* create a list of parallel ports that will be mutable */
12434 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12435 {
12436 unconst(mParallelPorts[slot]).createObject();
12437 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12438 }
12439
12440 /* create another USB device filters object that will be mutable */
12441 unconst(mUSBDeviceFilters).createObject();
12442 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12443
12444 /* create a list of network adapters that will be mutable */
12445 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12446 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12447 {
12448 unconst(mNetworkAdapters[slot]).createObject();
12449 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12450 }
12451
12452 /* create another bandwidth control object that will be mutable */
12453 unconst(mBandwidthControl).createObject();
12454 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12455
12456 /* default is to delete saved state on Saved -> PoweredOff transition */
12457 mRemoveSavedState = true;
12458
12459 /* Confirm a successful initialization when it's the case */
12460 autoInitSpan.setSucceeded();
12461
12462 miNATNetworksStarted = 0;
12463
12464 LogFlowThisFuncLeave();
12465 return rc;
12466}
12467
12468/**
12469 * Uninitializes this session object. If the reason is other than
12470 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12471 * or the client watcher code.
12472 *
12473 * @param aReason uninitialization reason
12474 *
12475 * @note Locks mParent + this object for writing.
12476 */
12477void SessionMachine::uninit(Uninit::Reason aReason)
12478{
12479 LogFlowThisFuncEnter();
12480 LogFlowThisFunc(("reason=%d\n", aReason));
12481
12482 /*
12483 * Strongly reference ourselves to prevent this object deletion after
12484 * mData->mSession.mMachine.setNull() below (which can release the last
12485 * reference and call the destructor). Important: this must be done before
12486 * accessing any members (and before AutoUninitSpan that does it as well).
12487 * This self reference will be released as the very last step on return.
12488 */
12489 ComObjPtr<SessionMachine> selfRef = this;
12490
12491 /* Enclose the state transition Ready->InUninit->NotReady */
12492 AutoUninitSpan autoUninitSpan(this);
12493 if (autoUninitSpan.uninitDone())
12494 {
12495 LogFlowThisFunc(("Already uninitialized\n"));
12496 LogFlowThisFuncLeave();
12497 return;
12498 }
12499
12500 if (autoUninitSpan.initFailed())
12501 {
12502 /* We've been called by init() because it's failed. It's not really
12503 * necessary (nor it's safe) to perform the regular uninit sequence
12504 * below, the following is enough.
12505 */
12506 LogFlowThisFunc(("Initialization failed.\n"));
12507 /* destroy the machine client token */
12508 if (mClientToken)
12509 {
12510 delete mClientToken;
12511 mClientToken = NULL;
12512 }
12513 uninitDataAndChildObjects();
12514 mData.free();
12515 unconst(mParent) = NULL;
12516 unconst(mPeer) = NULL;
12517 LogFlowThisFuncLeave();
12518 return;
12519 }
12520
12521 MachineState_T lastState;
12522 {
12523 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12524 lastState = mData->mMachineState;
12525 }
12526 NOREF(lastState);
12527
12528#ifdef VBOX_WITH_USB
12529 // release all captured USB devices, but do this before requesting the locks below
12530 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12531 {
12532 /* Console::captureUSBDevices() is called in the VM process only after
12533 * setting the machine state to Starting or Restoring.
12534 * Console::detachAllUSBDevices() will be called upon successful
12535 * termination. So, we need to release USB devices only if there was
12536 * an abnormal termination of a running VM.
12537 *
12538 * This is identical to SessionMachine::DetachAllUSBDevices except
12539 * for the aAbnormal argument. */
12540 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12541 AssertComRC(rc);
12542 NOREF(rc);
12543
12544 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12545 if (service)
12546 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12547 }
12548#endif /* VBOX_WITH_USB */
12549
12550 // we need to lock this object in uninit() because the lock is shared
12551 // with mPeer (as well as data we modify below). mParent lock is needed
12552 // by several calls to it, and USB needs host lock.
12553 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12554
12555#ifdef VBOX_WITH_RESOURCE_USAGE_API
12556 /*
12557 * It is safe to call Machine::i_unregisterMetrics() here because
12558 * PerformanceCollector::samplerCallback no longer accesses guest methods
12559 * holding the lock.
12560 */
12561 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12562 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12563 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12564 if (mCollectorGuest)
12565 {
12566 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12567 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12568 mCollectorGuest = NULL;
12569 }
12570#endif
12571
12572 if (aReason == Uninit::Abnormal)
12573 {
12574 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12575
12576 /* reset the state to Aborted */
12577 if (mData->mMachineState != MachineState_Aborted)
12578 i_setMachineState(MachineState_Aborted);
12579 }
12580
12581 // any machine settings modified?
12582 if (mData->flModifications)
12583 {
12584 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12585 i_rollback(false /* aNotify */);
12586 }
12587
12588 mData->mSession.mPID = NIL_RTPROCESS;
12589
12590 if (aReason == Uninit::Unexpected)
12591 {
12592 /* Uninitialization didn't come from #checkForDeath(), so tell the
12593 * client watcher thread to update the set of machines that have open
12594 * sessions. */
12595 mParent->i_updateClientWatcher();
12596 }
12597
12598 /* uninitialize all remote controls */
12599 if (mData->mSession.mRemoteControls.size())
12600 {
12601 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12602 mData->mSession.mRemoteControls.size()));
12603
12604 Data::Session::RemoteControlList::iterator it =
12605 mData->mSession.mRemoteControls.begin();
12606 while (it != mData->mSession.mRemoteControls.end())
12607 {
12608 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12609 HRESULT rc = (*it)->Uninitialize();
12610 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12611 if (FAILED(rc))
12612 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12613 ++it;
12614 }
12615 mData->mSession.mRemoteControls.clear();
12616 }
12617
12618 /* Remove all references to the NAT network service. The service will stop
12619 * if all references (also from other VMs) are removed. */
12620 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12621 {
12622 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12623 {
12624 NetworkAttachmentType_T type;
12625 HRESULT hrc;
12626
12627 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12628 if ( SUCCEEDED(hrc)
12629 && type == NetworkAttachmentType_NATNetwork)
12630 {
12631 Bstr name;
12632 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12633 if (SUCCEEDED(hrc))
12634 {
12635 multilock.release();
12636 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12637 mUserData->s.strName.c_str(), name.raw()));
12638 mParent->i_natNetworkRefDec(name.raw());
12639 multilock.acquire();
12640 }
12641 }
12642 }
12643 }
12644
12645 /*
12646 * An expected uninitialization can come only from #checkForDeath().
12647 * Otherwise it means that something's gone really wrong (for example,
12648 * the Session implementation has released the VirtualBox reference
12649 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12650 * etc). However, it's also possible, that the client releases the IPC
12651 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12652 * but the VirtualBox release event comes first to the server process.
12653 * This case is practically possible, so we should not assert on an
12654 * unexpected uninit, just log a warning.
12655 */
12656
12657 if ((aReason == Uninit::Unexpected))
12658 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12659
12660 if (aReason != Uninit::Normal)
12661 {
12662 mData->mSession.mDirectControl.setNull();
12663 }
12664 else
12665 {
12666 /* this must be null here (see #OnSessionEnd()) */
12667 Assert(mData->mSession.mDirectControl.isNull());
12668 Assert(mData->mSession.mState == SessionState_Unlocking);
12669 Assert(!mData->mSession.mProgress.isNull());
12670 }
12671 if (mData->mSession.mProgress)
12672 {
12673 if (aReason == Uninit::Normal)
12674 mData->mSession.mProgress->i_notifyComplete(S_OK);
12675 else
12676 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12677 COM_IIDOF(ISession),
12678 getComponentName(),
12679 tr("The VM session was aborted"));
12680 mData->mSession.mProgress.setNull();
12681 }
12682
12683 if (mConsoleTaskData.mProgress)
12684 {
12685 Assert(aReason == Uninit::Abnormal);
12686 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12687 COM_IIDOF(ISession),
12688 getComponentName(),
12689 tr("The VM session was aborted"));
12690 mConsoleTaskData.mProgress.setNull();
12691 }
12692
12693 /* remove the association between the peer machine and this session machine */
12694 Assert( (SessionMachine*)mData->mSession.mMachine == this
12695 || aReason == Uninit::Unexpected);
12696
12697 /* reset the rest of session data */
12698 mData->mSession.mLockType = LockType_Null;
12699 mData->mSession.mMachine.setNull();
12700 mData->mSession.mState = SessionState_Unlocked;
12701 mData->mSession.mName.setNull();
12702
12703 /* destroy the machine client token before leaving the exclusive lock */
12704 if (mClientToken)
12705 {
12706 delete mClientToken;
12707 mClientToken = NULL;
12708 }
12709
12710 /* fire an event */
12711 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12712
12713 uninitDataAndChildObjects();
12714
12715 /* free the essential data structure last */
12716 mData.free();
12717
12718 /* release the exclusive lock before setting the below two to NULL */
12719 multilock.release();
12720
12721 unconst(mParent) = NULL;
12722 unconst(mPeer) = NULL;
12723
12724 AuthLibUnload(&mAuthLibCtx);
12725
12726 LogFlowThisFuncLeave();
12727}
12728
12729// util::Lockable interface
12730////////////////////////////////////////////////////////////////////////////////
12731
12732/**
12733 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12734 * with the primary Machine instance (mPeer).
12735 */
12736RWLockHandle *SessionMachine::lockHandle() const
12737{
12738 AssertReturn(mPeer != NULL, NULL);
12739 return mPeer->lockHandle();
12740}
12741
12742// IInternalMachineControl methods
12743////////////////////////////////////////////////////////////////////////////////
12744
12745/**
12746 * Passes collected guest statistics to performance collector object
12747 */
12748HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12749 ULONG aCpuKernel, ULONG aCpuIdle,
12750 ULONG aMemTotal, ULONG aMemFree,
12751 ULONG aMemBalloon, ULONG aMemShared,
12752 ULONG aMemCache, ULONG aPageTotal,
12753 ULONG aAllocVMM, ULONG aFreeVMM,
12754 ULONG aBalloonedVMM, ULONG aSharedVMM,
12755 ULONG aVmNetRx, ULONG aVmNetTx)
12756{
12757#ifdef VBOX_WITH_RESOURCE_USAGE_API
12758 if (mCollectorGuest)
12759 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12760 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12761 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12762 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12763
12764 return S_OK;
12765#else
12766 NOREF(aValidStats);
12767 NOREF(aCpuUser);
12768 NOREF(aCpuKernel);
12769 NOREF(aCpuIdle);
12770 NOREF(aMemTotal);
12771 NOREF(aMemFree);
12772 NOREF(aMemBalloon);
12773 NOREF(aMemShared);
12774 NOREF(aMemCache);
12775 NOREF(aPageTotal);
12776 NOREF(aAllocVMM);
12777 NOREF(aFreeVMM);
12778 NOREF(aBalloonedVMM);
12779 NOREF(aSharedVMM);
12780 NOREF(aVmNetRx);
12781 NOREF(aVmNetTx);
12782 return E_NOTIMPL;
12783#endif
12784}
12785
12786////////////////////////////////////////////////////////////////////////////////
12787//
12788// SessionMachine task records
12789//
12790////////////////////////////////////////////////////////////////////////////////
12791
12792/**
12793 * Task record for saving the machine state.
12794 */
12795struct SessionMachine::SaveStateTask
12796 : public Machine::Task
12797{
12798 SaveStateTask(SessionMachine *m,
12799 Progress *p,
12800 const Utf8Str &t,
12801 Reason_T enmReason,
12802 const Utf8Str &strStateFilePath)
12803 : Task(m, p, t),
12804 m_enmReason(enmReason),
12805 m_strStateFilePath(strStateFilePath)
12806 {}
12807
12808 void handler()
12809 {
12810 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12811 }
12812
12813 Reason_T m_enmReason;
12814 Utf8Str m_strStateFilePath;
12815};
12816
12817/**
12818 * Task thread implementation for SessionMachine::SaveState(), called from
12819 * SessionMachine::taskHandler().
12820 *
12821 * @note Locks this object for writing.
12822 *
12823 * @param task
12824 * @return
12825 */
12826void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12827{
12828 LogFlowThisFuncEnter();
12829
12830 AutoCaller autoCaller(this);
12831 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12832 if (FAILED(autoCaller.rc()))
12833 {
12834 /* we might have been uninitialized because the session was accidentally
12835 * closed by the client, so don't assert */
12836 HRESULT rc = setError(E_FAIL,
12837 tr("The session has been accidentally closed"));
12838 task.m_pProgress->i_notifyComplete(rc);
12839 LogFlowThisFuncLeave();
12840 return;
12841 }
12842
12843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12844
12845 HRESULT rc = S_OK;
12846
12847 try
12848 {
12849 ComPtr<IInternalSessionControl> directControl;
12850 if (mData->mSession.mLockType == LockType_VM)
12851 directControl = mData->mSession.mDirectControl;
12852 if (directControl.isNull())
12853 throw setError(VBOX_E_INVALID_VM_STATE,
12854 tr("Trying to save state without a running VM"));
12855 alock.release();
12856 BOOL fSuspendedBySave;
12857 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12858 Assert(!fSuspendedBySave);
12859 alock.acquire();
12860
12861 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12862 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12863 throw E_FAIL);
12864
12865 if (SUCCEEDED(rc))
12866 {
12867 mSSData->strStateFilePath = task.m_strStateFilePath;
12868
12869 /* save all VM settings */
12870 rc = i_saveSettings(NULL);
12871 // no need to check whether VirtualBox.xml needs saving also since
12872 // we can't have a name change pending at this point
12873 }
12874 else
12875 {
12876 // On failure, set the state to the state we had at the beginning.
12877 i_setMachineState(task.m_machineStateBackup);
12878 i_updateMachineStateOnClient();
12879
12880 // Delete the saved state file (might have been already created).
12881 // No need to check whether this is shared with a snapshot here
12882 // because we certainly created a fresh saved state file here.
12883 RTFileDelete(task.m_strStateFilePath.c_str());
12884 }
12885 }
12886 catch (HRESULT aRC) { rc = aRC; }
12887
12888 task.m_pProgress->i_notifyComplete(rc);
12889
12890 LogFlowThisFuncLeave();
12891}
12892
12893/**
12894 * @note Locks this object for writing.
12895 */
12896HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12897{
12898 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12899}
12900
12901HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12902{
12903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12904
12905 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12906 if (FAILED(rc)) return rc;
12907
12908 if ( mData->mMachineState != MachineState_Running
12909 && mData->mMachineState != MachineState_Paused
12910 )
12911 return setError(VBOX_E_INVALID_VM_STATE,
12912 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12913 Global::stringifyMachineState(mData->mMachineState));
12914
12915 ComObjPtr<Progress> pProgress;
12916 pProgress.createObject();
12917 rc = pProgress->init(i_getVirtualBox(),
12918 static_cast<IMachine *>(this) /* aInitiator */,
12919 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12920 FALSE /* aCancelable */);
12921 if (FAILED(rc))
12922 return rc;
12923
12924 Utf8Str strStateFilePath;
12925 i_composeSavedStateFilename(strStateFilePath);
12926
12927 /* create and start the task on a separate thread (note that it will not
12928 * start working until we release alock) */
12929 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12930 rc = pTask->createThread();
12931 if (FAILED(rc))
12932 return rc;
12933
12934 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12935 i_setMachineState(MachineState_Saving);
12936 i_updateMachineStateOnClient();
12937
12938 pProgress.queryInterfaceTo(aProgress.asOutParam());
12939
12940 return S_OK;
12941}
12942
12943/**
12944 * @note Locks this object for writing.
12945 */
12946HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12947{
12948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12949
12950 HRESULT rc = i_checkStateDependency(MutableStateDep);
12951 if (FAILED(rc)) return rc;
12952
12953 if ( mData->mMachineState != MachineState_PoweredOff
12954 && mData->mMachineState != MachineState_Teleported
12955 && mData->mMachineState != MachineState_Aborted
12956 )
12957 return setError(VBOX_E_INVALID_VM_STATE,
12958 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12959 Global::stringifyMachineState(mData->mMachineState));
12960
12961 com::Utf8Str stateFilePathFull;
12962 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12963 if (RT_FAILURE(vrc))
12964 return setError(VBOX_E_FILE_ERROR,
12965 tr("Invalid saved state file path '%s' (%Rrc)"),
12966 aSavedStateFile.c_str(),
12967 vrc);
12968
12969 mSSData->strStateFilePath = stateFilePathFull;
12970
12971 /* The below i_setMachineState() will detect the state transition and will
12972 * update the settings file */
12973
12974 return i_setMachineState(MachineState_Saved);
12975}
12976
12977/**
12978 * @note Locks this object for writing.
12979 */
12980HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12981{
12982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12983
12984 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12985 if (FAILED(rc)) return rc;
12986
12987 if (mData->mMachineState != MachineState_Saved)
12988 return setError(VBOX_E_INVALID_VM_STATE,
12989 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12990 Global::stringifyMachineState(mData->mMachineState));
12991
12992 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12993
12994 /*
12995 * Saved -> PoweredOff transition will be detected in the SessionMachine
12996 * and properly handled.
12997 */
12998 rc = i_setMachineState(MachineState_PoweredOff);
12999 return rc;
13000}
13001
13002
13003/**
13004 * @note Locks the same as #i_setMachineState() does.
13005 */
13006HRESULT SessionMachine::updateState(MachineState_T aState)
13007{
13008 return i_setMachineState(aState);
13009}
13010
13011/**
13012 * @note Locks this object for writing.
13013 */
13014HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13015{
13016 IProgress* pProgress(aProgress);
13017
13018 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13019
13020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13021
13022 if (mData->mSession.mState != SessionState_Locked)
13023 return VBOX_E_INVALID_OBJECT_STATE;
13024
13025 if (!mData->mSession.mProgress.isNull())
13026 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13027
13028 /* If we didn't reference the NAT network service yet, add a reference to
13029 * force a start */
13030 if (miNATNetworksStarted < 1)
13031 {
13032 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13033 {
13034 NetworkAttachmentType_T type;
13035 HRESULT hrc;
13036 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13037 if ( SUCCEEDED(hrc)
13038 && type == NetworkAttachmentType_NATNetwork)
13039 {
13040 Bstr name;
13041 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13042 if (SUCCEEDED(hrc))
13043 {
13044 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13045 mUserData->s.strName.c_str(), name.raw()));
13046 mPeer->lockHandle()->unlockWrite();
13047 mParent->i_natNetworkRefInc(name.raw());
13048#ifdef RT_LOCK_STRICT
13049 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13050#else
13051 mPeer->lockHandle()->lockWrite();
13052#endif
13053 }
13054 }
13055 }
13056 miNATNetworksStarted++;
13057 }
13058
13059 LogFlowThisFunc(("returns S_OK.\n"));
13060 return S_OK;
13061}
13062
13063/**
13064 * @note Locks this object for writing.
13065 */
13066HRESULT SessionMachine::endPowerUp(LONG aResult)
13067{
13068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13069
13070 if (mData->mSession.mState != SessionState_Locked)
13071 return VBOX_E_INVALID_OBJECT_STATE;
13072
13073 /* Finalize the LaunchVMProcess progress object. */
13074 if (mData->mSession.mProgress)
13075 {
13076 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13077 mData->mSession.mProgress.setNull();
13078 }
13079
13080 if (SUCCEEDED((HRESULT)aResult))
13081 {
13082#ifdef VBOX_WITH_RESOURCE_USAGE_API
13083 /* The VM has been powered up successfully, so it makes sense
13084 * now to offer the performance metrics for a running machine
13085 * object. Doing it earlier wouldn't be safe. */
13086 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13087 mData->mSession.mPID);
13088#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13089 }
13090
13091 return S_OK;
13092}
13093
13094/**
13095 * @note Locks this object for writing.
13096 */
13097HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13098{
13099 LogFlowThisFuncEnter();
13100
13101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13102
13103 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13104 E_FAIL);
13105
13106 /* create a progress object to track operation completion */
13107 ComObjPtr<Progress> pProgress;
13108 pProgress.createObject();
13109 pProgress->init(i_getVirtualBox(),
13110 static_cast<IMachine *>(this) /* aInitiator */,
13111 Bstr(tr("Stopping the virtual machine")).raw(),
13112 FALSE /* aCancelable */);
13113
13114 /* fill in the console task data */
13115 mConsoleTaskData.mLastState = mData->mMachineState;
13116 mConsoleTaskData.mProgress = pProgress;
13117
13118 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13119 i_setMachineState(MachineState_Stopping);
13120
13121 pProgress.queryInterfaceTo(aProgress.asOutParam());
13122
13123 return S_OK;
13124}
13125
13126/**
13127 * @note Locks this object for writing.
13128 */
13129HRESULT SessionMachine::endPoweringDown(LONG aResult,
13130 const com::Utf8Str &aErrMsg)
13131{
13132 LogFlowThisFuncEnter();
13133
13134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13135
13136 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13137 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13138 && mConsoleTaskData.mLastState != MachineState_Null,
13139 E_FAIL);
13140
13141 /*
13142 * On failure, set the state to the state we had when BeginPoweringDown()
13143 * was called (this is expected by Console::PowerDown() and the associated
13144 * task). On success the VM process already changed the state to
13145 * MachineState_PoweredOff, so no need to do anything.
13146 */
13147 if (FAILED(aResult))
13148 i_setMachineState(mConsoleTaskData.mLastState);
13149
13150 /* notify the progress object about operation completion */
13151 Assert(mConsoleTaskData.mProgress);
13152 if (SUCCEEDED(aResult))
13153 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13154 else
13155 {
13156 if (aErrMsg.length())
13157 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13158 COM_IIDOF(ISession),
13159 getComponentName(),
13160 aErrMsg.c_str());
13161 else
13162 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13163 }
13164
13165 /* clear out the temporary saved state data */
13166 mConsoleTaskData.mLastState = MachineState_Null;
13167 mConsoleTaskData.mProgress.setNull();
13168
13169 LogFlowThisFuncLeave();
13170 return S_OK;
13171}
13172
13173
13174/**
13175 * Goes through the USB filters of the given machine to see if the given
13176 * device matches any filter or not.
13177 *
13178 * @note Locks the same as USBController::hasMatchingFilter() does.
13179 */
13180HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13181 BOOL *aMatched,
13182 ULONG *aMaskedInterfaces)
13183{
13184 LogFlowThisFunc(("\n"));
13185
13186#ifdef VBOX_WITH_USB
13187 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13188#else
13189 NOREF(aDevice);
13190 NOREF(aMaskedInterfaces);
13191 *aMatched = FALSE;
13192#endif
13193
13194 return S_OK;
13195}
13196
13197/**
13198 * @note Locks the same as Host::captureUSBDevice() does.
13199 */
13200HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13201{
13202 LogFlowThisFunc(("\n"));
13203
13204#ifdef VBOX_WITH_USB
13205 /* if captureDeviceForVM() fails, it must have set extended error info */
13206 clearError();
13207 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13208 if (FAILED(rc)) return rc;
13209
13210 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13211 AssertReturn(service, E_FAIL);
13212 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13213#else
13214 NOREF(aId);
13215 return E_NOTIMPL;
13216#endif
13217}
13218
13219/**
13220 * @note Locks the same as Host::detachUSBDevice() does.
13221 */
13222HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13223 BOOL aDone)
13224{
13225 LogFlowThisFunc(("\n"));
13226
13227#ifdef VBOX_WITH_USB
13228 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13229 AssertReturn(service, E_FAIL);
13230 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13231#else
13232 NOREF(aId);
13233 NOREF(aDone);
13234 return E_NOTIMPL;
13235#endif
13236}
13237
13238/**
13239 * Inserts all machine filters to the USB proxy service and then calls
13240 * Host::autoCaptureUSBDevices().
13241 *
13242 * Called by Console from the VM process upon VM startup.
13243 *
13244 * @note Locks what called methods lock.
13245 */
13246HRESULT SessionMachine::autoCaptureUSBDevices()
13247{
13248 LogFlowThisFunc(("\n"));
13249
13250#ifdef VBOX_WITH_USB
13251 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13252 AssertComRC(rc);
13253 NOREF(rc);
13254
13255 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13256 AssertReturn(service, E_FAIL);
13257 return service->autoCaptureDevicesForVM(this);
13258#else
13259 return S_OK;
13260#endif
13261}
13262
13263/**
13264 * Removes all machine filters from the USB proxy service and then calls
13265 * Host::detachAllUSBDevices().
13266 *
13267 * Called by Console from the VM process upon normal VM termination or by
13268 * SessionMachine::uninit() upon abnormal VM termination (from under the
13269 * Machine/SessionMachine lock).
13270 *
13271 * @note Locks what called methods lock.
13272 */
13273HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13274{
13275 LogFlowThisFunc(("\n"));
13276
13277#ifdef VBOX_WITH_USB
13278 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13279 AssertComRC(rc);
13280 NOREF(rc);
13281
13282 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13283 AssertReturn(service, E_FAIL);
13284 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13285#else
13286 NOREF(aDone);
13287 return S_OK;
13288#endif
13289}
13290
13291/**
13292 * @note Locks this object for writing.
13293 */
13294HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13295 ComPtr<IProgress> &aProgress)
13296{
13297 LogFlowThisFuncEnter();
13298
13299 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13300 /*
13301 * We don't assert below because it might happen that a non-direct session
13302 * informs us it is closed right after we've been uninitialized -- it's ok.
13303 */
13304
13305 /* get IInternalSessionControl interface */
13306 ComPtr<IInternalSessionControl> control(aSession);
13307
13308 ComAssertRet(!control.isNull(), E_INVALIDARG);
13309
13310 /* Creating a Progress object requires the VirtualBox lock, and
13311 * thus locking it here is required by the lock order rules. */
13312 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13313
13314 if (control == mData->mSession.mDirectControl)
13315 {
13316 /* The direct session is being normally closed by the client process
13317 * ----------------------------------------------------------------- */
13318
13319 /* go to the closing state (essential for all open*Session() calls and
13320 * for #checkForDeath()) */
13321 Assert(mData->mSession.mState == SessionState_Locked);
13322 mData->mSession.mState = SessionState_Unlocking;
13323
13324 /* set direct control to NULL to release the remote instance */
13325 mData->mSession.mDirectControl.setNull();
13326 LogFlowThisFunc(("Direct control is set to NULL\n"));
13327
13328 if (mData->mSession.mProgress)
13329 {
13330 /* finalize the progress, someone might wait if a frontend
13331 * closes the session before powering on the VM. */
13332 mData->mSession.mProgress->notifyComplete(E_FAIL,
13333 COM_IIDOF(ISession),
13334 getComponentName(),
13335 tr("The VM session was closed before any attempt to power it on"));
13336 mData->mSession.mProgress.setNull();
13337 }
13338
13339 /* Create the progress object the client will use to wait until
13340 * #checkForDeath() is called to uninitialize this session object after
13341 * it releases the IPC semaphore.
13342 * Note! Because we're "reusing" mProgress here, this must be a proxy
13343 * object just like for LaunchVMProcess. */
13344 Assert(mData->mSession.mProgress.isNull());
13345 ComObjPtr<ProgressProxy> progress;
13346 progress.createObject();
13347 ComPtr<IUnknown> pPeer(mPeer);
13348 progress->init(mParent, pPeer,
13349 Bstr(tr("Closing session")).raw(),
13350 FALSE /* aCancelable */);
13351 progress.queryInterfaceTo(aProgress.asOutParam());
13352 mData->mSession.mProgress = progress;
13353 }
13354 else
13355 {
13356 /* the remote session is being normally closed */
13357 Data::Session::RemoteControlList::iterator it =
13358 mData->mSession.mRemoteControls.begin();
13359 while (it != mData->mSession.mRemoteControls.end())
13360 {
13361 if (control == *it)
13362 break;
13363 ++it;
13364 }
13365 BOOL found = it != mData->mSession.mRemoteControls.end();
13366 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13367 E_INVALIDARG);
13368 // This MUST be erase(it), not remove(*it) as the latter triggers a
13369 // very nasty use after free due to the place where the value "lives".
13370 mData->mSession.mRemoteControls.erase(it);
13371 }
13372
13373 /* signal the client watcher thread, because the client is going away */
13374 mParent->i_updateClientWatcher();
13375
13376 LogFlowThisFuncLeave();
13377 return S_OK;
13378}
13379
13380HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13381 std::vector<com::Utf8Str> &aValues,
13382 std::vector<LONG64> &aTimestamps,
13383 std::vector<com::Utf8Str> &aFlags)
13384{
13385 LogFlowThisFunc(("\n"));
13386
13387#ifdef VBOX_WITH_GUEST_PROPS
13388 using namespace guestProp;
13389
13390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13391
13392 size_t cEntries = mHWData->mGuestProperties.size();
13393 aNames.resize(cEntries);
13394 aValues.resize(cEntries);
13395 aTimestamps.resize(cEntries);
13396 aFlags.resize(cEntries);
13397
13398 size_t i = 0;
13399 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13400 it != mHWData->mGuestProperties.end();
13401 ++it, ++i)
13402 {
13403 char szFlags[MAX_FLAGS_LEN + 1];
13404 aNames[i] = it->first;
13405 aValues[i] = it->second.strValue;
13406 aTimestamps[i] = it->second.mTimestamp;
13407
13408 /* If it is NULL, keep it NULL. */
13409 if (it->second.mFlags)
13410 {
13411 writeFlags(it->second.mFlags, szFlags);
13412 aFlags[i] = szFlags;
13413 }
13414 else
13415 aFlags[i] = "";
13416 }
13417 return S_OK;
13418#else
13419 ReturnComNotImplemented();
13420#endif
13421}
13422
13423HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13424 const com::Utf8Str &aValue,
13425 LONG64 aTimestamp,
13426 const com::Utf8Str &aFlags)
13427{
13428 LogFlowThisFunc(("\n"));
13429
13430#ifdef VBOX_WITH_GUEST_PROPS
13431 using namespace guestProp;
13432
13433 try
13434 {
13435 /*
13436 * Convert input up front.
13437 */
13438 uint32_t fFlags = NILFLAG;
13439 if (aFlags.length())
13440 {
13441 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13442 AssertRCReturn(vrc, E_INVALIDARG);
13443 }
13444
13445 /*
13446 * Now grab the object lock, validate the state and do the update.
13447 */
13448
13449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13450
13451 if (!Global::IsOnline(mData->mMachineState))
13452 {
13453 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13454 VBOX_E_INVALID_VM_STATE);
13455 }
13456
13457 i_setModified(IsModified_MachineData);
13458 mHWData.backup();
13459
13460 bool fDelete = !aValue.length();
13461 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13462 if (it != mHWData->mGuestProperties.end())
13463 {
13464 if (!fDelete)
13465 {
13466 it->second.strValue = aValue;
13467 it->second.mTimestamp = aTimestamp;
13468 it->second.mFlags = fFlags;
13469 }
13470 else
13471 mHWData->mGuestProperties.erase(it);
13472
13473 mData->mGuestPropertiesModified = TRUE;
13474 }
13475 else if (!fDelete)
13476 {
13477 HWData::GuestProperty prop;
13478 prop.strValue = aValue;
13479 prop.mTimestamp = aTimestamp;
13480 prop.mFlags = fFlags;
13481
13482 mHWData->mGuestProperties[aName] = prop;
13483 mData->mGuestPropertiesModified = TRUE;
13484 }
13485
13486 alock.release();
13487
13488 mParent->i_onGuestPropertyChange(mData->mUuid,
13489 Bstr(aName).raw(),
13490 Bstr(aValue).raw(),
13491 Bstr(aFlags).raw());
13492 }
13493 catch (...)
13494 {
13495 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13496 }
13497 return S_OK;
13498#else
13499 ReturnComNotImplemented();
13500#endif
13501}
13502
13503
13504HRESULT SessionMachine::lockMedia()
13505{
13506 AutoMultiWriteLock2 alock(this->lockHandle(),
13507 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13508
13509 AssertReturn( mData->mMachineState == MachineState_Starting
13510 || mData->mMachineState == MachineState_Restoring
13511 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13512
13513 clearError();
13514 alock.release();
13515 return i_lockMedia();
13516}
13517
13518HRESULT SessionMachine::unlockMedia()
13519{
13520 HRESULT hrc = i_unlockMedia();
13521 return hrc;
13522}
13523
13524HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13525 ComPtr<IMediumAttachment> &aNewAttachment)
13526{
13527 // request the host lock first, since might be calling Host methods for getting host drives;
13528 // next, protect the media tree all the while we're in here, as well as our member variables
13529 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13530 this->lockHandle(),
13531 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13532
13533 IMediumAttachment *iAttach = aAttachment;
13534 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13535
13536 Bstr ctrlName;
13537 LONG lPort;
13538 LONG lDevice;
13539 bool fTempEject;
13540 {
13541 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13542
13543 /* Need to query the details first, as the IMediumAttachment reference
13544 * might be to the original settings, which we are going to change. */
13545 ctrlName = pAttach->i_getControllerName();
13546 lPort = pAttach->i_getPort();
13547 lDevice = pAttach->i_getDevice();
13548 fTempEject = pAttach->i_getTempEject();
13549 }
13550
13551 if (!fTempEject)
13552 {
13553 /* Remember previously mounted medium. The medium before taking the
13554 * backup is not necessarily the same thing. */
13555 ComObjPtr<Medium> oldmedium;
13556 oldmedium = pAttach->i_getMedium();
13557
13558 i_setModified(IsModified_Storage);
13559 mMediaData.backup();
13560
13561 // The backup operation makes the pAttach reference point to the
13562 // old settings. Re-get the correct reference.
13563 pAttach = i_findAttachment(mMediaData->mAttachments,
13564 ctrlName.raw(),
13565 lPort,
13566 lDevice);
13567
13568 {
13569 AutoCaller autoAttachCaller(this);
13570 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13571
13572 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13573 if (!oldmedium.isNull())
13574 oldmedium->i_removeBackReference(mData->mUuid);
13575
13576 pAttach->i_updateMedium(NULL);
13577 pAttach->i_updateEjected();
13578 }
13579
13580 i_setModified(IsModified_Storage);
13581 }
13582 else
13583 {
13584 {
13585 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13586 pAttach->i_updateEjected();
13587 }
13588 }
13589
13590 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13591
13592 return S_OK;
13593}
13594
13595HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13596 com::Utf8Str &aResult)
13597{
13598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13599
13600 HRESULT hr = S_OK;
13601
13602 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13603 {
13604 enum VRDEAuthParams
13605 {
13606 parmUuid = 1,
13607 parmGuestJudgement,
13608 parmUser,
13609 parmPassword,
13610 parmDomain,
13611 parmClientId
13612 };
13613
13614 AuthResult result = AuthResultAccessDenied;
13615
13616 if (!mAuthLibCtx.hAuthLibrary)
13617 {
13618 /* Load the external authentication library. */
13619 Bstr authLibrary;
13620 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13621
13622 Utf8Str filename = authLibrary;
13623
13624 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13625 if (RT_FAILURE(rc))
13626 {
13627 hr = setError(E_FAIL,
13628 tr("Could not load the external authentication library '%s' (%Rrc)"),
13629 filename.c_str(), rc);
13630 }
13631 }
13632
13633 if (SUCCEEDED(hr))
13634 {
13635 Guid uuid(aAuthParams[parmUuid]);
13636 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13637 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13638
13639 result = AuthLibAuthenticate(&mAuthLibCtx,
13640 uuid.raw(), guestJudgement,
13641 aAuthParams[parmUser].c_str(),
13642 aAuthParams[parmPassword].c_str(),
13643 aAuthParams[parmDomain].c_str(),
13644 u32ClientId);
13645 }
13646
13647 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13648 size_t cbPassword = aAuthParams[parmPassword].length();
13649 if (cbPassword)
13650 {
13651 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13652 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13653 }
13654
13655 if (result == AuthResultAccessGranted)
13656 aResult = "granted";
13657 else
13658 aResult = "denied";
13659
13660 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13661 aAuthParams[parmUser].c_str(), aResult.c_str()));
13662 }
13663 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13664 {
13665 enum VRDEAuthDisconnectParams
13666 {
13667 parmUuid = 1,
13668 parmClientId
13669 };
13670
13671 Guid uuid(aAuthParams[parmUuid]);
13672 uint32_t u32ClientId = 0;
13673 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13674 }
13675 else
13676 {
13677 hr = E_INVALIDARG;
13678 }
13679
13680 return hr;
13681}
13682
13683// public methods only for internal purposes
13684/////////////////////////////////////////////////////////////////////////////
13685
13686#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13687/**
13688 * Called from the client watcher thread to check for expected or unexpected
13689 * death of the client process that has a direct session to this machine.
13690 *
13691 * On Win32 and on OS/2, this method is called only when we've got the
13692 * mutex (i.e. the client has either died or terminated normally) so it always
13693 * returns @c true (the client is terminated, the session machine is
13694 * uninitialized).
13695 *
13696 * On other platforms, the method returns @c true if the client process has
13697 * terminated normally or abnormally and the session machine was uninitialized,
13698 * and @c false if the client process is still alive.
13699 *
13700 * @note Locks this object for writing.
13701 */
13702bool SessionMachine::i_checkForDeath()
13703{
13704 Uninit::Reason reason;
13705 bool terminated = false;
13706
13707 /* Enclose autoCaller with a block because calling uninit() from under it
13708 * will deadlock. */
13709 {
13710 AutoCaller autoCaller(this);
13711 if (!autoCaller.isOk())
13712 {
13713 /* return true if not ready, to cause the client watcher to exclude
13714 * the corresponding session from watching */
13715 LogFlowThisFunc(("Already uninitialized!\n"));
13716 return true;
13717 }
13718
13719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13720
13721 /* Determine the reason of death: if the session state is Closing here,
13722 * everything is fine. Otherwise it means that the client did not call
13723 * OnSessionEnd() before it released the IPC semaphore. This may happen
13724 * either because the client process has abnormally terminated, or
13725 * because it simply forgot to call ISession::Close() before exiting. We
13726 * threat the latter also as an abnormal termination (see
13727 * Session::uninit() for details). */
13728 reason = mData->mSession.mState == SessionState_Unlocking ?
13729 Uninit::Normal :
13730 Uninit::Abnormal;
13731
13732 if (mClientToken)
13733 terminated = mClientToken->release();
13734 } /* AutoCaller block */
13735
13736 if (terminated)
13737 uninit(reason);
13738
13739 return terminated;
13740}
13741
13742void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13743{
13744 LogFlowThisFunc(("\n"));
13745
13746 strTokenId.setNull();
13747
13748 AutoCaller autoCaller(this);
13749 AssertComRCReturnVoid(autoCaller.rc());
13750
13751 Assert(mClientToken);
13752 if (mClientToken)
13753 mClientToken->getId(strTokenId);
13754}
13755#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13756IToken *SessionMachine::i_getToken()
13757{
13758 LogFlowThisFunc(("\n"));
13759
13760 AutoCaller autoCaller(this);
13761 AssertComRCReturn(autoCaller.rc(), NULL);
13762
13763 Assert(mClientToken);
13764 if (mClientToken)
13765 return mClientToken->getToken();
13766 else
13767 return NULL;
13768}
13769#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13770
13771Machine::ClientToken *SessionMachine::i_getClientToken()
13772{
13773 LogFlowThisFunc(("\n"));
13774
13775 AutoCaller autoCaller(this);
13776 AssertComRCReturn(autoCaller.rc(), NULL);
13777
13778 return mClientToken;
13779}
13780
13781
13782/**
13783 * @note Locks this object for reading.
13784 */
13785HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13786{
13787 LogFlowThisFunc(("\n"));
13788
13789 AutoCaller autoCaller(this);
13790 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13791
13792 ComPtr<IInternalSessionControl> directControl;
13793 {
13794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13795 if (mData->mSession.mLockType == LockType_VM)
13796 directControl = mData->mSession.mDirectControl;
13797 }
13798
13799 /* ignore notifications sent after #OnSessionEnd() is called */
13800 if (!directControl)
13801 return S_OK;
13802
13803 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13804}
13805
13806/**
13807 * @note Locks this object for reading.
13808 */
13809HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13810 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13811 IN_BSTR aGuestIp, LONG aGuestPort)
13812{
13813 LogFlowThisFunc(("\n"));
13814
13815 AutoCaller autoCaller(this);
13816 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13817
13818 ComPtr<IInternalSessionControl> directControl;
13819 {
13820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13821 if (mData->mSession.mLockType == LockType_VM)
13822 directControl = mData->mSession.mDirectControl;
13823 }
13824
13825 /* ignore notifications sent after #OnSessionEnd() is called */
13826 if (!directControl)
13827 return S_OK;
13828 /*
13829 * instead acting like callback we ask IVirtualBox deliver corresponding event
13830 */
13831
13832 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13833 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13834 return S_OK;
13835}
13836
13837/**
13838 * @note Locks this object for reading.
13839 */
13840HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13841{
13842 LogFlowThisFunc(("\n"));
13843
13844 AutoCaller autoCaller(this);
13845 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13846
13847 ComPtr<IInternalSessionControl> directControl;
13848 {
13849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13850 if (mData->mSession.mLockType == LockType_VM)
13851 directControl = mData->mSession.mDirectControl;
13852 }
13853
13854 /* ignore notifications sent after #OnSessionEnd() is called */
13855 if (!directControl)
13856 return S_OK;
13857
13858 return directControl->OnSerialPortChange(serialPort);
13859}
13860
13861/**
13862 * @note Locks this object for reading.
13863 */
13864HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13865{
13866 LogFlowThisFunc(("\n"));
13867
13868 AutoCaller autoCaller(this);
13869 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13870
13871 ComPtr<IInternalSessionControl> directControl;
13872 {
13873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13874 if (mData->mSession.mLockType == LockType_VM)
13875 directControl = mData->mSession.mDirectControl;
13876 }
13877
13878 /* ignore notifications sent after #OnSessionEnd() is called */
13879 if (!directControl)
13880 return S_OK;
13881
13882 return directControl->OnParallelPortChange(parallelPort);
13883}
13884
13885/**
13886 * @note Locks this object for reading.
13887 */
13888HRESULT SessionMachine::i_onStorageControllerChange()
13889{
13890 LogFlowThisFunc(("\n"));
13891
13892 AutoCaller autoCaller(this);
13893 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13894
13895 ComPtr<IInternalSessionControl> directControl;
13896 {
13897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13898 if (mData->mSession.mLockType == LockType_VM)
13899 directControl = mData->mSession.mDirectControl;
13900 }
13901
13902 /* ignore notifications sent after #OnSessionEnd() is called */
13903 if (!directControl)
13904 return S_OK;
13905
13906 return directControl->OnStorageControllerChange();
13907}
13908
13909/**
13910 * @note Locks this object for reading.
13911 */
13912HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13913{
13914 LogFlowThisFunc(("\n"));
13915
13916 AutoCaller autoCaller(this);
13917 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13918
13919 ComPtr<IInternalSessionControl> directControl;
13920 {
13921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13922 if (mData->mSession.mLockType == LockType_VM)
13923 directControl = mData->mSession.mDirectControl;
13924 }
13925
13926 /* ignore notifications sent after #OnSessionEnd() is called */
13927 if (!directControl)
13928 return S_OK;
13929
13930 return directControl->OnMediumChange(aAttachment, aForce);
13931}
13932
13933/**
13934 * @note Locks this object for reading.
13935 */
13936HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13937{
13938 LogFlowThisFunc(("\n"));
13939
13940 AutoCaller autoCaller(this);
13941 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13942
13943 ComPtr<IInternalSessionControl> directControl;
13944 {
13945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13946 if (mData->mSession.mLockType == LockType_VM)
13947 directControl = mData->mSession.mDirectControl;
13948 }
13949
13950 /* ignore notifications sent after #OnSessionEnd() is called */
13951 if (!directControl)
13952 return S_OK;
13953
13954 return directControl->OnCPUChange(aCPU, aRemove);
13955}
13956
13957HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13958{
13959 LogFlowThisFunc(("\n"));
13960
13961 AutoCaller autoCaller(this);
13962 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13963
13964 ComPtr<IInternalSessionControl> directControl;
13965 {
13966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13967 if (mData->mSession.mLockType == LockType_VM)
13968 directControl = mData->mSession.mDirectControl;
13969 }
13970
13971 /* ignore notifications sent after #OnSessionEnd() is called */
13972 if (!directControl)
13973 return S_OK;
13974
13975 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13976}
13977
13978/**
13979 * @note Locks this object for reading.
13980 */
13981HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13982{
13983 LogFlowThisFunc(("\n"));
13984
13985 AutoCaller autoCaller(this);
13986 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13987
13988 ComPtr<IInternalSessionControl> directControl;
13989 {
13990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13991 if (mData->mSession.mLockType == LockType_VM)
13992 directControl = mData->mSession.mDirectControl;
13993 }
13994
13995 /* ignore notifications sent after #OnSessionEnd() is called */
13996 if (!directControl)
13997 return S_OK;
13998
13999 return directControl->OnVRDEServerChange(aRestart);
14000}
14001
14002/**
14003 * @note Locks this object for reading.
14004 */
14005HRESULT SessionMachine::i_onVideoCaptureChange()
14006{
14007 LogFlowThisFunc(("\n"));
14008
14009 AutoCaller autoCaller(this);
14010 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14011
14012 ComPtr<IInternalSessionControl> directControl;
14013 {
14014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14015 if (mData->mSession.mLockType == LockType_VM)
14016 directControl = mData->mSession.mDirectControl;
14017 }
14018
14019 /* ignore notifications sent after #OnSessionEnd() is called */
14020 if (!directControl)
14021 return S_OK;
14022
14023 return directControl->OnVideoCaptureChange();
14024}
14025
14026/**
14027 * @note Locks this object for reading.
14028 */
14029HRESULT SessionMachine::i_onUSBControllerChange()
14030{
14031 LogFlowThisFunc(("\n"));
14032
14033 AutoCaller autoCaller(this);
14034 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14035
14036 ComPtr<IInternalSessionControl> directControl;
14037 {
14038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14039 if (mData->mSession.mLockType == LockType_VM)
14040 directControl = mData->mSession.mDirectControl;
14041 }
14042
14043 /* ignore notifications sent after #OnSessionEnd() is called */
14044 if (!directControl)
14045 return S_OK;
14046
14047 return directControl->OnUSBControllerChange();
14048}
14049
14050/**
14051 * @note Locks this object for reading.
14052 */
14053HRESULT SessionMachine::i_onSharedFolderChange()
14054{
14055 LogFlowThisFunc(("\n"));
14056
14057 AutoCaller autoCaller(this);
14058 AssertComRCReturnRC(autoCaller.rc());
14059
14060 ComPtr<IInternalSessionControl> directControl;
14061 {
14062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14063 if (mData->mSession.mLockType == LockType_VM)
14064 directControl = mData->mSession.mDirectControl;
14065 }
14066
14067 /* ignore notifications sent after #OnSessionEnd() is called */
14068 if (!directControl)
14069 return S_OK;
14070
14071 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14072}
14073
14074/**
14075 * @note Locks this object for reading.
14076 */
14077HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14078{
14079 LogFlowThisFunc(("\n"));
14080
14081 AutoCaller autoCaller(this);
14082 AssertComRCReturnRC(autoCaller.rc());
14083
14084 ComPtr<IInternalSessionControl> directControl;
14085 {
14086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14087 if (mData->mSession.mLockType == LockType_VM)
14088 directControl = mData->mSession.mDirectControl;
14089 }
14090
14091 /* ignore notifications sent after #OnSessionEnd() is called */
14092 if (!directControl)
14093 return S_OK;
14094
14095 return directControl->OnClipboardModeChange(aClipboardMode);
14096}
14097
14098/**
14099 * @note Locks this object for reading.
14100 */
14101HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14102{
14103 LogFlowThisFunc(("\n"));
14104
14105 AutoCaller autoCaller(this);
14106 AssertComRCReturnRC(autoCaller.rc());
14107
14108 ComPtr<IInternalSessionControl> directControl;
14109 {
14110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14111 if (mData->mSession.mLockType == LockType_VM)
14112 directControl = mData->mSession.mDirectControl;
14113 }
14114
14115 /* ignore notifications sent after #OnSessionEnd() is called */
14116 if (!directControl)
14117 return S_OK;
14118
14119 return directControl->OnDnDModeChange(aDnDMode);
14120}
14121
14122/**
14123 * @note Locks this object for reading.
14124 */
14125HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14126{
14127 LogFlowThisFunc(("\n"));
14128
14129 AutoCaller autoCaller(this);
14130 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14131
14132 ComPtr<IInternalSessionControl> directControl;
14133 {
14134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14135 if (mData->mSession.mLockType == LockType_VM)
14136 directControl = mData->mSession.mDirectControl;
14137 }
14138
14139 /* ignore notifications sent after #OnSessionEnd() is called */
14140 if (!directControl)
14141 return S_OK;
14142
14143 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14144}
14145
14146/**
14147 * @note Locks this object for reading.
14148 */
14149HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14150{
14151 LogFlowThisFunc(("\n"));
14152
14153 AutoCaller autoCaller(this);
14154 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14155
14156 ComPtr<IInternalSessionControl> directControl;
14157 {
14158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14159 if (mData->mSession.mLockType == LockType_VM)
14160 directControl = mData->mSession.mDirectControl;
14161 }
14162
14163 /* ignore notifications sent after #OnSessionEnd() is called */
14164 if (!directControl)
14165 return S_OK;
14166
14167 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14168}
14169
14170/**
14171 * Returns @c true if this machine's USB controller reports it has a matching
14172 * filter for the given USB device and @c false otherwise.
14173 *
14174 * @note locks this object for reading.
14175 */
14176bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14177{
14178 AutoCaller autoCaller(this);
14179 /* silently return if not ready -- this method may be called after the
14180 * direct machine session has been called */
14181 if (!autoCaller.isOk())
14182 return false;
14183
14184#ifdef VBOX_WITH_USB
14185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14186
14187 switch (mData->mMachineState)
14188 {
14189 case MachineState_Starting:
14190 case MachineState_Restoring:
14191 case MachineState_TeleportingIn:
14192 case MachineState_Paused:
14193 case MachineState_Running:
14194 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14195 * elsewhere... */
14196 alock.release();
14197 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14198 default: break;
14199 }
14200#else
14201 NOREF(aDevice);
14202 NOREF(aMaskedIfs);
14203#endif
14204 return false;
14205}
14206
14207/**
14208 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14209 */
14210HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14211 IVirtualBoxErrorInfo *aError,
14212 ULONG aMaskedIfs,
14213 const com::Utf8Str &aCaptureFilename)
14214{
14215 LogFlowThisFunc(("\n"));
14216
14217 AutoCaller autoCaller(this);
14218
14219 /* This notification may happen after the machine object has been
14220 * uninitialized (the session was closed), so don't assert. */
14221 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14222
14223 ComPtr<IInternalSessionControl> directControl;
14224 {
14225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14226 if (mData->mSession.mLockType == LockType_VM)
14227 directControl = mData->mSession.mDirectControl;
14228 }
14229
14230 /* fail on notifications sent after #OnSessionEnd() is called, it is
14231 * expected by the caller */
14232 if (!directControl)
14233 return E_FAIL;
14234
14235 /* No locks should be held at this point. */
14236 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14237 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14238
14239 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14240}
14241
14242/**
14243 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14244 */
14245HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14246 IVirtualBoxErrorInfo *aError)
14247{
14248 LogFlowThisFunc(("\n"));
14249
14250 AutoCaller autoCaller(this);
14251
14252 /* This notification may happen after the machine object has been
14253 * uninitialized (the session was closed), so don't assert. */
14254 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14255
14256 ComPtr<IInternalSessionControl> directControl;
14257 {
14258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14259 if (mData->mSession.mLockType == LockType_VM)
14260 directControl = mData->mSession.mDirectControl;
14261 }
14262
14263 /* fail on notifications sent after #OnSessionEnd() is called, it is
14264 * expected by the caller */
14265 if (!directControl)
14266 return E_FAIL;
14267
14268 /* No locks should be held at this point. */
14269 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14270 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14271
14272 return directControl->OnUSBDeviceDetach(aId, aError);
14273}
14274
14275// protected methods
14276/////////////////////////////////////////////////////////////////////////////
14277
14278/**
14279 * Deletes the given file if it is no longer in use by either the current machine state
14280 * (if the machine is "saved") or any of the machine's snapshots.
14281 *
14282 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14283 * but is different for each SnapshotMachine. When calling this, the order of calling this
14284 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14285 * is therefore critical. I know, it's all rather messy.
14286 *
14287 * @param strStateFile
14288 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14289 * the test for whether the saved state file is in use.
14290 */
14291void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14292 Snapshot *pSnapshotToIgnore)
14293{
14294 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14295 if ( (strStateFile.isNotEmpty())
14296 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14297 )
14298 // ... and it must also not be shared with other snapshots
14299 if ( !mData->mFirstSnapshot
14300 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14301 // this checks the SnapshotMachine's state file paths
14302 )
14303 RTFileDelete(strStateFile.c_str());
14304}
14305
14306/**
14307 * Locks the attached media.
14308 *
14309 * All attached hard disks are locked for writing and DVD/floppy are locked for
14310 * reading. Parents of attached hard disks (if any) are locked for reading.
14311 *
14312 * This method also performs accessibility check of all media it locks: if some
14313 * media is inaccessible, the method will return a failure and a bunch of
14314 * extended error info objects per each inaccessible medium.
14315 *
14316 * Note that this method is atomic: if it returns a success, all media are
14317 * locked as described above; on failure no media is locked at all (all
14318 * succeeded individual locks will be undone).
14319 *
14320 * The caller is responsible for doing the necessary state sanity checks.
14321 *
14322 * The locks made by this method must be undone by calling #unlockMedia() when
14323 * no more needed.
14324 */
14325HRESULT SessionMachine::i_lockMedia()
14326{
14327 AutoCaller autoCaller(this);
14328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14329
14330 AutoMultiWriteLock2 alock(this->lockHandle(),
14331 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14332
14333 /* bail out if trying to lock things with already set up locking */
14334 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14335
14336 MultiResult mrc(S_OK);
14337
14338 /* Collect locking information for all medium objects attached to the VM. */
14339 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14340 it != mMediaData->mAttachments.end();
14341 ++it)
14342 {
14343 MediumAttachment* pAtt = *it;
14344 DeviceType_T devType = pAtt->i_getType();
14345 Medium *pMedium = pAtt->i_getMedium();
14346
14347 MediumLockList *pMediumLockList(new MediumLockList());
14348 // There can be attachments without a medium (floppy/dvd), and thus
14349 // it's impossible to create a medium lock list. It still makes sense
14350 // to have the empty medium lock list in the map in case a medium is
14351 // attached later.
14352 if (pMedium != NULL)
14353 {
14354 MediumType_T mediumType = pMedium->i_getType();
14355 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14356 || mediumType == MediumType_Shareable;
14357 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14358
14359 alock.release();
14360 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14361 !fIsReadOnlyLock /* fMediumLockWrite */,
14362 false /* fMediumLockWriteAll */,
14363 NULL,
14364 *pMediumLockList);
14365 alock.acquire();
14366 if (FAILED(mrc))
14367 {
14368 delete pMediumLockList;
14369 mData->mSession.mLockedMedia.Clear();
14370 break;
14371 }
14372 }
14373
14374 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14375 if (FAILED(rc))
14376 {
14377 mData->mSession.mLockedMedia.Clear();
14378 mrc = setError(rc,
14379 tr("Collecting locking information for all attached media failed"));
14380 break;
14381 }
14382 }
14383
14384 if (SUCCEEDED(mrc))
14385 {
14386 /* Now lock all media. If this fails, nothing is locked. */
14387 alock.release();
14388 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14389 alock.acquire();
14390 if (FAILED(rc))
14391 {
14392 mrc = setError(rc,
14393 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14394 }
14395 }
14396
14397 return mrc;
14398}
14399
14400/**
14401 * Undoes the locks made by by #lockMedia().
14402 */
14403HRESULT SessionMachine::i_unlockMedia()
14404{
14405 AutoCaller autoCaller(this);
14406 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14407
14408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14409
14410 /* we may be holding important error info on the current thread;
14411 * preserve it */
14412 ErrorInfoKeeper eik;
14413
14414 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14415 AssertComRC(rc);
14416 return rc;
14417}
14418
14419/**
14420 * Helper to change the machine state (reimplementation).
14421 *
14422 * @note Locks this object for writing.
14423 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14424 * it can cause crashes in random places due to unexpectedly committing
14425 * the current settings. The caller is responsible for that. The call
14426 * to saveStateSettings is fine, because this method does not commit.
14427 */
14428HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14429{
14430 LogFlowThisFuncEnter();
14431 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14432
14433 AutoCaller autoCaller(this);
14434 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14435
14436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14437
14438 MachineState_T oldMachineState = mData->mMachineState;
14439
14440 AssertMsgReturn(oldMachineState != aMachineState,
14441 ("oldMachineState=%s, aMachineState=%s\n",
14442 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14443 E_FAIL);
14444
14445 HRESULT rc = S_OK;
14446
14447 int stsFlags = 0;
14448 bool deleteSavedState = false;
14449
14450 /* detect some state transitions */
14451
14452 if ( ( oldMachineState == MachineState_Saved
14453 && aMachineState == MachineState_Restoring)
14454 || ( ( oldMachineState == MachineState_PoweredOff
14455 || oldMachineState == MachineState_Teleported
14456 || oldMachineState == MachineState_Aborted
14457 )
14458 && ( aMachineState == MachineState_TeleportingIn
14459 || aMachineState == MachineState_Starting
14460 )
14461 )
14462 )
14463 {
14464 /* The EMT thread is about to start */
14465
14466 /* Nothing to do here for now... */
14467
14468 /// @todo NEWMEDIA don't let mDVDDrive and other children
14469 /// change anything when in the Starting/Restoring state
14470 }
14471 else if ( ( oldMachineState == MachineState_Running
14472 || oldMachineState == MachineState_Paused
14473 || oldMachineState == MachineState_Teleporting
14474 || oldMachineState == MachineState_OnlineSnapshotting
14475 || oldMachineState == MachineState_LiveSnapshotting
14476 || oldMachineState == MachineState_Stuck
14477 || oldMachineState == MachineState_Starting
14478 || oldMachineState == MachineState_Stopping
14479 || oldMachineState == MachineState_Saving
14480 || oldMachineState == MachineState_Restoring
14481 || oldMachineState == MachineState_TeleportingPausedVM
14482 || oldMachineState == MachineState_TeleportingIn
14483 )
14484 && ( aMachineState == MachineState_PoweredOff
14485 || aMachineState == MachineState_Saved
14486 || aMachineState == MachineState_Teleported
14487 || aMachineState == MachineState_Aborted
14488 )
14489 )
14490 {
14491 /* The EMT thread has just stopped, unlock attached media. Note that as
14492 * opposed to locking that is done from Console, we do unlocking here
14493 * because the VM process may have aborted before having a chance to
14494 * properly unlock all media it locked. */
14495
14496 unlockMedia();
14497 }
14498
14499 if (oldMachineState == MachineState_Restoring)
14500 {
14501 if (aMachineState != MachineState_Saved)
14502 {
14503 /*
14504 * delete the saved state file once the machine has finished
14505 * restoring from it (note that Console sets the state from
14506 * Restoring to Saved if the VM couldn't restore successfully,
14507 * to give the user an ability to fix an error and retry --
14508 * we keep the saved state file in this case)
14509 */
14510 deleteSavedState = true;
14511 }
14512 }
14513 else if ( oldMachineState == MachineState_Saved
14514 && ( aMachineState == MachineState_PoweredOff
14515 || aMachineState == MachineState_Aborted
14516 || aMachineState == MachineState_Teleported
14517 )
14518 )
14519 {
14520 /*
14521 * delete the saved state after SessionMachine::ForgetSavedState() is called
14522 * or if the VM process (owning a direct VM session) crashed while the
14523 * VM was Saved
14524 */
14525
14526 /// @todo (dmik)
14527 // Not sure that deleting the saved state file just because of the
14528 // client death before it attempted to restore the VM is a good
14529 // thing. But when it crashes we need to go to the Aborted state
14530 // which cannot have the saved state file associated... The only
14531 // way to fix this is to make the Aborted condition not a VM state
14532 // but a bool flag: i.e., when a crash occurs, set it to true and
14533 // change the state to PoweredOff or Saved depending on the
14534 // saved state presence.
14535
14536 deleteSavedState = true;
14537 mData->mCurrentStateModified = TRUE;
14538 stsFlags |= SaveSTS_CurStateModified;
14539 }
14540
14541 if ( aMachineState == MachineState_Starting
14542 || aMachineState == MachineState_Restoring
14543 || aMachineState == MachineState_TeleportingIn
14544 )
14545 {
14546 /* set the current state modified flag to indicate that the current
14547 * state is no more identical to the state in the
14548 * current snapshot */
14549 if (!mData->mCurrentSnapshot.isNull())
14550 {
14551 mData->mCurrentStateModified = TRUE;
14552 stsFlags |= SaveSTS_CurStateModified;
14553 }
14554 }
14555
14556 if (deleteSavedState)
14557 {
14558 if (mRemoveSavedState)
14559 {
14560 Assert(!mSSData->strStateFilePath.isEmpty());
14561
14562 // it is safe to delete the saved state file if ...
14563 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14564 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14565 // ... none of the snapshots share the saved state file
14566 )
14567 RTFileDelete(mSSData->strStateFilePath.c_str());
14568 }
14569
14570 mSSData->strStateFilePath.setNull();
14571 stsFlags |= SaveSTS_StateFilePath;
14572 }
14573
14574 /* redirect to the underlying peer machine */
14575 mPeer->i_setMachineState(aMachineState);
14576
14577 if ( oldMachineState != MachineState_RestoringSnapshot
14578 && ( aMachineState == MachineState_PoweredOff
14579 || aMachineState == MachineState_Teleported
14580 || aMachineState == MachineState_Aborted
14581 || aMachineState == MachineState_Saved))
14582 {
14583 /* the machine has stopped execution
14584 * (or the saved state file was adopted) */
14585 stsFlags |= SaveSTS_StateTimeStamp;
14586 }
14587
14588 if ( ( oldMachineState == MachineState_PoweredOff
14589 || oldMachineState == MachineState_Aborted
14590 || oldMachineState == MachineState_Teleported
14591 )
14592 && aMachineState == MachineState_Saved)
14593 {
14594 /* the saved state file was adopted */
14595 Assert(!mSSData->strStateFilePath.isEmpty());
14596 stsFlags |= SaveSTS_StateFilePath;
14597 }
14598
14599#ifdef VBOX_WITH_GUEST_PROPS
14600 if ( aMachineState == MachineState_PoweredOff
14601 || aMachineState == MachineState_Aborted
14602 || aMachineState == MachineState_Teleported)
14603 {
14604 /* Make sure any transient guest properties get removed from the
14605 * property store on shutdown. */
14606 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14607
14608 /* remove it from the settings representation */
14609 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14610 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14611 it != llGuestProperties.end();
14612 /*nothing*/)
14613 {
14614 const settings::GuestProperty &prop = *it;
14615 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14616 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14617 {
14618 it = llGuestProperties.erase(it);
14619 fNeedsSaving = true;
14620 }
14621 else
14622 {
14623 ++it;
14624 }
14625 }
14626
14627 /* Additionally remove it from the HWData representation. Required to
14628 * keep everything in sync, as this is what the API keeps using. */
14629 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14630 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14631 it != llHWGuestProperties.end();
14632 /*nothing*/)
14633 {
14634 uint32_t fFlags = it->second.mFlags;
14635 if ( fFlags & guestProp::TRANSIENT
14636 || fFlags & guestProp::TRANSRESET)
14637 {
14638 /* iterator where we need to continue after the erase call
14639 * (C++03 is a fact still, and it doesn't return the iterator
14640 * which would allow continuing) */
14641 HWData::GuestPropertyMap::iterator it2 = it;
14642 ++it2;
14643 llHWGuestProperties.erase(it);
14644 it = it2;
14645 fNeedsSaving = true;
14646 }
14647 else
14648 {
14649 ++it;
14650 }
14651 }
14652
14653 if (fNeedsSaving)
14654 {
14655 mData->mCurrentStateModified = TRUE;
14656 stsFlags |= SaveSTS_CurStateModified;
14657 }
14658 }
14659#endif /* VBOX_WITH_GUEST_PROPS */
14660
14661 rc = i_saveStateSettings(stsFlags);
14662
14663 if ( ( oldMachineState != MachineState_PoweredOff
14664 && oldMachineState != MachineState_Aborted
14665 && oldMachineState != MachineState_Teleported
14666 )
14667 && ( aMachineState == MachineState_PoweredOff
14668 || aMachineState == MachineState_Aborted
14669 || aMachineState == MachineState_Teleported
14670 )
14671 )
14672 {
14673 /* we've been shut down for any reason */
14674 /* no special action so far */
14675 }
14676
14677 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14678 LogFlowThisFuncLeave();
14679 return rc;
14680}
14681
14682/**
14683 * Sends the current machine state value to the VM process.
14684 *
14685 * @note Locks this object for reading, then calls a client process.
14686 */
14687HRESULT SessionMachine::i_updateMachineStateOnClient()
14688{
14689 AutoCaller autoCaller(this);
14690 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14691
14692 ComPtr<IInternalSessionControl> directControl;
14693 {
14694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14695 AssertReturn(!!mData, E_FAIL);
14696 if (mData->mSession.mLockType == LockType_VM)
14697 directControl = mData->mSession.mDirectControl;
14698
14699 /* directControl may be already set to NULL here in #OnSessionEnd()
14700 * called too early by the direct session process while there is still
14701 * some operation (like deleting the snapshot) in progress. The client
14702 * process in this case is waiting inside Session::close() for the
14703 * "end session" process object to complete, while #uninit() called by
14704 * #checkForDeath() on the Watcher thread is waiting for the pending
14705 * operation to complete. For now, we accept this inconsistent behavior
14706 * and simply do nothing here. */
14707
14708 if (mData->mSession.mState == SessionState_Unlocking)
14709 return S_OK;
14710 }
14711
14712 /* ignore notifications sent after #OnSessionEnd() is called */
14713 if (!directControl)
14714 return S_OK;
14715
14716 return directControl->UpdateMachineState(mData->mMachineState);
14717}
14718
14719
14720/**
14721 * Static Machine method that can get passed to RTThreadCreate to
14722 * have a thread started for a Task. See Machine::Task.
14723 */
14724/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14725{
14726 AssertReturn(pvUser, VERR_INVALID_POINTER);
14727
14728 Task *pTask = static_cast<Task *>(pvUser);
14729 pTask->handler();
14730 /** @todo r=klaus it would be safer to update the progress object here,
14731 * as it avoids possible races due to scoping issues/tricks in the handler */
14732 // it's our responsibility to delete the task
14733 delete pTask;
14734
14735 return 0;
14736}
14737
14738/*static*/
14739HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14740{
14741 va_list args;
14742 va_start(args, pcszMsg);
14743 HRESULT rc = setErrorInternal(aResultCode,
14744 getStaticClassIID(),
14745 getStaticComponentName(),
14746 Utf8Str(pcszMsg, args),
14747 false /* aWarning */,
14748 true /* aLogIt */);
14749 va_end(args);
14750 return rc;
14751}
14752
14753
14754HRESULT Machine::updateState(MachineState_T aState)
14755{
14756 NOREF(aState);
14757 ReturnComNotImplemented();
14758}
14759
14760HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14761{
14762 NOREF(aProgress);
14763 ReturnComNotImplemented();
14764}
14765
14766HRESULT Machine::endPowerUp(LONG aResult)
14767{
14768 NOREF(aResult);
14769 ReturnComNotImplemented();
14770}
14771
14772HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14773{
14774 NOREF(aProgress);
14775 ReturnComNotImplemented();
14776}
14777
14778HRESULT Machine::endPoweringDown(LONG aResult,
14779 const com::Utf8Str &aErrMsg)
14780{
14781 NOREF(aResult);
14782 NOREF(aErrMsg);
14783 ReturnComNotImplemented();
14784}
14785
14786HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14787 BOOL *aMatched,
14788 ULONG *aMaskedInterfaces)
14789{
14790 NOREF(aDevice);
14791 NOREF(aMatched);
14792 NOREF(aMaskedInterfaces);
14793 ReturnComNotImplemented();
14794
14795}
14796
14797HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14798{
14799 NOREF(aId); NOREF(aCaptureFilename);
14800 ReturnComNotImplemented();
14801}
14802
14803HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14804 BOOL aDone)
14805{
14806 NOREF(aId);
14807 NOREF(aDone);
14808 ReturnComNotImplemented();
14809}
14810
14811HRESULT Machine::autoCaptureUSBDevices()
14812{
14813 ReturnComNotImplemented();
14814}
14815
14816HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14817{
14818 NOREF(aDone);
14819 ReturnComNotImplemented();
14820}
14821
14822HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14823 ComPtr<IProgress> &aProgress)
14824{
14825 NOREF(aSession);
14826 NOREF(aProgress);
14827 ReturnComNotImplemented();
14828}
14829
14830HRESULT Machine::finishOnlineMergeMedium()
14831{
14832 ReturnComNotImplemented();
14833}
14834
14835HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14836 std::vector<com::Utf8Str> &aValues,
14837 std::vector<LONG64> &aTimestamps,
14838 std::vector<com::Utf8Str> &aFlags)
14839{
14840 NOREF(aNames);
14841 NOREF(aValues);
14842 NOREF(aTimestamps);
14843 NOREF(aFlags);
14844 ReturnComNotImplemented();
14845}
14846
14847HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14848 const com::Utf8Str &aValue,
14849 LONG64 aTimestamp,
14850 const com::Utf8Str &aFlags)
14851{
14852 NOREF(aName);
14853 NOREF(aValue);
14854 NOREF(aTimestamp);
14855 NOREF(aFlags);
14856 ReturnComNotImplemented();
14857}
14858
14859HRESULT Machine::lockMedia()
14860{
14861 ReturnComNotImplemented();
14862}
14863
14864HRESULT Machine::unlockMedia()
14865{
14866 ReturnComNotImplemented();
14867}
14868
14869HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14870 ComPtr<IMediumAttachment> &aNewAttachment)
14871{
14872 NOREF(aAttachment);
14873 NOREF(aNewAttachment);
14874 ReturnComNotImplemented();
14875}
14876
14877HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14878 ULONG aCpuUser,
14879 ULONG aCpuKernel,
14880 ULONG aCpuIdle,
14881 ULONG aMemTotal,
14882 ULONG aMemFree,
14883 ULONG aMemBalloon,
14884 ULONG aMemShared,
14885 ULONG aMemCache,
14886 ULONG aPagedTotal,
14887 ULONG aMemAllocTotal,
14888 ULONG aMemFreeTotal,
14889 ULONG aMemBalloonTotal,
14890 ULONG aMemSharedTotal,
14891 ULONG aVmNetRx,
14892 ULONG aVmNetTx)
14893{
14894 NOREF(aValidStats);
14895 NOREF(aCpuUser);
14896 NOREF(aCpuKernel);
14897 NOREF(aCpuIdle);
14898 NOREF(aMemTotal);
14899 NOREF(aMemFree);
14900 NOREF(aMemBalloon);
14901 NOREF(aMemShared);
14902 NOREF(aMemCache);
14903 NOREF(aPagedTotal);
14904 NOREF(aMemAllocTotal);
14905 NOREF(aMemFreeTotal);
14906 NOREF(aMemBalloonTotal);
14907 NOREF(aMemSharedTotal);
14908 NOREF(aVmNetRx);
14909 NOREF(aVmNetTx);
14910 ReturnComNotImplemented();
14911}
14912
14913HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14914 com::Utf8Str &aResult)
14915{
14916 NOREF(aAuthParams);
14917 NOREF(aResult);
14918 ReturnComNotImplemented();
14919}
14920
14921HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
14922{
14923 NOREF(aFlags);
14924 ReturnComNotImplemented();
14925}
14926
14927/* This isn't handled entirely by the wrapper generator yet. */
14928#ifdef VBOX_WITH_XPCOM
14929NS_DECL_CLASSINFO(SessionMachine)
14930NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
14931
14932NS_DECL_CLASSINFO(SnapshotMachine)
14933NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
14934#endif
Note: See TracBrowser for help on using the repository browser.

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