VirtualBox

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

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

Main/Machine: fix condition when permanent shared folders can be created or removed, was too strict

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 503.8 KB
Line 
1/* $Id: MachineImpl.cpp 55094 2015-04-02 16:33:37Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#ifdef VBOX_WITH_DTRACE_R3_MAIN
90# include "dtrace/VBoxAPI.h"
91#endif
92
93#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
94# define HOSTSUFF_EXE ".exe"
95#else /* !RT_OS_WINDOWS */
96# define HOSTSUFF_EXE ""
97#endif /* !RT_OS_WINDOWS */
98
99// defines / prototypes
100/////////////////////////////////////////////////////////////////////////////
101
102/////////////////////////////////////////////////////////////////////////////
103// Machine::Data structure
104/////////////////////////////////////////////////////////////////////////////
105
106Machine::Data::Data()
107{
108 mRegistered = FALSE;
109 pMachineConfigFile = NULL;
110 /* Contains hints on what has changed when the user is using the VM (config
111 * changes, running the VM, ...). This is used to decide if a config needs
112 * to be written to disk. */
113 flModifications = 0;
114 /* VM modification usually also trigger setting the current state to
115 * "Modified". Although this is not always the case. An e.g. is the VM
116 * initialization phase or when snapshot related data is changed. The
117 * actually behavior is controlled by the following flag. */
118 m_fAllowStateModification = false;
119 mAccessible = FALSE;
120 /* mUuid is initialized in Machine::init() */
121
122 mMachineState = MachineState_PoweredOff;
123 RTTimeNow(&mLastStateChange);
124
125 mMachineStateDeps = 0;
126 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
127 mMachineStateChangePending = 0;
128
129 mCurrentStateModified = TRUE;
130 mGuestPropertiesModified = FALSE;
131
132 mSession.mPID = NIL_RTPROCESS;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mTripleFaultReset = false;
197 mHPETEnabled = false;
198
199 /* default boot order: floppy - DVD - HDD */
200 mBootOrder[0] = DeviceType_Floppy;
201 mBootOrder[1] = DeviceType_DVD;
202 mBootOrder[2] = DeviceType_HardDisk;
203 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
204 mBootOrder[i] = DeviceType_Null;
205
206 mClipboardMode = ClipboardMode_Disabled;
207 mDnDMode = DnDMode_Disabled;
208 mGuestPropertyNotificationPatterns = "";
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222
223 /* Maximum CPU execution cap by default. */
224 mCpuExecutionCap = 100;
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HDData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::MediaData::MediaData()
236{
237}
238
239Machine::MediaData::~MediaData()
240{
241}
242
243/////////////////////////////////////////////////////////////////////////////
244// Machine class
245/////////////////////////////////////////////////////////////////////////////
246
247// constructor / destructor
248/////////////////////////////////////////////////////////////////////////////
249
250Machine::Machine() :
251#ifdef VBOX_WITH_RESOURCE_USAGE_API
252 mCollectorGuest(NULL),
253#endif
254 mPeer(NULL),
255 mParent(NULL),
256 mSerialPorts(),
257 mParallelPorts(),
258 uRegistryNeedsSaving(0)
259{}
260
261Machine::~Machine()
262{}
263
264HRESULT Machine::FinalConstruct()
265{
266 LogFlowThisFunc(("\n"));
267 return BaseFinalConstruct();
268}
269
270void Machine::FinalRelease()
271{
272 LogFlowThisFunc(("\n"));
273 uninit();
274 BaseFinalRelease();
275}
276
277/**
278 * Initializes a new machine instance; this init() variant creates a new, empty machine.
279 * This gets called from VirtualBox::CreateMachine().
280 *
281 * @param aParent Associated parent object
282 * @param strConfigFile Local file system path to the VM settings file (can
283 * be relative to the VirtualBox config directory).
284 * @param strName name for the machine
285 * @param llGroups list of groups for the machine
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Apply BIOS defaults */
349 mBIOSSettings->i_applyDefaults(aOsType);
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Let the OS type select 64-bit ness. */
360 mHWData->mLongMode = aOsType->i_is64Bit()
361 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 }
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 i_allowStateModification();
367
368 /* commit all changes made during the initialization */
369 i_commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param aConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = i_registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 i_allowStateModification();
469
470 i_commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->i_unregisterMachineMedia(i_getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent
607 * @return
608 */
609HRESULT Machine::initImpl(VirtualBox *aParent,
610 const Utf8Str &strConfigFile)
611{
612 LogFlowThisFuncEnter();
613
614 AssertReturn(aParent, E_INVALIDARG);
615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
616
617 HRESULT rc = S_OK;
618
619 /* share the parent weakly */
620 unconst(mParent) = aParent;
621
622 /* allocate the essential machine data structure (the rest will be
623 * allocated later by initDataAndChildObjects() */
624 mData.allocate();
625
626 /* memorize the config file name (as provided) */
627 mData->m_strConfigFile = strConfigFile;
628
629 /* get the full file name */
630 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
631 if (RT_FAILURE(vrc1))
632 return setError(VBOX_E_FILE_ERROR,
633 tr("Invalid machine settings file name '%s' (%Rrc)"),
634 strConfigFile.c_str(),
635 vrc1);
636
637 LogFlowThisFuncLeave();
638
639 return rc;
640}
641
642/**
643 * Tries to create a machine settings file in the path stored in the machine
644 * instance data. Used when a new machine is created to fail gracefully if
645 * the settings file could not be written (e.g. because machine dir is read-only).
646 * @return
647 */
648HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
649{
650 HRESULT rc = S_OK;
651
652 // when we create a new machine, we must be able to create the settings file
653 RTFILE f = NIL_RTFILE;
654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
655 if ( RT_SUCCESS(vrc)
656 || vrc == VERR_SHARING_VIOLATION
657 )
658 {
659 if (RT_SUCCESS(vrc))
660 RTFileClose(f);
661 if (!fForceOverwrite)
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Machine settings file '%s' already exists"),
664 mData->m_strConfigFileFull.c_str());
665 else
666 {
667 /* try to delete the config file, as otherwise the creation
668 * of a new settings file will fail. */
669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
670 if (RT_FAILURE(vrc2))
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Could not delete the existing settings file '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(), vrc2);
674 }
675 }
676 else if ( vrc != VERR_FILE_NOT_FOUND
677 && vrc != VERR_PATH_NOT_FOUND
678 )
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Invalid machine settings file name '%s' (%Rrc)"),
681 mData->m_strConfigFileFull.c_str(),
682 vrc);
683 return rc;
684}
685
686/**
687 * Initializes the registered machine by loading the settings file.
688 * This method is separated from #init() in order to make it possible to
689 * retry the operation after VirtualBox startup instead of refusing to
690 * startup the whole VirtualBox server in case if the settings file of some
691 * registered VM is invalid or inaccessible.
692 *
693 * @note Must be always called from this object's write lock
694 * (unless called from #init() that doesn't need any locking).
695 * @note Locks the mUSBController method for writing.
696 * @note Subclasses must not call this method.
697 */
698HRESULT Machine::i_registeredInit()
699{
700 AssertReturn(!i_isSessionMachine(), E_FAIL);
701 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
702 AssertReturn(mData->mUuid.isValid(), E_FAIL);
703 AssertReturn(!mData->mAccessible, E_FAIL);
704
705 HRESULT rc = initDataAndChildObjects();
706
707 if (SUCCEEDED(rc))
708 {
709 /* Temporarily reset the registered flag in order to let setters
710 * potentially called from loadSettings() succeed (isMutable() used in
711 * all setters will return FALSE for a Machine instance if mRegistered
712 * is TRUE). */
713 mData->mRegistered = FALSE;
714
715 try
716 {
717 // load and parse machine XML; this will throw on XML or logic errors
718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
719
720 if (mData->mUuid != mData->pMachineConfigFile->uuid)
721 throw setError(E_FAIL,
722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
723 mData->pMachineConfigFile->uuid.raw(),
724 mData->m_strConfigFileFull.c_str(),
725 mData->mUuid.toString().c_str(),
726 mParent->i_settingsFilePath().c_str());
727
728 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
729 NULL /* const Guid *puuidRegistry */);
730 if (FAILED(rc)) throw rc;
731 }
732 catch (HRESULT err)
733 {
734 /* we assume that error info is set by the thrower */
735 rc = err;
736 }
737 catch (...)
738 {
739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
740 }
741
742 /* Restore the registered flag (even on failure) */
743 mData->mRegistered = TRUE;
744 }
745
746 if (SUCCEEDED(rc))
747 {
748 /* Set mAccessible to TRUE only if we successfully locked and loaded
749 * the settings file */
750 mData->mAccessible = TRUE;
751
752 /* commit all changes made during loading the settings file */
753 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
754 /// @todo r=klaus for some reason the settings loading logic backs up
755 // the settings, and therefore a commit is needed. Should probably be changed.
756 }
757 else
758 {
759 /* If the machine is registered, then, instead of returning a
760 * failure, we mark it as inaccessible and set the result to
761 * success to give it a try later */
762
763 /* fetch the current error info */
764 mData->mAccessError = com::ErrorInfo();
765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
766 mData->mUuid.raw(),
767 mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 i_rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->i_unregisterMachineMedia(i_getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 i_saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!i_isSnapshotMachine());
820 Assert(!i_isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it does
835 * VirtualBox::addCaller() for the duration of the
836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 LogWarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(i_getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->i_unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
879 i_rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// Wrapped IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
894{
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 aParent = pVirtualBox;
898
899 return S_OK;
900}
901
902
903HRESULT Machine::getAccessible(BOOL *aAccessible)
904{
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->i_dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = i_registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->i_onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
959 {
960 /* return shortly */
961 aAccessError = NULL;
962 return S_OK;
963 }
964
965 HRESULT rc = S_OK;
966
967 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
968 rc = errorInfo.createObject();
969 if (SUCCEEDED(rc))
970 {
971 errorInfo->init(mData->mAccessError.getResultCode(),
972 mData->mAccessError.getInterfaceID().ref(),
973 Utf8Str(mData->mAccessError.getComponent()).c_str(),
974 Utf8Str(mData->mAccessError.getText()));
975 aAccessError = errorInfo;
976 }
977
978 return rc;
979}
980
981HRESULT Machine::getName(com::Utf8Str &aName)
982{
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 aName = mUserData->s.strName;
986
987 return S_OK;
988}
989
990HRESULT Machine::setName(const com::Utf8Str &aName)
991{
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995
996 if (test.isValid())
997 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
998
999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 HRESULT rc = i_checkStateDependency(MutableStateDep);
1002 if (FAILED(rc)) return rc;
1003
1004 i_setModified(IsModified_MachineData);
1005 mUserData.backup();
1006 mUserData->s.strName = aName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1012{
1013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 aDescription = mUserData->s.strDescription;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1021{
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 // this can be done in principle in any state as it doesn't affect the VM
1025 // significantly, but play safe by not messing around while complex
1026 // activities are going on
1027 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1028 if (FAILED(rc)) return rc;
1029
1030 i_setModified(IsModified_MachineData);
1031 mUserData.backup();
1032 mUserData->s.strDescription = aDescription;
1033
1034 return S_OK;
1035}
1036
1037HRESULT Machine::getId(com::Guid &aId)
1038{
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 aId = mData->mUuid;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049 aGroups.resize(mUserData->s.llGroups.size());
1050 size_t i = 0;
1051 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1052 it != mUserData->s.llGroups.end(); ++it, ++i)
1053 aGroups[i] = (*it);
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1059{
1060 StringsList llGroups;
1061 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1062 if (FAILED(rc))
1063 return rc;
1064
1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1066
1067 rc = i_checkStateDependency(MutableOrSavedStateDep);
1068 if (FAILED(rc)) return rc;
1069
1070 i_setModified(IsModified_MachineData);
1071 mUserData.backup();
1072 mUserData->s.llGroups = llGroups;
1073
1074 return S_OK;
1075}
1076
1077HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1078{
1079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1080
1081 aOSTypeId = mUserData->s.strOsType;
1082
1083 return S_OK;
1084}
1085
1086HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1087{
1088 /* look up the object by Id to check it is valid */
1089 ComPtr<IGuestOSType> guestOSType;
1090 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1091 if (FAILED(rc)) return rc;
1092
1093 /* when setting, always use the "etalon" value for consistency -- lookup
1094 * by ID is case-insensitive and the input value may have different case */
1095 Bstr osTypeId;
1096 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1097 if (FAILED(rc)) return rc;
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 rc = i_checkStateDependency(MutableStateDep);
1102 if (FAILED(rc)) return rc;
1103
1104 i_setModified(IsModified_MachineData);
1105 mUserData.backup();
1106 mUserData->s.strOsType = osTypeId;
1107
1108 return S_OK;
1109}
1110
1111HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1112{
1113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 *aFirmwareType = mHWData->mFirmwareType;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1121{
1122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 HRESULT rc = i_checkStateDependency(MutableStateDep);
1125 if (FAILED(rc)) return rc;
1126
1127 i_setModified(IsModified_MachineData);
1128 mHWData.backup();
1129 mHWData->mFirmwareType = aFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1135{
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1144{
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 HRESULT rc = i_checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 i_setModified(IsModified_MachineData);
1151 mHWData.backup();
1152 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aPointingHIDType = mHWData->mPointingHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mPointingHIDType = aPointingHIDType;
1176
1177 return S_OK;
1178}
1179
1180HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1181{
1182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 *aChipsetType = mHWData->mChipsetType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1190{
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = i_checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 if (aChipsetType != mHWData->mChipsetType)
1197 {
1198 i_setModified(IsModified_MachineData);
1199 mHWData.backup();
1200 mHWData->mChipsetType = aChipsetType;
1201
1202 // Resize network adapter array, to be finalized on commit/rollback.
1203 // We must not throw away entries yet, otherwise settings are lost
1204 // without a way to roll back.
1205 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1206 size_t oldCount = mNetworkAdapters.size();
1207 if (newCount > oldCount)
1208 {
1209 mNetworkAdapters.resize(newCount);
1210 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1211 {
1212 unconst(mNetworkAdapters[slot]).createObject();
1213 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1214 }
1215 }
1216 }
1217
1218 return S_OK;
1219}
1220
1221HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1222{
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 *aParavirtProvider = mHWData->mParavirtProvider;
1226
1227 return S_OK;
1228}
1229
1230HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1231{
1232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 HRESULT rc = i_checkStateDependency(MutableStateDep);
1235 if (FAILED(rc)) return rc;
1236
1237 if (aParavirtProvider != mHWData->mParavirtProvider)
1238 {
1239 i_setModified(IsModified_MachineData);
1240 mHWData.backup();
1241 mHWData->mParavirtProvider = aParavirtProvider;
1242 }
1243
1244 return S_OK;
1245}
1246
1247HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1248{
1249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 *aParavirtProvider = mHWData->mParavirtProvider;
1252 switch (mHWData->mParavirtProvider)
1253 {
1254 case ParavirtProvider_None:
1255 case ParavirtProvider_HyperV:
1256 case ParavirtProvider_KVM:
1257 case ParavirtProvider_Minimal:
1258 break;
1259
1260 /* Resolve dynamic provider types to the effective types. */
1261 default:
1262 {
1263 ComPtr<IGuestOSType> ptrGuestOSType;
1264 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1266
1267 Bstr guestTypeFamilyId;
1268 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1269 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1270 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1271
1272 switch (mHWData->mParavirtProvider)
1273 {
1274 case ParavirtProvider_Legacy:
1275 {
1276 if (fOsXGuest)
1277 *aParavirtProvider = ParavirtProvider_Minimal;
1278 else
1279 *aParavirtProvider = ParavirtProvider_None;
1280 break;
1281 }
1282
1283 case ParavirtProvider_Default:
1284 {
1285 if (fOsXGuest)
1286 *aParavirtProvider = ParavirtProvider_Minimal;
1287 else if ( mUserData->s.strOsType == "Windows10"
1288 || mUserData->s.strOsType == "Windows10_64"
1289 || mUserData->s.strOsType == "Windows81"
1290 || mUserData->s.strOsType == "Windows81_64"
1291 || mUserData->s.strOsType == "Windows8"
1292 || mUserData->s.strOsType == "Windows8_64"
1293 || mUserData->s.strOsType == "Windows7"
1294 || mUserData->s.strOsType == "Windows7_64"
1295 || mUserData->s.strOsType == "WindowsVista"
1296 || mUserData->s.strOsType == "WindowsVista_64"
1297 || mUserData->s.strOsType == "Windows2012"
1298 || mUserData->s.strOsType == "Windows2012_64"
1299 || mUserData->s.strOsType == "Windows2008"
1300 || mUserData->s.strOsType == "Windows2008_64")
1301 {
1302 *aParavirtProvider = ParavirtProvider_HyperV;
1303 }
1304 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1305 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1306 || mUserData->s.strOsType == "Linux"
1307 || mUserData->s.strOsType == "Linux_64"
1308 || mUserData->s.strOsType == "ArchLinux"
1309 || mUserData->s.strOsType == "ArchLinux_64"
1310 || mUserData->s.strOsType == "Debian"
1311 || mUserData->s.strOsType == "Debian_64"
1312 || mUserData->s.strOsType == "Fedora"
1313 || mUserData->s.strOsType == "Fedora_64"
1314 || mUserData->s.strOsType == "Gentoo"
1315 || mUserData->s.strOsType == "Gentoo_64"
1316 || mUserData->s.strOsType == "Mandriva"
1317 || mUserData->s.strOsType == "Mandriva_64"
1318 || mUserData->s.strOsType == "OpenSUSE"
1319 || mUserData->s.strOsType == "OpenSUSE_64"
1320 || mUserData->s.strOsType == "Oracle"
1321 || mUserData->s.strOsType == "Oracle_64"
1322 || mUserData->s.strOsType == "RedHat"
1323 || mUserData->s.strOsType == "RedHat_64"
1324 || mUserData->s.strOsType == "Turbolinux"
1325 || mUserData->s.strOsType == "Turbolinux_64"
1326 || mUserData->s.strOsType == "Ubuntu"
1327 || mUserData->s.strOsType == "Ubuntu_64"
1328 || mUserData->s.strOsType == "Xandros"
1329 || mUserData->s.strOsType == "Xandros_64")
1330 {
1331 *aParavirtProvider = ParavirtProvider_KVM;
1332 }
1333 else
1334 *aParavirtProvider = ParavirtProvider_None;
1335 break;
1336 }
1337 }
1338 break;
1339 }
1340 }
1341
1342 Assert( *aParavirtProvider == ParavirtProvider_None
1343 || *aParavirtProvider == ParavirtProvider_Minimal
1344 || *aParavirtProvider == ParavirtProvider_HyperV
1345 || *aParavirtProvider == ParavirtProvider_KVM);
1346 return S_OK;
1347}
1348
1349HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1350{
1351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 aHardwareVersion = mHWData->mHWVersion;
1354
1355 return S_OK;
1356}
1357
1358HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1359{
1360 /* check known version */
1361 Utf8Str hwVersion = aHardwareVersion;
1362 if ( hwVersion.compare("1") != 0
1363 && hwVersion.compare("2") != 0)
1364 return setError(E_INVALIDARG,
1365 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1366
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 HRESULT rc = i_checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 i_setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 mHWData->mHWVersion = aHardwareVersion;
1375
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 if (!mHWData->mHardwareUUID.isZero())
1384 aHardwareUUID = mHWData->mHardwareUUID;
1385 else
1386 aHardwareUUID = mData->mUuid;
1387
1388 return S_OK;
1389}
1390
1391HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1392{
1393 if (!aHardwareUUID.isValid())
1394 return E_INVALIDARG;
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 HRESULT rc = i_checkStateDependency(MutableStateDep);
1399 if (FAILED(rc)) return rc;
1400
1401 i_setModified(IsModified_MachineData);
1402 mHWData.backup();
1403 if (aHardwareUUID == mData->mUuid)
1404 mHWData->mHardwareUUID.clear();
1405 else
1406 mHWData->mHardwareUUID = aHardwareUUID;
1407
1408 return S_OK;
1409}
1410
1411HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1412{
1413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 *aMemorySize = mHWData->mMemorySize;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::setMemorySize(ULONG aMemorySize)
1421{
1422 /* check RAM limits */
1423 if ( aMemorySize < MM_RAM_MIN_IN_MB
1424 || aMemorySize > MM_RAM_MAX_IN_MB
1425 )
1426 return setError(E_INVALIDARG,
1427 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1428 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1429
1430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 HRESULT rc = i_checkStateDependency(MutableStateDep);
1433 if (FAILED(rc)) return rc;
1434
1435 i_setModified(IsModified_MachineData);
1436 mHWData.backup();
1437 mHWData->mMemorySize = aMemorySize;
1438
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 *aCPUCount = mHWData->mCPUCount;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setCPUCount(ULONG aCPUCount)
1452{
1453 /* check CPU limits */
1454 if ( aCPUCount < SchemaDefs::MinCPUCount
1455 || aCPUCount > SchemaDefs::MaxCPUCount
1456 )
1457 return setError(E_INVALIDARG,
1458 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1459 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1460
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1464 if (mHWData->mCPUHotPlugEnabled)
1465 {
1466 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1467 {
1468 if (mHWData->mCPUAttached[idx])
1469 return setError(E_INVALIDARG,
1470 tr("There is still a CPU attached to socket %lu."
1471 "Detach the CPU before removing the socket"),
1472 aCPUCount, idx+1);
1473 }
1474 }
1475
1476 HRESULT rc = i_checkStateDependency(MutableStateDep);
1477 if (FAILED(rc)) return rc;
1478
1479 i_setModified(IsModified_MachineData);
1480 mHWData.backup();
1481 mHWData->mCPUCount = aCPUCount;
1482
1483 return S_OK;
1484}
1485
1486HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1487{
1488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1491
1492 return S_OK;
1493}
1494
1495HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1496{
1497 HRESULT rc = S_OK;
1498
1499 /* check throttle limits */
1500 if ( aCPUExecutionCap < 1
1501 || aCPUExecutionCap > 100
1502 )
1503 return setError(E_INVALIDARG,
1504 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1505 aCPUExecutionCap, 1, 100);
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 alock.release();
1510 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1511 alock.acquire();
1512 if (FAILED(rc)) return rc;
1513
1514 i_setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1517
1518 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1519 if (Global::IsOnline(mData->mMachineState))
1520 i_saveSettings(NULL);
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1539
1540 rc = i_checkStateDependency(MutableStateDep);
1541 if (FAILED(rc)) return rc;
1542
1543 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1544 {
1545 if (aCPUHotPlugEnabled)
1546 {
1547 i_setModified(IsModified_MachineData);
1548 mHWData.backup();
1549
1550 /* Add the amount of CPUs currently attached */
1551 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1552 mHWData->mCPUAttached[i] = true;
1553 }
1554 else
1555 {
1556 /*
1557 * We can disable hotplug only if the amount of maximum CPUs is equal
1558 * to the amount of attached CPUs
1559 */
1560 unsigned cCpusAttached = 0;
1561 unsigned iHighestId = 0;
1562
1563 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1564 {
1565 if (mHWData->mCPUAttached[i])
1566 {
1567 cCpusAttached++;
1568 iHighestId = i;
1569 }
1570 }
1571
1572 if ( (cCpusAttached != mHWData->mCPUCount)
1573 || (iHighestId >= mHWData->mCPUCount))
1574 return setError(E_INVALIDARG,
1575 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1576
1577 i_setModified(IsModified_MachineData);
1578 mHWData.backup();
1579 }
1580 }
1581
1582 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1583
1584 return rc;
1585}
1586
1587HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1588{
1589#ifdef VBOX_WITH_USB_CARDREADER
1590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1591
1592 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1593
1594 return S_OK;
1595#else
1596 NOREF(aEmulatedUSBCardReaderEnabled);
1597 return E_NOTIMPL;
1598#endif
1599}
1600
1601HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1602{
1603#ifdef VBOX_WITH_USB_CARDREADER
1604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1605
1606 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1607 if (FAILED(rc)) return rc;
1608
1609 i_setModified(IsModified_MachineData);
1610 mHWData.backup();
1611 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1612
1613 return S_OK;
1614#else
1615 NOREF(aEmulatedUSBCardReaderEnabled);
1616 return E_NOTIMPL;
1617#endif
1618}
1619
1620HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1621{
1622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 *aHPETEnabled = mHWData->mHPETEnabled;
1625
1626 return S_OK;
1627}
1628
1629HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1630{
1631 HRESULT rc = S_OK;
1632
1633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1634
1635 rc = i_checkStateDependency(MutableStateDep);
1636 if (FAILED(rc)) return rc;
1637
1638 i_setModified(IsModified_MachineData);
1639 mHWData.backup();
1640
1641 mHWData->mHPETEnabled = aHPETEnabled;
1642
1643 return rc;
1644}
1645
1646HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1647{
1648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1649
1650 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1651 return S_OK;
1652}
1653
1654HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1655{
1656 HRESULT rc = S_OK;
1657
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659
1660 i_setModified(IsModified_MachineData);
1661 mHWData.backup();
1662 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1663
1664 alock.release();
1665 rc = i_onVideoCaptureChange();
1666 alock.acquire();
1667 if (FAILED(rc))
1668 {
1669 /*
1670 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1671 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1672 * determine if it should start or stop capturing. Therefore we need to manually
1673 * undo change.
1674 */
1675 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1676 return rc;
1677 }
1678
1679 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1680 if (Global::IsOnline(mData->mMachineState))
1681 i_saveSettings(NULL);
1682
1683 return rc;
1684}
1685
1686HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1687{
1688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1689 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1690 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1691 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1692 return S_OK;
1693}
1694
1695HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1696{
1697 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1698 bool fChanged = false;
1699
1700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1701
1702 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1703 {
1704 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1705 {
1706 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1707 fChanged = true;
1708 }
1709 }
1710 if (fChanged)
1711 {
1712 alock.release();
1713 HRESULT rc = i_onVideoCaptureChange();
1714 alock.acquire();
1715 if (FAILED(rc)) return rc;
1716 i_setModified(IsModified_MachineData);
1717
1718 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1719 if (Global::IsOnline(mData->mMachineState))
1720 i_saveSettings(NULL);
1721 }
1722
1723 return S_OK;
1724}
1725
1726HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1727{
1728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1729 if (mHWData->mVideoCaptureFile.isEmpty())
1730 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1731 else
1732 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1733 return S_OK;
1734}
1735
1736HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1737{
1738 Utf8Str strFile(aVideoCaptureFile);
1739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1740
1741 if ( Global::IsOnline(mData->mMachineState)
1742 && mHWData->mVideoCaptureEnabled)
1743 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1744
1745 if (!RTPathStartsWithRoot(strFile.c_str()))
1746 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1747
1748 if (!strFile.isEmpty())
1749 {
1750 Utf8Str defaultFile;
1751 i_getDefaultVideoCaptureFile(defaultFile);
1752 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1753 strFile.setNull();
1754 }
1755
1756 i_setModified(IsModified_MachineData);
1757 mHWData.backup();
1758 mHWData->mVideoCaptureFile = strFile;
1759
1760 return S_OK;
1761}
1762
1763HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1767 return S_OK;
1768}
1769
1770HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1771{
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 if ( Global::IsOnline(mData->mMachineState)
1775 && mHWData->mVideoCaptureEnabled)
1776 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1786{
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1789 return S_OK;
1790}
1791
1792HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1793{
1794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 if ( Global::IsOnline(mData->mMachineState)
1797 && mHWData->mVideoCaptureEnabled)
1798 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1799
1800 i_setModified(IsModified_MachineData);
1801 mHWData.backup();
1802 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1808{
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1811 return S_OK;
1812}
1813
1814HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1815{
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 i_setModified(IsModified_MachineData);
1823 mHWData.backup();
1824 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1833 return S_OK;
1834}
1835
1836HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1837{
1838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 if ( Global::IsOnline(mData->mMachineState)
1841 && mHWData->mVideoCaptureEnabled)
1842 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1855 return S_OK;
1856}
1857
1858HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1859{
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 i_setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1877 return S_OK;
1878}
1879
1880HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1881{
1882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 if ( Global::IsOnline(mData->mMachineState)
1885 && mHWData->mVideoCaptureEnabled)
1886 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1887
1888 i_setModified(IsModified_MachineData);
1889 mHWData.backup();
1890 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1896{
1897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1900 return S_OK;
1901}
1902
1903HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1904{
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 if ( Global::IsOnline(mData->mMachineState)
1908 && mHWData->mVideoCaptureEnabled)
1909 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1910
1911 i_setModified(IsModified_MachineData);
1912 mHWData.backup();
1913 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1914
1915 return S_OK;
1916}
1917
1918HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1919{
1920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1921
1922 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1923
1924 return S_OK;
1925}
1926
1927HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1928{
1929 switch (aGraphicsControllerType)
1930 {
1931 case GraphicsControllerType_Null:
1932 case GraphicsControllerType_VBoxVGA:
1933#ifdef VBOX_WITH_VMSVGA
1934 case GraphicsControllerType_VMSVGA:
1935#endif
1936 break;
1937 default:
1938 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1939 }
1940
1941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1942
1943 HRESULT rc = i_checkStateDependency(MutableStateDep);
1944 if (FAILED(rc)) return rc;
1945
1946 i_setModified(IsModified_MachineData);
1947 mHWData.backup();
1948 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1949
1950 return S_OK;
1951}
1952
1953HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1954{
1955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 *aVRAMSize = mHWData->mVRAMSize;
1958
1959 return S_OK;
1960}
1961
1962HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1963{
1964 /* check VRAM limits */
1965 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1966 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1967 return setError(E_INVALIDARG,
1968 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1969 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1970
1971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 HRESULT rc = i_checkStateDependency(MutableStateDep);
1974 if (FAILED(rc)) return rc;
1975
1976 i_setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mVRAMSize = aVRAMSize;
1979
1980 return S_OK;
1981}
1982
1983/** @todo this method should not be public */
1984HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1985{
1986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1989
1990 return S_OK;
1991}
1992
1993/**
1994 * Set the memory balloon size.
1995 *
1996 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1997 * we have to make sure that we never call IGuest from here.
1998 */
1999HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2000{
2001 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2002#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2003 /* check limits */
2004 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2005 return setError(E_INVALIDARG,
2006 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2007 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2008
2009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2014
2015 return S_OK;
2016#else
2017 NOREF(aMemoryBalloonSize);
2018 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2019#endif
2020}
2021
2022HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2023{
2024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2027 return S_OK;
2028}
2029
2030HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2031{
2032#ifdef VBOX_WITH_PAGE_SHARING
2033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2039 return S_OK;
2040#else
2041 NOREF(aPageFusionEnabled);
2042 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2043#endif
2044}
2045
2046HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2047{
2048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2051
2052 return S_OK;
2053}
2054
2055HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2056{
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 HRESULT rc = i_checkStateDependency(MutableStateDep);
2060 if (FAILED(rc)) return rc;
2061
2062 /** @todo check validity! */
2063
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2067
2068 return S_OK;
2069}
2070
2071
2072HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2073{
2074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2077
2078 return S_OK;
2079}
2080
2081HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2082{
2083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 HRESULT rc = i_checkStateDependency(MutableStateDep);
2086 if (FAILED(rc)) return rc;
2087
2088 /** @todo check validity! */
2089 i_setModified(IsModified_MachineData);
2090 mHWData.backup();
2091 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2092
2093 return S_OK;
2094}
2095
2096HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2097{
2098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2099
2100 *aMonitorCount = mHWData->mMonitorCount;
2101
2102 return S_OK;
2103}
2104
2105HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2106{
2107 /* make sure monitor count is a sensible number */
2108 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2109 return setError(E_INVALIDARG,
2110 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2111 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2112
2113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 HRESULT rc = i_checkStateDependency(MutableStateDep);
2116 if (FAILED(rc)) return rc;
2117
2118 i_setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mMonitorCount = aMonitorCount;
2121
2122 return S_OK;
2123}
2124
2125HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2126{
2127 /* mBIOSSettings is constant during life time, no need to lock */
2128 aBIOSSettings = mBIOSSettings;
2129
2130 return S_OK;
2131}
2132
2133HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2134{
2135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 switch (aProperty)
2138 {
2139 case CPUPropertyType_PAE:
2140 *aValue = mHWData->mPAEEnabled;
2141 break;
2142
2143 case CPUPropertyType_Synthetic:
2144 *aValue = mHWData->mSyntheticCpu;
2145 break;
2146
2147 case CPUPropertyType_LongMode:
2148 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2149 *aValue = TRUE;
2150 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2151 *aValue = FALSE;
2152#if HC_ARCH_BITS == 64
2153 else
2154 *aValue = TRUE;
2155#else
2156 else
2157 {
2158 *aValue = FALSE;
2159
2160 ComPtr<IGuestOSType> ptrGuestOSType;
2161 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2162 if (SUCCEEDED(hrc2))
2163 {
2164 BOOL fIs64Bit = FALSE;
2165 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2166 if (SUCCEEDED(hrc2) && fIs64Bit)
2167 {
2168 ComObjPtr<Host> ptrHost = mParent->i_host();
2169 alock.release();
2170
2171 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2172 if (FAILED(hrc2))
2173 *aValue = FALSE;
2174 }
2175 }
2176 }
2177#endif
2178 break;
2179
2180 case CPUPropertyType_TripleFaultReset:
2181 *aValue = mHWData->mTripleFaultReset;
2182 break;
2183
2184 default:
2185 return E_INVALIDARG;
2186 }
2187 return S_OK;
2188}
2189
2190HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2191{
2192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2193
2194 HRESULT rc = i_checkStateDependency(MutableStateDep);
2195 if (FAILED(rc)) return rc;
2196
2197 switch (aProperty)
2198 {
2199 case CPUPropertyType_PAE:
2200 i_setModified(IsModified_MachineData);
2201 mHWData.backup();
2202 mHWData->mPAEEnabled = !!aValue;
2203 break;
2204
2205 case CPUPropertyType_Synthetic:
2206 i_setModified(IsModified_MachineData);
2207 mHWData.backup();
2208 mHWData->mSyntheticCpu = !!aValue;
2209 break;
2210
2211 case CPUPropertyType_LongMode:
2212 i_setModified(IsModified_MachineData);
2213 mHWData.backup();
2214 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2215 break;
2216
2217 case CPUPropertyType_TripleFaultReset:
2218 i_setModified(IsModified_MachineData);
2219 mHWData.backup();
2220 mHWData->mTripleFaultReset = !!aValue;
2221 break;
2222
2223 default:
2224 return E_INVALIDARG;
2225 }
2226 return S_OK;
2227}
2228
2229HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2230{
2231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
2233 switch(aId)
2234 {
2235 case 0x0:
2236 case 0x1:
2237 case 0x2:
2238 case 0x3:
2239 case 0x4:
2240 case 0x5:
2241 case 0x6:
2242 case 0x7:
2243 case 0x8:
2244 case 0x9:
2245 case 0xA:
2246 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2247 return E_INVALIDARG;
2248
2249 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2250 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2251 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2252 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2253 break;
2254
2255 case 0x80000000:
2256 case 0x80000001:
2257 case 0x80000002:
2258 case 0x80000003:
2259 case 0x80000004:
2260 case 0x80000005:
2261 case 0x80000006:
2262 case 0x80000007:
2263 case 0x80000008:
2264 case 0x80000009:
2265 case 0x8000000A:
2266 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2267 return E_INVALIDARG;
2268
2269 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2270 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2271 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2272 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2273 break;
2274
2275 default:
2276 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2277 }
2278 return S_OK;
2279}
2280
2281
2282HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2283{
2284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2285
2286 HRESULT rc = i_checkStateDependency(MutableStateDep);
2287 if (FAILED(rc)) return rc;
2288
2289 switch(aId)
2290 {
2291 case 0x0:
2292 case 0x1:
2293 case 0x2:
2294 case 0x3:
2295 case 0x4:
2296 case 0x5:
2297 case 0x6:
2298 case 0x7:
2299 case 0x8:
2300 case 0x9:
2301 case 0xA:
2302 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2303 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2304 i_setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2307 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2308 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2309 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2310 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2311 break;
2312
2313 case 0x80000000:
2314 case 0x80000001:
2315 case 0x80000002:
2316 case 0x80000003:
2317 case 0x80000004:
2318 case 0x80000005:
2319 case 0x80000006:
2320 case 0x80000007:
2321 case 0x80000008:
2322 case 0x80000009:
2323 case 0x8000000A:
2324 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2325 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2329 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2330 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2331 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2332 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2333 break;
2334
2335 default:
2336 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2337 }
2338 return S_OK;
2339}
2340
2341HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2342{
2343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2344
2345 HRESULT rc = i_checkStateDependency(MutableStateDep);
2346 if (FAILED(rc)) return rc;
2347
2348 switch(aId)
2349 {
2350 case 0x0:
2351 case 0x1:
2352 case 0x2:
2353 case 0x3:
2354 case 0x4:
2355 case 0x5:
2356 case 0x6:
2357 case 0x7:
2358 case 0x8:
2359 case 0x9:
2360 case 0xA:
2361 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2362 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2363 i_setModified(IsModified_MachineData);
2364 mHWData.backup();
2365 /* Invalidate leaf. */
2366 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2367 break;
2368
2369 case 0x80000000:
2370 case 0x80000001:
2371 case 0x80000002:
2372 case 0x80000003:
2373 case 0x80000004:
2374 case 0x80000005:
2375 case 0x80000006:
2376 case 0x80000007:
2377 case 0x80000008:
2378 case 0x80000009:
2379 case 0x8000000A:
2380 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2381 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2382 i_setModified(IsModified_MachineData);
2383 mHWData.backup();
2384 /* Invalidate leaf. */
2385 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2386 break;
2387
2388 default:
2389 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2390 }
2391 return S_OK;
2392}
2393
2394HRESULT Machine::removeAllCPUIDLeaves()
2395{
2396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 HRESULT rc = i_checkStateDependency(MutableStateDep);
2399 if (FAILED(rc)) return rc;
2400
2401 i_setModified(IsModified_MachineData);
2402 mHWData.backup();
2403
2404 /* Invalidate all standard leafs. */
2405 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2406 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2407
2408 /* Invalidate all extended leafs. */
2409 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2410 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2411
2412 return S_OK;
2413}
2414HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2415{
2416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2417
2418 switch(aProperty)
2419 {
2420 case HWVirtExPropertyType_Enabled:
2421 *aValue = mHWData->mHWVirtExEnabled;
2422 break;
2423
2424 case HWVirtExPropertyType_VPID:
2425 *aValue = mHWData->mHWVirtExVPIDEnabled;
2426 break;
2427
2428 case HWVirtExPropertyType_NestedPaging:
2429 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2430 break;
2431
2432 case HWVirtExPropertyType_UnrestrictedExecution:
2433 *aValue = mHWData->mHWVirtExUXEnabled;
2434 break;
2435
2436 case HWVirtExPropertyType_LargePages:
2437 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2438#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2439 *aValue = FALSE;
2440#endif
2441 break;
2442
2443 case HWVirtExPropertyType_Force:
2444 *aValue = mHWData->mHWVirtExForceEnabled;
2445 break;
2446
2447 default:
2448 return E_INVALIDARG;
2449 }
2450 return S_OK;
2451}
2452
2453HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2454{
2455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2456
2457 HRESULT rc = i_checkStateDependency(MutableStateDep);
2458 if (FAILED(rc)) return rc;
2459
2460 switch(aProperty)
2461 {
2462 case HWVirtExPropertyType_Enabled:
2463 i_setModified(IsModified_MachineData);
2464 mHWData.backup();
2465 mHWData->mHWVirtExEnabled = !!aValue;
2466 break;
2467
2468 case HWVirtExPropertyType_VPID:
2469 i_setModified(IsModified_MachineData);
2470 mHWData.backup();
2471 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2472 break;
2473
2474 case HWVirtExPropertyType_NestedPaging:
2475 i_setModified(IsModified_MachineData);
2476 mHWData.backup();
2477 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2478 break;
2479
2480 case HWVirtExPropertyType_UnrestrictedExecution:
2481 i_setModified(IsModified_MachineData);
2482 mHWData.backup();
2483 mHWData->mHWVirtExUXEnabled = !!aValue;
2484 break;
2485
2486 case HWVirtExPropertyType_LargePages:
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2490 break;
2491
2492 case HWVirtExPropertyType_Force:
2493 i_setModified(IsModified_MachineData);
2494 mHWData.backup();
2495 mHWData->mHWVirtExForceEnabled = !!aValue;
2496 break;
2497
2498 default:
2499 return E_INVALIDARG;
2500 }
2501
2502 return S_OK;
2503}
2504
2505HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2506{
2507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2515{
2516 /* @todo (r=dmik):
2517 * 1. Allow to change the name of the snapshot folder containing snapshots
2518 * 2. Rename the folder on disk instead of just changing the property
2519 * value (to be smart and not to leave garbage). Note that it cannot be
2520 * done here because the change may be rolled back. Thus, the right
2521 * place is #saveSettings().
2522 */
2523
2524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2525
2526 HRESULT rc = i_checkStateDependency(MutableStateDep);
2527 if (FAILED(rc)) return rc;
2528
2529 if (!mData->mCurrentSnapshot.isNull())
2530 return setError(E_FAIL,
2531 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2532
2533 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2534
2535 if (strSnapshotFolder.isEmpty())
2536 strSnapshotFolder = "Snapshots";
2537 int vrc = i_calculateFullPath(strSnapshotFolder,
2538 strSnapshotFolder);
2539 if (RT_FAILURE(vrc))
2540 return setError(E_FAIL,
2541 tr("Invalid snapshot folder '%s' (%Rrc)"),
2542 strSnapshotFolder.c_str(), vrc);
2543
2544 i_setModified(IsModified_MachineData);
2545 mUserData.backup();
2546
2547 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2548
2549 return S_OK;
2550}
2551
2552HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2553{
2554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2555
2556 aMediumAttachments.resize(mMediaData->mAttachments.size());
2557 size_t i = 0;
2558 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2559 it != mMediaData->mAttachments.end(); ++it, ++i)
2560 aMediumAttachments[i] = *it;
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 Assert(!!mVRDEServer);
2570
2571 aVRDEServer = mVRDEServer;
2572
2573 return S_OK;
2574}
2575
2576HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2577{
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 aAudioAdapter = mAudioAdapter;
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2586{
2587#ifdef VBOX_WITH_VUSB
2588 clearError();
2589 MultiResult rc(S_OK);
2590
2591# ifdef VBOX_WITH_USB
2592 rc = mParent->i_host()->i_checkUSBProxyService();
2593 if (FAILED(rc)) return rc;
2594# endif
2595
2596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2597
2598 USBControllerList data = *mUSBControllers.data();
2599 aUSBControllers.resize(data.size());
2600 size_t i = 0;
2601 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2602 aUSBControllers[i] = *it;
2603
2604 return S_OK;
2605#else
2606 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2607 * extended error info to indicate that USB is simply not available
2608 * (w/o treating it as a failure), for example, as in OSE */
2609 NOREF(aUSBControllers);
2610 ReturnComNotImplemented();
2611#endif /* VBOX_WITH_VUSB */
2612}
2613
2614HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2615{
2616#ifdef VBOX_WITH_VUSB
2617 clearError();
2618 MultiResult rc(S_OK);
2619
2620# ifdef VBOX_WITH_USB
2621 rc = mParent->i_host()->i_checkUSBProxyService();
2622 if (FAILED(rc)) return rc;
2623# endif
2624
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 aUSBDeviceFilters = mUSBDeviceFilters;
2628 return rc;
2629#else
2630 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2631 * extended error info to indicate that USB is simply not available
2632 * (w/o treating it as a failure), for example, as in OSE */
2633 NOREF(aUSBDeviceFilters);
2634 ReturnComNotImplemented();
2635#endif /* VBOX_WITH_VUSB */
2636}
2637
2638HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 aSettingsFilePath = mData->m_strConfigFileFull;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2652 if (FAILED(rc)) return rc;
2653
2654 if (!mData->pMachineConfigFile->fileExists())
2655 // this is a new machine, and no config file exists yet:
2656 *aSettingsModified = TRUE;
2657 else
2658 *aSettingsModified = (mData->flModifications != 0);
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2664{
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 *aSessionState = mData->mSession.mState;
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2674{
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 aSessionType = mData->mSession.mType;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 *aSessionPID = mData->mSession.mPID;
2687
2688 return S_OK;
2689}
2690
2691HRESULT Machine::getState(MachineState_T *aState)
2692{
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 *aState = mData->mMachineState;
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2710{
2711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2712
2713 aStateFilePath = mSSData->strStateFilePath;
2714
2715 return S_OK;
2716}
2717
2718HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2719{
2720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 i_getLogFolder(aLogFolder);
2723
2724 return S_OK;
2725}
2726
2727HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2728{
2729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2730
2731 aCurrentSnapshot = mData->mCurrentSnapshot;
2732
2733 return S_OK;
2734}
2735
2736HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2737{
2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2741 ? 0
2742 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 /* Note: for machines with no snapshots, we always return FALSE
2752 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2753 * reasons :) */
2754
2755 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2756 ? FALSE
2757 : mData->mCurrentStateModified;
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 aSharedFolders.resize(mHWData->mSharedFolders.size());
2767 size_t i = 0;
2768 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2769 it != mHWData->mSharedFolders.end(); ++i, ++it)
2770 aSharedFolders[i] = *it;
2771
2772 return S_OK;
2773}
2774
2775HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 *aClipboardMode = mHWData->mClipboardMode;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2785{
2786 HRESULT rc = S_OK;
2787
2788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 alock.release();
2791 rc = i_onClipboardModeChange(aClipboardMode);
2792 alock.acquire();
2793 if (FAILED(rc)) return rc;
2794
2795 i_setModified(IsModified_MachineData);
2796 mHWData.backup();
2797 mHWData->mClipboardMode = aClipboardMode;
2798
2799 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2800 if (Global::IsOnline(mData->mMachineState))
2801 i_saveSettings(NULL);
2802
2803 return S_OK;
2804}
2805
2806HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2807{
2808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2809
2810 *aDnDMode = mHWData->mDnDMode;
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2816{
2817 HRESULT rc = S_OK;
2818
2819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 alock.release();
2822 rc = i_onDnDModeChange(aDnDMode);
2823
2824 alock.acquire();
2825 if (FAILED(rc)) return rc;
2826
2827 i_setModified(IsModified_MachineData);
2828 mHWData.backup();
2829 mHWData->mDnDMode = aDnDMode;
2830
2831 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2832 if (Global::IsOnline(mData->mMachineState))
2833 i_saveSettings(NULL);
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 try
2843 {
2844 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2845 }
2846 catch (...)
2847 {
2848 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2849 }
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2855{
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2859 if (FAILED(rc)) return rc;
2860
2861 i_setModified(IsModified_MachineData);
2862 mHWData.backup();
2863 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2864 return rc;
2865}
2866
2867HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870 StorageControllerList data = *mStorageControllers.data();
2871 size_t i = 0;
2872 aStorageControllers.resize(data.size());
2873 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2874 aStorageControllers[i] = *it;
2875 return S_OK;
2876}
2877
2878HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2879{
2880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2881
2882 *aEnabled = mUserData->s.fTeleporterEnabled;
2883
2884 return S_OK;
2885}
2886
2887HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2888{
2889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 /* Only allow it to be set to true when PoweredOff or Aborted.
2892 (Clearing it is always permitted.) */
2893 if ( aTeleporterEnabled
2894 && mData->mRegistered
2895 && ( !i_isSessionMachine()
2896 || ( mData->mMachineState != MachineState_PoweredOff
2897 && mData->mMachineState != MachineState_Teleported
2898 && mData->mMachineState != MachineState_Aborted
2899 )
2900 )
2901 )
2902 return setError(VBOX_E_INVALID_VM_STATE,
2903 tr("The machine is not powered off (state is %s)"),
2904 Global::stringifyMachineState(mData->mMachineState));
2905
2906 i_setModified(IsModified_MachineData);
2907 mUserData.backup();
2908 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2914{
2915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2923{
2924 if (aTeleporterPort >= _64K)
2925 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2926
2927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2930 if (FAILED(rc)) return rc;
2931
2932 i_setModified(IsModified_MachineData);
2933 mUserData.backup();
2934 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2935
2936 return S_OK;
2937}
2938
2939HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2940{
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2953 if (FAILED(rc)) return rc;
2954
2955 i_setModified(IsModified_MachineData);
2956 mUserData.backup();
2957 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2958
2959 return S_OK;
2960}
2961
2962HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2963{
2964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2965 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2966
2967 return S_OK;
2968}
2969
2970HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2971{
2972 /*
2973 * Hash the password first.
2974 */
2975 com::Utf8Str aT = aTeleporterPassword;
2976
2977 if (!aT.isEmpty())
2978 {
2979 if (VBoxIsPasswordHashed(&aT))
2980 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2981 VBoxHashPassword(&aT);
2982 }
2983
2984 /*
2985 * Do the update.
2986 */
2987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2988 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2989 if (SUCCEEDED(hrc))
2990 {
2991 i_setModified(IsModified_MachineData);
2992 mUserData.backup();
2993 mUserData->s.strTeleporterPassword = aT;
2994 }
2995
2996 return hrc;
2997}
2998
2999HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3000{
3001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3004 return S_OK;
3005}
3006
3007HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3008{
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 /* @todo deal with running state change. */
3012 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3013 if (FAILED(rc)) return rc;
3014
3015 i_setModified(IsModified_MachineData);
3016 mUserData.backup();
3017 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3018 return S_OK;
3019}
3020
3021HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3022{
3023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3026 return S_OK;
3027}
3028
3029HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3030{
3031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 /* @todo deal with running state change. */
3034 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3035 if (FAILED(rc)) return rc;
3036
3037 i_setModified(IsModified_MachineData);
3038 mUserData.backup();
3039 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3040 return S_OK;
3041}
3042
3043HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3044{
3045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3048 return S_OK;
3049}
3050
3051HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3052{
3053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3054
3055 /* @todo deal with running state change. */
3056 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3057 if (FAILED(rc)) return rc;
3058
3059 i_setModified(IsModified_MachineData);
3060 mUserData.backup();
3061 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3062 return S_OK;
3063}
3064
3065HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3066{
3067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3068
3069 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3070
3071 return S_OK;
3072}
3073
3074HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3075{
3076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3077
3078 /* @todo deal with running state change. */
3079 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3080 if (FAILED(rc)) return rc;
3081
3082 i_setModified(IsModified_MachineData);
3083 mUserData.backup();
3084 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3085
3086 return S_OK;
3087}
3088
3089HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3090{
3091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3094 return S_OK;
3095}
3096
3097HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3098{
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 /* @todo deal with running state change. */
3102 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3103 if (FAILED(rc)) return rc;
3104
3105 i_setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3108 return S_OK;
3109}
3110
3111HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3112{
3113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3114
3115 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3116
3117 return S_OK;
3118}
3119
3120HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3121{
3122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 /* Only allow it to be set to true when PoweredOff or Aborted.
3125 (Clearing it is always permitted.) */
3126 if ( aRTCUseUTC
3127 && mData->mRegistered
3128 && ( !i_isSessionMachine()
3129 || ( mData->mMachineState != MachineState_PoweredOff
3130 && mData->mMachineState != MachineState_Teleported
3131 && mData->mMachineState != MachineState_Aborted
3132 )
3133 )
3134 )
3135 return setError(VBOX_E_INVALID_VM_STATE,
3136 tr("The machine is not powered off (state is %s)"),
3137 Global::stringifyMachineState(mData->mMachineState));
3138
3139 i_setModified(IsModified_MachineData);
3140 mUserData.backup();
3141 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3142
3143 return S_OK;
3144}
3145
3146HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3147{
3148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3151
3152 return S_OK;
3153}
3154
3155HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3156{
3157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3158
3159 HRESULT rc = i_checkStateDependency(MutableStateDep);
3160 if (FAILED(rc)) return rc;
3161
3162 i_setModified(IsModified_MachineData);
3163 mHWData.backup();
3164 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3165
3166 return S_OK;
3167}
3168
3169HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3170{
3171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3172
3173 *aIOCacheSize = mHWData->mIOCacheSize;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3179{
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 HRESULT rc = i_checkStateDependency(MutableStateDep);
3183 if (FAILED(rc)) return rc;
3184
3185 i_setModified(IsModified_MachineData);
3186 mHWData.backup();
3187 mHWData->mIOCacheSize = aIOCacheSize;
3188
3189 return S_OK;
3190}
3191
3192
3193/**
3194 * @note Locks objects!
3195 */
3196HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3197 LockType_T aLockType)
3198
3199{
3200 /* check the session state */
3201 SessionState_T state;
3202 HRESULT rc = aSession->COMGETTER(State)(&state);
3203 if (FAILED(rc)) return rc;
3204
3205 if (state != SessionState_Unlocked)
3206 return setError(VBOX_E_INVALID_OBJECT_STATE,
3207 tr("The given session is busy"));
3208
3209 // get the client's IInternalSessionControl interface
3210 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3211 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3212 E_INVALIDARG);
3213
3214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3215
3216 if (!mData->mRegistered)
3217 return setError(E_UNEXPECTED,
3218 tr("The machine '%s' is not registered"),
3219 mUserData->s.strName.c_str());
3220
3221 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3222
3223 SessionState_T oldState = mData->mSession.mState;
3224 /* Hack: in case the session is closing and there is a progress object
3225 * which allows waiting for the session to be closed, take the opportunity
3226 * and do a limited wait (max. 1 second). This helps a lot when the system
3227 * is busy and thus session closing can take a little while. */
3228 if ( mData->mSession.mState == SessionState_Unlocking
3229 && mData->mSession.mProgress)
3230 {
3231 alock.release();
3232 mData->mSession.mProgress->WaitForCompletion(1000);
3233 alock.acquire();
3234 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3235 }
3236
3237 // try again now
3238 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3239 // (i.e. session machine exists)
3240 && (aLockType == LockType_Shared) // caller wants a shared link to the
3241 // existing session that holds the write lock:
3242 )
3243 {
3244 // OK, share the session... we are now dealing with three processes:
3245 // 1) VBoxSVC (where this code runs);
3246 // 2) process C: the caller's client process (who wants a shared session);
3247 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3248
3249 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3250 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3251 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3252 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3253 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3254
3255 /*
3256 * Release the lock before calling the client process. It's safe here
3257 * since the only thing to do after we get the lock again is to add
3258 * the remote control to the list (which doesn't directly influence
3259 * anything).
3260 */
3261 alock.release();
3262
3263 // get the console of the session holding the write lock (this is a remote call)
3264 ComPtr<IConsole> pConsoleW;
3265 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3266 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3267 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3268 if (FAILED(rc))
3269 // the failure may occur w/o any error info (from RPC), so provide one
3270 return setError(VBOX_E_VM_ERROR,
3271 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3272
3273 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3274
3275 // share the session machine and W's console with the caller's session
3276 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3277 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3278 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3279
3280 if (FAILED(rc))
3281 // the failure may occur w/o any error info (from RPC), so provide one
3282 return setError(VBOX_E_VM_ERROR,
3283 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3284 alock.acquire();
3285
3286 // need to revalidate the state after acquiring the lock again
3287 if (mData->mSession.mState != SessionState_Locked)
3288 {
3289 pSessionControl->Uninitialize();
3290 return setError(VBOX_E_INVALID_SESSION_STATE,
3291 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3292 mUserData->s.strName.c_str());
3293 }
3294
3295 // add the caller's session to the list
3296 mData->mSession.mRemoteControls.push_back(pSessionControl);
3297 }
3298 else if ( mData->mSession.mState == SessionState_Locked
3299 || mData->mSession.mState == SessionState_Unlocking
3300 )
3301 {
3302 // sharing not permitted, or machine still unlocking:
3303 return setError(VBOX_E_INVALID_OBJECT_STATE,
3304 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3305 mUserData->s.strName.c_str());
3306 }
3307 else
3308 {
3309 // machine is not locked: then write-lock the machine (create the session machine)
3310
3311 // must not be busy
3312 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3313
3314 // get the caller's session PID
3315 RTPROCESS pid = NIL_RTPROCESS;
3316 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3317 pSessionControl->GetPID((ULONG*)&pid);
3318 Assert(pid != NIL_RTPROCESS);
3319
3320 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3321
3322 if (fLaunchingVMProcess)
3323 {
3324 if (mData->mSession.mPID == NIL_RTPROCESS)
3325 {
3326 // two or more clients racing for a lock, the one which set the
3327 // session state to Spawning will win, the others will get an
3328 // error as we can't decide here if waiting a little would help
3329 // (only for shared locks this would avoid an error)
3330 return setError(VBOX_E_INVALID_OBJECT_STATE,
3331 tr("The machine '%s' already has a lock request pending"),
3332 mUserData->s.strName.c_str());
3333 }
3334
3335 // this machine is awaiting for a spawning session to be opened:
3336 // then the calling process must be the one that got started by
3337 // LaunchVMProcess()
3338
3339 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3340 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3341
3342#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3343 /* Hardened windows builds spawns three processes when a VM is
3344 launched, the 3rd one is the one that will end up here. */
3345 RTPROCESS ppid;
3346 int rc = RTProcQueryParent(pid, &ppid);
3347 if (RT_SUCCESS(rc))
3348 rc = RTProcQueryParent(ppid, &ppid);
3349 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3350 || rc == VERR_ACCESS_DENIED)
3351 {
3352 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3353 mData->mSession.mPID = pid;
3354 }
3355#endif
3356
3357 if (mData->mSession.mPID != pid)
3358 return setError(E_ACCESSDENIED,
3359 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3360 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3361 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3362 }
3363
3364 // create the mutable SessionMachine from the current machine
3365 ComObjPtr<SessionMachine> sessionMachine;
3366 sessionMachine.createObject();
3367 rc = sessionMachine->init(this);
3368 AssertComRC(rc);
3369
3370 /* NOTE: doing return from this function after this point but
3371 * before the end is forbidden since it may call SessionMachine::uninit()
3372 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3373 * lock while still holding the Machine lock in alock so that a deadlock
3374 * is possible due to the wrong lock order. */
3375
3376 if (SUCCEEDED(rc))
3377 {
3378 /*
3379 * Set the session state to Spawning to protect against subsequent
3380 * attempts to open a session and to unregister the machine after
3381 * we release the lock.
3382 */
3383 SessionState_T origState = mData->mSession.mState;
3384 mData->mSession.mState = SessionState_Spawning;
3385
3386#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3387 /* Get the client token ID to be passed to the client process */
3388 Utf8Str strTokenId;
3389 sessionMachine->i_getTokenId(strTokenId);
3390 Assert(!strTokenId.isEmpty());
3391#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3392 /* Get the client token to be passed to the client process */
3393 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3394 /* The token is now "owned" by pToken, fix refcount */
3395 if (!pToken.isNull())
3396 pToken->Release();
3397#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3398
3399 /*
3400 * Release the lock before calling the client process -- it will call
3401 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3402 * because the state is Spawning, so that LaunchVMProcess() and
3403 * LockMachine() calls will fail. This method, called before we
3404 * acquire the lock again, will fail because of the wrong PID.
3405 *
3406 * Note that mData->mSession.mRemoteControls accessed outside
3407 * the lock may not be modified when state is Spawning, so it's safe.
3408 */
3409 alock.release();
3410
3411 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3412#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3413 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3414#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3415 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3416 /* Now the token is owned by the client process. */
3417 pToken.setNull();
3418#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3419 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3420
3421 /* The failure may occur w/o any error info (from RPC), so provide one */
3422 if (FAILED(rc))
3423 setError(VBOX_E_VM_ERROR,
3424 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3425
3426 if ( SUCCEEDED(rc)
3427 && fLaunchingVMProcess
3428 )
3429 {
3430 /* complete the remote session initialization */
3431
3432 /* get the console from the direct session */
3433 ComPtr<IConsole> console;
3434 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3435 ComAssertComRC(rc);
3436
3437 if (SUCCEEDED(rc) && !console)
3438 {
3439 ComAssert(!!console);
3440 rc = E_FAIL;
3441 }
3442
3443 /* assign machine & console to the remote session */
3444 if (SUCCEEDED(rc))
3445 {
3446 /*
3447 * after LaunchVMProcess(), the first and the only
3448 * entry in remoteControls is that remote session
3449 */
3450 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3451 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3452 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3453
3454 /* The failure may occur w/o any error info (from RPC), so provide one */
3455 if (FAILED(rc))
3456 setError(VBOX_E_VM_ERROR,
3457 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3458 }
3459
3460 if (FAILED(rc))
3461 pSessionControl->Uninitialize();
3462 }
3463
3464 /* acquire the lock again */
3465 alock.acquire();
3466
3467 /* Restore the session state */
3468 mData->mSession.mState = origState;
3469 }
3470
3471 // finalize spawning anyway (this is why we don't return on errors above)
3472 if (fLaunchingVMProcess)
3473 {
3474 /* Note that the progress object is finalized later */
3475 /** @todo Consider checking mData->mSession.mProgress for cancellation
3476 * around here. */
3477
3478 /* We don't reset mSession.mPID here because it is necessary for
3479 * SessionMachine::uninit() to reap the child process later. */
3480
3481 if (FAILED(rc))
3482 {
3483 /* Close the remote session, remove the remote control from the list
3484 * and reset session state to Closed (@note keep the code in sync
3485 * with the relevant part in checkForSpawnFailure()). */
3486
3487 Assert(mData->mSession.mRemoteControls.size() == 1);
3488 if (mData->mSession.mRemoteControls.size() == 1)
3489 {
3490 ErrorInfoKeeper eik;
3491 mData->mSession.mRemoteControls.front()->Uninitialize();
3492 }
3493
3494 mData->mSession.mRemoteControls.clear();
3495 mData->mSession.mState = SessionState_Unlocked;
3496 }
3497 }
3498 else
3499 {
3500 /* memorize PID of the directly opened session */
3501 if (SUCCEEDED(rc))
3502 mData->mSession.mPID = pid;
3503 }
3504
3505 if (SUCCEEDED(rc))
3506 {
3507 /* memorize the direct session control and cache IUnknown for it */
3508 mData->mSession.mDirectControl = pSessionControl;
3509 mData->mSession.mState = SessionState_Locked;
3510 /* associate the SessionMachine with this Machine */
3511 mData->mSession.mMachine = sessionMachine;
3512
3513 /* request an IUnknown pointer early from the remote party for later
3514 * identity checks (it will be internally cached within mDirectControl
3515 * at least on XPCOM) */
3516 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3517 NOREF(unk);
3518 }
3519
3520 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3521 * would break the lock order */
3522 alock.release();
3523
3524 /* uninitialize the created session machine on failure */
3525 if (FAILED(rc))
3526 sessionMachine->uninit();
3527
3528 }
3529
3530 if (SUCCEEDED(rc))
3531 {
3532 /*
3533 * tell the client watcher thread to update the set of
3534 * machines that have open sessions
3535 */
3536 mParent->i_updateClientWatcher();
3537
3538 if (oldState != SessionState_Locked)
3539 /* fire an event */
3540 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3541 }
3542
3543 return rc;
3544}
3545
3546/**
3547 * @note Locks objects!
3548 */
3549HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3550 const com::Utf8Str &aType,
3551 const com::Utf8Str &aEnvironment,
3552 ComPtr<IProgress> &aProgress)
3553{
3554 Utf8Str strFrontend(aType);
3555 /* "emergencystop" doesn't need the session, so skip the checks/interface
3556 * retrieval. This code doesn't quite fit in here, but introducing a
3557 * special API method would be even more effort, and would require explicit
3558 * support by every API client. It's better to hide the feature a bit. */
3559 if (strFrontend != "emergencystop")
3560 CheckComArgNotNull(aSession);
3561
3562 HRESULT rc = S_OK;
3563 if (strFrontend.isEmpty())
3564 {
3565 Bstr bstrFrontend;
3566 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3567 if (FAILED(rc))
3568 return rc;
3569 strFrontend = bstrFrontend;
3570 if (strFrontend.isEmpty())
3571 {
3572 ComPtr<ISystemProperties> systemProperties;
3573 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3574 if (FAILED(rc))
3575 return rc;
3576 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3577 if (FAILED(rc))
3578 return rc;
3579 strFrontend = bstrFrontend;
3580 }
3581 /* paranoia - emergencystop is not a valid default */
3582 if (strFrontend == "emergencystop")
3583 strFrontend = Utf8Str::Empty;
3584 }
3585 /* default frontend: Qt GUI */
3586 if (strFrontend.isEmpty())
3587 strFrontend = "GUI/Qt";
3588
3589 if (strFrontend != "emergencystop")
3590 {
3591 /* check the session state */
3592 SessionState_T state;
3593 rc = aSession->COMGETTER(State)(&state);
3594 if (FAILED(rc))
3595 return rc;
3596
3597 if (state != SessionState_Unlocked)
3598 return setError(VBOX_E_INVALID_OBJECT_STATE,
3599 tr("The given session is busy"));
3600
3601 /* get the IInternalSessionControl interface */
3602 ComPtr<IInternalSessionControl> control(aSession);
3603 ComAssertMsgRet(!control.isNull(),
3604 ("No IInternalSessionControl interface"),
3605 E_INVALIDARG);
3606
3607 /* get the teleporter enable state for the progress object init. */
3608 BOOL fTeleporterEnabled;
3609 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3610 if (FAILED(rc))
3611 return rc;
3612
3613 /* create a progress object */
3614 ComObjPtr<ProgressProxy> progress;
3615 progress.createObject();
3616 rc = progress->init(mParent,
3617 static_cast<IMachine*>(this),
3618 Bstr(tr("Starting VM")).raw(),
3619 TRUE /* aCancelable */,
3620 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3621 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3622 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3623 2 /* uFirstOperationWeight */,
3624 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3625
3626 if (SUCCEEDED(rc))
3627 {
3628 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3629 if (SUCCEEDED(rc))
3630 {
3631 aProgress = progress;
3632
3633 /* signal the client watcher thread */
3634 mParent->i_updateClientWatcher();
3635
3636 /* fire an event */
3637 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3638 }
3639 }
3640 }
3641 else
3642 {
3643 /* no progress object - either instant success or failure */
3644 aProgress = NULL;
3645
3646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3647
3648 if (mData->mSession.mState != SessionState_Locked)
3649 return setError(VBOX_E_INVALID_OBJECT_STATE,
3650 tr("The machine '%s' is not locked by a session"),
3651 mUserData->s.strName.c_str());
3652
3653 /* must have a VM process associated - do not kill normal API clients
3654 * with an open session */
3655 if (!Global::IsOnline(mData->mMachineState))
3656 return setError(VBOX_E_INVALID_OBJECT_STATE,
3657 tr("The machine '%s' does not have a VM process"),
3658 mUserData->s.strName.c_str());
3659
3660 /* forcibly terminate the VM process */
3661 if (mData->mSession.mPID != NIL_RTPROCESS)
3662 RTProcTerminate(mData->mSession.mPID);
3663
3664 /* signal the client watcher thread, as most likely the client has
3665 * been terminated */
3666 mParent->i_updateClientWatcher();
3667 }
3668
3669 return rc;
3670}
3671
3672HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3673{
3674 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3675 return setError(E_INVALIDARG,
3676 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3677 aPosition, SchemaDefs::MaxBootPosition);
3678
3679 if (aDevice == DeviceType_USB)
3680 return setError(E_NOTIMPL,
3681 tr("Booting from USB device is currently not supported"));
3682
3683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3684
3685 HRESULT rc = i_checkStateDependency(MutableStateDep);
3686 if (FAILED(rc)) return rc;
3687
3688 i_setModified(IsModified_MachineData);
3689 mHWData.backup();
3690 mHWData->mBootOrder[aPosition - 1] = aDevice;
3691
3692 return S_OK;
3693}
3694
3695HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3696{
3697 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3698 return setError(E_INVALIDARG,
3699 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3700 aPosition, SchemaDefs::MaxBootPosition);
3701
3702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3703
3704 *aDevice = mHWData->mBootOrder[aPosition - 1];
3705
3706 return S_OK;
3707}
3708
3709HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3710 LONG aControllerPort,
3711 LONG aDevice,
3712 DeviceType_T aType,
3713 const ComPtr<IMedium> &aMedium)
3714{
3715 IMedium *aM = aMedium;
3716 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3717 aName.c_str(), aControllerPort, aDevice, aType, aM));
3718
3719 // request the host lock first, since might be calling Host methods for getting host drives;
3720 // next, protect the media tree all the while we're in here, as well as our member variables
3721 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3722 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3723
3724 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3725 if (FAILED(rc)) return rc;
3726
3727 /// @todo NEWMEDIA implicit machine registration
3728 if (!mData->mRegistered)
3729 return setError(VBOX_E_INVALID_OBJECT_STATE,
3730 tr("Cannot attach storage devices to an unregistered machine"));
3731
3732 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3733
3734 /* Check for an existing controller. */
3735 ComObjPtr<StorageController> ctl;
3736 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3737 if (FAILED(rc)) return rc;
3738
3739 StorageControllerType_T ctrlType;
3740 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3741 if (FAILED(rc))
3742 return setError(E_FAIL,
3743 tr("Could not get type of controller '%s'"),
3744 aName.c_str());
3745
3746 bool fSilent = false;
3747 Utf8Str strReconfig;
3748
3749 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3750 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3751 if ( mData->mMachineState == MachineState_Paused
3752 && strReconfig == "1")
3753 fSilent = true;
3754
3755 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3756 bool fHotplug = false;
3757 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3758 fHotplug = true;
3759
3760 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3761 return setError(VBOX_E_INVALID_VM_STATE,
3762 tr("Controller '%s' does not support hotplugging"),
3763 aName.c_str());
3764
3765 // check that the port and device are not out of range
3766 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3767 if (FAILED(rc)) return rc;
3768
3769 /* check if the device slot is already busy */
3770 MediumAttachment *pAttachTemp;
3771 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3772 Bstr(aName).raw(),
3773 aControllerPort,
3774 aDevice)))
3775 {
3776 Medium *pMedium = pAttachTemp->i_getMedium();
3777 if (pMedium)
3778 {
3779 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3780 return setError(VBOX_E_OBJECT_IN_USE,
3781 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3782 pMedium->i_getLocationFull().c_str(),
3783 aControllerPort,
3784 aDevice,
3785 aName.c_str());
3786 }
3787 else
3788 return setError(VBOX_E_OBJECT_IN_USE,
3789 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3790 aControllerPort, aDevice, aName.c_str());
3791 }
3792
3793 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3794 if (aMedium && medium.isNull())
3795 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3796
3797 AutoCaller mediumCaller(medium);
3798 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3799
3800 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3801
3802 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3803 && !medium.isNull()
3804 )
3805 return setError(VBOX_E_OBJECT_IN_USE,
3806 tr("Medium '%s' is already attached to this virtual machine"),
3807 medium->i_getLocationFull().c_str());
3808
3809 if (!medium.isNull())
3810 {
3811 MediumType_T mtype = medium->i_getType();
3812 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3813 // For DVDs it's not written to the config file, so needs no global config
3814 // version bump. For floppies it's a new attribute "type", which is ignored
3815 // by older VirtualBox version, so needs no global config version bump either.
3816 // For hard disks this type is not accepted.
3817 if (mtype == MediumType_MultiAttach)
3818 {
3819 // This type is new with VirtualBox 4.0 and therefore requires settings
3820 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3821 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3822 // two reasons: The medium type is a property of the media registry tree, which
3823 // can reside in the global config file (for pre-4.0 media); we would therefore
3824 // possibly need to bump the global config version. We don't want to do that though
3825 // because that might make downgrading to pre-4.0 impossible.
3826 // As a result, we can only use these two new types if the medium is NOT in the
3827 // global registry:
3828 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3829 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3830 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3831 )
3832 return setError(VBOX_E_INVALID_OBJECT_STATE,
3833 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3834 "to machines that were created with VirtualBox 4.0 or later"),
3835 medium->i_getLocationFull().c_str());
3836 }
3837 }
3838
3839 bool fIndirect = false;
3840 if (!medium.isNull())
3841 fIndirect = medium->i_isReadOnly();
3842 bool associate = true;
3843
3844 do
3845 {
3846 if ( aType == DeviceType_HardDisk
3847 && mMediaData.isBackedUp())
3848 {
3849 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3850
3851 /* check if the medium was attached to the VM before we started
3852 * changing attachments in which case the attachment just needs to
3853 * be restored */
3854 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3855 {
3856 AssertReturn(!fIndirect, E_FAIL);
3857
3858 /* see if it's the same bus/channel/device */
3859 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3860 {
3861 /* the simplest case: restore the whole attachment
3862 * and return, nothing else to do */
3863 mMediaData->mAttachments.push_back(pAttachTemp);
3864
3865 /* Reattach the medium to the VM. */
3866 if (fHotplug || fSilent)
3867 {
3868 mediumLock.release();
3869 treeLock.release();
3870 alock.release();
3871
3872 MediumLockList *pMediumLockList(new MediumLockList());
3873
3874 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3875 true /* fMediumLockWrite */,
3876 false /* fMediumLockWriteAll */,
3877 NULL,
3878 *pMediumLockList);
3879 alock.acquire();
3880 if (FAILED(rc))
3881 delete pMediumLockList;
3882 else
3883 {
3884 mData->mSession.mLockedMedia.Unlock();
3885 alock.release();
3886 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3887 mData->mSession.mLockedMedia.Lock();
3888 alock.acquire();
3889 }
3890 alock.release();
3891
3892 if (SUCCEEDED(rc))
3893 {
3894 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3895 /* Remove lock list in case of error. */
3896 if (FAILED(rc))
3897 {
3898 mData->mSession.mLockedMedia.Unlock();
3899 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3900 mData->mSession.mLockedMedia.Lock();
3901 }
3902 }
3903 }
3904
3905 return S_OK;
3906 }
3907
3908 /* bus/channel/device differ; we need a new attachment object,
3909 * but don't try to associate it again */
3910 associate = false;
3911 break;
3912 }
3913 }
3914
3915 /* go further only if the attachment is to be indirect */
3916 if (!fIndirect)
3917 break;
3918
3919 /* perform the so called smart attachment logic for indirect
3920 * attachments. Note that smart attachment is only applicable to base
3921 * hard disks. */
3922
3923 if (medium->i_getParent().isNull())
3924 {
3925 /* first, investigate the backup copy of the current hard disk
3926 * attachments to make it possible to re-attach existing diffs to
3927 * another device slot w/o losing their contents */
3928 if (mMediaData.isBackedUp())
3929 {
3930 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3931
3932 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3933 uint32_t foundLevel = 0;
3934
3935 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3936 {
3937 uint32_t level = 0;
3938 MediumAttachment *pAttach = *it;
3939 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3940 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3941 if (pMedium.isNull())
3942 continue;
3943
3944 if (pMedium->i_getBase(&level) == medium)
3945 {
3946 /* skip the hard disk if its currently attached (we
3947 * cannot attach the same hard disk twice) */
3948 if (i_findAttachment(mMediaData->mAttachments,
3949 pMedium))
3950 continue;
3951
3952 /* matched device, channel and bus (i.e. attached to the
3953 * same place) will win and immediately stop the search;
3954 * otherwise the attachment that has the youngest
3955 * descendant of medium will be used
3956 */
3957 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3958 {
3959 /* the simplest case: restore the whole attachment
3960 * and return, nothing else to do */
3961 mMediaData->mAttachments.push_back(*it);
3962
3963 /* Reattach the medium to the VM. */
3964 if (fHotplug || fSilent)
3965 {
3966 mediumLock.release();
3967 treeLock.release();
3968 alock.release();
3969
3970 MediumLockList *pMediumLockList(new MediumLockList());
3971
3972 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3973 true /* fMediumLockWrite */,
3974 false /* fMediumLockWriteAll */,
3975 NULL,
3976 *pMediumLockList);
3977 alock.acquire();
3978 if (FAILED(rc))
3979 delete pMediumLockList;
3980 else
3981 {
3982 mData->mSession.mLockedMedia.Unlock();
3983 alock.release();
3984 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3985 mData->mSession.mLockedMedia.Lock();
3986 alock.acquire();
3987 }
3988 alock.release();
3989
3990 if (SUCCEEDED(rc))
3991 {
3992 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3993 /* Remove lock list in case of error. */
3994 if (FAILED(rc))
3995 {
3996 mData->mSession.mLockedMedia.Unlock();
3997 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3998 mData->mSession.mLockedMedia.Lock();
3999 }
4000 }
4001 }
4002
4003 return S_OK;
4004 }
4005 else if ( foundIt == oldAtts.end()
4006 || level > foundLevel /* prefer younger */
4007 )
4008 {
4009 foundIt = it;
4010 foundLevel = level;
4011 }
4012 }
4013 }
4014
4015 if (foundIt != oldAtts.end())
4016 {
4017 /* use the previously attached hard disk */
4018 medium = (*foundIt)->i_getMedium();
4019 mediumCaller.attach(medium);
4020 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4021 mediumLock.attach(medium);
4022 /* not implicit, doesn't require association with this VM */
4023 fIndirect = false;
4024 associate = false;
4025 /* go right to the MediumAttachment creation */
4026 break;
4027 }
4028 }
4029
4030 /* must give up the medium lock and medium tree lock as below we
4031 * go over snapshots, which needs a lock with higher lock order. */
4032 mediumLock.release();
4033 treeLock.release();
4034
4035 /* then, search through snapshots for the best diff in the given
4036 * hard disk's chain to base the new diff on */
4037
4038 ComObjPtr<Medium> base;
4039 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4040 while (snap)
4041 {
4042 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4043
4044 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4045
4046 MediumAttachment *pAttachFound = NULL;
4047 uint32_t foundLevel = 0;
4048
4049 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4050 {
4051 MediumAttachment *pAttach = *it;
4052 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4053 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4054 if (pMedium.isNull())
4055 continue;
4056
4057 uint32_t level = 0;
4058 if (pMedium->i_getBase(&level) == medium)
4059 {
4060 /* matched device, channel and bus (i.e. attached to the
4061 * same place) will win and immediately stop the search;
4062 * otherwise the attachment that has the youngest
4063 * descendant of medium will be used
4064 */
4065 if ( pAttach->i_getDevice() == aDevice
4066 && pAttach->i_getPort() == aControllerPort
4067 && pAttach->i_getControllerName() == aName
4068 )
4069 {
4070 pAttachFound = pAttach;
4071 break;
4072 }
4073 else if ( !pAttachFound
4074 || level > foundLevel /* prefer younger */
4075 )
4076 {
4077 pAttachFound = pAttach;
4078 foundLevel = level;
4079 }
4080 }
4081 }
4082
4083 if (pAttachFound)
4084 {
4085 base = pAttachFound->i_getMedium();
4086 break;
4087 }
4088
4089 snap = snap->i_getParent();
4090 }
4091
4092 /* re-lock medium tree and the medium, as we need it below */
4093 treeLock.acquire();
4094 mediumLock.acquire();
4095
4096 /* found a suitable diff, use it as a base */
4097 if (!base.isNull())
4098 {
4099 medium = base;
4100 mediumCaller.attach(medium);
4101 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4102 mediumLock.attach(medium);
4103 }
4104 }
4105
4106 Utf8Str strFullSnapshotFolder;
4107 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4108
4109 ComObjPtr<Medium> diff;
4110 diff.createObject();
4111 // store this diff in the same registry as the parent
4112 Guid uuidRegistryParent;
4113 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4114 {
4115 // parent image has no registry: this can happen if we're attaching a new immutable
4116 // image that has not yet been attached (medium then points to the base and we're
4117 // creating the diff image for the immutable, and the parent is not yet registered);
4118 // put the parent in the machine registry then
4119 mediumLock.release();
4120 treeLock.release();
4121 alock.release();
4122 i_addMediumToRegistry(medium);
4123 alock.acquire();
4124 treeLock.acquire();
4125 mediumLock.acquire();
4126 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4127 }
4128 rc = diff->init(mParent,
4129 medium->i_getPreferredDiffFormat(),
4130 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4131 uuidRegistryParent,
4132 DeviceType_HardDisk);
4133 if (FAILED(rc)) return rc;
4134
4135 /* Apply the normal locking logic to the entire chain. */
4136 MediumLockList *pMediumLockList(new MediumLockList());
4137 mediumLock.release();
4138 treeLock.release();
4139 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4140 true /* fMediumLockWrite */,
4141 false /* fMediumLockWriteAll */,
4142 medium,
4143 *pMediumLockList);
4144 treeLock.acquire();
4145 mediumLock.acquire();
4146 if (SUCCEEDED(rc))
4147 {
4148 mediumLock.release();
4149 treeLock.release();
4150 rc = pMediumLockList->Lock();
4151 treeLock.acquire();
4152 mediumLock.acquire();
4153 if (FAILED(rc))
4154 setError(rc,
4155 tr("Could not lock medium when creating diff '%s'"),
4156 diff->i_getLocationFull().c_str());
4157 else
4158 {
4159 /* will release the lock before the potentially lengthy
4160 * operation, so protect with the special state */
4161 MachineState_T oldState = mData->mMachineState;
4162 i_setMachineState(MachineState_SettingUp);
4163
4164 mediumLock.release();
4165 treeLock.release();
4166 alock.release();
4167
4168 rc = medium->i_createDiffStorage(diff,
4169 MediumVariant_Standard,
4170 pMediumLockList,
4171 NULL /* aProgress */,
4172 true /* aWait */);
4173
4174 alock.acquire();
4175 treeLock.acquire();
4176 mediumLock.acquire();
4177
4178 i_setMachineState(oldState);
4179 }
4180 }
4181
4182 /* Unlock the media and free the associated memory. */
4183 delete pMediumLockList;
4184
4185 if (FAILED(rc)) return rc;
4186
4187 /* use the created diff for the actual attachment */
4188 medium = diff;
4189 mediumCaller.attach(medium);
4190 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4191 mediumLock.attach(medium);
4192 }
4193 while (0);
4194
4195 ComObjPtr<MediumAttachment> attachment;
4196 attachment.createObject();
4197 rc = attachment->init(this,
4198 medium,
4199 aName,
4200 aControllerPort,
4201 aDevice,
4202 aType,
4203 fIndirect,
4204 false /* fPassthrough */,
4205 false /* fTempEject */,
4206 false /* fNonRotational */,
4207 false /* fDiscard */,
4208 fHotplug /* fHotPluggable */,
4209 Utf8Str::Empty);
4210 if (FAILED(rc)) return rc;
4211
4212 if (associate && !medium.isNull())
4213 {
4214 // as the last step, associate the medium to the VM
4215 rc = medium->i_addBackReference(mData->mUuid);
4216 // here we can fail because of Deleting, or being in process of creating a Diff
4217 if (FAILED(rc)) return rc;
4218
4219 mediumLock.release();
4220 treeLock.release();
4221 alock.release();
4222 i_addMediumToRegistry(medium);
4223 alock.acquire();
4224 treeLock.acquire();
4225 mediumLock.acquire();
4226 }
4227
4228 /* success: finally remember the attachment */
4229 i_setModified(IsModified_Storage);
4230 mMediaData.backup();
4231 mMediaData->mAttachments.push_back(attachment);
4232
4233 mediumLock.release();
4234 treeLock.release();
4235 alock.release();
4236
4237 if (fHotplug || fSilent)
4238 {
4239 if (!medium.isNull())
4240 {
4241 MediumLockList *pMediumLockList(new MediumLockList());
4242
4243 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4244 true /* fMediumLockWrite */,
4245 false /* fMediumLockWriteAll */,
4246 NULL,
4247 *pMediumLockList);
4248 alock.acquire();
4249 if (FAILED(rc))
4250 delete pMediumLockList;
4251 else
4252 {
4253 mData->mSession.mLockedMedia.Unlock();
4254 alock.release();
4255 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4256 mData->mSession.mLockedMedia.Lock();
4257 alock.acquire();
4258 }
4259 alock.release();
4260 }
4261
4262 if (SUCCEEDED(rc))
4263 {
4264 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4265 /* Remove lock list in case of error. */
4266 if (FAILED(rc))
4267 {
4268 mData->mSession.mLockedMedia.Unlock();
4269 mData->mSession.mLockedMedia.Remove(attachment);
4270 mData->mSession.mLockedMedia.Lock();
4271 }
4272 }
4273 }
4274
4275 /* Save modified registries, but skip this machine as it's the caller's
4276 * job to save its settings like all other settings changes. */
4277 mParent->i_unmarkRegistryModified(i_getId());
4278 mParent->i_saveModifiedRegistries();
4279
4280 return rc;
4281}
4282
4283HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4284 LONG aDevice)
4285{
4286 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4287 aName.c_str(), aControllerPort, aDevice));
4288
4289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4290
4291 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4292 if (FAILED(rc)) return rc;
4293
4294 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4295
4296 /* Check for an existing controller. */
4297 ComObjPtr<StorageController> ctl;
4298 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4299 if (FAILED(rc)) return rc;
4300
4301 StorageControllerType_T ctrlType;
4302 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4303 if (FAILED(rc))
4304 return setError(E_FAIL,
4305 tr("Could not get type of controller '%s'"),
4306 aName.c_str());
4307
4308 bool fSilent = false;
4309 Utf8Str strReconfig;
4310
4311 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4312 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4313 if ( mData->mMachineState == MachineState_Paused
4314 && strReconfig == "1")
4315 fSilent = true;
4316
4317 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4318 bool fHotplug = false;
4319 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4320 fHotplug = true;
4321
4322 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4323 return setError(VBOX_E_INVALID_VM_STATE,
4324 tr("Controller '%s' does not support hotplugging"),
4325 aName.c_str());
4326
4327 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4328 Bstr(aName).raw(),
4329 aControllerPort,
4330 aDevice);
4331 if (!pAttach)
4332 return setError(VBOX_E_OBJECT_NOT_FOUND,
4333 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4334 aDevice, aControllerPort, aName.c_str());
4335
4336 if (fHotplug && !pAttach->i_getHotPluggable())
4337 return setError(VBOX_E_NOT_SUPPORTED,
4338 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4339 aDevice, aControllerPort, aName.c_str());
4340
4341 /*
4342 * The VM has to detach the device before we delete any implicit diffs.
4343 * If this fails we can roll back without loosing data.
4344 */
4345 if (fHotplug || fSilent)
4346 {
4347 alock.release();
4348 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4349 alock.acquire();
4350 }
4351 if (FAILED(rc)) return rc;
4352
4353 /* If we are here everything went well and we can delete the implicit now. */
4354 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4355
4356 alock.release();
4357
4358 /* Save modified registries, but skip this machine as it's the caller's
4359 * job to save its settings like all other settings changes. */
4360 mParent->i_unmarkRegistryModified(i_getId());
4361 mParent->i_saveModifiedRegistries();
4362
4363 return rc;
4364}
4365
4366HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4367 LONG aDevice, BOOL aPassthrough)
4368{
4369 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4370 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4371
4372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4373
4374 HRESULT rc = i_checkStateDependency(MutableStateDep);
4375 if (FAILED(rc)) return rc;
4376
4377 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4378
4379 if (Global::IsOnlineOrTransient(mData->mMachineState))
4380 return setError(VBOX_E_INVALID_VM_STATE,
4381 tr("Invalid machine state: %s"),
4382 Global::stringifyMachineState(mData->mMachineState));
4383
4384 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4385 Bstr(aName).raw(),
4386 aControllerPort,
4387 aDevice);
4388 if (!pAttach)
4389 return setError(VBOX_E_OBJECT_NOT_FOUND,
4390 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4391 aDevice, aControllerPort, aName.c_str());
4392
4393
4394 i_setModified(IsModified_Storage);
4395 mMediaData.backup();
4396
4397 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4398
4399 if (pAttach->i_getType() != DeviceType_DVD)
4400 return setError(E_INVALIDARG,
4401 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4402 aDevice, aControllerPort, aName.c_str());
4403 pAttach->i_updatePassthrough(!!aPassthrough);
4404
4405 return S_OK;
4406}
4407
4408HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4409 LONG aDevice, BOOL aTemporaryEject)
4410{
4411
4412 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4413 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4414
4415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4416
4417 HRESULT rc = i_checkStateDependency(MutableStateDep);
4418 if (FAILED(rc)) return rc;
4419
4420 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4421 Bstr(aName).raw(),
4422 aControllerPort,
4423 aDevice);
4424 if (!pAttach)
4425 return setError(VBOX_E_OBJECT_NOT_FOUND,
4426 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4427 aDevice, aControllerPort, aName.c_str());
4428
4429
4430 i_setModified(IsModified_Storage);
4431 mMediaData.backup();
4432
4433 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4434
4435 if (pAttach->i_getType() != DeviceType_DVD)
4436 return setError(E_INVALIDARG,
4437 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4438 aDevice, aControllerPort, aName.c_str());
4439 pAttach->i_updateTempEject(!!aTemporaryEject);
4440
4441 return S_OK;
4442}
4443
4444HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4445 LONG aDevice, BOOL aNonRotational)
4446{
4447
4448 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4449 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4450
4451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4452
4453 HRESULT rc = i_checkStateDependency(MutableStateDep);
4454 if (FAILED(rc)) return rc;
4455
4456 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4457
4458 if (Global::IsOnlineOrTransient(mData->mMachineState))
4459 return setError(VBOX_E_INVALID_VM_STATE,
4460 tr("Invalid machine state: %s"),
4461 Global::stringifyMachineState(mData->mMachineState));
4462
4463 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4464 Bstr(aName).raw(),
4465 aControllerPort,
4466 aDevice);
4467 if (!pAttach)
4468 return setError(VBOX_E_OBJECT_NOT_FOUND,
4469 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4470 aDevice, aControllerPort, aName.c_str());
4471
4472
4473 i_setModified(IsModified_Storage);
4474 mMediaData.backup();
4475
4476 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4477
4478 if (pAttach->i_getType() != DeviceType_HardDisk)
4479 return setError(E_INVALIDARG,
4480 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4481 aDevice, aControllerPort, aName.c_str());
4482 pAttach->i_updateNonRotational(!!aNonRotational);
4483
4484 return S_OK;
4485}
4486
4487HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4488 LONG aDevice, BOOL aDiscard)
4489{
4490
4491 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4492 aName.c_str(), aControllerPort, aDevice, aDiscard));
4493
4494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4495
4496 HRESULT rc = i_checkStateDependency(MutableStateDep);
4497 if (FAILED(rc)) return rc;
4498
4499 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4500
4501 if (Global::IsOnlineOrTransient(mData->mMachineState))
4502 return setError(VBOX_E_INVALID_VM_STATE,
4503 tr("Invalid machine state: %s"),
4504 Global::stringifyMachineState(mData->mMachineState));
4505
4506 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4507 Bstr(aName).raw(),
4508 aControllerPort,
4509 aDevice);
4510 if (!pAttach)
4511 return setError(VBOX_E_OBJECT_NOT_FOUND,
4512 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4513 aDevice, aControllerPort, aName.c_str());
4514
4515
4516 i_setModified(IsModified_Storage);
4517 mMediaData.backup();
4518
4519 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4520
4521 if (pAttach->i_getType() != DeviceType_HardDisk)
4522 return setError(E_INVALIDARG,
4523 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4524 aDevice, aControllerPort, aName.c_str());
4525 pAttach->i_updateDiscard(!!aDiscard);
4526
4527 return S_OK;
4528}
4529
4530HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4531 LONG aDevice, BOOL aHotPluggable)
4532{
4533 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4534 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4535
4536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4537
4538 HRESULT rc = i_checkStateDependency(MutableStateDep);
4539 if (FAILED(rc)) return rc;
4540
4541 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4542
4543 if (Global::IsOnlineOrTransient(mData->mMachineState))
4544 return setError(VBOX_E_INVALID_VM_STATE,
4545 tr("Invalid machine state: %s"),
4546 Global::stringifyMachineState(mData->mMachineState));
4547
4548 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4549 Bstr(aName).raw(),
4550 aControllerPort,
4551 aDevice);
4552 if (!pAttach)
4553 return setError(VBOX_E_OBJECT_NOT_FOUND,
4554 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4555 aDevice, aControllerPort, aName.c_str());
4556
4557 /* Check for an existing controller. */
4558 ComObjPtr<StorageController> ctl;
4559 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4560 if (FAILED(rc)) return rc;
4561
4562 StorageControllerType_T ctrlType;
4563 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4564 if (FAILED(rc))
4565 return setError(E_FAIL,
4566 tr("Could not get type of controller '%s'"),
4567 aName.c_str());
4568
4569 if (!i_isControllerHotplugCapable(ctrlType))
4570 return setError(VBOX_E_NOT_SUPPORTED,
4571 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4572 aName.c_str());
4573
4574 i_setModified(IsModified_Storage);
4575 mMediaData.backup();
4576
4577 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4578
4579 if (pAttach->i_getType() == DeviceType_Floppy)
4580 return setError(E_INVALIDARG,
4581 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4582 aDevice, aControllerPort, aName.c_str());
4583 pAttach->i_updateHotPluggable(!!aHotPluggable);
4584
4585 return S_OK;
4586}
4587
4588HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4589 LONG aDevice)
4590{
4591 int rc = S_OK;
4592 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4593 aName.c_str(), aControllerPort, aDevice));
4594
4595 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4596
4597 return rc;
4598}
4599
4600HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4601 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4602{
4603 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4604 aName.c_str(), aControllerPort, aDevice));
4605
4606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4607
4608 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4609 if (FAILED(rc)) return rc;
4610
4611 if (Global::IsOnlineOrTransient(mData->mMachineState))
4612 return setError(VBOX_E_INVALID_VM_STATE,
4613 tr("Invalid machine state: %s"),
4614 Global::stringifyMachineState(mData->mMachineState));
4615
4616 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4617 Bstr(aName).raw(),
4618 aControllerPort,
4619 aDevice);
4620 if (!pAttach)
4621 return setError(VBOX_E_OBJECT_NOT_FOUND,
4622 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4623 aDevice, aControllerPort, aName.c_str());
4624
4625
4626 i_setModified(IsModified_Storage);
4627 mMediaData.backup();
4628
4629 IBandwidthGroup *iB = aBandwidthGroup;
4630 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4631 if (aBandwidthGroup && group.isNull())
4632 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4633
4634 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4635
4636 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4637 if (strBandwidthGroupOld.isNotEmpty())
4638 {
4639 /* Get the bandwidth group object and release it - this must not fail. */
4640 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4641 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4642 Assert(SUCCEEDED(rc));
4643
4644 pBandwidthGroupOld->i_release();
4645 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4646 }
4647
4648 if (!group.isNull())
4649 {
4650 group->i_reference();
4651 pAttach->i_updateBandwidthGroup(group->i_getName());
4652 }
4653
4654 return S_OK;
4655}
4656
4657HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4658 LONG aControllerPort,
4659 LONG aDevice,
4660 DeviceType_T aType)
4661{
4662 HRESULT rc = S_OK;
4663
4664 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4665 aName.c_str(), aControllerPort, aDevice, aType));
4666
4667 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4668
4669 return rc;
4670}
4671
4672
4673HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4674 LONG aControllerPort,
4675 LONG aDevice,
4676 BOOL aForce)
4677{
4678 int rc = S_OK;
4679 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4680 aName.c_str(), aControllerPort, aForce));
4681
4682 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4683
4684 return rc;
4685}
4686
4687HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4688 LONG aControllerPort,
4689 LONG aDevice,
4690 const ComPtr<IMedium> &aMedium,
4691 BOOL aForce)
4692{
4693 int rc = S_OK;
4694 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4695 aName.c_str(), aControllerPort, aDevice, aForce));
4696
4697 // request the host lock first, since might be calling Host methods for getting host drives;
4698 // next, protect the media tree all the while we're in here, as well as our member variables
4699 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4700 this->lockHandle(),
4701 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4702
4703 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4704 Bstr(aName).raw(),
4705 aControllerPort,
4706 aDevice);
4707 if (pAttach.isNull())
4708 return setError(VBOX_E_OBJECT_NOT_FOUND,
4709 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4710 aDevice, aControllerPort, aName.c_str());
4711
4712 /* Remember previously mounted medium. The medium before taking the
4713 * backup is not necessarily the same thing. */
4714 ComObjPtr<Medium> oldmedium;
4715 oldmedium = pAttach->i_getMedium();
4716
4717 IMedium *iM = aMedium;
4718 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4719 if (aMedium && pMedium.isNull())
4720 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4721
4722 AutoCaller mediumCaller(pMedium);
4723 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4724
4725 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4726 if (pMedium)
4727 {
4728 DeviceType_T mediumType = pAttach->i_getType();
4729 switch (mediumType)
4730 {
4731 case DeviceType_DVD:
4732 case DeviceType_Floppy:
4733 break;
4734
4735 default:
4736 return setError(VBOX_E_INVALID_OBJECT_STATE,
4737 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4738 aControllerPort,
4739 aDevice,
4740 aName.c_str());
4741 }
4742 }
4743
4744 i_setModified(IsModified_Storage);
4745 mMediaData.backup();
4746
4747 {
4748 // The backup operation makes the pAttach reference point to the
4749 // old settings. Re-get the correct reference.
4750 pAttach = i_findAttachment(mMediaData->mAttachments,
4751 Bstr(aName).raw(),
4752 aControllerPort,
4753 aDevice);
4754 if (!oldmedium.isNull())
4755 oldmedium->i_removeBackReference(mData->mUuid);
4756 if (!pMedium.isNull())
4757 {
4758 pMedium->i_addBackReference(mData->mUuid);
4759
4760 mediumLock.release();
4761 multiLock.release();
4762 i_addMediumToRegistry(pMedium);
4763 multiLock.acquire();
4764 mediumLock.acquire();
4765 }
4766
4767 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4768 pAttach->i_updateMedium(pMedium);
4769 }
4770
4771 i_setModified(IsModified_Storage);
4772
4773 mediumLock.release();
4774 multiLock.release();
4775 rc = i_onMediumChange(pAttach, aForce);
4776 multiLock.acquire();
4777 mediumLock.acquire();
4778
4779 /* On error roll back this change only. */
4780 if (FAILED(rc))
4781 {
4782 if (!pMedium.isNull())
4783 pMedium->i_removeBackReference(mData->mUuid);
4784 pAttach = i_findAttachment(mMediaData->mAttachments,
4785 Bstr(aName).raw(),
4786 aControllerPort,
4787 aDevice);
4788 /* If the attachment is gone in the meantime, bail out. */
4789 if (pAttach.isNull())
4790 return rc;
4791 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4792 if (!oldmedium.isNull())
4793 oldmedium->i_addBackReference(mData->mUuid);
4794 pAttach->i_updateMedium(oldmedium);
4795 }
4796
4797 mediumLock.release();
4798 multiLock.release();
4799
4800 /* Save modified registries, but skip this machine as it's the caller's
4801 * job to save its settings like all other settings changes. */
4802 mParent->i_unmarkRegistryModified(i_getId());
4803 mParent->i_saveModifiedRegistries();
4804
4805 return rc;
4806}
4807HRESULT Machine::getMedium(const com::Utf8Str &aName,
4808 LONG aControllerPort,
4809 LONG aDevice,
4810 ComPtr<IMedium> &aMedium)
4811{
4812 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4813 aName.c_str(), aControllerPort, aDevice));
4814
4815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4816
4817 aMedium = NULL;
4818
4819 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4820 Bstr(aName).raw(),
4821 aControllerPort,
4822 aDevice);
4823 if (pAttach.isNull())
4824 return setError(VBOX_E_OBJECT_NOT_FOUND,
4825 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4826 aDevice, aControllerPort, aName.c_str());
4827
4828 aMedium = pAttach->i_getMedium();
4829
4830 return S_OK;
4831}
4832
4833HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4834{
4835
4836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4837
4838 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4839
4840 return S_OK;
4841}
4842
4843HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4844{
4845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4846
4847 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4848
4849 return S_OK;
4850}
4851
4852HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4853{
4854 /* Do not assert if slot is out of range, just return the advertised
4855 status. testdriver/vbox.py triggers this in logVmInfo. */
4856 if (aSlot >= mNetworkAdapters.size())
4857 return setError(E_INVALIDARG,
4858 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4859 aSlot, mNetworkAdapters.size());
4860
4861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4862
4863 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4864
4865 return S_OK;
4866}
4867
4868HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4869{
4870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4871
4872 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4873 size_t i = 0;
4874 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4875 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4876 ++it, ++i)
4877 aKeys[i] = it->first;
4878
4879 return S_OK;
4880}
4881
4882 /**
4883 * @note Locks this object for reading.
4884 */
4885HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4886 com::Utf8Str &aValue)
4887{
4888 /* start with nothing found */
4889 aValue = "";
4890
4891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4892
4893 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4894 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4895 // found:
4896 aValue = it->second; // source is a Utf8Str
4897
4898 /* return the result to caller (may be empty) */
4899 return S_OK;
4900}
4901
4902 /**
4903 * @note Locks mParent for writing + this object for writing.
4904 */
4905HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4906{
4907 Utf8Str strOldValue; // empty
4908
4909 // locking note: we only hold the read lock briefly to look up the old value,
4910 // then release it and call the onExtraCanChange callbacks. There is a small
4911 // chance of a race insofar as the callback might be called twice if two callers
4912 // change the same key at the same time, but that's a much better solution
4913 // than the deadlock we had here before. The actual changing of the extradata
4914 // is then performed under the write lock and race-free.
4915
4916 // look up the old value first; if nothing has changed then we need not do anything
4917 {
4918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4919
4920 // For snapshots don't even think about allowing changes, extradata
4921 // is global for a machine, so there is nothing snapshot specific.
4922 if (i_isSnapshotMachine())
4923 return setError(VBOX_E_INVALID_VM_STATE,
4924 tr("Cannot set extradata for a snapshot"));
4925
4926 // check if the right IMachine instance is used
4927 if (!i_isSessionMachine())
4928 return setError(VBOX_E_INVALID_VM_STATE,
4929 tr("Cannot set extradata for an immutable machine"));
4930
4931 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4932 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4933 strOldValue = it->second;
4934 }
4935
4936 bool fChanged;
4937 if ((fChanged = (strOldValue != aValue)))
4938 {
4939 // ask for permission from all listeners outside the locks;
4940 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4941 // lock to copy the list of callbacks to invoke
4942 Bstr error;
4943 Bstr bstrValue(aValue);
4944
4945 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4946 {
4947 const char *sep = error.isEmpty() ? "" : ": ";
4948 CBSTR err = error.raw();
4949 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4950 sep, err));
4951 return setError(E_ACCESSDENIED,
4952 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4953 aKey.c_str(),
4954 aValue.c_str(),
4955 sep,
4956 err);
4957 }
4958
4959 // data is changing and change not vetoed: then write it out under the lock
4960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4961
4962 if (aValue.isEmpty())
4963 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4964 else
4965 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4966 // creates a new key if needed
4967
4968 bool fNeedsGlobalSaveSettings = false;
4969 // This saving of settings is tricky: there is no "old state" for the
4970 // extradata items at all (unlike all other settings), so the old/new
4971 // settings comparison would give a wrong result!
4972 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4973
4974 if (fNeedsGlobalSaveSettings)
4975 {
4976 // save the global settings; for that we should hold only the VirtualBox lock
4977 alock.release();
4978 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4979 mParent->i_saveSettings();
4980 }
4981 }
4982
4983 // fire notification outside the lock
4984 if (fChanged)
4985 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4986
4987 return S_OK;
4988}
4989
4990HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4991{
4992 aProgress = NULL;
4993 NOREF(aSettingsFilePath);
4994 ReturnComNotImplemented();
4995}
4996
4997HRESULT Machine::saveSettings()
4998{
4999 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5000
5001 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5002 if (FAILED(rc)) return rc;
5003
5004 /* the settings file path may never be null */
5005 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5006
5007 /* save all VM data excluding snapshots */
5008 bool fNeedsGlobalSaveSettings = false;
5009 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5010 mlock.release();
5011
5012 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5013 {
5014 // save the global settings; for that we should hold only the VirtualBox lock
5015 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5016 rc = mParent->i_saveSettings();
5017 }
5018
5019 return rc;
5020}
5021
5022
5023HRESULT Machine::discardSettings()
5024{
5025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5026
5027 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5028 if (FAILED(rc)) return rc;
5029
5030 /*
5031 * during this rollback, the session will be notified if data has
5032 * been actually changed
5033 */
5034 i_rollback(true /* aNotify */);
5035
5036 return S_OK;
5037}
5038
5039/** @note Locks objects! */
5040HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5041 std::vector<ComPtr<IMedium> > &aMedia)
5042{
5043 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5044 AutoLimitedCaller autoCaller(this);
5045 AssertComRCReturnRC(autoCaller.rc());
5046
5047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5048
5049 Guid id(i_getId());
5050
5051 if (mData->mSession.mState != SessionState_Unlocked)
5052 return setError(VBOX_E_INVALID_OBJECT_STATE,
5053 tr("Cannot unregister the machine '%s' while it is locked"),
5054 mUserData->s.strName.c_str());
5055
5056 // wait for state dependents to drop to zero
5057 i_ensureNoStateDependencies();
5058
5059 if (!mData->mAccessible)
5060 {
5061 // inaccessible maschines can only be unregistered; uninitialize ourselves
5062 // here because currently there may be no unregistered that are inaccessible
5063 // (this state combination is not supported). Note releasing the caller and
5064 // leaving the lock before calling uninit()
5065 alock.release();
5066 autoCaller.release();
5067
5068 uninit();
5069
5070 mParent->i_unregisterMachine(this, id);
5071 // calls VirtualBox::i_saveSettings()
5072
5073 return S_OK;
5074 }
5075
5076 HRESULT rc = S_OK;
5077
5078 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5079 // discard saved state
5080 if (mData->mMachineState == MachineState_Saved)
5081 {
5082 // add the saved state file to the list of files the caller should delete
5083 Assert(!mSSData->strStateFilePath.isEmpty());
5084 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5085
5086 mSSData->strStateFilePath.setNull();
5087
5088 // unconditionally set the machine state to powered off, we now
5089 // know no session has locked the machine
5090 mData->mMachineState = MachineState_PoweredOff;
5091 }
5092
5093 size_t cSnapshots = 0;
5094 if (mData->mFirstSnapshot)
5095 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5096 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5097 // fail now before we start detaching media
5098 return setError(VBOX_E_INVALID_OBJECT_STATE,
5099 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5100 mUserData->s.strName.c_str(), cSnapshots);
5101
5102 // This list collects the medium objects from all medium attachments
5103 // which we will detach from the machine and its snapshots, in a specific
5104 // order which allows for closing all media without getting "media in use"
5105 // errors, simply by going through the list from the front to the back:
5106 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5107 // and must be closed before the parent media from the snapshots, or closing the parents
5108 // will fail because they still have children);
5109 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5110 // the root ("first") snapshot of the machine.
5111 MediaList llMedia;
5112
5113 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5114 && mMediaData->mAttachments.size()
5115 )
5116 {
5117 // we have media attachments: detach them all and add the Medium objects to our list
5118 if (aCleanupMode != CleanupMode_UnregisterOnly)
5119 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5120 else
5121 return setError(VBOX_E_INVALID_OBJECT_STATE,
5122 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5123 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5124 }
5125
5126 if (cSnapshots)
5127 {
5128 // add the media from the medium attachments of the snapshots to llMedia
5129 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5130 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5131 // into the children first
5132
5133 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5134 MachineState_T oldState = mData->mMachineState;
5135 mData->mMachineState = MachineState_DeletingSnapshot;
5136
5137 // make a copy of the first snapshot so the refcount does not drop to 0
5138 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5139 // because of the AutoCaller voodoo)
5140 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5141
5142 // GO!
5143 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5144
5145 mData->mMachineState = oldState;
5146 }
5147
5148 if (FAILED(rc))
5149 {
5150 i_rollbackMedia();
5151 return rc;
5152 }
5153
5154 // commit all the media changes made above
5155 i_commitMedia();
5156
5157 mData->mRegistered = false;
5158
5159 // machine lock no longer needed
5160 alock.release();
5161
5162 // return media to caller
5163 size_t i = 0;
5164 aMedia.resize(llMedia.size());
5165 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5166 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5167
5168 mParent->i_unregisterMachine(this, id);
5169 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5170
5171 return S_OK;
5172}
5173
5174struct Machine::DeleteTask
5175{
5176 ComObjPtr<Machine> pMachine;
5177 RTCList<ComPtr<IMedium> > llMediums;
5178 StringsList llFilesToDelete;
5179 ComObjPtr<Progress> pProgress;
5180};
5181
5182HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5183{
5184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5185
5186 HRESULT rc = i_checkStateDependency(MutableStateDep);
5187 if (FAILED(rc)) return rc;
5188
5189 if (mData->mRegistered)
5190 return setError(VBOX_E_INVALID_VM_STATE,
5191 tr("Cannot delete settings of a registered machine"));
5192
5193 DeleteTask *pTask = new DeleteTask;
5194 pTask->pMachine = this;
5195
5196 // collect files to delete
5197 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5198
5199 for (size_t i = 0; i < aMedia.size(); ++i)
5200 {
5201 IMedium *pIMedium(aMedia[i]);
5202 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5203 if (pMedium.isNull())
5204 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5205 SafeArray<BSTR> ids;
5206 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5207 if (FAILED(rc)) return rc;
5208 /* At this point the medium should not have any back references
5209 * anymore. If it has it is attached to another VM and *must* not
5210 * deleted. */
5211 if (ids.size() < 1)
5212 pTask->llMediums.append(pMedium);
5213 }
5214 if (mData->pMachineConfigFile->fileExists())
5215 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5216
5217 pTask->pProgress.createObject();
5218 pTask->pProgress->init(i_getVirtualBox(),
5219 static_cast<IMachine*>(this) /* aInitiator */,
5220 Bstr(tr("Deleting files")).raw(),
5221 true /* fCancellable */,
5222 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5223 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5224
5225 int vrc = RTThreadCreate(NULL,
5226 Machine::deleteThread,
5227 (void*)pTask,
5228 0,
5229 RTTHREADTYPE_MAIN_WORKER,
5230 0,
5231 "MachineDelete");
5232
5233 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5234
5235 if (RT_FAILURE(vrc))
5236 {
5237 delete pTask;
5238 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5239 }
5240
5241 LogFlowFuncLeave();
5242
5243 return S_OK;
5244}
5245
5246/**
5247 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5248 * calls Machine::deleteTaskWorker() on the actual machine object.
5249 * @param Thread
5250 * @param pvUser
5251 * @return
5252 */
5253/*static*/
5254DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5255{
5256 LogFlowFuncEnter();
5257
5258 DeleteTask *pTask = (DeleteTask*)pvUser;
5259 Assert(pTask);
5260 Assert(pTask->pMachine);
5261 Assert(pTask->pProgress);
5262
5263 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5264 pTask->pProgress->i_notifyComplete(rc);
5265
5266 delete pTask;
5267
5268 LogFlowFuncLeave();
5269
5270 NOREF(Thread);
5271
5272 return VINF_SUCCESS;
5273}
5274
5275/**
5276 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5277 * @param task
5278 * @return
5279 */
5280HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5281{
5282 AutoCaller autoCaller(this);
5283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5284
5285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5286
5287 HRESULT rc = S_OK;
5288
5289 try
5290 {
5291 ULONG uLogHistoryCount = 3;
5292 ComPtr<ISystemProperties> systemProperties;
5293 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5294 if (FAILED(rc)) throw rc;
5295
5296 if (!systemProperties.isNull())
5297 {
5298 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5299 if (FAILED(rc)) throw rc;
5300 }
5301
5302 MachineState_T oldState = mData->mMachineState;
5303 i_setMachineState(MachineState_SettingUp);
5304 alock.release();
5305 for (size_t i = 0; i < task.llMediums.size(); ++i)
5306 {
5307 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5308 {
5309 AutoCaller mac(pMedium);
5310 if (FAILED(mac.rc())) throw mac.rc();
5311 Utf8Str strLocation = pMedium->i_getLocationFull();
5312 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5313 if (FAILED(rc)) throw rc;
5314 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5315 }
5316 if (pMedium->i_isMediumFormatFile())
5317 {
5318 ComPtr<IProgress> pProgress2;
5319 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5320 if (FAILED(rc)) throw rc;
5321 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5322 if (FAILED(rc)) throw rc;
5323 /* Check the result of the asynchronous process. */
5324 LONG iRc;
5325 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5326 if (FAILED(rc)) throw rc;
5327 /* If the thread of the progress object has an error, then
5328 * retrieve the error info from there, or it'll be lost. */
5329 if (FAILED(iRc))
5330 throw setError(ProgressErrorInfo(pProgress2));
5331 }
5332
5333 /* Close the medium, deliberately without checking the return
5334 * code, and without leaving any trace in the error info, as
5335 * a failure here is a very minor issue, which shouldn't happen
5336 * as above we even managed to delete the medium. */
5337 {
5338 ErrorInfoKeeper eik;
5339 pMedium->Close();
5340 }
5341 }
5342 i_setMachineState(oldState);
5343 alock.acquire();
5344
5345 // delete the files pushed on the task list by Machine::Delete()
5346 // (this includes saved states of the machine and snapshots and
5347 // medium storage files from the IMedium list passed in, and the
5348 // machine XML file)
5349 StringsList::const_iterator it = task.llFilesToDelete.begin();
5350 while (it != task.llFilesToDelete.end())
5351 {
5352 const Utf8Str &strFile = *it;
5353 LogFunc(("Deleting file %s\n", strFile.c_str()));
5354 int vrc = RTFileDelete(strFile.c_str());
5355 if (RT_FAILURE(vrc))
5356 throw setError(VBOX_E_IPRT_ERROR,
5357 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5358
5359 ++it;
5360 if (it == task.llFilesToDelete.end())
5361 {
5362 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5363 if (FAILED(rc)) throw rc;
5364 break;
5365 }
5366
5367 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5368 if (FAILED(rc)) throw rc;
5369 }
5370
5371 /* delete the settings only when the file actually exists */
5372 if (mData->pMachineConfigFile->fileExists())
5373 {
5374 /* Delete any backup or uncommitted XML files. Ignore failures.
5375 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5376 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5377 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5378 RTFileDelete(otherXml.c_str());
5379 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5380 RTFileDelete(otherXml.c_str());
5381
5382 /* delete the Logs folder, nothing important should be left
5383 * there (we don't check for errors because the user might have
5384 * some private files there that we don't want to delete) */
5385 Utf8Str logFolder;
5386 getLogFolder(logFolder);
5387 Assert(logFolder.length());
5388 if (RTDirExists(logFolder.c_str()))
5389 {
5390 /* Delete all VBox.log[.N] files from the Logs folder
5391 * (this must be in sync with the rotation logic in
5392 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5393 * files that may have been created by the GUI. */
5394 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5395 logFolder.c_str(), RTPATH_DELIMITER);
5396 RTFileDelete(log.c_str());
5397 log = Utf8StrFmt("%s%cVBox.png",
5398 logFolder.c_str(), RTPATH_DELIMITER);
5399 RTFileDelete(log.c_str());
5400 for (int i = uLogHistoryCount; i > 0; i--)
5401 {
5402 log = Utf8StrFmt("%s%cVBox.log.%d",
5403 logFolder.c_str(), RTPATH_DELIMITER, i);
5404 RTFileDelete(log.c_str());
5405 log = Utf8StrFmt("%s%cVBox.png.%d",
5406 logFolder.c_str(), RTPATH_DELIMITER, i);
5407 RTFileDelete(log.c_str());
5408 }
5409#if defined(RT_OS_WINDOWS)
5410 log = Utf8StrFmt("%s%cVBoxStartup.log",
5411 logFolder.c_str(), RTPATH_DELIMITER);
5412 RTFileDelete(log.c_str());
5413#endif
5414
5415 RTDirRemove(logFolder.c_str());
5416 }
5417
5418 /* delete the Snapshots folder, nothing important should be left
5419 * there (we don't check for errors because the user might have
5420 * some private files there that we don't want to delete) */
5421 Utf8Str strFullSnapshotFolder;
5422 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5423 Assert(!strFullSnapshotFolder.isEmpty());
5424 if (RTDirExists(strFullSnapshotFolder.c_str()))
5425 RTDirRemove(strFullSnapshotFolder.c_str());
5426
5427 // delete the directory that contains the settings file, but only
5428 // if it matches the VM name
5429 Utf8Str settingsDir;
5430 if (i_isInOwnDir(&settingsDir))
5431 RTDirRemove(settingsDir.c_str());
5432 }
5433
5434 alock.release();
5435
5436 mParent->i_saveModifiedRegistries();
5437 }
5438 catch (HRESULT aRC) { rc = aRC; }
5439
5440 return rc;
5441}
5442
5443HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5444{
5445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5446
5447 ComObjPtr<Snapshot> pSnapshot;
5448 HRESULT rc;
5449
5450 if (aNameOrId.isEmpty())
5451 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5452 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5453 else
5454 {
5455 Guid uuid(aNameOrId);
5456 if (uuid.isValid())
5457 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5458 else
5459 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5460 }
5461 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5462
5463 return rc;
5464}
5465
5466HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5467{
5468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5469
5470 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5471 if (FAILED(rc)) return rc;
5472
5473 ComObjPtr<SharedFolder> sharedFolder;
5474 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5475 if (SUCCEEDED(rc))
5476 return setError(VBOX_E_OBJECT_IN_USE,
5477 tr("Shared folder named '%s' already exists"),
5478 aName.c_str());
5479
5480 sharedFolder.createObject();
5481 rc = sharedFolder->init(i_getMachine(),
5482 aName,
5483 aHostPath,
5484 !!aWritable,
5485 !!aAutomount,
5486 true /* fFailOnError */);
5487 if (FAILED(rc)) return rc;
5488
5489 i_setModified(IsModified_SharedFolders);
5490 mHWData.backup();
5491 mHWData->mSharedFolders.push_back(sharedFolder);
5492
5493 /* inform the direct session if any */
5494 alock.release();
5495 i_onSharedFolderChange();
5496
5497 return S_OK;
5498}
5499
5500HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5501{
5502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5503
5504 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5505 if (FAILED(rc)) return rc;
5506
5507 ComObjPtr<SharedFolder> sharedFolder;
5508 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5509 if (FAILED(rc)) return rc;
5510
5511 i_setModified(IsModified_SharedFolders);
5512 mHWData.backup();
5513 mHWData->mSharedFolders.remove(sharedFolder);
5514
5515 /* inform the direct session if any */
5516 alock.release();
5517 i_onSharedFolderChange();
5518
5519 return S_OK;
5520}
5521
5522HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5523{
5524 /* start with No */
5525 *aCanShow = FALSE;
5526
5527 ComPtr<IInternalSessionControl> directControl;
5528 {
5529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5530
5531 if (mData->mSession.mState != SessionState_Locked)
5532 return setError(VBOX_E_INVALID_VM_STATE,
5533 tr("Machine is not locked for session (session state: %s)"),
5534 Global::stringifySessionState(mData->mSession.mState));
5535
5536 directControl = mData->mSession.mDirectControl;
5537 }
5538
5539 /* ignore calls made after #OnSessionEnd() is called */
5540 if (!directControl)
5541 return S_OK;
5542
5543 LONG64 dummy;
5544 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5545}
5546
5547HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5548{
5549 ComPtr<IInternalSessionControl> directControl;
5550 {
5551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5552
5553 if (mData->mSession.mState != SessionState_Locked)
5554 return setError(E_FAIL,
5555 tr("Machine is not locked for session (session state: %s)"),
5556 Global::stringifySessionState(mData->mSession.mState));
5557
5558 directControl = mData->mSession.mDirectControl;
5559 }
5560
5561 /* ignore calls made after #OnSessionEnd() is called */
5562 if (!directControl)
5563 return S_OK;
5564
5565 BOOL dummy;
5566 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5567}
5568
5569#ifdef VBOX_WITH_GUEST_PROPS
5570/**
5571 * Look up a guest property in VBoxSVC's internal structures.
5572 */
5573HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5574 com::Utf8Str &aValue,
5575 LONG64 *aTimestamp,
5576 com::Utf8Str &aFlags) const
5577{
5578 using namespace guestProp;
5579
5580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5581 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5582
5583 if (it != mHWData->mGuestProperties.end())
5584 {
5585 char szFlags[MAX_FLAGS_LEN + 1];
5586 aValue = it->second.strValue;
5587 *aTimestamp = it->second.mTimestamp;
5588 writeFlags(it->second.mFlags, szFlags);
5589 aFlags = Utf8Str(szFlags);
5590 }
5591
5592 return S_OK;
5593}
5594
5595/**
5596 * Query the VM that a guest property belongs to for the property.
5597 * @returns E_ACCESSDENIED if the VM process is not available or not
5598 * currently handling queries and the lookup should then be done in
5599 * VBoxSVC.
5600 */
5601HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5602 com::Utf8Str &aValue,
5603 LONG64 *aTimestamp,
5604 com::Utf8Str &aFlags) const
5605{
5606 HRESULT rc = S_OK;
5607 BSTR bValue = NULL;
5608 BSTR bFlags = NULL;
5609
5610 ComPtr<IInternalSessionControl> directControl;
5611 directControl = mData->mSession.mDirectControl;
5612
5613 /* fail if we were called after #OnSessionEnd() is called. This is a
5614 * silly race condition. */
5615
5616 /** @todo This code is bothering API clients (like python script clients) with
5617 * the AccessGuestProperty call, creating unncessary IPC. Need to
5618 * have a way of figuring out which kind of direct session it is... */
5619 if (!directControl)
5620 rc = E_ACCESSDENIED;
5621 else
5622 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5623 0 /* accessMode */,
5624 &bValue, aTimestamp, &bFlags);
5625
5626 aValue = bValue;
5627 aFlags = bFlags;
5628
5629 return rc;
5630}
5631#endif // VBOX_WITH_GUEST_PROPS
5632
5633HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5634 com::Utf8Str &aValue,
5635 LONG64 *aTimestamp,
5636 com::Utf8Str &aFlags)
5637{
5638#ifndef VBOX_WITH_GUEST_PROPS
5639 ReturnComNotImplemented();
5640#else // VBOX_WITH_GUEST_PROPS
5641
5642 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5643
5644 if (rc == E_ACCESSDENIED)
5645 /* The VM is not running or the service is not (yet) accessible */
5646 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5647 return rc;
5648#endif // VBOX_WITH_GUEST_PROPS
5649}
5650
5651HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5652{
5653 LONG64 dummyTimestamp;
5654 com::Utf8Str dummyFlags;
5655 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5656 return rc;
5657
5658}
5659HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5660{
5661 com::Utf8Str dummyFlags;
5662 com::Utf8Str dummyValue;
5663 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5664 return rc;
5665}
5666
5667#ifdef VBOX_WITH_GUEST_PROPS
5668/**
5669 * Set a guest property in VBoxSVC's internal structures.
5670 */
5671HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5672 const com::Utf8Str &aFlags, bool fDelete)
5673{
5674 using namespace guestProp;
5675
5676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5677 HRESULT rc = S_OK;
5678
5679 rc = i_checkStateDependency(MutableOrSavedStateDep);
5680 if (FAILED(rc)) return rc;
5681
5682 try
5683 {
5684 uint32_t fFlags = NILFLAG;
5685 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5686 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5687
5688 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5689 if (it == mHWData->mGuestProperties.end())
5690 {
5691 if (!fDelete)
5692 {
5693 i_setModified(IsModified_MachineData);
5694 mHWData.backupEx();
5695
5696 RTTIMESPEC time;
5697 HWData::GuestProperty prop;
5698 prop.strValue = Bstr(aValue).raw();
5699 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5700 prop.mFlags = fFlags;
5701 mHWData->mGuestProperties[aName] = prop;
5702 }
5703 }
5704 else
5705 {
5706 if (it->second.mFlags & (RDONLYHOST))
5707 {
5708 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5709 }
5710 else
5711 {
5712 i_setModified(IsModified_MachineData);
5713 mHWData.backupEx();
5714
5715 /* The backupEx() operation invalidates our iterator,
5716 * so get a new one. */
5717 it = mHWData->mGuestProperties.find(aName);
5718 Assert(it != mHWData->mGuestProperties.end());
5719
5720 if (!fDelete)
5721 {
5722 RTTIMESPEC time;
5723 it->second.strValue = aValue;
5724 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5725 it->second.mFlags = fFlags;
5726 }
5727 else
5728 mHWData->mGuestProperties.erase(it);
5729 }
5730 }
5731
5732 if ( SUCCEEDED(rc)
5733 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5734 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5735 RTSTR_MAX,
5736 aName.c_str(),
5737 RTSTR_MAX,
5738 NULL)
5739 )
5740 )
5741 {
5742 alock.release();
5743
5744 mParent->i_onGuestPropertyChange(mData->mUuid,
5745 Bstr(aName).raw(),
5746 Bstr(aValue).raw(),
5747 Bstr(aFlags).raw());
5748 }
5749 }
5750 catch (std::bad_alloc &)
5751 {
5752 rc = E_OUTOFMEMORY;
5753 }
5754
5755 return rc;
5756}
5757
5758/**
5759 * Set a property on the VM that that property belongs to.
5760 * @returns E_ACCESSDENIED if the VM process is not available or not
5761 * currently handling queries and the setting should then be done in
5762 * VBoxSVC.
5763 */
5764HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5765 const com::Utf8Str &aFlags, bool fDelete)
5766{
5767 HRESULT rc;
5768
5769 try
5770 {
5771 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5772
5773 BSTR dummy = NULL; /* will not be changed (setter) */
5774 LONG64 dummy64;
5775 if (!directControl)
5776 rc = E_ACCESSDENIED;
5777 else
5778 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5779 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5780 fDelete? 2: 1 /* accessMode */,
5781 &dummy, &dummy64, &dummy);
5782 }
5783 catch (std::bad_alloc &)
5784 {
5785 rc = E_OUTOFMEMORY;
5786 }
5787
5788 return rc;
5789}
5790#endif // VBOX_WITH_GUEST_PROPS
5791
5792HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5793 const com::Utf8Str &aFlags)
5794{
5795#ifndef VBOX_WITH_GUEST_PROPS
5796 ReturnComNotImplemented();
5797#else // VBOX_WITH_GUEST_PROPS
5798 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5799 if (rc == E_ACCESSDENIED)
5800 /* The VM is not running or the service is not (yet) accessible */
5801 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5802 return rc;
5803#endif // VBOX_WITH_GUEST_PROPS
5804}
5805
5806HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5807{
5808 return setGuestProperty(aProperty, aValue, "");
5809}
5810
5811HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5812{
5813#ifndef VBOX_WITH_GUEST_PROPS
5814 ReturnComNotImplemented();
5815#else // VBOX_WITH_GUEST_PROPS
5816 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5817 if (rc == E_ACCESSDENIED)
5818 /* The VM is not running or the service is not (yet) accessible */
5819 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5820 return rc;
5821#endif // VBOX_WITH_GUEST_PROPS
5822}
5823
5824#ifdef VBOX_WITH_GUEST_PROPS
5825/**
5826 * Enumerate the guest properties in VBoxSVC's internal structures.
5827 */
5828HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5829 std::vector<com::Utf8Str> &aNames,
5830 std::vector<com::Utf8Str> &aValues,
5831 std::vector<LONG64> &aTimestamps,
5832 std::vector<com::Utf8Str> &aFlags)
5833{
5834 using namespace guestProp;
5835
5836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5837 Utf8Str strPatterns(aPatterns);
5838
5839 HWData::GuestPropertyMap propMap;
5840
5841 /*
5842 * Look for matching patterns and build up a list.
5843 */
5844 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5845 while (it != mHWData->mGuestProperties.end())
5846 {
5847 if ( strPatterns.isEmpty()
5848 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5849 RTSTR_MAX,
5850 it->first.c_str(),
5851 RTSTR_MAX,
5852 NULL)
5853 )
5854 propMap.insert(*it);
5855 it++;
5856 }
5857
5858 alock.release();
5859
5860 /*
5861 * And build up the arrays for returning the property information.
5862 */
5863 size_t cEntries = propMap.size();
5864
5865 aNames.resize(cEntries);
5866 aValues.resize(cEntries);
5867 aTimestamps.resize(cEntries);
5868 aFlags.resize(cEntries);
5869
5870 char szFlags[MAX_FLAGS_LEN + 1];
5871 size_t i= 0;
5872 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5873 {
5874 aNames[i] = it->first;
5875 aValues[i] = it->second.strValue;
5876 aTimestamps[i] = it->second.mTimestamp;
5877 writeFlags(it->second.mFlags, szFlags);
5878 aFlags[i] = Utf8Str(szFlags);
5879 }
5880
5881 return S_OK;
5882}
5883
5884/**
5885 * Enumerate the properties managed by a VM.
5886 * @returns E_ACCESSDENIED if the VM process is not available or not
5887 * currently handling queries and the setting should then be done in
5888 * VBoxSVC.
5889 */
5890HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5891 std::vector<com::Utf8Str> &aNames,
5892 std::vector<com::Utf8Str> &aValues,
5893 std::vector<LONG64> &aTimestamps,
5894 std::vector<com::Utf8Str> &aFlags)
5895{
5896 HRESULT rc;
5897 ComPtr<IInternalSessionControl> directControl;
5898 directControl = mData->mSession.mDirectControl;
5899
5900
5901 com::SafeArray<BSTR> bNames;
5902 com::SafeArray<BSTR> bValues;
5903 com::SafeArray<LONG64> bTimestamps;
5904 com::SafeArray<BSTR> bFlags;
5905
5906 if (!directControl)
5907 rc = E_ACCESSDENIED;
5908 else
5909 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5910 ComSafeArrayAsOutParam(bNames),
5911 ComSafeArrayAsOutParam(bValues),
5912 ComSafeArrayAsOutParam(bTimestamps),
5913 ComSafeArrayAsOutParam(bFlags));
5914 size_t i;
5915 aNames.resize(bNames.size());
5916 for (i = 0; i < bNames.size(); ++i)
5917 aNames[i] = Utf8Str(bNames[i]);
5918 aValues.resize(bValues.size());
5919 for (i = 0; i < bValues.size(); ++i)
5920 aValues[i] = Utf8Str(bValues[i]);
5921 aTimestamps.resize(bTimestamps.size());
5922 for (i = 0; i < bTimestamps.size(); ++i)
5923 aTimestamps[i] = bTimestamps[i];
5924 aFlags.resize(bFlags.size());
5925 for (i = 0; i < bFlags.size(); ++i)
5926 aFlags[i] = Utf8Str(bFlags[i]);
5927
5928 return rc;
5929}
5930#endif // VBOX_WITH_GUEST_PROPS
5931HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5932 std::vector<com::Utf8Str> &aNames,
5933 std::vector<com::Utf8Str> &aValues,
5934 std::vector<LONG64> &aTimestamps,
5935 std::vector<com::Utf8Str> &aFlags)
5936{
5937#ifndef VBOX_WITH_GUEST_PROPS
5938 ReturnComNotImplemented();
5939#else // VBOX_WITH_GUEST_PROPS
5940
5941 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5942
5943 if (rc == E_ACCESSDENIED)
5944 /* The VM is not running or the service is not (yet) accessible */
5945 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5946 return rc;
5947#endif // VBOX_WITH_GUEST_PROPS
5948}
5949
5950HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5951 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5952{
5953 MediaData::AttachmentList atts;
5954
5955 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5956 if (FAILED(rc)) return rc;
5957
5958 size_t i = 0;
5959 aMediumAttachments.resize(atts.size());
5960 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5961 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5962
5963 return S_OK;
5964}
5965
5966HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5967 LONG aControllerPort,
5968 LONG aDevice,
5969 ComPtr<IMediumAttachment> &aAttachment)
5970{
5971 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5972 aName.c_str(), aControllerPort, aDevice));
5973
5974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5975
5976 aAttachment = NULL;
5977
5978 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5979 Bstr(aName).raw(),
5980 aControllerPort,
5981 aDevice);
5982 if (pAttach.isNull())
5983 return setError(VBOX_E_OBJECT_NOT_FOUND,
5984 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5985 aDevice, aControllerPort, aName.c_str());
5986
5987 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5988
5989 return S_OK;
5990}
5991
5992
5993HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5994 StorageBus_T aConnectionType,
5995 ComPtr<IStorageController> &aController)
5996{
5997 if ( (aConnectionType <= StorageBus_Null)
5998 || (aConnectionType > StorageBus_USB))
5999 return setError(E_INVALIDARG,
6000 tr("Invalid connection type: %d"),
6001 aConnectionType);
6002
6003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6004
6005 HRESULT rc = i_checkStateDependency(MutableStateDep);
6006 if (FAILED(rc)) return rc;
6007
6008 /* try to find one with the name first. */
6009 ComObjPtr<StorageController> ctrl;
6010
6011 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6012 if (SUCCEEDED(rc))
6013 return setError(VBOX_E_OBJECT_IN_USE,
6014 tr("Storage controller named '%s' already exists"),
6015 aName.c_str());
6016
6017 ctrl.createObject();
6018
6019 /* get a new instance number for the storage controller */
6020 ULONG ulInstance = 0;
6021 bool fBootable = true;
6022 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6023 it != mStorageControllers->end();
6024 ++it)
6025 {
6026 if ((*it)->i_getStorageBus() == aConnectionType)
6027 {
6028 ULONG ulCurInst = (*it)->i_getInstance();
6029
6030 if (ulCurInst >= ulInstance)
6031 ulInstance = ulCurInst + 1;
6032
6033 /* Only one controller of each type can be marked as bootable. */
6034 if ((*it)->i_getBootable())
6035 fBootable = false;
6036 }
6037 }
6038
6039 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6040 if (FAILED(rc)) return rc;
6041
6042 i_setModified(IsModified_Storage);
6043 mStorageControllers.backup();
6044 mStorageControllers->push_back(ctrl);
6045
6046 ctrl.queryInterfaceTo(aController.asOutParam());
6047
6048 /* inform the direct session if any */
6049 alock.release();
6050 i_onStorageControllerChange();
6051
6052 return S_OK;
6053}
6054
6055HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6056 ComPtr<IStorageController> &aStorageController)
6057{
6058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6059
6060 ComObjPtr<StorageController> ctrl;
6061
6062 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6063 if (SUCCEEDED(rc))
6064 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6065
6066 return rc;
6067}
6068
6069HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6070 ComPtr<IStorageController> &aStorageController)
6071{
6072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6073
6074 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6075 it != mStorageControllers->end();
6076 ++it)
6077 {
6078 if ((*it)->i_getInstance() == aInstance)
6079 {
6080 (*it).queryInterfaceTo(aStorageController.asOutParam());
6081 return S_OK;
6082 }
6083 }
6084
6085 return setError(VBOX_E_OBJECT_NOT_FOUND,
6086 tr("Could not find a storage controller with instance number '%lu'"),
6087 aInstance);
6088}
6089
6090HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6091{
6092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6093
6094 HRESULT rc = i_checkStateDependency(MutableStateDep);
6095 if (FAILED(rc)) return rc;
6096
6097 ComObjPtr<StorageController> ctrl;
6098
6099 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6100 if (SUCCEEDED(rc))
6101 {
6102 /* Ensure that only one controller of each type is marked as bootable. */
6103 if (aBootable == TRUE)
6104 {
6105 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6106 it != mStorageControllers->end();
6107 ++it)
6108 {
6109 ComObjPtr<StorageController> aCtrl = (*it);
6110
6111 if ( (aCtrl->i_getName() != aName)
6112 && aCtrl->i_getBootable() == TRUE
6113 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6114 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6115 {
6116 aCtrl->i_setBootable(FALSE);
6117 break;
6118 }
6119 }
6120 }
6121
6122 if (SUCCEEDED(rc))
6123 {
6124 ctrl->i_setBootable(aBootable);
6125 i_setModified(IsModified_Storage);
6126 }
6127 }
6128
6129 if (SUCCEEDED(rc))
6130 {
6131 /* inform the direct session if any */
6132 alock.release();
6133 i_onStorageControllerChange();
6134 }
6135
6136 return rc;
6137}
6138
6139HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6140{
6141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6142
6143 HRESULT rc = i_checkStateDependency(MutableStateDep);
6144 if (FAILED(rc)) return rc;
6145
6146 ComObjPtr<StorageController> ctrl;
6147 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6148 if (FAILED(rc)) return rc;
6149
6150 {
6151 /* find all attached devices to the appropriate storage controller and detach them all */
6152 // make a temporary list because detachDevice invalidates iterators into
6153 // mMediaData->mAttachments
6154 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6155
6156 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6157 it != llAttachments2.end();
6158 ++it)
6159 {
6160 MediumAttachment *pAttachTemp = *it;
6161
6162 AutoCaller localAutoCaller(pAttachTemp);
6163 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6164
6165 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6166
6167 if (pAttachTemp->i_getControllerName() == aName)
6168 {
6169 rc = i_detachDevice(pAttachTemp, alock, NULL);
6170 if (FAILED(rc)) return rc;
6171 }
6172 }
6173 }
6174
6175 /* We can remove it now. */
6176 i_setModified(IsModified_Storage);
6177 mStorageControllers.backup();
6178
6179 ctrl->i_unshare();
6180
6181 mStorageControllers->remove(ctrl);
6182
6183 /* inform the direct session if any */
6184 alock.release();
6185 i_onStorageControllerChange();
6186
6187 return S_OK;
6188}
6189
6190HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6191 ComPtr<IUSBController> &aController)
6192{
6193 if ( (aType <= USBControllerType_Null)
6194 || (aType >= USBControllerType_Last))
6195 return setError(E_INVALIDARG,
6196 tr("Invalid USB controller type: %d"),
6197 aType);
6198
6199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6200
6201 HRESULT rc = i_checkStateDependency(MutableStateDep);
6202 if (FAILED(rc)) return rc;
6203
6204 /* try to find one with the same type first. */
6205 ComObjPtr<USBController> ctrl;
6206
6207 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6208 if (SUCCEEDED(rc))
6209 return setError(VBOX_E_OBJECT_IN_USE,
6210 tr("USB controller named '%s' already exists"),
6211 aName.c_str());
6212
6213 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6214 ULONG maxInstances;
6215 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6216 if (FAILED(rc))
6217 return rc;
6218
6219 ULONG cInstances = i_getUSBControllerCountByType(aType);
6220 if (cInstances >= maxInstances)
6221 return setError(E_INVALIDARG,
6222 tr("Too many USB controllers of this type"));
6223
6224 ctrl.createObject();
6225
6226 rc = ctrl->init(this, aName, aType);
6227 if (FAILED(rc)) return rc;
6228
6229 i_setModified(IsModified_USB);
6230 mUSBControllers.backup();
6231 mUSBControllers->push_back(ctrl);
6232
6233 ctrl.queryInterfaceTo(aController.asOutParam());
6234
6235 /* inform the direct session if any */
6236 alock.release();
6237 i_onUSBControllerChange();
6238
6239 return S_OK;
6240}
6241
6242HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6243{
6244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6245
6246 ComObjPtr<USBController> ctrl;
6247
6248 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6249 if (SUCCEEDED(rc))
6250 ctrl.queryInterfaceTo(aController.asOutParam());
6251
6252 return rc;
6253}
6254
6255HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6256 ULONG *aControllers)
6257{
6258 if ( (aType <= USBControllerType_Null)
6259 || (aType >= USBControllerType_Last))
6260 return setError(E_INVALIDARG,
6261 tr("Invalid USB controller type: %d"),
6262 aType);
6263
6264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6265
6266 ComObjPtr<USBController> ctrl;
6267
6268 *aControllers = i_getUSBControllerCountByType(aType);
6269
6270 return S_OK;
6271}
6272
6273HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6274{
6275
6276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6277
6278 HRESULT rc = i_checkStateDependency(MutableStateDep);
6279 if (FAILED(rc)) return rc;
6280
6281 ComObjPtr<USBController> ctrl;
6282 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6283 if (FAILED(rc)) return rc;
6284
6285 i_setModified(IsModified_USB);
6286 mUSBControllers.backup();
6287
6288 ctrl->i_unshare();
6289
6290 mUSBControllers->remove(ctrl);
6291
6292 /* inform the direct session if any */
6293 alock.release();
6294 i_onUSBControllerChange();
6295
6296 return S_OK;
6297}
6298
6299HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6300 ULONG *aOriginX,
6301 ULONG *aOriginY,
6302 ULONG *aWidth,
6303 ULONG *aHeight,
6304 BOOL *aEnabled)
6305{
6306 uint32_t u32OriginX= 0;
6307 uint32_t u32OriginY= 0;
6308 uint32_t u32Width = 0;
6309 uint32_t u32Height = 0;
6310 uint16_t u16Flags = 0;
6311
6312 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6313 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6314 if (RT_FAILURE(vrc))
6315 {
6316#ifdef RT_OS_WINDOWS
6317 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6318 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6319 * So just assign fEnable to TRUE again.
6320 * The right fix would be to change GUI API wrappers to make sure that parameters
6321 * are changed only if API succeeds.
6322 */
6323 *aEnabled = TRUE;
6324#endif
6325 return setError(VBOX_E_IPRT_ERROR,
6326 tr("Saved guest size is not available (%Rrc)"),
6327 vrc);
6328 }
6329
6330 *aOriginX = u32OriginX;
6331 *aOriginY = u32OriginY;
6332 *aWidth = u32Width;
6333 *aHeight = u32Height;
6334 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6335
6336 return S_OK;
6337}
6338
6339HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6340{
6341 if (aScreenId != 0)
6342 return E_NOTIMPL;
6343
6344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6345
6346 uint8_t *pu8Data = NULL;
6347 uint32_t cbData = 0;
6348 uint32_t u32Width = 0;
6349 uint32_t u32Height = 0;
6350
6351 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6352
6353 if (RT_FAILURE(vrc))
6354 return setError(VBOX_E_IPRT_ERROR,
6355 tr("Saved screenshot data is not available (%Rrc)"),
6356 vrc);
6357
6358 *aSize = cbData;
6359 *aWidth = u32Width;
6360 *aHeight = u32Height;
6361
6362 freeSavedDisplayScreenshot(pu8Data);
6363
6364 return S_OK;
6365}
6366
6367HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6368 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6369{
6370 if (aScreenId != 0)
6371 return E_NOTIMPL;
6372
6373 if ( aBitmapFormat != BitmapFormat_BGR0
6374 && aBitmapFormat != BitmapFormat_BGRA
6375 && aBitmapFormat != BitmapFormat_RGBA
6376 && aBitmapFormat != BitmapFormat_PNG)
6377 return setError(E_NOTIMPL,
6378 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6379
6380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6381
6382 uint8_t *pu8Data = NULL;
6383 uint32_t cbData = 0;
6384 uint32_t u32Width = 0;
6385 uint32_t u32Height = 0;
6386
6387 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6388
6389 if (RT_FAILURE(vrc))
6390 return setError(VBOX_E_IPRT_ERROR,
6391 tr("Saved thumbnail data is not available (%Rrc)"),
6392 vrc);
6393
6394 HRESULT hr = S_OK;
6395
6396 *aWidth = u32Width;
6397 *aHeight = u32Height;
6398
6399 if (cbData > 0)
6400 {
6401 /* Convert pixels to the format expected by the API caller. */
6402 if (aBitmapFormat == BitmapFormat_BGR0)
6403 {
6404 /* [0] B, [1] G, [2] R, [3] 0. */
6405 aData.resize(cbData);
6406 memcpy(&aData.front(), pu8Data, cbData);
6407 }
6408 else if (aBitmapFormat == BitmapFormat_BGRA)
6409 {
6410 /* [0] B, [1] G, [2] R, [3] A. */
6411 aData.resize(cbData);
6412 for (uint32_t i = 0; i < cbData; i += 4)
6413 {
6414 aData[i] = pu8Data[i];
6415 aData[i + 1] = pu8Data[i + 1];
6416 aData[i + 2] = pu8Data[i + 2];
6417 aData[i + 3] = 0xff;
6418 }
6419 }
6420 else if (aBitmapFormat == BitmapFormat_RGBA)
6421 {
6422 /* [0] R, [1] G, [2] B, [3] A. */
6423 aData.resize(cbData);
6424 for (uint32_t i = 0; i < cbData; i += 4)
6425 {
6426 aData[i] = pu8Data[i + 2];
6427 aData[i + 1] = pu8Data[i + 1];
6428 aData[i + 2] = pu8Data[i];
6429 aData[i + 3] = 0xff;
6430 }
6431 }
6432 else if (aBitmapFormat == BitmapFormat_PNG)
6433 {
6434 uint8_t *pu8PNG = NULL;
6435 uint32_t cbPNG = 0;
6436 uint32_t cxPNG = 0;
6437 uint32_t cyPNG = 0;
6438
6439 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6440
6441 if (RT_SUCCESS(vrc))
6442 {
6443 aData.resize(cbPNG);
6444 if (cbPNG)
6445 memcpy(&aData.front(), pu8PNG, cbPNG);
6446 }
6447 else
6448 hr = setError(VBOX_E_IPRT_ERROR,
6449 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6450 vrc);
6451
6452 RTMemFree(pu8PNG);
6453 }
6454 }
6455
6456 freeSavedDisplayScreenshot(pu8Data);
6457
6458 return hr;
6459}
6460
6461HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6462{
6463 if (aScreenId != 0)
6464 return E_NOTIMPL;
6465
6466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6467
6468 uint8_t *pu8Data = NULL;
6469 uint32_t cbData = 0;
6470 uint32_t u32Width = 0;
6471 uint32_t u32Height = 0;
6472
6473 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6474
6475 if (RT_FAILURE(vrc))
6476 return setError(VBOX_E_IPRT_ERROR,
6477 tr("Saved screenshot data is not available (%Rrc)"),
6478 vrc);
6479
6480 *aSize = cbData;
6481 *aWidth = u32Width;
6482 *aHeight = u32Height;
6483
6484 freeSavedDisplayScreenshot(pu8Data);
6485
6486 return S_OK;
6487}
6488
6489HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6490{
6491 if (aScreenId != 0)
6492 return E_NOTIMPL;
6493
6494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6495
6496 uint8_t *pu8Data = NULL;
6497 uint32_t cbData = 0;
6498 uint32_t u32Width = 0;
6499 uint32_t u32Height = 0;
6500
6501 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6502
6503 if (RT_FAILURE(vrc))
6504 return setError(VBOX_E_IPRT_ERROR,
6505 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6506 vrc);
6507
6508 *aWidth = u32Width;
6509 *aHeight = u32Height;
6510
6511 aData.resize(cbData);
6512 if (cbData)
6513 memcpy(&aData.front(), pu8Data, cbData);
6514
6515 freeSavedDisplayScreenshot(pu8Data);
6516
6517 return S_OK;
6518}
6519
6520HRESULT Machine::hotPlugCPU(ULONG aCpu)
6521{
6522 HRESULT rc = S_OK;
6523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6524
6525 if (!mHWData->mCPUHotPlugEnabled)
6526 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6527
6528 if (aCpu >= mHWData->mCPUCount)
6529 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6530
6531 if (mHWData->mCPUAttached[aCpu])
6532 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6533
6534 alock.release();
6535 rc = i_onCPUChange(aCpu, false);
6536 alock.acquire();
6537 if (FAILED(rc)) return rc;
6538
6539 i_setModified(IsModified_MachineData);
6540 mHWData.backup();
6541 mHWData->mCPUAttached[aCpu] = true;
6542
6543 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6544 if (Global::IsOnline(mData->mMachineState))
6545 i_saveSettings(NULL);
6546
6547 return S_OK;
6548}
6549
6550HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6551{
6552 HRESULT rc = S_OK;
6553
6554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 if (!mHWData->mCPUHotPlugEnabled)
6557 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6558
6559 if (aCpu >= SchemaDefs::MaxCPUCount)
6560 return setError(E_INVALIDARG,
6561 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6562 SchemaDefs::MaxCPUCount);
6563
6564 if (!mHWData->mCPUAttached[aCpu])
6565 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6566
6567 /* CPU 0 can't be detached */
6568 if (aCpu == 0)
6569 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6570
6571 alock.release();
6572 rc = i_onCPUChange(aCpu, true);
6573 alock.acquire();
6574 if (FAILED(rc)) return rc;
6575
6576 i_setModified(IsModified_MachineData);
6577 mHWData.backup();
6578 mHWData->mCPUAttached[aCpu] = false;
6579
6580 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6581 if (Global::IsOnline(mData->mMachineState))
6582 i_saveSettings(NULL);
6583
6584 return S_OK;
6585}
6586
6587HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6588{
6589 *aAttached = false;
6590
6591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6592
6593 /* If hotplug is enabled the CPU is always enabled. */
6594 if (!mHWData->mCPUHotPlugEnabled)
6595 {
6596 if (aCpu < mHWData->mCPUCount)
6597 *aAttached = true;
6598 }
6599 else
6600 {
6601 if (aCpu < SchemaDefs::MaxCPUCount)
6602 *aAttached = mHWData->mCPUAttached[aCpu];
6603 }
6604
6605 return S_OK;
6606}
6607
6608HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6609{
6610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6611
6612 Utf8Str log = i_queryLogFilename(aIdx);
6613 if (!RTFileExists(log.c_str()))
6614 log.setNull();
6615 aFilename = log;
6616
6617 return S_OK;
6618}
6619
6620HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6621{
6622 if (aSize < 0)
6623 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6624
6625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6626
6627 HRESULT rc = S_OK;
6628 Utf8Str log = i_queryLogFilename(aIdx);
6629
6630 /* do not unnecessarily hold the lock while doing something which does
6631 * not need the lock and potentially takes a long time. */
6632 alock.release();
6633
6634 /* Limit the chunk size to 32K for now, as that gives better performance
6635 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6636 * One byte expands to approx. 25 bytes of breathtaking XML. */
6637 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6638 aData.resize(cbData);
6639
6640 RTFILE LogFile;
6641 int vrc = RTFileOpen(&LogFile, log.c_str(),
6642 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6643 if (RT_SUCCESS(vrc))
6644 {
6645 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6646 if (RT_SUCCESS(vrc))
6647 aData.resize(cbData);
6648 else
6649 rc = setError(VBOX_E_IPRT_ERROR,
6650 tr("Could not read log file '%s' (%Rrc)"),
6651 log.c_str(), vrc);
6652 RTFileClose(LogFile);
6653 }
6654 else
6655 rc = setError(VBOX_E_IPRT_ERROR,
6656 tr("Could not open log file '%s' (%Rrc)"),
6657 log.c_str(), vrc);
6658
6659 if (FAILED(rc))
6660 aData.resize(0);
6661
6662 return rc;
6663}
6664
6665
6666/**
6667 * Currently this method doesn't attach device to the running VM,
6668 * just makes sure it's plugged on next VM start.
6669 */
6670HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6671{
6672 // lock scope
6673 {
6674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6675
6676 HRESULT rc = i_checkStateDependency(MutableStateDep);
6677 if (FAILED(rc)) return rc;
6678
6679 ChipsetType_T aChipset = ChipsetType_PIIX3;
6680 COMGETTER(ChipsetType)(&aChipset);
6681
6682 if (aChipset != ChipsetType_ICH9)
6683 {
6684 return setError(E_INVALIDARG,
6685 tr("Host PCI attachment only supported with ICH9 chipset"));
6686 }
6687
6688 // check if device with this host PCI address already attached
6689 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6690 it != mHWData->mPCIDeviceAssignments.end();
6691 ++it)
6692 {
6693 LONG iHostAddress = -1;
6694 ComPtr<PCIDeviceAttachment> pAttach;
6695 pAttach = *it;
6696 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6697 if (iHostAddress == aHostAddress)
6698 return setError(E_INVALIDARG,
6699 tr("Device with host PCI address already attached to this VM"));
6700 }
6701
6702 ComObjPtr<PCIDeviceAttachment> pda;
6703 char name[32];
6704
6705 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6706 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6707 Bstr bname(name);
6708 pda.createObject();
6709 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6710 i_setModified(IsModified_MachineData);
6711 mHWData.backup();
6712 mHWData->mPCIDeviceAssignments.push_back(pda);
6713 }
6714
6715 return S_OK;
6716}
6717
6718/**
6719 * Currently this method doesn't detach device from the running VM,
6720 * just makes sure it's not plugged on next VM start.
6721 */
6722HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6723{
6724 ComObjPtr<PCIDeviceAttachment> pAttach;
6725 bool fRemoved = false;
6726 HRESULT rc;
6727
6728 // lock scope
6729 {
6730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6731
6732 rc = i_checkStateDependency(MutableStateDep);
6733 if (FAILED(rc)) return rc;
6734
6735 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6736 it != mHWData->mPCIDeviceAssignments.end();
6737 ++it)
6738 {
6739 LONG iHostAddress = -1;
6740 pAttach = *it;
6741 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6742 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6743 {
6744 i_setModified(IsModified_MachineData);
6745 mHWData.backup();
6746 mHWData->mPCIDeviceAssignments.remove(pAttach);
6747 fRemoved = true;
6748 break;
6749 }
6750 }
6751 }
6752
6753
6754 /* Fire event outside of the lock */
6755 if (fRemoved)
6756 {
6757 Assert(!pAttach.isNull());
6758 ComPtr<IEventSource> es;
6759 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6760 Assert(SUCCEEDED(rc));
6761 Bstr mid;
6762 rc = this->COMGETTER(Id)(mid.asOutParam());
6763 Assert(SUCCEEDED(rc));
6764 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6765 }
6766
6767 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6768 tr("No host PCI device %08x attached"),
6769 aHostAddress
6770 );
6771}
6772
6773HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6774{
6775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6776
6777 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6778
6779 size_t i = 0;
6780 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6781 it != mHWData->mPCIDeviceAssignments.end();
6782 ++i, ++it)
6783 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6784
6785 return S_OK;
6786}
6787
6788HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6789{
6790 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6791
6792 return S_OK;
6793}
6794
6795HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6796{
6797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6798
6799 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6800
6801 return S_OK;
6802}
6803
6804HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6805{
6806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6807 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6808 if (SUCCEEDED(hrc))
6809 {
6810 hrc = mHWData.backupEx();
6811 if (SUCCEEDED(hrc))
6812 {
6813 i_setModified(IsModified_MachineData);
6814 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6815 }
6816 }
6817 return hrc;
6818}
6819
6820HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6821{
6822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6823 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6824 return S_OK;
6825}
6826
6827HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6828{
6829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6830 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6831 if (SUCCEEDED(hrc))
6832 {
6833 hrc = mHWData.backupEx();
6834 if (SUCCEEDED(hrc))
6835 {
6836 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6837 if (SUCCEEDED(hrc))
6838 i_setModified(IsModified_MachineData);
6839 }
6840 }
6841 return hrc;
6842}
6843
6844HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6845{
6846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6847
6848 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6849
6850 return S_OK;
6851}
6852
6853HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6854{
6855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6856 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6857 if (SUCCEEDED(hrc))
6858 {
6859 hrc = mHWData.backupEx();
6860 if (SUCCEEDED(hrc))
6861 {
6862 i_setModified(IsModified_MachineData);
6863 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6864 }
6865 }
6866 return hrc;
6867}
6868
6869HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6870{
6871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6872
6873 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6874
6875 return S_OK;
6876}
6877
6878HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6879{
6880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6881
6882 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6883 if ( SUCCEEDED(hrc)
6884 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6885 {
6886 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6887 int vrc;
6888
6889 if (aAutostartEnabled)
6890 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6891 else
6892 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6893
6894 if (RT_SUCCESS(vrc))
6895 {
6896 hrc = mHWData.backupEx();
6897 if (SUCCEEDED(hrc))
6898 {
6899 i_setModified(IsModified_MachineData);
6900 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6901 }
6902 }
6903 else if (vrc == VERR_NOT_SUPPORTED)
6904 hrc = setError(VBOX_E_NOT_SUPPORTED,
6905 tr("The VM autostart feature is not supported on this platform"));
6906 else if (vrc == VERR_PATH_NOT_FOUND)
6907 hrc = setError(E_FAIL,
6908 tr("The path to the autostart database is not set"));
6909 else
6910 hrc = setError(E_UNEXPECTED,
6911 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6912 aAutostartEnabled ? "Adding" : "Removing",
6913 mUserData->s.strName.c_str(), vrc);
6914 }
6915 return hrc;
6916}
6917
6918HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6919{
6920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6921
6922 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6923
6924 return S_OK;
6925}
6926
6927HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6928{
6929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6930 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6931 if (SUCCEEDED(hrc))
6932 {
6933 hrc = mHWData.backupEx();
6934 if (SUCCEEDED(hrc))
6935 {
6936 i_setModified(IsModified_MachineData);
6937 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6938 }
6939 }
6940 return hrc;
6941}
6942
6943HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6944{
6945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6946
6947 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6948
6949 return S_OK;
6950}
6951
6952HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6953{
6954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6955 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6956 if ( SUCCEEDED(hrc)
6957 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6958 {
6959 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6960 int vrc;
6961
6962 if (aAutostopType != AutostopType_Disabled)
6963 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6964 else
6965 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6966
6967 if (RT_SUCCESS(vrc))
6968 {
6969 hrc = mHWData.backupEx();
6970 if (SUCCEEDED(hrc))
6971 {
6972 i_setModified(IsModified_MachineData);
6973 mHWData->mAutostart.enmAutostopType = aAutostopType;
6974 }
6975 }
6976 else if (vrc == VERR_NOT_SUPPORTED)
6977 hrc = setError(VBOX_E_NOT_SUPPORTED,
6978 tr("The VM autostop feature is not supported on this platform"));
6979 else if (vrc == VERR_PATH_NOT_FOUND)
6980 hrc = setError(E_FAIL,
6981 tr("The path to the autostart database is not set"));
6982 else
6983 hrc = setError(E_UNEXPECTED,
6984 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6985 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6986 mUserData->s.strName.c_str(), vrc);
6987 }
6988 return hrc;
6989}
6990
6991HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6992{
6993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6994
6995 aDefaultFrontend = mHWData->mDefaultFrontend;
6996
6997 return S_OK;
6998}
6999
7000HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7001{
7002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7003 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7004 if (SUCCEEDED(hrc))
7005 {
7006 hrc = mHWData.backupEx();
7007 if (SUCCEEDED(hrc))
7008 {
7009 i_setModified(IsModified_MachineData);
7010 mHWData->mDefaultFrontend = aDefaultFrontend;
7011 }
7012 }
7013 return hrc;
7014}
7015
7016HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7017{
7018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7019 size_t cbIcon = mUserData->mIcon.size();
7020 aIcon.resize(cbIcon);
7021 if (cbIcon)
7022 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7023 return S_OK;
7024}
7025
7026HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7027{
7028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7029 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7030 if (SUCCEEDED(hrc))
7031 {
7032 i_setModified(IsModified_MachineData);
7033 mUserData.backup();
7034 size_t cbIcon = aIcon.size();
7035 mUserData->mIcon.resize(cbIcon);
7036 if (cbIcon)
7037 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7038 }
7039 return hrc;
7040}
7041
7042HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7043{
7044#ifdef VBOX_WITH_USB
7045 *aUSBProxyAvailable = true;
7046#else
7047 *aUSBProxyAvailable = false;
7048#endif
7049 return S_OK;
7050}
7051
7052HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7053 ComPtr<IProgress> &aProgress)
7054{
7055 ComObjPtr<Progress> pP;
7056 Progress *ppP = pP;
7057 IProgress *iP = static_cast<IProgress *>(ppP);
7058 IProgress **pProgress = &iP;
7059
7060 IMachine *pTarget = aTarget;
7061
7062 /* Convert the options. */
7063 RTCList<CloneOptions_T> optList;
7064 if (aOptions.size())
7065 for (size_t i = 0; i < aOptions.size(); ++i)
7066 optList.append(aOptions[i]);
7067
7068 if (optList.contains(CloneOptions_Link))
7069 {
7070 if (!i_isSnapshotMachine())
7071 return setError(E_INVALIDARG,
7072 tr("Linked clone can only be created from a snapshot"));
7073 if (aMode != CloneMode_MachineState)
7074 return setError(E_INVALIDARG,
7075 tr("Linked clone can only be created for a single machine state"));
7076 }
7077 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7078
7079 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7080
7081 HRESULT rc = pWorker->start(pProgress);
7082
7083 pP = static_cast<Progress *>(*pProgress);
7084 pP.queryInterfaceTo(aProgress.asOutParam());
7085
7086 return rc;
7087
7088}
7089
7090// public methods for internal purposes
7091/////////////////////////////////////////////////////////////////////////////
7092
7093/**
7094 * Adds the given IsModified_* flag to the dirty flags of the machine.
7095 * This must be called either during i_loadSettings or under the machine write lock.
7096 * @param fl
7097 */
7098void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7099{
7100 mData->flModifications |= fl;
7101 if (fAllowStateModification && i_isStateModificationAllowed())
7102 mData->mCurrentStateModified = true;
7103}
7104
7105/**
7106 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7107 * care of the write locking.
7108 *
7109 * @param fModifications The flag to add.
7110 */
7111void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7112{
7113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7114 i_setModified(fModification, fAllowStateModification);
7115}
7116
7117/**
7118 * Saves the registry entry of this machine to the given configuration node.
7119 *
7120 * @param aEntryNode Node to save the registry entry to.
7121 *
7122 * @note locks this object for reading.
7123 */
7124HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7125{
7126 AutoLimitedCaller autoCaller(this);
7127 AssertComRCReturnRC(autoCaller.rc());
7128
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 data.uuid = mData->mUuid;
7132 data.strSettingsFile = mData->m_strConfigFile;
7133
7134 return S_OK;
7135}
7136
7137/**
7138 * Calculates the absolute path of the given path taking the directory of the
7139 * machine settings file as the current directory.
7140 *
7141 * @param aPath Path to calculate the absolute path for.
7142 * @param aResult Where to put the result (used only on success, can be the
7143 * same Utf8Str instance as passed in @a aPath).
7144 * @return IPRT result.
7145 *
7146 * @note Locks this object for reading.
7147 */
7148int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7149{
7150 AutoCaller autoCaller(this);
7151 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7152
7153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7156
7157 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7158
7159 strSettingsDir.stripFilename();
7160 char folder[RTPATH_MAX];
7161 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7162 if (RT_SUCCESS(vrc))
7163 aResult = folder;
7164
7165 return vrc;
7166}
7167
7168/**
7169 * Copies strSource to strTarget, making it relative to the machine folder
7170 * if it is a subdirectory thereof, or simply copying it otherwise.
7171 *
7172 * @param strSource Path to evaluate and copy.
7173 * @param strTarget Buffer to receive target path.
7174 *
7175 * @note Locks this object for reading.
7176 */
7177void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7178 Utf8Str &strTarget)
7179{
7180 AutoCaller autoCaller(this);
7181 AssertComRCReturn(autoCaller.rc(), (void)0);
7182
7183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7184
7185 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7186 // use strTarget as a temporary buffer to hold the machine settings dir
7187 strTarget = mData->m_strConfigFileFull;
7188 strTarget.stripFilename();
7189 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7190 {
7191 // is relative: then append what's left
7192 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7193 // for empty paths (only possible for subdirs) use "." to avoid
7194 // triggering default settings for not present config attributes.
7195 if (strTarget.isEmpty())
7196 strTarget = ".";
7197 }
7198 else
7199 // is not relative: then overwrite
7200 strTarget = strSource;
7201}
7202
7203/**
7204 * Returns the full path to the machine's log folder in the
7205 * \a aLogFolder argument.
7206 */
7207void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7208{
7209 AutoCaller autoCaller(this);
7210 AssertComRCReturnVoid(autoCaller.rc());
7211
7212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7213
7214 char szTmp[RTPATH_MAX];
7215 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7216 if (RT_SUCCESS(vrc))
7217 {
7218 if (szTmp[0] && !mUserData.isNull())
7219 {
7220 char szTmp2[RTPATH_MAX];
7221 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7222 if (RT_SUCCESS(vrc))
7223 aLogFolder = BstrFmt("%s%c%s",
7224 szTmp2,
7225 RTPATH_DELIMITER,
7226 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7227 }
7228 else
7229 vrc = VERR_PATH_IS_RELATIVE;
7230 }
7231
7232 if (RT_FAILURE(vrc))
7233 {
7234 // fallback if VBOX_USER_LOGHOME is not set or invalid
7235 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7236 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7237 aLogFolder.append(RTPATH_DELIMITER);
7238 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7239 }
7240}
7241
7242/**
7243 * Returns the full path to the machine's log file for an given index.
7244 */
7245Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7246 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7247{
7248 Utf8Str logFolder;
7249 getLogFolder(logFolder);
7250 Assert(logFolder.length());
7251 Utf8Str log;
7252 if (idx == 0)
7253 log = Utf8StrFmt("%s%cVBox.log",
7254 logFolder.c_str(), RTPATH_DELIMITER);
7255 else
7256 log = Utf8StrFmt("%s%cVBox.log.%d",
7257 logFolder.c_str(), RTPATH_DELIMITER, idx);
7258 return log;
7259}
7260
7261/**
7262 * Returns the full path to the machine's (hardened) startup log file.
7263 */
7264Utf8Str Machine::i_getStartupLogFilename(void)
7265{
7266 Utf8Str strFilename;
7267 getLogFolder(strFilename);
7268 Assert(strFilename.length());
7269 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7270 return strFilename;
7271}
7272
7273
7274/**
7275 * Composes a unique saved state filename based on the current system time. The filename is
7276 * granular to the second so this will work so long as no more than one snapshot is taken on
7277 * a machine per second.
7278 *
7279 * Before version 4.1, we used this formula for saved state files:
7280 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7281 * which no longer works because saved state files can now be shared between the saved state of the
7282 * "saved" machine and an online snapshot, and the following would cause problems:
7283 * 1) save machine
7284 * 2) create online snapshot from that machine state --> reusing saved state file
7285 * 3) save machine again --> filename would be reused, breaking the online snapshot
7286 *
7287 * So instead we now use a timestamp.
7288 *
7289 * @param str
7290 */
7291
7292void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7293{
7294 AutoCaller autoCaller(this);
7295 AssertComRCReturnVoid(autoCaller.rc());
7296
7297 {
7298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7299 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7300 }
7301
7302 RTTIMESPEC ts;
7303 RTTimeNow(&ts);
7304 RTTIME time;
7305 RTTimeExplode(&time, &ts);
7306
7307 strStateFilePath += RTPATH_DELIMITER;
7308 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7309 time.i32Year, time.u8Month, time.u8MonthDay,
7310 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7311}
7312
7313/**
7314 * Returns the full path to the default video capture file.
7315 */
7316void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7317{
7318 AutoCaller autoCaller(this);
7319 AssertComRCReturnVoid(autoCaller.rc());
7320
7321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7322
7323 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7324 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7325 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7326}
7327
7328/**
7329 * Returns whether at least one USB controller is present for the VM.
7330 */
7331bool Machine::i_isUSBControllerPresent()
7332{
7333 AutoCaller autoCaller(this);
7334 AssertComRCReturn(autoCaller.rc(), false);
7335
7336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7337
7338 return (mUSBControllers->size() > 0);
7339}
7340
7341/**
7342 * @note Locks this object for writing, calls the client process
7343 * (inside the lock).
7344 */
7345HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7346 const Utf8Str &strFrontend,
7347 const Utf8Str &strEnvironment,
7348 ProgressProxy *aProgress)
7349{
7350 LogFlowThisFuncEnter();
7351
7352 AssertReturn(aControl, E_FAIL);
7353 AssertReturn(aProgress, E_FAIL);
7354 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7355
7356 AutoCaller autoCaller(this);
7357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7358
7359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7360
7361 if (!mData->mRegistered)
7362 return setError(E_UNEXPECTED,
7363 tr("The machine '%s' is not registered"),
7364 mUserData->s.strName.c_str());
7365
7366 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7367
7368 if ( mData->mSession.mState == SessionState_Locked
7369 || mData->mSession.mState == SessionState_Spawning
7370 || mData->mSession.mState == SessionState_Unlocking)
7371 return setError(VBOX_E_INVALID_OBJECT_STATE,
7372 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7373 mUserData->s.strName.c_str());
7374
7375 /* may not be busy */
7376 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7377
7378 /* get the path to the executable */
7379 char szPath[RTPATH_MAX];
7380 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7381 size_t cchBufLeft = strlen(szPath);
7382 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7383 szPath[cchBufLeft] = 0;
7384 char *pszNamePart = szPath + cchBufLeft;
7385 cchBufLeft = sizeof(szPath) - cchBufLeft;
7386
7387 int vrc = VINF_SUCCESS;
7388 RTPROCESS pid = NIL_RTPROCESS;
7389
7390 RTENV env = RTENV_DEFAULT;
7391
7392 if (!strEnvironment.isEmpty())
7393 {
7394 char *newEnvStr = NULL;
7395
7396 do
7397 {
7398 /* clone the current environment */
7399 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7400 AssertRCBreakStmt(vrc2, vrc = vrc2);
7401
7402 newEnvStr = RTStrDup(strEnvironment.c_str());
7403 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7404
7405 /* put new variables to the environment
7406 * (ignore empty variable names here since RTEnv API
7407 * intentionally doesn't do that) */
7408 char *var = newEnvStr;
7409 for (char *p = newEnvStr; *p; ++p)
7410 {
7411 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7412 {
7413 *p = '\0';
7414 if (*var)
7415 {
7416 char *val = strchr(var, '=');
7417 if (val)
7418 {
7419 *val++ = '\0';
7420 vrc2 = RTEnvSetEx(env, var, val);
7421 }
7422 else
7423 vrc2 = RTEnvUnsetEx(env, var);
7424 if (RT_FAILURE(vrc2))
7425 break;
7426 }
7427 var = p + 1;
7428 }
7429 }
7430 if (RT_SUCCESS(vrc2) && *var)
7431 vrc2 = RTEnvPutEx(env, var);
7432
7433 AssertRCBreakStmt(vrc2, vrc = vrc2);
7434 }
7435 while (0);
7436
7437 if (newEnvStr != NULL)
7438 RTStrFree(newEnvStr);
7439 }
7440
7441 /* Hardened startup logging */
7442#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7443 Utf8Str strSupStartLogArg("--sup-startup-log=");
7444 {
7445 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7446 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7447 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7448 {
7449 Utf8Str strStartupLogDir = strStartupLogFile;
7450 strStartupLogDir.stripFilename();
7451 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7452 file without stripping the file. */
7453 }
7454 strSupStartLogArg.append(strStartupLogFile);
7455 }
7456 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7457#else
7458 const char *pszSupStartupLogArg = NULL;
7459#endif
7460
7461
7462#ifdef VBOX_WITH_QTGUI
7463 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7464 {
7465# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7466 /* Modify the base path so that we don't need to use ".." below. */
7467 RTPathStripTrailingSlash(szPath);
7468 RTPathStripFilename(szPath);
7469 cchBufLeft = strlen(szPath);
7470 pszNamePart = szPath + cchBufLeft;
7471 cchBufLeft = sizeof(szPath) - cchBufLeft;
7472
7473# define OSX_APP_NAME "VirtualBoxVM"
7474# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7475
7476 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7477 if ( strAppOverride.contains(".")
7478 || strAppOverride.contains("/")
7479 || strAppOverride.contains("\\")
7480 || strAppOverride.contains(":"))
7481 strAppOverride.setNull();
7482 Utf8Str strAppPath;
7483 if (!strAppOverride.isEmpty())
7484 {
7485 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7486 Utf8Str strFullPath(szPath);
7487 strFullPath.append(strAppPath);
7488 /* there is a race, but people using this deserve the failure */
7489 if (!RTFileExists(strFullPath.c_str()))
7490 strAppOverride.setNull();
7491 }
7492 if (strAppOverride.isEmpty())
7493 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7494 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7495 strcpy(pszNamePart, strAppPath.c_str());
7496# else
7497 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7498 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7499 strcpy(pszNamePart, s_szVirtualBox_exe);
7500# endif
7501
7502 Utf8Str idStr = mData->mUuid.toString();
7503 const char *apszArgs[] =
7504 {
7505 szPath,
7506 "--comment", mUserData->s.strName.c_str(),
7507 "--startvm", idStr.c_str(),
7508 "--no-startvm-errormsgbox",
7509 pszSupStartupLogArg,
7510 NULL
7511 };
7512 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7513 }
7514#else /* !VBOX_WITH_QTGUI */
7515 if (0)
7516 ;
7517#endif /* VBOX_WITH_QTGUI */
7518
7519 else
7520
7521#ifdef VBOX_WITH_VBOXSDL
7522 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7523 {
7524 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7525 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7526 strcpy(pszNamePart, s_szVBoxSDL_exe);
7527
7528 Utf8Str idStr = mData->mUuid.toString();
7529 const char *apszArgs[] =
7530 {
7531 szPath,
7532 "--comment", mUserData->s.strName.c_str(),
7533 "--startvm", idStr.c_str(),
7534 pszSupStartupLogArg,
7535 NULL
7536 };
7537 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7538 }
7539#else /* !VBOX_WITH_VBOXSDL */
7540 if (0)
7541 ;
7542#endif /* !VBOX_WITH_VBOXSDL */
7543
7544 else
7545
7546#ifdef VBOX_WITH_HEADLESS
7547 if ( strFrontend == "headless"
7548 || strFrontend == "capture"
7549 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7550 )
7551 {
7552 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7553 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7554 * and a VM works even if the server has not been installed.
7555 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7556 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7557 * differently in 4.0 and 3.x.
7558 */
7559 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7560 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7561 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7562
7563 Utf8Str idStr = mData->mUuid.toString();
7564 const char *apszArgs[] =
7565 {
7566 szPath,
7567 "--comment", mUserData->s.strName.c_str(),
7568 "--startvm", idStr.c_str(),
7569 "--vrde", "config",
7570 0, /* For "--capture". */
7571 0, /* For "--sup-startup-log". */
7572 0
7573 };
7574 unsigned iArg = 7;
7575 if (strFrontend == "capture")
7576 apszArgs[iArg++] = "--capture";
7577 apszArgs[iArg++] = pszSupStartupLogArg;
7578
7579# ifdef RT_OS_WINDOWS
7580 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7581# else
7582 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7583# endif
7584 }
7585#else /* !VBOX_WITH_HEADLESS */
7586 if (0)
7587 ;
7588#endif /* !VBOX_WITH_HEADLESS */
7589 else
7590 {
7591 RTEnvDestroy(env);
7592 return setError(E_INVALIDARG,
7593 tr("Invalid frontend name: '%s'"),
7594 strFrontend.c_str());
7595 }
7596
7597 RTEnvDestroy(env);
7598
7599 if (RT_FAILURE(vrc))
7600 return setError(VBOX_E_IPRT_ERROR,
7601 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7602 mUserData->s.strName.c_str(), vrc);
7603
7604 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7605
7606 /*
7607 * Note that we don't release the lock here before calling the client,
7608 * because it doesn't need to call us back if called with a NULL argument.
7609 * Releasing the lock here is dangerous because we didn't prepare the
7610 * launch data yet, but the client we've just started may happen to be
7611 * too fast and call LockMachine() that will fail (because of PID, etc.),
7612 * so that the Machine will never get out of the Spawning session state.
7613 */
7614
7615 /* inform the session that it will be a remote one */
7616 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7617#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7618 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7619#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7620 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7621#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7622 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7623
7624 if (FAILED(rc))
7625 {
7626 /* restore the session state */
7627 mData->mSession.mState = SessionState_Unlocked;
7628 alock.release();
7629 mParent->i_addProcessToReap(pid);
7630 /* The failure may occur w/o any error info (from RPC), so provide one */
7631 return setError(VBOX_E_VM_ERROR,
7632 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7633 }
7634
7635 /* attach launch data to the machine */
7636 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7637 mData->mSession.mRemoteControls.push_back(aControl);
7638 mData->mSession.mProgress = aProgress;
7639 mData->mSession.mPID = pid;
7640 mData->mSession.mState = SessionState_Spawning;
7641 mData->mSession.mType = strFrontend;
7642
7643 alock.release();
7644 mParent->i_addProcessToReap(pid);
7645
7646 LogFlowThisFuncLeave();
7647 return S_OK;
7648}
7649
7650/**
7651 * Returns @c true if the given session machine instance has an open direct
7652 * session (and optionally also for direct sessions which are closing) and
7653 * returns the session control machine instance if so.
7654 *
7655 * Note that when the method returns @c false, the arguments remain unchanged.
7656 *
7657 * @param aMachine Session machine object.
7658 * @param aControl Direct session control object (optional).
7659 * @param aAllowClosing If true then additionally a session which is currently
7660 * being closed will also be allowed.
7661 *
7662 * @note locks this object for reading.
7663 */
7664bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7665 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7666 bool aAllowClosing /*= false*/)
7667{
7668 AutoLimitedCaller autoCaller(this);
7669 AssertComRCReturn(autoCaller.rc(), false);
7670
7671 /* just return false for inaccessible machines */
7672 if (getObjectState().getState() != ObjectState::Ready)
7673 return false;
7674
7675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7676
7677 if ( mData->mSession.mState == SessionState_Locked
7678 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7679 )
7680 {
7681 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7682
7683 aMachine = mData->mSession.mMachine;
7684
7685 if (aControl != NULL)
7686 *aControl = mData->mSession.mDirectControl;
7687
7688 return true;
7689 }
7690
7691 return false;
7692}
7693
7694/**
7695 * Returns @c true if the given machine has an spawning direct session.
7696 *
7697 * @note locks this object for reading.
7698 */
7699bool Machine::i_isSessionSpawning()
7700{
7701 AutoLimitedCaller autoCaller(this);
7702 AssertComRCReturn(autoCaller.rc(), false);
7703
7704 /* just return false for inaccessible machines */
7705 if (getObjectState().getState() != ObjectState::Ready)
7706 return false;
7707
7708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7709
7710 if (mData->mSession.mState == SessionState_Spawning)
7711 return true;
7712
7713 return false;
7714}
7715
7716/**
7717 * Called from the client watcher thread to check for unexpected client process
7718 * death during Session_Spawning state (e.g. before it successfully opened a
7719 * direct session).
7720 *
7721 * On Win32 and on OS/2, this method is called only when we've got the
7722 * direct client's process termination notification, so it always returns @c
7723 * true.
7724 *
7725 * On other platforms, this method returns @c true if the client process is
7726 * terminated and @c false if it's still alive.
7727 *
7728 * @note Locks this object for writing.
7729 */
7730bool Machine::i_checkForSpawnFailure()
7731{
7732 AutoCaller autoCaller(this);
7733 if (!autoCaller.isOk())
7734 {
7735 /* nothing to do */
7736 LogFlowThisFunc(("Already uninitialized!\n"));
7737 return true;
7738 }
7739
7740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7741
7742 if (mData->mSession.mState != SessionState_Spawning)
7743 {
7744 /* nothing to do */
7745 LogFlowThisFunc(("Not spawning any more!\n"));
7746 return true;
7747 }
7748
7749 HRESULT rc = S_OK;
7750
7751 /* PID not yet initialized, skip check. */
7752 if (mData->mSession.mPID == NIL_RTPROCESS)
7753 return false;
7754
7755 RTPROCSTATUS status;
7756 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7757
7758 if (vrc != VERR_PROCESS_RUNNING)
7759 {
7760 Utf8Str strExtraInfo;
7761
7762#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7763 /* If the startup logfile exists and is of non-zero length, tell the
7764 user to look there for more details to encourage them to attach it
7765 when reporting startup issues. */
7766 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7767 uint64_t cbStartupLogFile = 0;
7768 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7769 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7770 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7771#endif
7772
7773 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7774 rc = setError(E_FAIL,
7775 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7776 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7777 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7778 rc = setError(E_FAIL,
7779 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7780 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7781 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7782 rc = setError(E_FAIL,
7783 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7784 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7785 else
7786 rc = setError(E_FAIL,
7787 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7788 i_getName().c_str(), vrc, strExtraInfo.c_str());
7789 }
7790
7791 if (FAILED(rc))
7792 {
7793 /* Close the remote session, remove the remote control from the list
7794 * and reset session state to Closed (@note keep the code in sync with
7795 * the relevant part in LockMachine()). */
7796
7797 Assert(mData->mSession.mRemoteControls.size() == 1);
7798 if (mData->mSession.mRemoteControls.size() == 1)
7799 {
7800 ErrorInfoKeeper eik;
7801 mData->mSession.mRemoteControls.front()->Uninitialize();
7802 }
7803
7804 mData->mSession.mRemoteControls.clear();
7805 mData->mSession.mState = SessionState_Unlocked;
7806
7807 /* finalize the progress after setting the state */
7808 if (!mData->mSession.mProgress.isNull())
7809 {
7810 mData->mSession.mProgress->notifyComplete(rc);
7811 mData->mSession.mProgress.setNull();
7812 }
7813
7814 mData->mSession.mPID = NIL_RTPROCESS;
7815
7816 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7817 return true;
7818 }
7819
7820 return false;
7821}
7822
7823/**
7824 * Checks whether the machine can be registered. If so, commits and saves
7825 * all settings.
7826 *
7827 * @note Must be called from mParent's write lock. Locks this object and
7828 * children for writing.
7829 */
7830HRESULT Machine::i_prepareRegister()
7831{
7832 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7833
7834 AutoLimitedCaller autoCaller(this);
7835 AssertComRCReturnRC(autoCaller.rc());
7836
7837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7838
7839 /* wait for state dependents to drop to zero */
7840 i_ensureNoStateDependencies();
7841
7842 if (!mData->mAccessible)
7843 return setError(VBOX_E_INVALID_OBJECT_STATE,
7844 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7845 mUserData->s.strName.c_str(),
7846 mData->mUuid.toString().c_str());
7847
7848 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7849
7850 if (mData->mRegistered)
7851 return setError(VBOX_E_INVALID_OBJECT_STATE,
7852 tr("The machine '%s' with UUID {%s} is already registered"),
7853 mUserData->s.strName.c_str(),
7854 mData->mUuid.toString().c_str());
7855
7856 HRESULT rc = S_OK;
7857
7858 // Ensure the settings are saved. If we are going to be registered and
7859 // no config file exists yet, create it by calling i_saveSettings() too.
7860 if ( (mData->flModifications)
7861 || (!mData->pMachineConfigFile->fileExists())
7862 )
7863 {
7864 rc = i_saveSettings(NULL);
7865 // no need to check whether VirtualBox.xml needs saving too since
7866 // we can't have a machine XML file rename pending
7867 if (FAILED(rc)) return rc;
7868 }
7869
7870 /* more config checking goes here */
7871
7872 if (SUCCEEDED(rc))
7873 {
7874 /* we may have had implicit modifications we want to fix on success */
7875 i_commit();
7876
7877 mData->mRegistered = true;
7878 }
7879 else
7880 {
7881 /* we may have had implicit modifications we want to cancel on failure*/
7882 i_rollback(false /* aNotify */);
7883 }
7884
7885 return rc;
7886}
7887
7888/**
7889 * Increases the number of objects dependent on the machine state or on the
7890 * registered state. Guarantees that these two states will not change at least
7891 * until #releaseStateDependency() is called.
7892 *
7893 * Depending on the @a aDepType value, additional state checks may be made.
7894 * These checks will set extended error info on failure. See
7895 * #checkStateDependency() for more info.
7896 *
7897 * If this method returns a failure, the dependency is not added and the caller
7898 * is not allowed to rely on any particular machine state or registration state
7899 * value and may return the failed result code to the upper level.
7900 *
7901 * @param aDepType Dependency type to add.
7902 * @param aState Current machine state (NULL if not interested).
7903 * @param aRegistered Current registered state (NULL if not interested).
7904 *
7905 * @note Locks this object for writing.
7906 */
7907HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7908 MachineState_T *aState /* = NULL */,
7909 BOOL *aRegistered /* = NULL */)
7910{
7911 AutoCaller autoCaller(this);
7912 AssertComRCReturnRC(autoCaller.rc());
7913
7914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7915
7916 HRESULT rc = i_checkStateDependency(aDepType);
7917 if (FAILED(rc)) return rc;
7918
7919 {
7920 if (mData->mMachineStateChangePending != 0)
7921 {
7922 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7923 * drop to zero so don't add more. It may make sense to wait a bit
7924 * and retry before reporting an error (since the pending state
7925 * transition should be really quick) but let's just assert for
7926 * now to see if it ever happens on practice. */
7927
7928 AssertFailed();
7929
7930 return setError(E_ACCESSDENIED,
7931 tr("Machine state change is in progress. Please retry the operation later."));
7932 }
7933
7934 ++mData->mMachineStateDeps;
7935 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7936 }
7937
7938 if (aState)
7939 *aState = mData->mMachineState;
7940 if (aRegistered)
7941 *aRegistered = mData->mRegistered;
7942
7943 return S_OK;
7944}
7945
7946/**
7947 * Decreases the number of objects dependent on the machine state.
7948 * Must always complete the #addStateDependency() call after the state
7949 * dependency is no more necessary.
7950 */
7951void Machine::i_releaseStateDependency()
7952{
7953 AutoCaller autoCaller(this);
7954 AssertComRCReturnVoid(autoCaller.rc());
7955
7956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7957
7958 /* releaseStateDependency() w/o addStateDependency()? */
7959 AssertReturnVoid(mData->mMachineStateDeps != 0);
7960 -- mData->mMachineStateDeps;
7961
7962 if (mData->mMachineStateDeps == 0)
7963 {
7964 /* inform i_ensureNoStateDependencies() that there are no more deps */
7965 if (mData->mMachineStateChangePending != 0)
7966 {
7967 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7968 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7969 }
7970 }
7971}
7972
7973Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7974{
7975 /* start with nothing found */
7976 Utf8Str strResult("");
7977
7978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7979
7980 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7981 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7982 // found:
7983 strResult = it->second; // source is a Utf8Str
7984
7985 return strResult;
7986}
7987
7988// protected methods
7989/////////////////////////////////////////////////////////////////////////////
7990
7991/**
7992 * Performs machine state checks based on the @a aDepType value. If a check
7993 * fails, this method will set extended error info, otherwise it will return
7994 * S_OK. It is supposed, that on failure, the caller will immediately return
7995 * the return value of this method to the upper level.
7996 *
7997 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7998 *
7999 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8000 * current state of this machine object allows to change settings of the
8001 * machine (i.e. the machine is not registered, or registered but not running
8002 * and not saved). It is useful to call this method from Machine setters
8003 * before performing any change.
8004 *
8005 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8006 * as for MutableStateDep except that if the machine is saved, S_OK is also
8007 * returned. This is useful in setters which allow changing machine
8008 * properties when it is in the saved state.
8009 *
8010 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8011 * if the current state of this machine object allows to change runtime
8012 * changeable settings of the machine (i.e. the machine is not registered, or
8013 * registered but either running or not running and not saved). It is useful
8014 * to call this method from Machine setters before performing any changes to
8015 * runtime changeable settings.
8016 *
8017 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8018 * the same as for MutableOrRunningStateDep except that if the machine is
8019 * saved, S_OK is also returned. This is useful in setters which allow
8020 * changing runtime and saved state changeable machine properties.
8021 *
8022 * @param aDepType Dependency type to check.
8023 *
8024 * @note Non Machine based classes should use #addStateDependency() and
8025 * #releaseStateDependency() methods or the smart AutoStateDependency
8026 * template.
8027 *
8028 * @note This method must be called from under this object's read or write
8029 * lock.
8030 */
8031HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8032{
8033 switch (aDepType)
8034 {
8035 case AnyStateDep:
8036 {
8037 break;
8038 }
8039 case MutableStateDep:
8040 {
8041 if ( mData->mRegistered
8042 && ( !i_isSessionMachine()
8043 || ( mData->mMachineState != MachineState_Aborted
8044 && mData->mMachineState != MachineState_Teleported
8045 && mData->mMachineState != MachineState_PoweredOff
8046 )
8047 )
8048 )
8049 return setError(VBOX_E_INVALID_VM_STATE,
8050 tr("The machine is not mutable (state is %s)"),
8051 Global::stringifyMachineState(mData->mMachineState));
8052 break;
8053 }
8054 case MutableOrSavedStateDep:
8055 {
8056 if ( mData->mRegistered
8057 && ( !i_isSessionMachine()
8058 || ( mData->mMachineState != MachineState_Aborted
8059 && mData->mMachineState != MachineState_Teleported
8060 && mData->mMachineState != MachineState_Saved
8061 && mData->mMachineState != MachineState_PoweredOff
8062 )
8063 )
8064 )
8065 return setError(VBOX_E_INVALID_VM_STATE,
8066 tr("The machine is not mutable (state is %s)"),
8067 Global::stringifyMachineState(mData->mMachineState));
8068 break;
8069 }
8070 case MutableOrRunningStateDep:
8071 {
8072 if ( mData->mRegistered
8073 && ( !i_isSessionMachine()
8074 || ( mData->mMachineState != MachineState_Aborted
8075 && mData->mMachineState != MachineState_Teleported
8076 && mData->mMachineState != MachineState_PoweredOff
8077 && !Global::IsOnline(mData->mMachineState)
8078 )
8079 )
8080 )
8081 return setError(VBOX_E_INVALID_VM_STATE,
8082 tr("The machine is not mutable (state is %s)"),
8083 Global::stringifyMachineState(mData->mMachineState));
8084 break;
8085 }
8086 case MutableOrSavedOrRunningStateDep:
8087 {
8088 if ( mData->mRegistered
8089 && ( !i_isSessionMachine()
8090 || ( mData->mMachineState != MachineState_Aborted
8091 && mData->mMachineState != MachineState_Teleported
8092 && mData->mMachineState != MachineState_Saved
8093 && mData->mMachineState != MachineState_PoweredOff
8094 && !Global::IsOnline(mData->mMachineState)
8095 )
8096 )
8097 )
8098 return setError(VBOX_E_INVALID_VM_STATE,
8099 tr("The machine is not mutable (state is %s)"),
8100 Global::stringifyMachineState(mData->mMachineState));
8101 break;
8102 }
8103 }
8104
8105 return S_OK;
8106}
8107
8108/**
8109 * Helper to initialize all associated child objects and allocate data
8110 * structures.
8111 *
8112 * This method must be called as a part of the object's initialization procedure
8113 * (usually done in the #init() method).
8114 *
8115 * @note Must be called only from #init() or from #registeredInit().
8116 */
8117HRESULT Machine::initDataAndChildObjects()
8118{
8119 AutoCaller autoCaller(this);
8120 AssertComRCReturnRC(autoCaller.rc());
8121 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8122 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8123
8124 AssertReturn(!mData->mAccessible, E_FAIL);
8125
8126 /* allocate data structures */
8127 mSSData.allocate();
8128 mUserData.allocate();
8129 mHWData.allocate();
8130 mMediaData.allocate();
8131 mStorageControllers.allocate();
8132 mUSBControllers.allocate();
8133
8134 /* initialize mOSTypeId */
8135 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8136
8137 /* create associated BIOS settings object */
8138 unconst(mBIOSSettings).createObject();
8139 mBIOSSettings->init(this);
8140
8141 /* create an associated VRDE object (default is disabled) */
8142 unconst(mVRDEServer).createObject();
8143 mVRDEServer->init(this);
8144
8145 /* create associated serial port objects */
8146 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8147 {
8148 unconst(mSerialPorts[slot]).createObject();
8149 mSerialPorts[slot]->init(this, slot);
8150 }
8151
8152 /* create associated parallel port objects */
8153 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8154 {
8155 unconst(mParallelPorts[slot]).createObject();
8156 mParallelPorts[slot]->init(this, slot);
8157 }
8158
8159 /* create the audio adapter object (always present, default is disabled) */
8160 unconst(mAudioAdapter).createObject();
8161 mAudioAdapter->init(this);
8162
8163 /* create the USB device filters object (always present) */
8164 unconst(mUSBDeviceFilters).createObject();
8165 mUSBDeviceFilters->init(this);
8166
8167 /* create associated network adapter objects */
8168 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8169 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8170 {
8171 unconst(mNetworkAdapters[slot]).createObject();
8172 mNetworkAdapters[slot]->init(this, slot);
8173 }
8174
8175 /* create the bandwidth control */
8176 unconst(mBandwidthControl).createObject();
8177 mBandwidthControl->init(this);
8178
8179 return S_OK;
8180}
8181
8182/**
8183 * Helper to uninitialize all associated child objects and to free all data
8184 * structures.
8185 *
8186 * This method must be called as a part of the object's uninitialization
8187 * procedure (usually done in the #uninit() method).
8188 *
8189 * @note Must be called only from #uninit() or from #registeredInit().
8190 */
8191void Machine::uninitDataAndChildObjects()
8192{
8193 AutoCaller autoCaller(this);
8194 AssertComRCReturnVoid(autoCaller.rc());
8195 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8196 || getObjectState().getState() == ObjectState::Limited);
8197
8198 /* tell all our other child objects we've been uninitialized */
8199 if (mBandwidthControl)
8200 {
8201 mBandwidthControl->uninit();
8202 unconst(mBandwidthControl).setNull();
8203 }
8204
8205 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8206 {
8207 if (mNetworkAdapters[slot])
8208 {
8209 mNetworkAdapters[slot]->uninit();
8210 unconst(mNetworkAdapters[slot]).setNull();
8211 }
8212 }
8213
8214 if (mUSBDeviceFilters)
8215 {
8216 mUSBDeviceFilters->uninit();
8217 unconst(mUSBDeviceFilters).setNull();
8218 }
8219
8220 if (mAudioAdapter)
8221 {
8222 mAudioAdapter->uninit();
8223 unconst(mAudioAdapter).setNull();
8224 }
8225
8226 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8227 {
8228 if (mParallelPorts[slot])
8229 {
8230 mParallelPorts[slot]->uninit();
8231 unconst(mParallelPorts[slot]).setNull();
8232 }
8233 }
8234
8235 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8236 {
8237 if (mSerialPorts[slot])
8238 {
8239 mSerialPorts[slot]->uninit();
8240 unconst(mSerialPorts[slot]).setNull();
8241 }
8242 }
8243
8244 if (mVRDEServer)
8245 {
8246 mVRDEServer->uninit();
8247 unconst(mVRDEServer).setNull();
8248 }
8249
8250 if (mBIOSSettings)
8251 {
8252 mBIOSSettings->uninit();
8253 unconst(mBIOSSettings).setNull();
8254 }
8255
8256 /* Deassociate media (only when a real Machine or a SnapshotMachine
8257 * instance is uninitialized; SessionMachine instances refer to real
8258 * Machine media). This is necessary for a clean re-initialization of
8259 * the VM after successfully re-checking the accessibility state. Note
8260 * that in case of normal Machine or SnapshotMachine uninitialization (as
8261 * a result of unregistering or deleting the snapshot), outdated media
8262 * attachments will already be uninitialized and deleted, so this
8263 * code will not affect them. */
8264 if ( !!mMediaData
8265 && (!i_isSessionMachine())
8266 )
8267 {
8268 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8269 it != mMediaData->mAttachments.end();
8270 ++it)
8271 {
8272 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8273 if (pMedium.isNull())
8274 continue;
8275 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8276 AssertComRC(rc);
8277 }
8278 }
8279
8280 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8281 {
8282 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8283 if (mData->mFirstSnapshot)
8284 {
8285 // snapshots tree is protected by machine write lock; strictly
8286 // this isn't necessary here since we're deleting the entire
8287 // machine, but otherwise we assert in Snapshot::uninit()
8288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8289 mData->mFirstSnapshot->uninit();
8290 mData->mFirstSnapshot.setNull();
8291 }
8292
8293 mData->mCurrentSnapshot.setNull();
8294 }
8295
8296 /* free data structures (the essential mData structure is not freed here
8297 * since it may be still in use) */
8298 mMediaData.free();
8299 mStorageControllers.free();
8300 mUSBControllers.free();
8301 mHWData.free();
8302 mUserData.free();
8303 mSSData.free();
8304}
8305
8306/**
8307 * Returns a pointer to the Machine object for this machine that acts like a
8308 * parent for complex machine data objects such as shared folders, etc.
8309 *
8310 * For primary Machine objects and for SnapshotMachine objects, returns this
8311 * object's pointer itself. For SessionMachine objects, returns the peer
8312 * (primary) machine pointer.
8313 */
8314Machine* Machine::i_getMachine()
8315{
8316 if (i_isSessionMachine())
8317 return (Machine*)mPeer;
8318 return this;
8319}
8320
8321/**
8322 * Makes sure that there are no machine state dependents. If necessary, waits
8323 * for the number of dependents to drop to zero.
8324 *
8325 * Make sure this method is called from under this object's write lock to
8326 * guarantee that no new dependents may be added when this method returns
8327 * control to the caller.
8328 *
8329 * @note Locks this object for writing. The lock will be released while waiting
8330 * (if necessary).
8331 *
8332 * @warning To be used only in methods that change the machine state!
8333 */
8334void Machine::i_ensureNoStateDependencies()
8335{
8336 AssertReturnVoid(isWriteLockOnCurrentThread());
8337
8338 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8339
8340 /* Wait for all state dependents if necessary */
8341 if (mData->mMachineStateDeps != 0)
8342 {
8343 /* lazy semaphore creation */
8344 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8345 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8346
8347 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8348 mData->mMachineStateDeps));
8349
8350 ++mData->mMachineStateChangePending;
8351
8352 /* reset the semaphore before waiting, the last dependent will signal
8353 * it */
8354 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8355
8356 alock.release();
8357
8358 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8359
8360 alock.acquire();
8361
8362 -- mData->mMachineStateChangePending;
8363 }
8364}
8365
8366/**
8367 * Changes the machine state and informs callbacks.
8368 *
8369 * This method is not intended to fail so it either returns S_OK or asserts (and
8370 * returns a failure).
8371 *
8372 * @note Locks this object for writing.
8373 */
8374HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8375{
8376 LogFlowThisFuncEnter();
8377 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8378
8379 AutoCaller autoCaller(this);
8380 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8381
8382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8383
8384 /* wait for state dependents to drop to zero */
8385 i_ensureNoStateDependencies();
8386
8387 MachineState_T const enmOldState = mData->mMachineState;
8388 if (enmOldState != aMachineState)
8389 {
8390 mData->mMachineState = aMachineState;
8391 RTTimeNow(&mData->mLastStateChange);
8392
8393#ifdef VBOX_WITH_DTRACE_R3_MAIN
8394 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8395#endif
8396 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8397 }
8398
8399 LogFlowThisFuncLeave();
8400 return S_OK;
8401}
8402
8403/**
8404 * Searches for a shared folder with the given logical name
8405 * in the collection of shared folders.
8406 *
8407 * @param aName logical name of the shared folder
8408 * @param aSharedFolder where to return the found object
8409 * @param aSetError whether to set the error info if the folder is
8410 * not found
8411 * @return
8412 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8413 *
8414 * @note
8415 * must be called from under the object's lock!
8416 */
8417HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8418 ComObjPtr<SharedFolder> &aSharedFolder,
8419 bool aSetError /* = false */)
8420{
8421 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8422 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8423 it != mHWData->mSharedFolders.end();
8424 ++it)
8425 {
8426 SharedFolder *pSF = *it;
8427 AutoCaller autoCaller(pSF);
8428 if (pSF->i_getName() == aName)
8429 {
8430 aSharedFolder = pSF;
8431 rc = S_OK;
8432 break;
8433 }
8434 }
8435
8436 if (aSetError && FAILED(rc))
8437 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8438
8439 return rc;
8440}
8441
8442/**
8443 * Initializes all machine instance data from the given settings structures
8444 * from XML. The exception is the machine UUID which needs special handling
8445 * depending on the caller's use case, so the caller needs to set that herself.
8446 *
8447 * This gets called in several contexts during machine initialization:
8448 *
8449 * -- When machine XML exists on disk already and needs to be loaded into memory,
8450 * for example, from registeredInit() to load all registered machines on
8451 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8452 * attached to the machine should be part of some media registry already.
8453 *
8454 * -- During OVF import, when a machine config has been constructed from an
8455 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8456 * ensure that the media listed as attachments in the config (which have
8457 * been imported from the OVF) receive the correct registry ID.
8458 *
8459 * -- During VM cloning.
8460 *
8461 * @param config Machine settings from XML.
8462 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8463 * for each attached medium in the config.
8464 * @return
8465 */
8466HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8467 const Guid *puuidRegistry)
8468{
8469 // copy name, description, OS type, teleporter, UTC etc.
8470 mUserData->s = config.machineUserData;
8471
8472 // Decode the Icon overide data from config userdata and set onto Machine.
8473 #define DECODE_STR_MAX _1M
8474 const char* pszStr = config.machineUserData.ovIcon.c_str();
8475 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8476 if (cbOut > DECODE_STR_MAX)
8477 return setError(E_FAIL,
8478 tr("Icon Data too long.'%d' > '%d'"),
8479 cbOut,
8480 DECODE_STR_MAX);
8481 mUserData->mIcon.resize(cbOut);
8482 int vrc = VINF_SUCCESS;
8483 if (cbOut)
8484 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8485 if (RT_FAILURE(vrc))
8486 {
8487 mUserData->mIcon.resize(0);
8488 return setError(E_FAIL,
8489 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8490 pszStr,
8491 vrc);
8492 }
8493
8494 // look up the object by Id to check it is valid
8495 ComPtr<IGuestOSType> guestOSType;
8496 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8497 guestOSType.asOutParam());
8498 if (FAILED(rc)) return rc;
8499
8500 // stateFile (optional)
8501 if (config.strStateFile.isEmpty())
8502 mSSData->strStateFilePath.setNull();
8503 else
8504 {
8505 Utf8Str stateFilePathFull(config.strStateFile);
8506 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8507 if (RT_FAILURE(vrc))
8508 return setError(E_FAIL,
8509 tr("Invalid saved state file path '%s' (%Rrc)"),
8510 config.strStateFile.c_str(),
8511 vrc);
8512 mSSData->strStateFilePath = stateFilePathFull;
8513 }
8514
8515 // snapshot folder needs special processing so set it again
8516 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8517 if (FAILED(rc)) return rc;
8518
8519 /* Copy the extra data items (Not in any case config is already the same as
8520 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8521 * make sure the extra data map is copied). */
8522 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8523
8524 /* currentStateModified (optional, default is true) */
8525 mData->mCurrentStateModified = config.fCurrentStateModified;
8526
8527 mData->mLastStateChange = config.timeLastStateChange;
8528
8529 /*
8530 * note: all mUserData members must be assigned prior this point because
8531 * we need to commit changes in order to let mUserData be shared by all
8532 * snapshot machine instances.
8533 */
8534 mUserData.commitCopy();
8535
8536 // machine registry, if present (must be loaded before snapshots)
8537 if (config.canHaveOwnMediaRegistry())
8538 {
8539 // determine machine folder
8540 Utf8Str strMachineFolder = i_getSettingsFileFull();
8541 strMachineFolder.stripFilename();
8542 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8543 config.mediaRegistry,
8544 strMachineFolder);
8545 if (FAILED(rc)) return rc;
8546 }
8547
8548 /* Snapshot node (optional) */
8549 size_t cRootSnapshots;
8550 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8551 {
8552 // there must be only one root snapshot
8553 Assert(cRootSnapshots == 1);
8554
8555 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8556
8557 rc = i_loadSnapshot(snap,
8558 config.uuidCurrentSnapshot,
8559 NULL); // no parent == first snapshot
8560 if (FAILED(rc)) return rc;
8561 }
8562
8563 // hardware data
8564 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8565 if (FAILED(rc)) return rc;
8566
8567 // load storage controllers
8568 rc = i_loadStorageControllers(config.storageMachine,
8569 puuidRegistry,
8570 NULL /* puuidSnapshot */);
8571 if (FAILED(rc)) return rc;
8572
8573 /*
8574 * NOTE: the assignment below must be the last thing to do,
8575 * otherwise it will be not possible to change the settings
8576 * somewhere in the code above because all setters will be
8577 * blocked by i_checkStateDependency(MutableStateDep).
8578 */
8579
8580 /* set the machine state to Aborted or Saved when appropriate */
8581 if (config.fAborted)
8582 {
8583 mSSData->strStateFilePath.setNull();
8584
8585 /* no need to use i_setMachineState() during init() */
8586 mData->mMachineState = MachineState_Aborted;
8587 }
8588 else if (!mSSData->strStateFilePath.isEmpty())
8589 {
8590 /* no need to use i_setMachineState() during init() */
8591 mData->mMachineState = MachineState_Saved;
8592 }
8593
8594 // after loading settings, we are no longer different from the XML on disk
8595 mData->flModifications = 0;
8596
8597 return S_OK;
8598}
8599
8600/**
8601 * Recursively loads all snapshots starting from the given.
8602 *
8603 * @param aNode <Snapshot> node.
8604 * @param aCurSnapshotId Current snapshot ID from the settings file.
8605 * @param aParentSnapshot Parent snapshot.
8606 */
8607HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8608 const Guid &aCurSnapshotId,
8609 Snapshot *aParentSnapshot)
8610{
8611 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8612 AssertReturn(!i_isSessionMachine(), E_FAIL);
8613
8614 HRESULT rc = S_OK;
8615
8616 Utf8Str strStateFile;
8617 if (!data.strStateFile.isEmpty())
8618 {
8619 /* optional */
8620 strStateFile = data.strStateFile;
8621 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8622 if (RT_FAILURE(vrc))
8623 return setError(E_FAIL,
8624 tr("Invalid saved state file path '%s' (%Rrc)"),
8625 strStateFile.c_str(),
8626 vrc);
8627 }
8628
8629 /* create a snapshot machine object */
8630 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8631 pSnapshotMachine.createObject();
8632 rc = pSnapshotMachine->initFromSettings(this,
8633 data.hardware,
8634 &data.debugging,
8635 &data.autostart,
8636 data.storage,
8637 data.uuid.ref(),
8638 strStateFile);
8639 if (FAILED(rc)) return rc;
8640
8641 /* create a snapshot object */
8642 ComObjPtr<Snapshot> pSnapshot;
8643 pSnapshot.createObject();
8644 /* initialize the snapshot */
8645 rc = pSnapshot->init(mParent, // VirtualBox object
8646 data.uuid,
8647 data.strName,
8648 data.strDescription,
8649 data.timestamp,
8650 pSnapshotMachine,
8651 aParentSnapshot);
8652 if (FAILED(rc)) return rc;
8653
8654 /* memorize the first snapshot if necessary */
8655 if (!mData->mFirstSnapshot)
8656 mData->mFirstSnapshot = pSnapshot;
8657
8658 /* memorize the current snapshot when appropriate */
8659 if ( !mData->mCurrentSnapshot
8660 && pSnapshot->i_getId() == aCurSnapshotId
8661 )
8662 mData->mCurrentSnapshot = pSnapshot;
8663
8664 // now create the children
8665 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8666 it != data.llChildSnapshots.end();
8667 ++it)
8668 {
8669 const settings::Snapshot &childData = *it;
8670 // recurse
8671 rc = i_loadSnapshot(childData,
8672 aCurSnapshotId,
8673 pSnapshot); // parent = the one we created above
8674 if (FAILED(rc)) return rc;
8675 }
8676
8677 return rc;
8678}
8679
8680/**
8681 * Loads settings into mHWData.
8682 *
8683 * @param data Reference to the hardware settings.
8684 * @param pDbg Pointer to the debugging settings.
8685 * @param pAutostart Pointer to the autostart settings.
8686 */
8687HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8688 const settings::Autostart *pAutostart)
8689{
8690 AssertReturn(!i_isSessionMachine(), E_FAIL);
8691
8692 HRESULT rc = S_OK;
8693
8694 try
8695 {
8696 /* The hardware version attribute (optional). */
8697 mHWData->mHWVersion = data.strVersion;
8698 mHWData->mHardwareUUID = data.uuid;
8699
8700 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8701 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8702 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8703 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8704 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8705 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8706 mHWData->mPAEEnabled = data.fPAE;
8707 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8708 mHWData->mLongMode = data.enmLongMode;
8709 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8710 mHWData->mCPUCount = data.cCPUs;
8711 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8712 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8713
8714 // cpu
8715 if (mHWData->mCPUHotPlugEnabled)
8716 {
8717 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8718 it != data.llCpus.end();
8719 ++it)
8720 {
8721 const settings::Cpu &cpu = *it;
8722
8723 mHWData->mCPUAttached[cpu.ulId] = true;
8724 }
8725 }
8726
8727 // cpuid leafs
8728 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8729 it != data.llCpuIdLeafs.end();
8730 ++it)
8731 {
8732 const settings::CpuIdLeaf &leaf = *it;
8733
8734 switch (leaf.ulId)
8735 {
8736 case 0x0:
8737 case 0x1:
8738 case 0x2:
8739 case 0x3:
8740 case 0x4:
8741 case 0x5:
8742 case 0x6:
8743 case 0x7:
8744 case 0x8:
8745 case 0x9:
8746 case 0xA:
8747 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8748 break;
8749
8750 case 0x80000000:
8751 case 0x80000001:
8752 case 0x80000002:
8753 case 0x80000003:
8754 case 0x80000004:
8755 case 0x80000005:
8756 case 0x80000006:
8757 case 0x80000007:
8758 case 0x80000008:
8759 case 0x80000009:
8760 case 0x8000000A:
8761 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8762 break;
8763
8764 default:
8765 /* just ignore */
8766 break;
8767 }
8768 }
8769
8770 mHWData->mMemorySize = data.ulMemorySizeMB;
8771 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8772
8773 // boot order
8774 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8775 {
8776 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8777 if (it == data.mapBootOrder.end())
8778 mHWData->mBootOrder[i] = DeviceType_Null;
8779 else
8780 mHWData->mBootOrder[i] = it->second;
8781 }
8782
8783 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8784 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8785 mHWData->mMonitorCount = data.cMonitors;
8786 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8787 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8788 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8789 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8790 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8791 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8792 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8793 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8794 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8795 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8796 if (!data.strVideoCaptureFile.isEmpty())
8797 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8798 else
8799 mHWData->mVideoCaptureFile.setNull();
8800 mHWData->mFirmwareType = data.firmwareType;
8801 mHWData->mPointingHIDType = data.pointingHIDType;
8802 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8803 mHWData->mChipsetType = data.chipsetType;
8804 mHWData->mParavirtProvider = data.paravirtProvider;
8805 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8806 mHWData->mHPETEnabled = data.fHPETEnabled;
8807
8808 /* VRDEServer */
8809 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8810 if (FAILED(rc)) return rc;
8811
8812 /* BIOS */
8813 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8814 if (FAILED(rc)) return rc;
8815
8816 // Bandwidth control (must come before network adapters)
8817 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8818 if (FAILED(rc)) return rc;
8819
8820 /* Shared folders */
8821 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8822 it != data.usbSettings.llUSBControllers.end();
8823 ++it)
8824 {
8825 const settings::USBController &settingsCtrl = *it;
8826 ComObjPtr<USBController> newCtrl;
8827
8828 newCtrl.createObject();
8829 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8830 mUSBControllers->push_back(newCtrl);
8831 }
8832
8833 /* USB device filters */
8834 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8835 if (FAILED(rc)) return rc;
8836
8837 // network adapters
8838 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8839 size_t oldCount = mNetworkAdapters.size();
8840 if (newCount > oldCount)
8841 {
8842 mNetworkAdapters.resize(newCount);
8843 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8844 {
8845 unconst(mNetworkAdapters[slot]).createObject();
8846 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8847 }
8848 }
8849 else if (newCount < oldCount)
8850 mNetworkAdapters.resize(newCount);
8851 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8852 it != data.llNetworkAdapters.end();
8853 ++it)
8854 {
8855 const settings::NetworkAdapter &nic = *it;
8856
8857 /* slot unicity is guaranteed by XML Schema */
8858 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8859 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8860 if (FAILED(rc)) return rc;
8861 }
8862
8863 // serial ports
8864 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8865 it != data.llSerialPorts.end();
8866 ++it)
8867 {
8868 const settings::SerialPort &s = *it;
8869
8870 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8871 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8872 if (FAILED(rc)) return rc;
8873 }
8874
8875 // parallel ports (optional)
8876 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8877 it != data.llParallelPorts.end();
8878 ++it)
8879 {
8880 const settings::ParallelPort &p = *it;
8881
8882 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8883 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8884 if (FAILED(rc)) return rc;
8885 }
8886
8887 /* AudioAdapter */
8888 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8889 if (FAILED(rc)) return rc;
8890
8891 /* Shared folders */
8892 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8893 it != data.llSharedFolders.end();
8894 ++it)
8895 {
8896 const settings::SharedFolder &sf = *it;
8897
8898 ComObjPtr<SharedFolder> sharedFolder;
8899 /* Check for double entries. Not allowed! */
8900 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8901 if (SUCCEEDED(rc))
8902 return setError(VBOX_E_OBJECT_IN_USE,
8903 tr("Shared folder named '%s' already exists"),
8904 sf.strName.c_str());
8905
8906 /* Create the new shared folder. Don't break on error. This will be
8907 * reported when the machine starts. */
8908 sharedFolder.createObject();
8909 rc = sharedFolder->init(i_getMachine(),
8910 sf.strName,
8911 sf.strHostPath,
8912 RT_BOOL(sf.fWritable),
8913 RT_BOOL(sf.fAutoMount),
8914 false /* fFailOnError */);
8915 if (FAILED(rc)) return rc;
8916 mHWData->mSharedFolders.push_back(sharedFolder);
8917 }
8918
8919 // Clipboard
8920 mHWData->mClipboardMode = data.clipboardMode;
8921
8922 // drag'n'drop
8923 mHWData->mDnDMode = data.dndMode;
8924
8925 // guest settings
8926 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8927
8928 // IO settings
8929 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8930 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8931
8932 // Host PCI devices
8933 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8934 it != data.pciAttachments.end();
8935 ++it)
8936 {
8937 const settings::HostPCIDeviceAttachment &hpda = *it;
8938 ComObjPtr<PCIDeviceAttachment> pda;
8939
8940 pda.createObject();
8941 pda->i_loadSettings(this, hpda);
8942 mHWData->mPCIDeviceAssignments.push_back(pda);
8943 }
8944
8945 /*
8946 * (The following isn't really real hardware, but it lives in HWData
8947 * for reasons of convenience.)
8948 */
8949
8950#ifdef VBOX_WITH_GUEST_PROPS
8951 /* Guest properties (optional) */
8952 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8953 it != data.llGuestProperties.end();
8954 ++it)
8955 {
8956 const settings::GuestProperty &prop = *it;
8957 uint32_t fFlags = guestProp::NILFLAG;
8958 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8959 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8960 mHWData->mGuestProperties[prop.strName] = property;
8961 }
8962
8963 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8964#endif /* VBOX_WITH_GUEST_PROPS defined */
8965
8966 rc = i_loadDebugging(pDbg);
8967 if (FAILED(rc))
8968 return rc;
8969
8970 mHWData->mAutostart = *pAutostart;
8971
8972 /* default frontend */
8973 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8974 }
8975 catch(std::bad_alloc &)
8976 {
8977 return E_OUTOFMEMORY;
8978 }
8979
8980 AssertComRC(rc);
8981 return rc;
8982}
8983
8984/**
8985 * Called from Machine::loadHardware() to load the debugging settings of the
8986 * machine.
8987 *
8988 * @param pDbg Pointer to the settings.
8989 */
8990HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8991{
8992 mHWData->mDebugging = *pDbg;
8993 /* no more processing currently required, this will probably change. */
8994 return S_OK;
8995}
8996
8997/**
8998 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8999 *
9000 * @param data
9001 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9002 * @param puuidSnapshot
9003 * @return
9004 */
9005HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9006 const Guid *puuidRegistry,
9007 const Guid *puuidSnapshot)
9008{
9009 AssertReturn(!i_isSessionMachine(), E_FAIL);
9010
9011 HRESULT rc = S_OK;
9012
9013 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9014 it != data.llStorageControllers.end();
9015 ++it)
9016 {
9017 const settings::StorageController &ctlData = *it;
9018
9019 ComObjPtr<StorageController> pCtl;
9020 /* Try to find one with the name first. */
9021 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9022 if (SUCCEEDED(rc))
9023 return setError(VBOX_E_OBJECT_IN_USE,
9024 tr("Storage controller named '%s' already exists"),
9025 ctlData.strName.c_str());
9026
9027 pCtl.createObject();
9028 rc = pCtl->init(this,
9029 ctlData.strName,
9030 ctlData.storageBus,
9031 ctlData.ulInstance,
9032 ctlData.fBootable);
9033 if (FAILED(rc)) return rc;
9034
9035 mStorageControllers->push_back(pCtl);
9036
9037 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9038 if (FAILED(rc)) return rc;
9039
9040 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9041 if (FAILED(rc)) return rc;
9042
9043 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9044 if (FAILED(rc)) return rc;
9045
9046 /* Set IDE emulation settings (only for AHCI controller). */
9047 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9048 {
9049 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9050 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9051 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9052 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9053 )
9054 return rc;
9055 }
9056
9057 /* Load the attached devices now. */
9058 rc = i_loadStorageDevices(pCtl,
9059 ctlData,
9060 puuidRegistry,
9061 puuidSnapshot);
9062 if (FAILED(rc)) return rc;
9063 }
9064
9065 return S_OK;
9066}
9067
9068/**
9069 * Called from i_loadStorageControllers for a controller's devices.
9070 *
9071 * @param aStorageController
9072 * @param data
9073 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9074 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9075 * @return
9076 */
9077HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9078 const settings::StorageController &data,
9079 const Guid *puuidRegistry,
9080 const Guid *puuidSnapshot)
9081{
9082 HRESULT rc = S_OK;
9083
9084 /* paranoia: detect duplicate attachments */
9085 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9086 it != data.llAttachedDevices.end();
9087 ++it)
9088 {
9089 const settings::AttachedDevice &ad = *it;
9090
9091 for (settings::AttachedDevicesList::const_iterator it2 = it;
9092 it2 != data.llAttachedDevices.end();
9093 ++it2)
9094 {
9095 if (it == it2)
9096 continue;
9097
9098 const settings::AttachedDevice &ad2 = *it2;
9099
9100 if ( ad.lPort == ad2.lPort
9101 && ad.lDevice == ad2.lDevice)
9102 {
9103 return setError(E_FAIL,
9104 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9105 aStorageController->i_getName().c_str(),
9106 ad.lPort,
9107 ad.lDevice,
9108 mUserData->s.strName.c_str());
9109 }
9110 }
9111 }
9112
9113 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9114 it != data.llAttachedDevices.end();
9115 ++it)
9116 {
9117 const settings::AttachedDevice &dev = *it;
9118 ComObjPtr<Medium> medium;
9119
9120 switch (dev.deviceType)
9121 {
9122 case DeviceType_Floppy:
9123 case DeviceType_DVD:
9124 if (dev.strHostDriveSrc.isNotEmpty())
9125 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9126 false /* fRefresh */, medium);
9127 else
9128 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9129 dev.uuid,
9130 false /* fRefresh */,
9131 false /* aSetError */,
9132 medium);
9133 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9134 // This is not an error. The host drive or UUID might have vanished, so just go
9135 // ahead without this removeable medium attachment
9136 rc = S_OK;
9137 break;
9138
9139 case DeviceType_HardDisk:
9140 {
9141 /* find a hard disk by UUID */
9142 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9143 if (FAILED(rc))
9144 {
9145 if (i_isSnapshotMachine())
9146 {
9147 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9148 // so the user knows that the bad disk is in a snapshot somewhere
9149 com::ErrorInfo info;
9150 return setError(E_FAIL,
9151 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9152 puuidSnapshot->raw(),
9153 info.getText().raw());
9154 }
9155 else
9156 return rc;
9157 }
9158
9159 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9160
9161 if (medium->i_getType() == MediumType_Immutable)
9162 {
9163 if (i_isSnapshotMachine())
9164 return setError(E_FAIL,
9165 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9166 "of the virtual machine '%s' ('%s')"),
9167 medium->i_getLocationFull().c_str(),
9168 dev.uuid.raw(),
9169 puuidSnapshot->raw(),
9170 mUserData->s.strName.c_str(),
9171 mData->m_strConfigFileFull.c_str());
9172
9173 return setError(E_FAIL,
9174 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9175 medium->i_getLocationFull().c_str(),
9176 dev.uuid.raw(),
9177 mUserData->s.strName.c_str(),
9178 mData->m_strConfigFileFull.c_str());
9179 }
9180
9181 if (medium->i_getType() == MediumType_MultiAttach)
9182 {
9183 if (i_isSnapshotMachine())
9184 return setError(E_FAIL,
9185 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9186 "of the virtual machine '%s' ('%s')"),
9187 medium->i_getLocationFull().c_str(),
9188 dev.uuid.raw(),
9189 puuidSnapshot->raw(),
9190 mUserData->s.strName.c_str(),
9191 mData->m_strConfigFileFull.c_str());
9192
9193 return setError(E_FAIL,
9194 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9195 medium->i_getLocationFull().c_str(),
9196 dev.uuid.raw(),
9197 mUserData->s.strName.c_str(),
9198 mData->m_strConfigFileFull.c_str());
9199 }
9200
9201 if ( !i_isSnapshotMachine()
9202 && medium->i_getChildren().size() != 0
9203 )
9204 return setError(E_FAIL,
9205 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9206 "because it has %d differencing child hard disks"),
9207 medium->i_getLocationFull().c_str(),
9208 dev.uuid.raw(),
9209 mUserData->s.strName.c_str(),
9210 mData->m_strConfigFileFull.c_str(),
9211 medium->i_getChildren().size());
9212
9213 if (i_findAttachment(mMediaData->mAttachments,
9214 medium))
9215 return setError(E_FAIL,
9216 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9217 medium->i_getLocationFull().c_str(),
9218 dev.uuid.raw(),
9219 mUserData->s.strName.c_str(),
9220 mData->m_strConfigFileFull.c_str());
9221
9222 break;
9223 }
9224
9225 default:
9226 return setError(E_FAIL,
9227 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9228 medium->i_getLocationFull().c_str(),
9229 mUserData->s.strName.c_str(),
9230 mData->m_strConfigFileFull.c_str());
9231 }
9232
9233 if (FAILED(rc))
9234 break;
9235
9236 /* Bandwidth groups are loaded at this point. */
9237 ComObjPtr<BandwidthGroup> pBwGroup;
9238
9239 if (!dev.strBwGroup.isEmpty())
9240 {
9241 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9242 if (FAILED(rc))
9243 return setError(E_FAIL,
9244 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9245 medium->i_getLocationFull().c_str(),
9246 dev.strBwGroup.c_str(),
9247 mUserData->s.strName.c_str(),
9248 mData->m_strConfigFileFull.c_str());
9249 pBwGroup->i_reference();
9250 }
9251
9252 const Bstr controllerName = aStorageController->i_getName();
9253 ComObjPtr<MediumAttachment> pAttachment;
9254 pAttachment.createObject();
9255 rc = pAttachment->init(this,
9256 medium,
9257 controllerName,
9258 dev.lPort,
9259 dev.lDevice,
9260 dev.deviceType,
9261 false,
9262 dev.fPassThrough,
9263 dev.fTempEject,
9264 dev.fNonRotational,
9265 dev.fDiscard,
9266 dev.fHotPluggable,
9267 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9268 if (FAILED(rc)) break;
9269
9270 /* associate the medium with this machine and snapshot */
9271 if (!medium.isNull())
9272 {
9273 AutoCaller medCaller(medium);
9274 if (FAILED(medCaller.rc())) return medCaller.rc();
9275 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9276
9277 if (i_isSnapshotMachine())
9278 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9279 else
9280 rc = medium->i_addBackReference(mData->mUuid);
9281 /* If the medium->addBackReference fails it sets an appropriate
9282 * error message, so no need to do any guesswork here. */
9283
9284 if (puuidRegistry)
9285 // caller wants registry ID to be set on all attached media (OVF import case)
9286 medium->i_addRegistry(*puuidRegistry);
9287 }
9288
9289 if (FAILED(rc))
9290 break;
9291
9292 /* back up mMediaData to let registeredInit() properly rollback on failure
9293 * (= limited accessibility) */
9294 i_setModified(IsModified_Storage);
9295 mMediaData.backup();
9296 mMediaData->mAttachments.push_back(pAttachment);
9297 }
9298
9299 return rc;
9300}
9301
9302/**
9303 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9304 *
9305 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9306 * @param aSnapshot where to return the found snapshot
9307 * @param aSetError true to set extended error info on failure
9308 */
9309HRESULT Machine::i_findSnapshotById(const Guid &aId,
9310 ComObjPtr<Snapshot> &aSnapshot,
9311 bool aSetError /* = false */)
9312{
9313 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9314
9315 if (!mData->mFirstSnapshot)
9316 {
9317 if (aSetError)
9318 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9319 return E_FAIL;
9320 }
9321
9322 if (aId.isZero())
9323 aSnapshot = mData->mFirstSnapshot;
9324 else
9325 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9326
9327 if (!aSnapshot)
9328 {
9329 if (aSetError)
9330 return setError(E_FAIL,
9331 tr("Could not find a snapshot with UUID {%s}"),
9332 aId.toString().c_str());
9333 return E_FAIL;
9334 }
9335
9336 return S_OK;
9337}
9338
9339/**
9340 * Returns the snapshot with the given name or fails of no such snapshot.
9341 *
9342 * @param aName snapshot name to find
9343 * @param aSnapshot where to return the found snapshot
9344 * @param aSetError true to set extended error info on failure
9345 */
9346HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9347 ComObjPtr<Snapshot> &aSnapshot,
9348 bool aSetError /* = false */)
9349{
9350 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9351
9352 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9353
9354 if (!mData->mFirstSnapshot)
9355 {
9356 if (aSetError)
9357 return setError(VBOX_E_OBJECT_NOT_FOUND,
9358 tr("This machine does not have any snapshots"));
9359 return VBOX_E_OBJECT_NOT_FOUND;
9360 }
9361
9362 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9363
9364 if (!aSnapshot)
9365 {
9366 if (aSetError)
9367 return setError(VBOX_E_OBJECT_NOT_FOUND,
9368 tr("Could not find a snapshot named '%s'"), strName.c_str());
9369 return VBOX_E_OBJECT_NOT_FOUND;
9370 }
9371
9372 return S_OK;
9373}
9374
9375/**
9376 * Returns a storage controller object with the given name.
9377 *
9378 * @param aName storage controller name to find
9379 * @param aStorageController where to return the found storage controller
9380 * @param aSetError true to set extended error info on failure
9381 */
9382HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9383 ComObjPtr<StorageController> &aStorageController,
9384 bool aSetError /* = false */)
9385{
9386 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9387
9388 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9389 it != mStorageControllers->end();
9390 ++it)
9391 {
9392 if ((*it)->i_getName() == aName)
9393 {
9394 aStorageController = (*it);
9395 return S_OK;
9396 }
9397 }
9398
9399 if (aSetError)
9400 return setError(VBOX_E_OBJECT_NOT_FOUND,
9401 tr("Could not find a storage controller named '%s'"),
9402 aName.c_str());
9403 return VBOX_E_OBJECT_NOT_FOUND;
9404}
9405
9406/**
9407 * Returns a USB controller object with the given name.
9408 *
9409 * @param aName USB controller name to find
9410 * @param aUSBController where to return the found USB controller
9411 * @param aSetError true to set extended error info on failure
9412 */
9413HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9414 ComObjPtr<USBController> &aUSBController,
9415 bool aSetError /* = false */)
9416{
9417 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9418
9419 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9420 it != mUSBControllers->end();
9421 ++it)
9422 {
9423 if ((*it)->i_getName() == aName)
9424 {
9425 aUSBController = (*it);
9426 return S_OK;
9427 }
9428 }
9429
9430 if (aSetError)
9431 return setError(VBOX_E_OBJECT_NOT_FOUND,
9432 tr("Could not find a storage controller named '%s'"),
9433 aName.c_str());
9434 return VBOX_E_OBJECT_NOT_FOUND;
9435}
9436
9437/**
9438 * Returns the number of USB controller instance of the given type.
9439 *
9440 * @param enmType USB controller type.
9441 */
9442ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9443{
9444 ULONG cCtrls = 0;
9445
9446 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9447 it != mUSBControllers->end();
9448 ++it)
9449 {
9450 if ((*it)->i_getControllerType() == enmType)
9451 cCtrls++;
9452 }
9453
9454 return cCtrls;
9455}
9456
9457HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9458 MediaData::AttachmentList &atts)
9459{
9460 AutoCaller autoCaller(this);
9461 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9462
9463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9464
9465 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9466 it != mMediaData->mAttachments.end();
9467 ++it)
9468 {
9469 const ComObjPtr<MediumAttachment> &pAtt = *it;
9470 // should never happen, but deal with NULL pointers in the list.
9471 AssertStmt(!pAtt.isNull(), continue);
9472
9473 // getControllerName() needs caller+read lock
9474 AutoCaller autoAttCaller(pAtt);
9475 if (FAILED(autoAttCaller.rc()))
9476 {
9477 atts.clear();
9478 return autoAttCaller.rc();
9479 }
9480 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9481
9482 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9483 atts.push_back(pAtt);
9484 }
9485
9486 return S_OK;
9487}
9488
9489
9490/**
9491 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9492 * file if the machine name was changed and about creating a new settings file
9493 * if this is a new machine.
9494 *
9495 * @note Must be never called directly but only from #saveSettings().
9496 */
9497HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9498{
9499 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9500
9501 HRESULT rc = S_OK;
9502
9503 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9504
9505 /// @todo need to handle primary group change, too
9506
9507 /* attempt to rename the settings file if machine name is changed */
9508 if ( mUserData->s.fNameSync
9509 && mUserData.isBackedUp()
9510 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9511 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9512 )
9513 {
9514 bool dirRenamed = false;
9515 bool fileRenamed = false;
9516
9517 Utf8Str configFile, newConfigFile;
9518 Utf8Str configFilePrev, newConfigFilePrev;
9519 Utf8Str configDir, newConfigDir;
9520
9521 do
9522 {
9523 int vrc = VINF_SUCCESS;
9524
9525 Utf8Str name = mUserData.backedUpData()->s.strName;
9526 Utf8Str newName = mUserData->s.strName;
9527 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9528 if (group == "/")
9529 group.setNull();
9530 Utf8Str newGroup = mUserData->s.llGroups.front();
9531 if (newGroup == "/")
9532 newGroup.setNull();
9533
9534 configFile = mData->m_strConfigFileFull;
9535
9536 /* first, rename the directory if it matches the group and machine name */
9537 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9538 group.c_str(), RTPATH_DELIMITER, name.c_str());
9539 /** @todo hack, make somehow use of ComposeMachineFilename */
9540 if (mUserData->s.fDirectoryIncludesUUID)
9541 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9542 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9543 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9544 /** @todo hack, make somehow use of ComposeMachineFilename */
9545 if (mUserData->s.fDirectoryIncludesUUID)
9546 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9547 configDir = configFile;
9548 configDir.stripFilename();
9549 newConfigDir = configDir;
9550 if ( configDir.length() >= groupPlusName.length()
9551 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9552 groupPlusName.c_str()))
9553 {
9554 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9555 Utf8Str newConfigBaseDir(newConfigDir);
9556 newConfigDir.append(newGroupPlusName);
9557 /* consistency: use \ if appropriate on the platform */
9558 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9559 /* new dir and old dir cannot be equal here because of 'if'
9560 * above and because name != newName */
9561 Assert(configDir != newConfigDir);
9562 if (!fSettingsFileIsNew)
9563 {
9564 /* perform real rename only if the machine is not new */
9565 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9566 if ( vrc == VERR_FILE_NOT_FOUND
9567 || vrc == VERR_PATH_NOT_FOUND)
9568 {
9569 /* create the parent directory, then retry renaming */
9570 Utf8Str parent(newConfigDir);
9571 parent.stripFilename();
9572 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9573 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9574 }
9575 if (RT_FAILURE(vrc))
9576 {
9577 rc = setError(E_FAIL,
9578 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9579 configDir.c_str(),
9580 newConfigDir.c_str(),
9581 vrc);
9582 break;
9583 }
9584 /* delete subdirectories which are no longer needed */
9585 Utf8Str dir(configDir);
9586 dir.stripFilename();
9587 while (dir != newConfigBaseDir && dir != ".")
9588 {
9589 vrc = RTDirRemove(dir.c_str());
9590 if (RT_FAILURE(vrc))
9591 break;
9592 dir.stripFilename();
9593 }
9594 dirRenamed = true;
9595 }
9596 }
9597
9598 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9599 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9600
9601 /* then try to rename the settings file itself */
9602 if (newConfigFile != configFile)
9603 {
9604 /* get the path to old settings file in renamed directory */
9605 configFile = Utf8StrFmt("%s%c%s",
9606 newConfigDir.c_str(),
9607 RTPATH_DELIMITER,
9608 RTPathFilename(configFile.c_str()));
9609 if (!fSettingsFileIsNew)
9610 {
9611 /* perform real rename only if the machine is not new */
9612 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9613 if (RT_FAILURE(vrc))
9614 {
9615 rc = setError(E_FAIL,
9616 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9617 configFile.c_str(),
9618 newConfigFile.c_str(),
9619 vrc);
9620 break;
9621 }
9622 fileRenamed = true;
9623 configFilePrev = configFile;
9624 configFilePrev += "-prev";
9625 newConfigFilePrev = newConfigFile;
9626 newConfigFilePrev += "-prev";
9627 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9628 }
9629 }
9630
9631 // update m_strConfigFileFull amd mConfigFile
9632 mData->m_strConfigFileFull = newConfigFile;
9633 // compute the relative path too
9634 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9635
9636 // store the old and new so that VirtualBox::i_saveSettings() can update
9637 // the media registry
9638 if ( mData->mRegistered
9639 && (configDir != newConfigDir || configFile != newConfigFile))
9640 {
9641 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9642
9643 if (pfNeedsGlobalSaveSettings)
9644 *pfNeedsGlobalSaveSettings = true;
9645 }
9646
9647 // in the saved state file path, replace the old directory with the new directory
9648 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9649 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9650
9651 // and do the same thing for the saved state file paths of all the online snapshots
9652 if (mData->mFirstSnapshot)
9653 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9654 newConfigDir.c_str());
9655 }
9656 while (0);
9657
9658 if (FAILED(rc))
9659 {
9660 /* silently try to rename everything back */
9661 if (fileRenamed)
9662 {
9663 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9664 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9665 }
9666 if (dirRenamed)
9667 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9668 }
9669
9670 if (FAILED(rc)) return rc;
9671 }
9672
9673 if (fSettingsFileIsNew)
9674 {
9675 /* create a virgin config file */
9676 int vrc = VINF_SUCCESS;
9677
9678 /* ensure the settings directory exists */
9679 Utf8Str path(mData->m_strConfigFileFull);
9680 path.stripFilename();
9681 if (!RTDirExists(path.c_str()))
9682 {
9683 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9684 if (RT_FAILURE(vrc))
9685 {
9686 return setError(E_FAIL,
9687 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9688 path.c_str(),
9689 vrc);
9690 }
9691 }
9692
9693 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9694 path = Utf8Str(mData->m_strConfigFileFull);
9695 RTFILE f = NIL_RTFILE;
9696 vrc = RTFileOpen(&f, path.c_str(),
9697 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9698 if (RT_FAILURE(vrc))
9699 return setError(E_FAIL,
9700 tr("Could not create the settings file '%s' (%Rrc)"),
9701 path.c_str(),
9702 vrc);
9703 RTFileClose(f);
9704 }
9705
9706 return rc;
9707}
9708
9709/**
9710 * Saves and commits machine data, user data and hardware data.
9711 *
9712 * Note that on failure, the data remains uncommitted.
9713 *
9714 * @a aFlags may combine the following flags:
9715 *
9716 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9717 * Used when saving settings after an operation that makes them 100%
9718 * correspond to the settings from the current snapshot.
9719 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9720 * #isReallyModified() returns false. This is necessary for cases when we
9721 * change machine data directly, not through the backup()/commit() mechanism.
9722 * - SaveS_Force: settings will be saved without doing a deep compare of the
9723 * settings structures. This is used when this is called because snapshots
9724 * have changed to avoid the overhead of the deep compare.
9725 *
9726 * @note Must be called from under this object's write lock. Locks children for
9727 * writing.
9728 *
9729 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9730 * initialized to false and that will be set to true by this function if
9731 * the caller must invoke VirtualBox::i_saveSettings() because the global
9732 * settings have changed. This will happen if a machine rename has been
9733 * saved and the global machine and media registries will therefore need
9734 * updating.
9735 */
9736HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9737 int aFlags /*= 0*/)
9738{
9739 LogFlowThisFuncEnter();
9740
9741 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9742
9743 /* make sure child objects are unable to modify the settings while we are
9744 * saving them */
9745 i_ensureNoStateDependencies();
9746
9747 AssertReturn(!i_isSnapshotMachine(),
9748 E_FAIL);
9749
9750 HRESULT rc = S_OK;
9751 bool fNeedsWrite = false;
9752
9753 /* First, prepare to save settings. It will care about renaming the
9754 * settings directory and file if the machine name was changed and about
9755 * creating a new settings file if this is a new machine. */
9756 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9757 if (FAILED(rc)) return rc;
9758
9759 // keep a pointer to the current settings structures
9760 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9761 settings::MachineConfigFile *pNewConfig = NULL;
9762
9763 try
9764 {
9765 // make a fresh one to have everyone write stuff into
9766 pNewConfig = new settings::MachineConfigFile(NULL);
9767 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9768
9769 // now go and copy all the settings data from COM to the settings structures
9770 // (this calles i_saveSettings() on all the COM objects in the machine)
9771 i_copyMachineDataToSettings(*pNewConfig);
9772
9773 if (aFlags & SaveS_ResetCurStateModified)
9774 {
9775 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9776 mData->mCurrentStateModified = FALSE;
9777 fNeedsWrite = true; // always, no need to compare
9778 }
9779 else if (aFlags & SaveS_Force)
9780 {
9781 fNeedsWrite = true; // always, no need to compare
9782 }
9783 else
9784 {
9785 if (!mData->mCurrentStateModified)
9786 {
9787 // do a deep compare of the settings that we just saved with the settings
9788 // previously stored in the config file; this invokes MachineConfigFile::operator==
9789 // which does a deep compare of all the settings, which is expensive but less expensive
9790 // than writing out XML in vain
9791 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9792
9793 // could still be modified if any settings changed
9794 mData->mCurrentStateModified = fAnySettingsChanged;
9795
9796 fNeedsWrite = fAnySettingsChanged;
9797 }
9798 else
9799 fNeedsWrite = true;
9800 }
9801
9802 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9803
9804 if (fNeedsWrite)
9805 // now spit it all out!
9806 pNewConfig->write(mData->m_strConfigFileFull);
9807
9808 mData->pMachineConfigFile = pNewConfig;
9809 delete pOldConfig;
9810 i_commit();
9811
9812 // after saving settings, we are no longer different from the XML on disk
9813 mData->flModifications = 0;
9814 }
9815 catch (HRESULT err)
9816 {
9817 // we assume that error info is set by the thrower
9818 rc = err;
9819
9820 // restore old config
9821 delete pNewConfig;
9822 mData->pMachineConfigFile = pOldConfig;
9823 }
9824 catch (...)
9825 {
9826 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9827 }
9828
9829 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9830 {
9831 /* Fire the data change event, even on failure (since we've already
9832 * committed all data). This is done only for SessionMachines because
9833 * mutable Machine instances are always not registered (i.e. private
9834 * to the client process that creates them) and thus don't need to
9835 * inform callbacks. */
9836 if (i_isSessionMachine())
9837 mParent->i_onMachineDataChange(mData->mUuid);
9838 }
9839
9840 LogFlowThisFunc(("rc=%08X\n", rc));
9841 LogFlowThisFuncLeave();
9842 return rc;
9843}
9844
9845/**
9846 * Implementation for saving the machine settings into the given
9847 * settings::MachineConfigFile instance. This copies machine extradata
9848 * from the previous machine config file in the instance data, if any.
9849 *
9850 * This gets called from two locations:
9851 *
9852 * -- Machine::i_saveSettings(), during the regular XML writing;
9853 *
9854 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9855 * exported to OVF and we write the VirtualBox proprietary XML
9856 * into a <vbox:Machine> tag.
9857 *
9858 * This routine fills all the fields in there, including snapshots, *except*
9859 * for the following:
9860 *
9861 * -- fCurrentStateModified. There is some special logic associated with that.
9862 *
9863 * The caller can then call MachineConfigFile::write() or do something else
9864 * with it.
9865 *
9866 * Caller must hold the machine lock!
9867 *
9868 * This throws XML errors and HRESULT, so the caller must have a catch block!
9869 */
9870void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9871{
9872 // deep copy extradata
9873 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9874
9875 config.uuid = mData->mUuid;
9876
9877 // copy name, description, OS type, teleport, UTC etc.
9878 config.machineUserData = mUserData->s;
9879
9880 // Encode the Icon Override data from Machine and store on config userdata.
9881 std::vector<BYTE> iconByte;
9882 getIcon(iconByte);
9883 ssize_t cbData = iconByte.size();
9884 if (cbData > 0)
9885 {
9886 ssize_t cchOut = RTBase64EncodedLength(cbData);
9887 Utf8Str strIconData;
9888 strIconData.reserve(cchOut+1);
9889 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9890 strIconData.mutableRaw(), strIconData.capacity(),
9891 NULL);
9892 if (RT_FAILURE(vrc))
9893 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9894 strIconData.jolt();
9895 config.machineUserData.ovIcon = strIconData;
9896 }
9897 else
9898 config.machineUserData.ovIcon.setNull();
9899
9900 if ( mData->mMachineState == MachineState_Saved
9901 || mData->mMachineState == MachineState_Restoring
9902 // when deleting a snapshot we may or may not have a saved state in the current state,
9903 // so let's not assert here please
9904 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9905 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9906 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9907 && (!mSSData->strStateFilePath.isEmpty())
9908 )
9909 )
9910 {
9911 Assert(!mSSData->strStateFilePath.isEmpty());
9912 /* try to make the file name relative to the settings file dir */
9913 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9914 }
9915 else
9916 {
9917 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9918 config.strStateFile.setNull();
9919 }
9920
9921 if (mData->mCurrentSnapshot)
9922 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9923 else
9924 config.uuidCurrentSnapshot.clear();
9925
9926 config.timeLastStateChange = mData->mLastStateChange;
9927 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9928 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9929
9930 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9931 if (FAILED(rc)) throw rc;
9932
9933 rc = i_saveStorageControllers(config.storageMachine);
9934 if (FAILED(rc)) throw rc;
9935
9936 // save machine's media registry if this is VirtualBox 4.0 or later
9937 if (config.canHaveOwnMediaRegistry())
9938 {
9939 // determine machine folder
9940 Utf8Str strMachineFolder = i_getSettingsFileFull();
9941 strMachineFolder.stripFilename();
9942 mParent->i_saveMediaRegistry(config.mediaRegistry,
9943 i_getId(), // only media with registry ID == machine UUID
9944 strMachineFolder);
9945 // this throws HRESULT
9946 }
9947
9948 // save snapshots
9949 rc = i_saveAllSnapshots(config);
9950 if (FAILED(rc)) throw rc;
9951}
9952
9953/**
9954 * Saves all snapshots of the machine into the given machine config file. Called
9955 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9956 * @param config
9957 * @return
9958 */
9959HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9960{
9961 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9962
9963 HRESULT rc = S_OK;
9964
9965 try
9966 {
9967 config.llFirstSnapshot.clear();
9968
9969 if (mData->mFirstSnapshot)
9970 {
9971 // the settings use a list for "the first snapshot"
9972 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
9973
9974 // get reference to the snapshot on the list and work on that
9975 // element straight in the list to avoid excessive copying later
9976 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9977 if (FAILED(rc)) throw rc;
9978 }
9979
9980// if (mType == IsSessionMachine)
9981// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9982
9983 }
9984 catch (HRESULT err)
9985 {
9986 /* we assume that error info is set by the thrower */
9987 rc = err;
9988 }
9989 catch (...)
9990 {
9991 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9992 }
9993
9994 return rc;
9995}
9996
9997/**
9998 * Saves the VM hardware configuration. It is assumed that the
9999 * given node is empty.
10000 *
10001 * @param data Reference to the settings object for the hardware config.
10002 * @param pDbg Pointer to the settings object for the debugging config
10003 * which happens to live in mHWData.
10004 * @param pAutostart Pointer to the settings object for the autostart config
10005 * which happens to live in mHWData.
10006 */
10007HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10008 settings::Autostart *pAutostart)
10009{
10010 HRESULT rc = S_OK;
10011
10012 try
10013 {
10014 /* The hardware version attribute (optional).
10015 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10016 if ( mHWData->mHWVersion == "1"
10017 && mSSData->strStateFilePath.isEmpty()
10018 )
10019 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10020 other point needs to be found where this can be done. */
10021
10022 data.strVersion = mHWData->mHWVersion;
10023 data.uuid = mHWData->mHardwareUUID;
10024
10025 // CPU
10026 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10027 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10028 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10029 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10030 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10031 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10032 data.fPAE = !!mHWData->mPAEEnabled;
10033 data.enmLongMode = mHWData->mLongMode;
10034 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10035 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10036
10037 /* Standard and Extended CPUID leafs. */
10038 data.llCpuIdLeafs.clear();
10039 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10040 {
10041 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10042 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10043 }
10044 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10045 {
10046 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10047 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10048 }
10049
10050 data.cCPUs = mHWData->mCPUCount;
10051 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10052 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10053
10054 data.llCpus.clear();
10055 if (data.fCpuHotPlug)
10056 {
10057 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10058 {
10059 if (mHWData->mCPUAttached[idx])
10060 {
10061 settings::Cpu cpu;
10062 cpu.ulId = idx;
10063 data.llCpus.push_back(cpu);
10064 }
10065 }
10066 }
10067
10068 // memory
10069 data.ulMemorySizeMB = mHWData->mMemorySize;
10070 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10071
10072 // firmware
10073 data.firmwareType = mHWData->mFirmwareType;
10074
10075 // HID
10076 data.pointingHIDType = mHWData->mPointingHIDType;
10077 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10078
10079 // chipset
10080 data.chipsetType = mHWData->mChipsetType;
10081
10082 // paravirt
10083 data.paravirtProvider = mHWData->mParavirtProvider;
10084
10085
10086 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10087
10088 // HPET
10089 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10090
10091 // boot order
10092 data.mapBootOrder.clear();
10093 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10094 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10095
10096 // display
10097 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10098 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10099 data.cMonitors = mHWData->mMonitorCount;
10100 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10101 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10102 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10103 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10104 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10105 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10106 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10107 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10108 {
10109 if (mHWData->maVideoCaptureScreens[i])
10110 ASMBitSet(&data.u64VideoCaptureScreens, i);
10111 else
10112 ASMBitClear(&data.u64VideoCaptureScreens, i);
10113 }
10114 /* store relative video capture file if possible */
10115 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10116
10117 /* VRDEServer settings (optional) */
10118 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10119 if (FAILED(rc)) throw rc;
10120
10121 /* BIOS (required) */
10122 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10123 if (FAILED(rc)) throw rc;
10124
10125 /* USB Controller (required) */
10126 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10127 {
10128 ComObjPtr<USBController> ctrl = *it;
10129 settings::USBController settingsCtrl;
10130
10131 settingsCtrl.strName = ctrl->i_getName();
10132 settingsCtrl.enmType = ctrl->i_getControllerType();
10133
10134 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10135 }
10136
10137 /* USB device filters (required) */
10138 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10139 if (FAILED(rc)) throw rc;
10140
10141 /* Network adapters (required) */
10142 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10143 data.llNetworkAdapters.clear();
10144 /* Write out only the nominal number of network adapters for this
10145 * chipset type. Since Machine::commit() hasn't been called there
10146 * may be extra NIC settings in the vector. */
10147 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10148 {
10149 settings::NetworkAdapter nic;
10150 nic.ulSlot = (uint32_t)slot;
10151 /* paranoia check... must not be NULL, but must not crash either. */
10152 if (mNetworkAdapters[slot])
10153 {
10154 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10155 if (FAILED(rc)) throw rc;
10156
10157 data.llNetworkAdapters.push_back(nic);
10158 }
10159 }
10160
10161 /* Serial ports */
10162 data.llSerialPorts.clear();
10163 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10164 {
10165 settings::SerialPort s;
10166 s.ulSlot = slot;
10167 rc = mSerialPorts[slot]->i_saveSettings(s);
10168 if (FAILED(rc)) return rc;
10169
10170 data.llSerialPorts.push_back(s);
10171 }
10172
10173 /* Parallel ports */
10174 data.llParallelPorts.clear();
10175 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10176 {
10177 settings::ParallelPort p;
10178 p.ulSlot = slot;
10179 rc = mParallelPorts[slot]->i_saveSettings(p);
10180 if (FAILED(rc)) return rc;
10181
10182 data.llParallelPorts.push_back(p);
10183 }
10184
10185 /* Audio adapter */
10186 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10187 if (FAILED(rc)) return rc;
10188
10189 /* Shared folders */
10190 data.llSharedFolders.clear();
10191 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10192 it != mHWData->mSharedFolders.end();
10193 ++it)
10194 {
10195 SharedFolder *pSF = *it;
10196 AutoCaller sfCaller(pSF);
10197 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10198 settings::SharedFolder sf;
10199 sf.strName = pSF->i_getName();
10200 sf.strHostPath = pSF->i_getHostPath();
10201 sf.fWritable = !!pSF->i_isWritable();
10202 sf.fAutoMount = !!pSF->i_isAutoMounted();
10203
10204 data.llSharedFolders.push_back(sf);
10205 }
10206
10207 // clipboard
10208 data.clipboardMode = mHWData->mClipboardMode;
10209
10210 // drag'n'drop
10211 data.dndMode = mHWData->mDnDMode;
10212
10213 /* Guest */
10214 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10215
10216 // IO settings
10217 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10218 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10219
10220 /* BandwidthControl (required) */
10221 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10222 if (FAILED(rc)) throw rc;
10223
10224 /* Host PCI devices */
10225 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10226 it != mHWData->mPCIDeviceAssignments.end();
10227 ++it)
10228 {
10229 ComObjPtr<PCIDeviceAttachment> pda = *it;
10230 settings::HostPCIDeviceAttachment hpda;
10231
10232 rc = pda->i_saveSettings(hpda);
10233 if (FAILED(rc)) throw rc;
10234
10235 data.pciAttachments.push_back(hpda);
10236 }
10237
10238
10239 // guest properties
10240 data.llGuestProperties.clear();
10241#ifdef VBOX_WITH_GUEST_PROPS
10242 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10243 it != mHWData->mGuestProperties.end();
10244 ++it)
10245 {
10246 HWData::GuestProperty property = it->second;
10247
10248 /* Remove transient guest properties at shutdown unless we
10249 * are saving state */
10250 if ( ( mData->mMachineState == MachineState_PoweredOff
10251 || mData->mMachineState == MachineState_Aborted
10252 || mData->mMachineState == MachineState_Teleported)
10253 && ( property.mFlags & guestProp::TRANSIENT
10254 || property.mFlags & guestProp::TRANSRESET))
10255 continue;
10256 settings::GuestProperty prop;
10257 prop.strName = it->first;
10258 prop.strValue = property.strValue;
10259 prop.timestamp = property.mTimestamp;
10260 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10261 guestProp::writeFlags(property.mFlags, szFlags);
10262 prop.strFlags = szFlags;
10263
10264 data.llGuestProperties.push_back(prop);
10265 }
10266
10267 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10268 /* I presume this doesn't require a backup(). */
10269 mData->mGuestPropertiesModified = FALSE;
10270#endif /* VBOX_WITH_GUEST_PROPS defined */
10271
10272 *pDbg = mHWData->mDebugging;
10273 *pAutostart = mHWData->mAutostart;
10274
10275 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10276 }
10277 catch(std::bad_alloc &)
10278 {
10279 return E_OUTOFMEMORY;
10280 }
10281
10282 AssertComRC(rc);
10283 return rc;
10284}
10285
10286/**
10287 * Saves the storage controller configuration.
10288 *
10289 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10290 */
10291HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10292{
10293 data.llStorageControllers.clear();
10294
10295 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10296 it != mStorageControllers->end();
10297 ++it)
10298 {
10299 HRESULT rc;
10300 ComObjPtr<StorageController> pCtl = *it;
10301
10302 settings::StorageController ctl;
10303 ctl.strName = pCtl->i_getName();
10304 ctl.controllerType = pCtl->i_getControllerType();
10305 ctl.storageBus = pCtl->i_getStorageBus();
10306 ctl.ulInstance = pCtl->i_getInstance();
10307 ctl.fBootable = pCtl->i_getBootable();
10308
10309 /* Save the port count. */
10310 ULONG portCount;
10311 rc = pCtl->COMGETTER(PortCount)(&portCount);
10312 ComAssertComRCRet(rc, rc);
10313 ctl.ulPortCount = portCount;
10314
10315 /* Save fUseHostIOCache */
10316 BOOL fUseHostIOCache;
10317 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10318 ComAssertComRCRet(rc, rc);
10319 ctl.fUseHostIOCache = !!fUseHostIOCache;
10320
10321 /* Save IDE emulation settings. */
10322 if (ctl.controllerType == StorageControllerType_IntelAhci)
10323 {
10324 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10325 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10326 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10327 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10328 )
10329 ComAssertComRCRet(rc, rc);
10330 }
10331
10332 /* save the devices now. */
10333 rc = i_saveStorageDevices(pCtl, ctl);
10334 ComAssertComRCRet(rc, rc);
10335
10336 data.llStorageControllers.push_back(ctl);
10337 }
10338
10339 return S_OK;
10340}
10341
10342/**
10343 * Saves the hard disk configuration.
10344 */
10345HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10346 settings::StorageController &data)
10347{
10348 MediaData::AttachmentList atts;
10349
10350 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10351 if (FAILED(rc)) return rc;
10352
10353 data.llAttachedDevices.clear();
10354 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10355 it != atts.end();
10356 ++it)
10357 {
10358 settings::AttachedDevice dev;
10359 IMediumAttachment *iA = *it;
10360 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10361 Medium *pMedium = pAttach->i_getMedium();
10362
10363 dev.deviceType = pAttach->i_getType();
10364 dev.lPort = pAttach->i_getPort();
10365 dev.lDevice = pAttach->i_getDevice();
10366 dev.fPassThrough = pAttach->i_getPassthrough();
10367 dev.fHotPluggable = pAttach->i_getHotPluggable();
10368 if (pMedium)
10369 {
10370 if (pMedium->i_isHostDrive())
10371 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10372 else
10373 dev.uuid = pMedium->i_getId();
10374 dev.fTempEject = pAttach->i_getTempEject();
10375 dev.fNonRotational = pAttach->i_getNonRotational();
10376 dev.fDiscard = pAttach->i_getDiscard();
10377 }
10378
10379 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10380
10381 data.llAttachedDevices.push_back(dev);
10382 }
10383
10384 return S_OK;
10385}
10386
10387/**
10388 * Saves machine state settings as defined by aFlags
10389 * (SaveSTS_* values).
10390 *
10391 * @param aFlags Combination of SaveSTS_* flags.
10392 *
10393 * @note Locks objects for writing.
10394 */
10395HRESULT Machine::i_saveStateSettings(int aFlags)
10396{
10397 if (aFlags == 0)
10398 return S_OK;
10399
10400 AutoCaller autoCaller(this);
10401 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10402
10403 /* This object's write lock is also necessary to serialize file access
10404 * (prevent concurrent reads and writes) */
10405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10406
10407 HRESULT rc = S_OK;
10408
10409 Assert(mData->pMachineConfigFile);
10410
10411 try
10412 {
10413 if (aFlags & SaveSTS_CurStateModified)
10414 mData->pMachineConfigFile->fCurrentStateModified = true;
10415
10416 if (aFlags & SaveSTS_StateFilePath)
10417 {
10418 if (!mSSData->strStateFilePath.isEmpty())
10419 /* try to make the file name relative to the settings file dir */
10420 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10421 else
10422 mData->pMachineConfigFile->strStateFile.setNull();
10423 }
10424
10425 if (aFlags & SaveSTS_StateTimeStamp)
10426 {
10427 Assert( mData->mMachineState != MachineState_Aborted
10428 || mSSData->strStateFilePath.isEmpty());
10429
10430 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10431
10432 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10433//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10434 }
10435
10436 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10437 }
10438 catch (...)
10439 {
10440 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10441 }
10442
10443 return rc;
10444}
10445
10446/**
10447 * Ensures that the given medium is added to a media registry. If this machine
10448 * was created with 4.0 or later, then the machine registry is used. Otherwise
10449 * the global VirtualBox media registry is used.
10450 *
10451 * Caller must NOT hold machine lock, media tree or any medium locks!
10452 *
10453 * @param pMedium
10454 */
10455void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10456{
10457 /* Paranoia checks: do not hold machine or media tree locks. */
10458 AssertReturnVoid(!isWriteLockOnCurrentThread());
10459 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10460
10461 ComObjPtr<Medium> pBase;
10462 {
10463 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10464 pBase = pMedium->i_getBase();
10465 }
10466
10467 /* Paranoia checks: do not hold medium locks. */
10468 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10469 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10470
10471 // decide which medium registry to use now that the medium is attached:
10472 Guid uuid;
10473 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10474 // machine XML is VirtualBox 4.0 or higher:
10475 uuid = i_getId(); // machine UUID
10476 else
10477 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10478
10479 if (pMedium->i_addRegistry(uuid))
10480 mParent->i_markRegistryModified(uuid);
10481
10482 /* For more complex hard disk structures it can happen that the base
10483 * medium isn't yet associated with any medium registry. Do that now. */
10484 if (pMedium != pBase)
10485 {
10486 /* Tree lock needed by Medium::addRegistry when recursing. */
10487 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10488 if (pBase->i_addRegistryRecursive(uuid))
10489 {
10490 treeLock.release();
10491 mParent->i_markRegistryModified(uuid);
10492 }
10493 }
10494}
10495
10496/**
10497 * Creates differencing hard disks for all normal hard disks attached to this
10498 * machine and a new set of attachments to refer to created disks.
10499 *
10500 * Used when taking a snapshot or when deleting the current state. Gets called
10501 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10502 *
10503 * This method assumes that mMediaData contains the original hard disk attachments
10504 * it needs to create diffs for. On success, these attachments will be replaced
10505 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10506 * called to delete created diffs which will also rollback mMediaData and restore
10507 * whatever was backed up before calling this method.
10508 *
10509 * Attachments with non-normal hard disks are left as is.
10510 *
10511 * If @a aOnline is @c false then the original hard disks that require implicit
10512 * diffs will be locked for reading. Otherwise it is assumed that they are
10513 * already locked for writing (when the VM was started). Note that in the latter
10514 * case it is responsibility of the caller to lock the newly created diffs for
10515 * writing if this method succeeds.
10516 *
10517 * @param aProgress Progress object to run (must contain at least as
10518 * many operations left as the number of hard disks
10519 * attached).
10520 * @param aOnline Whether the VM was online prior to this operation.
10521 *
10522 * @note The progress object is not marked as completed, neither on success nor
10523 * on failure. This is a responsibility of the caller.
10524 *
10525 * @note Locks this object and the media tree for writing.
10526 */
10527HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10528 ULONG aWeight,
10529 bool aOnline)
10530{
10531 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10532
10533 AutoCaller autoCaller(this);
10534 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10535
10536 AutoMultiWriteLock2 alock(this->lockHandle(),
10537 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10538
10539 /* must be in a protective state because we release the lock below */
10540 AssertReturn( mData->mMachineState == MachineState_Saving
10541 || mData->mMachineState == MachineState_LiveSnapshotting
10542 || mData->mMachineState == MachineState_RestoringSnapshot
10543 || mData->mMachineState == MachineState_DeletingSnapshot
10544 , E_FAIL);
10545
10546 HRESULT rc = S_OK;
10547
10548 // use appropriate locked media map (online or offline)
10549 MediumLockListMap lockedMediaOffline;
10550 MediumLockListMap *lockedMediaMap;
10551 if (aOnline)
10552 lockedMediaMap = &mData->mSession.mLockedMedia;
10553 else
10554 lockedMediaMap = &lockedMediaOffline;
10555
10556 try
10557 {
10558 if (!aOnline)
10559 {
10560 /* lock all attached hard disks early to detect "in use"
10561 * situations before creating actual diffs */
10562 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10563 it != mMediaData->mAttachments.end();
10564 ++it)
10565 {
10566 MediumAttachment* pAtt = *it;
10567 if (pAtt->i_getType() == DeviceType_HardDisk)
10568 {
10569 Medium* pMedium = pAtt->i_getMedium();
10570 Assert(pMedium);
10571
10572 MediumLockList *pMediumLockList(new MediumLockList());
10573 alock.release();
10574 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10575 false /* fMediumLockWrite */,
10576 false /* fMediumLockWriteAll */,
10577 NULL,
10578 *pMediumLockList);
10579 alock.acquire();
10580 if (FAILED(rc))
10581 {
10582 delete pMediumLockList;
10583 throw rc;
10584 }
10585 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10586 if (FAILED(rc))
10587 {
10588 throw setError(rc,
10589 tr("Collecting locking information for all attached media failed"));
10590 }
10591 }
10592 }
10593
10594 /* Now lock all media. If this fails, nothing is locked. */
10595 alock.release();
10596 rc = lockedMediaMap->Lock();
10597 alock.acquire();
10598 if (FAILED(rc))
10599 {
10600 throw setError(rc,
10601 tr("Locking of attached media failed"));
10602 }
10603 }
10604
10605 /* remember the current list (note that we don't use backup() since
10606 * mMediaData may be already backed up) */
10607 MediaData::AttachmentList atts = mMediaData->mAttachments;
10608
10609 /* start from scratch */
10610 mMediaData->mAttachments.clear();
10611
10612 /* go through remembered attachments and create diffs for normal hard
10613 * disks and attach them */
10614 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10615 it != atts.end();
10616 ++it)
10617 {
10618 MediumAttachment* pAtt = *it;
10619
10620 DeviceType_T devType = pAtt->i_getType();
10621 Medium* pMedium = pAtt->i_getMedium();
10622
10623 if ( devType != DeviceType_HardDisk
10624 || pMedium == NULL
10625 || pMedium->i_getType() != MediumType_Normal)
10626 {
10627 /* copy the attachment as is */
10628
10629 /** @todo the progress object created in Console::TakeSnaphot
10630 * only expects operations for hard disks. Later other
10631 * device types need to show up in the progress as well. */
10632 if (devType == DeviceType_HardDisk)
10633 {
10634 if (pMedium == NULL)
10635 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10636 aWeight); // weight
10637 else
10638 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10639 pMedium->i_getBase()->i_getName().c_str()).raw(),
10640 aWeight); // weight
10641 }
10642
10643 mMediaData->mAttachments.push_back(pAtt);
10644 continue;
10645 }
10646
10647 /* need a diff */
10648 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10649 pMedium->i_getBase()->i_getName().c_str()).raw(),
10650 aWeight); // weight
10651
10652 Utf8Str strFullSnapshotFolder;
10653 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10654
10655 ComObjPtr<Medium> diff;
10656 diff.createObject();
10657 // store the diff in the same registry as the parent
10658 // (this cannot fail here because we can't create implicit diffs for
10659 // unregistered images)
10660 Guid uuidRegistryParent;
10661 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10662 Assert(fInRegistry); NOREF(fInRegistry);
10663 rc = diff->init(mParent,
10664 pMedium->i_getPreferredDiffFormat(),
10665 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10666 uuidRegistryParent,
10667 DeviceType_HardDisk);
10668 if (FAILED(rc)) throw rc;
10669
10670 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10671 * the push_back? Looks like we're going to release medium with the
10672 * wrong kind of lock (general issue with if we fail anywhere at all)
10673 * and an orphaned VDI in the snapshots folder. */
10674
10675 /* update the appropriate lock list */
10676 MediumLockList *pMediumLockList;
10677 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10678 AssertComRCThrowRC(rc);
10679 if (aOnline)
10680 {
10681 alock.release();
10682 /* The currently attached medium will be read-only, change
10683 * the lock type to read. */
10684 rc = pMediumLockList->Update(pMedium, false);
10685 alock.acquire();
10686 AssertComRCThrowRC(rc);
10687 }
10688
10689 /* release the locks before the potentially lengthy operation */
10690 alock.release();
10691 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10692 pMediumLockList,
10693 NULL /* aProgress */,
10694 true /* aWait */);
10695 alock.acquire();
10696 if (FAILED(rc)) throw rc;
10697
10698 /* actual lock list update is done in Medium::commitMedia */
10699
10700 rc = diff->i_addBackReference(mData->mUuid);
10701 AssertComRCThrowRC(rc);
10702
10703 /* add a new attachment */
10704 ComObjPtr<MediumAttachment> attachment;
10705 attachment.createObject();
10706 rc = attachment->init(this,
10707 diff,
10708 pAtt->i_getControllerName(),
10709 pAtt->i_getPort(),
10710 pAtt->i_getDevice(),
10711 DeviceType_HardDisk,
10712 true /* aImplicit */,
10713 false /* aPassthrough */,
10714 false /* aTempEject */,
10715 pAtt->i_getNonRotational(),
10716 pAtt->i_getDiscard(),
10717 pAtt->i_getHotPluggable(),
10718 pAtt->i_getBandwidthGroup());
10719 if (FAILED(rc)) throw rc;
10720
10721 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10722 AssertComRCThrowRC(rc);
10723 mMediaData->mAttachments.push_back(attachment);
10724 }
10725 }
10726 catch (HRESULT aRC) { rc = aRC; }
10727
10728 /* unlock all hard disks we locked when there is no VM */
10729 if (!aOnline)
10730 {
10731 ErrorInfoKeeper eik;
10732
10733 HRESULT rc1 = lockedMediaMap->Clear();
10734 AssertComRC(rc1);
10735 }
10736
10737 return rc;
10738}
10739
10740/**
10741 * Deletes implicit differencing hard disks created either by
10742 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10743 *
10744 * Note that to delete hard disks created by #AttachDevice() this method is
10745 * called from #fixupMedia() when the changes are rolled back.
10746 *
10747 * @note Locks this object and the media tree for writing.
10748 */
10749HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10750{
10751 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10752
10753 AutoCaller autoCaller(this);
10754 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10755
10756 AutoMultiWriteLock2 alock(this->lockHandle(),
10757 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10758
10759 /* We absolutely must have backed up state. */
10760 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10761
10762 /* Check if there are any implicitly created diff images. */
10763 bool fImplicitDiffs = false;
10764 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10765 it != mMediaData->mAttachments.end();
10766 ++it)
10767 {
10768 const ComObjPtr<MediumAttachment> &pAtt = *it;
10769 if (pAtt->i_isImplicit())
10770 {
10771 fImplicitDiffs = true;
10772 break;
10773 }
10774 }
10775 /* If there is nothing to do, leave early. This saves lots of image locking
10776 * effort. It also avoids a MachineStateChanged event without real reason.
10777 * This is important e.g. when loading a VM config, because there should be
10778 * no events. Otherwise API clients can become thoroughly confused for
10779 * inaccessible VMs (the code for loading VM configs uses this method for
10780 * cleanup if the config makes no sense), as they take such events as an
10781 * indication that the VM is alive, and they would force the VM config to
10782 * be reread, leading to an endless loop. */
10783 if (!fImplicitDiffs)
10784 return S_OK;
10785
10786 HRESULT rc = S_OK;
10787 MachineState_T oldState = mData->mMachineState;
10788
10789 /* will release the lock before the potentially lengthy operation,
10790 * so protect with the special state (unless already protected) */
10791 if ( oldState != MachineState_Saving
10792 && oldState != MachineState_LiveSnapshotting
10793 && oldState != MachineState_RestoringSnapshot
10794 && oldState != MachineState_DeletingSnapshot
10795 && oldState != MachineState_DeletingSnapshotOnline
10796 && oldState != MachineState_DeletingSnapshotPaused
10797 )
10798 i_setMachineState(MachineState_SettingUp);
10799
10800 // use appropriate locked media map (online or offline)
10801 MediumLockListMap lockedMediaOffline;
10802 MediumLockListMap *lockedMediaMap;
10803 if (aOnline)
10804 lockedMediaMap = &mData->mSession.mLockedMedia;
10805 else
10806 lockedMediaMap = &lockedMediaOffline;
10807
10808 try
10809 {
10810 if (!aOnline)
10811 {
10812 /* lock all attached hard disks early to detect "in use"
10813 * situations before deleting actual diffs */
10814 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10815 it != mMediaData->mAttachments.end();
10816 ++it)
10817 {
10818 MediumAttachment* pAtt = *it;
10819 if (pAtt->i_getType() == DeviceType_HardDisk)
10820 {
10821 Medium* pMedium = pAtt->i_getMedium();
10822 Assert(pMedium);
10823
10824 MediumLockList *pMediumLockList(new MediumLockList());
10825 alock.release();
10826 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10827 false /* fMediumLockWrite */,
10828 false /* fMediumLockWriteAll */,
10829 NULL,
10830 *pMediumLockList);
10831 alock.acquire();
10832
10833 if (FAILED(rc))
10834 {
10835 delete pMediumLockList;
10836 throw rc;
10837 }
10838
10839 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10840 if (FAILED(rc))
10841 throw rc;
10842 }
10843 }
10844
10845 if (FAILED(rc))
10846 throw rc;
10847 } // end of offline
10848
10849 /* Lock lists are now up to date and include implicitly created media */
10850
10851 /* Go through remembered attachments and delete all implicitly created
10852 * diffs and fix up the attachment information */
10853 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10854 MediaData::AttachmentList implicitAtts;
10855 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10856 it != mMediaData->mAttachments.end();
10857 ++it)
10858 {
10859 ComObjPtr<MediumAttachment> pAtt = *it;
10860 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10861 if (pMedium.isNull())
10862 continue;
10863
10864 // Implicit attachments go on the list for deletion and back references are removed.
10865 if (pAtt->i_isImplicit())
10866 {
10867 /* Deassociate and mark for deletion */
10868 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10869 rc = pMedium->i_removeBackReference(mData->mUuid);
10870 if (FAILED(rc))
10871 throw rc;
10872 implicitAtts.push_back(pAtt);
10873 continue;
10874 }
10875
10876 /* Was this medium attached before? */
10877 if (!i_findAttachment(oldAtts, pMedium))
10878 {
10879 /* no: de-associate */
10880 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10881 rc = pMedium->i_removeBackReference(mData->mUuid);
10882 if (FAILED(rc))
10883 throw rc;
10884 continue;
10885 }
10886 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10887 }
10888
10889 /* If there are implicit attachments to delete, throw away the lock
10890 * map contents (which will unlock all media) since the medium
10891 * attachments will be rolled back. Below we need to completely
10892 * recreate the lock map anyway since it is infinitely complex to
10893 * do this incrementally (would need reconstructing each attachment
10894 * change, which would be extremely hairy). */
10895 if (implicitAtts.size() != 0)
10896 {
10897 ErrorInfoKeeper eik;
10898
10899 HRESULT rc1 = lockedMediaMap->Clear();
10900 AssertComRC(rc1);
10901 }
10902
10903 /* rollback hard disk changes */
10904 mMediaData.rollback();
10905
10906 MultiResult mrc(S_OK);
10907
10908 // Delete unused implicit diffs.
10909 if (implicitAtts.size() != 0)
10910 {
10911 alock.release();
10912
10913 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10914 {
10915 // Remove medium associated with this attachment.
10916 ComObjPtr<MediumAttachment> pAtt = *it;
10917 Assert(pAtt);
10918 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10919 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10920 Assert(pMedium);
10921
10922 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10923 // continue on delete failure, just collect error messages
10924 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10925 pMedium->i_getLocationFull().c_str() ));
10926 mrc = rc;
10927 }
10928
10929 alock.acquire();
10930
10931 /* if there is a VM recreate media lock map as mentioned above,
10932 * otherwise it is a waste of time and we leave things unlocked */
10933 if (aOnline)
10934 {
10935 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10936 /* must never be NULL, but better safe than sorry */
10937 if (!pMachine.isNull())
10938 {
10939 alock.release();
10940 rc = mData->mSession.mMachine->i_lockMedia();
10941 alock.acquire();
10942 if (FAILED(rc))
10943 throw rc;
10944 }
10945 }
10946 }
10947 }
10948 catch (HRESULT aRC) {rc = aRC;}
10949
10950 if (mData->mMachineState == MachineState_SettingUp)
10951 i_setMachineState(oldState);
10952
10953 /* unlock all hard disks we locked when there is no VM */
10954 if (!aOnline)
10955 {
10956 ErrorInfoKeeper eik;
10957
10958 HRESULT rc1 = lockedMediaMap->Clear();
10959 AssertComRC(rc1);
10960 }
10961
10962 return rc;
10963}
10964
10965
10966/**
10967 * Looks through the given list of media attachments for one with the given parameters
10968 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10969 * can be searched as well if needed.
10970 *
10971 * @param list
10972 * @param aControllerName
10973 * @param aControllerPort
10974 * @param aDevice
10975 * @return
10976 */
10977MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10978 IN_BSTR aControllerName,
10979 LONG aControllerPort,
10980 LONG aDevice)
10981{
10982 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10983 {
10984 MediumAttachment *pAttach = *it;
10985 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10986 return pAttach;
10987 }
10988
10989 return NULL;
10990}
10991
10992/**
10993 * Looks through the given list of media attachments for one with the given parameters
10994 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10995 * can be searched as well if needed.
10996 *
10997 * @param list
10998 * @param aControllerName
10999 * @param aControllerPort
11000 * @param aDevice
11001 * @return
11002 */
11003MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11004 ComObjPtr<Medium> pMedium)
11005{
11006 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11007 {
11008 MediumAttachment *pAttach = *it;
11009 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11010 if (pMediumThis == pMedium)
11011 return pAttach;
11012 }
11013
11014 return NULL;
11015}
11016
11017/**
11018 * Looks through the given list of media attachments for one with the given parameters
11019 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11020 * can be searched as well if needed.
11021 *
11022 * @param list
11023 * @param aControllerName
11024 * @param aControllerPort
11025 * @param aDevice
11026 * @return
11027 */
11028MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11029 Guid &id)
11030{
11031 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11032 {
11033 MediumAttachment *pAttach = *it;
11034 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11035 if (pMediumThis->i_getId() == id)
11036 return pAttach;
11037 }
11038
11039 return NULL;
11040}
11041
11042/**
11043 * Main implementation for Machine::DetachDevice. This also gets called
11044 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11045 *
11046 * @param pAttach Medium attachment to detach.
11047 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11048 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11049 * SnapshotMachine, and this must be its snapshot.
11050 * @return
11051 */
11052HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11053 AutoWriteLock &writeLock,
11054 Snapshot *pSnapshot)
11055{
11056 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11057 DeviceType_T mediumType = pAttach->i_getType();
11058
11059 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11060
11061 if (pAttach->i_isImplicit())
11062 {
11063 /* attempt to implicitly delete the implicitly created diff */
11064
11065 /// @todo move the implicit flag from MediumAttachment to Medium
11066 /// and forbid any hard disk operation when it is implicit. Or maybe
11067 /// a special media state for it to make it even more simple.
11068
11069 Assert(mMediaData.isBackedUp());
11070
11071 /* will release the lock before the potentially lengthy operation, so
11072 * protect with the special state */
11073 MachineState_T oldState = mData->mMachineState;
11074 i_setMachineState(MachineState_SettingUp);
11075
11076 writeLock.release();
11077
11078 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11079 true /*aWait*/);
11080
11081 writeLock.acquire();
11082
11083 i_setMachineState(oldState);
11084
11085 if (FAILED(rc)) return rc;
11086 }
11087
11088 i_setModified(IsModified_Storage);
11089 mMediaData.backup();
11090 mMediaData->mAttachments.remove(pAttach);
11091
11092 if (!oldmedium.isNull())
11093 {
11094 // if this is from a snapshot, do not defer detachment to commitMedia()
11095 if (pSnapshot)
11096 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11097 // else if non-hard disk media, do not defer detachment to commitMedia() either
11098 else if (mediumType != DeviceType_HardDisk)
11099 oldmedium->i_removeBackReference(mData->mUuid);
11100 }
11101
11102 return S_OK;
11103}
11104
11105/**
11106 * Goes thru all media of the given list and
11107 *
11108 * 1) calls i_detachDevice() on each of them for this machine and
11109 * 2) adds all Medium objects found in the process to the given list,
11110 * depending on cleanupMode.
11111 *
11112 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11113 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11114 * media to the list.
11115 *
11116 * This gets called from Machine::Unregister, both for the actual Machine and
11117 * the SnapshotMachine objects that might be found in the snapshots.
11118 *
11119 * Requires caller and locking. The machine lock must be passed in because it
11120 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11121 *
11122 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11123 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11124 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11125 * Full, then all media get added;
11126 * otherwise no media get added.
11127 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11128 * @return
11129 */
11130HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11131 Snapshot *pSnapshot,
11132 CleanupMode_T cleanupMode,
11133 MediaList &llMedia)
11134{
11135 Assert(isWriteLockOnCurrentThread());
11136
11137 HRESULT rc;
11138
11139 // make a temporary list because i_detachDevice invalidates iterators into
11140 // mMediaData->mAttachments
11141 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11142
11143 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11144 {
11145 ComObjPtr<MediumAttachment> &pAttach = *it;
11146 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11147
11148 if (!pMedium.isNull())
11149 {
11150 AutoCaller mac(pMedium);
11151 if (FAILED(mac.rc())) return mac.rc();
11152 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11153 DeviceType_T devType = pMedium->i_getDeviceType();
11154 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11155 && devType == DeviceType_HardDisk)
11156 || (cleanupMode == CleanupMode_Full)
11157 )
11158 {
11159 llMedia.push_back(pMedium);
11160 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11161 /* Not allowed to keep this lock as below we need the parent
11162 * medium lock, and the lock order is parent to child. */
11163 lock.release();
11164 /*
11165 * Search for medias which are not attached to any machine, but
11166 * in the chain to an attached disk. Mediums are only consided
11167 * if they are:
11168 * - have only one child
11169 * - no references to any machines
11170 * - are of normal medium type
11171 */
11172 while (!pParent.isNull())
11173 {
11174 AutoCaller mac1(pParent);
11175 if (FAILED(mac1.rc())) return mac1.rc();
11176 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11177 if (pParent->i_getChildren().size() == 1)
11178 {
11179 if ( pParent->i_getMachineBackRefCount() == 0
11180 && pParent->i_getType() == MediumType_Normal
11181 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11182 llMedia.push_back(pParent);
11183 }
11184 else
11185 break;
11186 pParent = pParent->i_getParent();
11187 }
11188 }
11189 }
11190
11191 // real machine: then we need to use the proper method
11192 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11193
11194 if (FAILED(rc))
11195 return rc;
11196 }
11197
11198 return S_OK;
11199}
11200
11201/**
11202 * Perform deferred hard disk detachments.
11203 *
11204 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11205 * backed up).
11206 *
11207 * If @a aOnline is @c true then this method will also unlock the old hard disks
11208 * for which the new implicit diffs were created and will lock these new diffs for
11209 * writing.
11210 *
11211 * @param aOnline Whether the VM was online prior to this operation.
11212 *
11213 * @note Locks this object for writing!
11214 */
11215void Machine::i_commitMedia(bool aOnline /*= false*/)
11216{
11217 AutoCaller autoCaller(this);
11218 AssertComRCReturnVoid(autoCaller.rc());
11219
11220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11221
11222 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11223
11224 HRESULT rc = S_OK;
11225
11226 /* no attach/detach operations -- nothing to do */
11227 if (!mMediaData.isBackedUp())
11228 return;
11229
11230 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11231 bool fMediaNeedsLocking = false;
11232
11233 /* enumerate new attachments */
11234 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11235 it != mMediaData->mAttachments.end();
11236 ++it)
11237 {
11238 MediumAttachment *pAttach = *it;
11239
11240 pAttach->i_commit();
11241
11242 Medium* pMedium = pAttach->i_getMedium();
11243 bool fImplicit = pAttach->i_isImplicit();
11244
11245 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11246 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11247 fImplicit));
11248
11249 /** @todo convert all this Machine-based voodoo to MediumAttachment
11250 * based commit logic. */
11251 if (fImplicit)
11252 {
11253 /* convert implicit attachment to normal */
11254 pAttach->i_setImplicit(false);
11255
11256 if ( aOnline
11257 && pMedium
11258 && pAttach->i_getType() == DeviceType_HardDisk
11259 )
11260 {
11261 /* update the appropriate lock list */
11262 MediumLockList *pMediumLockList;
11263 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11264 AssertComRC(rc);
11265 if (pMediumLockList)
11266 {
11267 /* unlock if there's a need to change the locking */
11268 if (!fMediaNeedsLocking)
11269 {
11270 rc = mData->mSession.mLockedMedia.Unlock();
11271 AssertComRC(rc);
11272 fMediaNeedsLocking = true;
11273 }
11274 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11275 AssertComRC(rc);
11276 rc = pMediumLockList->Append(pMedium, true);
11277 AssertComRC(rc);
11278 }
11279 }
11280
11281 continue;
11282 }
11283
11284 if (pMedium)
11285 {
11286 /* was this medium attached before? */
11287 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11288 {
11289 MediumAttachment *pOldAttach = *oldIt;
11290 if (pOldAttach->i_getMedium() == pMedium)
11291 {
11292 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11293
11294 /* yes: remove from old to avoid de-association */
11295 oldAtts.erase(oldIt);
11296 break;
11297 }
11298 }
11299 }
11300 }
11301
11302 /* enumerate remaining old attachments and de-associate from the
11303 * current machine state */
11304 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11305 {
11306 MediumAttachment *pAttach = *it;
11307 Medium* pMedium = pAttach->i_getMedium();
11308
11309 /* Detach only hard disks, since DVD/floppy media is detached
11310 * instantly in MountMedium. */
11311 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11312 {
11313 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11314
11315 /* now de-associate from the current machine state */
11316 rc = pMedium->i_removeBackReference(mData->mUuid);
11317 AssertComRC(rc);
11318
11319 if (aOnline)
11320 {
11321 /* unlock since medium is not used anymore */
11322 MediumLockList *pMediumLockList;
11323 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11324 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11325 {
11326 /* this happens for online snapshots, there the attachment
11327 * is changing, but only to a diff image created under
11328 * the old one, so there is no separate lock list */
11329 Assert(!pMediumLockList);
11330 }
11331 else
11332 {
11333 AssertComRC(rc);
11334 if (pMediumLockList)
11335 {
11336 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11337 AssertComRC(rc);
11338 }
11339 }
11340 }
11341 }
11342 }
11343
11344 /* take media locks again so that the locking state is consistent */
11345 if (fMediaNeedsLocking)
11346 {
11347 Assert(aOnline);
11348 rc = mData->mSession.mLockedMedia.Lock();
11349 AssertComRC(rc);
11350 }
11351
11352 /* commit the hard disk changes */
11353 mMediaData.commit();
11354
11355 if (i_isSessionMachine())
11356 {
11357 /*
11358 * Update the parent machine to point to the new owner.
11359 * This is necessary because the stored parent will point to the
11360 * session machine otherwise and cause crashes or errors later
11361 * when the session machine gets invalid.
11362 */
11363 /** @todo Change the MediumAttachment class to behave like any other
11364 * class in this regard by creating peer MediumAttachment
11365 * objects for session machines and share the data with the peer
11366 * machine.
11367 */
11368 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11369 it != mMediaData->mAttachments.end();
11370 ++it)
11371 (*it)->i_updateParentMachine(mPeer);
11372
11373 /* attach new data to the primary machine and reshare it */
11374 mPeer->mMediaData.attach(mMediaData);
11375 }
11376
11377 return;
11378}
11379
11380/**
11381 * Perform deferred deletion of implicitly created diffs.
11382 *
11383 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11384 * backed up).
11385 *
11386 * @note Locks this object for writing!
11387 */
11388void Machine::i_rollbackMedia()
11389{
11390 AutoCaller autoCaller(this);
11391 AssertComRCReturnVoid(autoCaller.rc());
11392
11393 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11394 LogFlowThisFunc(("Entering rollbackMedia\n"));
11395
11396 HRESULT rc = S_OK;
11397
11398 /* no attach/detach operations -- nothing to do */
11399 if (!mMediaData.isBackedUp())
11400 return;
11401
11402 /* enumerate new attachments */
11403 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11404 it != mMediaData->mAttachments.end();
11405 ++it)
11406 {
11407 MediumAttachment *pAttach = *it;
11408 /* Fix up the backrefs for DVD/floppy media. */
11409 if (pAttach->i_getType() != DeviceType_HardDisk)
11410 {
11411 Medium* pMedium = pAttach->i_getMedium();
11412 if (pMedium)
11413 {
11414 rc = pMedium->i_removeBackReference(mData->mUuid);
11415 AssertComRC(rc);
11416 }
11417 }
11418
11419 (*it)->i_rollback();
11420
11421 pAttach = *it;
11422 /* Fix up the backrefs for DVD/floppy media. */
11423 if (pAttach->i_getType() != DeviceType_HardDisk)
11424 {
11425 Medium* pMedium = pAttach->i_getMedium();
11426 if (pMedium)
11427 {
11428 rc = pMedium->i_addBackReference(mData->mUuid);
11429 AssertComRC(rc);
11430 }
11431 }
11432 }
11433
11434 /** @todo convert all this Machine-based voodoo to MediumAttachment
11435 * based rollback logic. */
11436 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11437
11438 return;
11439}
11440
11441/**
11442 * Returns true if the settings file is located in the directory named exactly
11443 * as the machine; this means, among other things, that the machine directory
11444 * should be auto-renamed.
11445 *
11446 * @param aSettingsDir if not NULL, the full machine settings file directory
11447 * name will be assigned there.
11448 *
11449 * @note Doesn't lock anything.
11450 * @note Not thread safe (must be called from this object's lock).
11451 */
11452bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11453{
11454 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11455 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11456 if (aSettingsDir)
11457 *aSettingsDir = strMachineDirName;
11458 strMachineDirName.stripPath(); // vmname
11459 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11460 strConfigFileOnly.stripPath() // vmname.vbox
11461 .stripSuffix(); // vmname
11462 /** @todo hack, make somehow use of ComposeMachineFilename */
11463 if (mUserData->s.fDirectoryIncludesUUID)
11464 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11465
11466 AssertReturn(!strMachineDirName.isEmpty(), false);
11467 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11468
11469 return strMachineDirName == strConfigFileOnly;
11470}
11471
11472/**
11473 * Discards all changes to machine settings.
11474 *
11475 * @param aNotify Whether to notify the direct session about changes or not.
11476 *
11477 * @note Locks objects for writing!
11478 */
11479void Machine::i_rollback(bool aNotify)
11480{
11481 AutoCaller autoCaller(this);
11482 AssertComRCReturn(autoCaller.rc(), (void)0);
11483
11484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11485
11486 if (!mStorageControllers.isNull())
11487 {
11488 if (mStorageControllers.isBackedUp())
11489 {
11490 /* unitialize all new devices (absent in the backed up list). */
11491 StorageControllerList::const_iterator it = mStorageControllers->begin();
11492 StorageControllerList *backedList = mStorageControllers.backedUpData();
11493 while (it != mStorageControllers->end())
11494 {
11495 if ( std::find(backedList->begin(), backedList->end(), *it)
11496 == backedList->end()
11497 )
11498 {
11499 (*it)->uninit();
11500 }
11501 ++it;
11502 }
11503
11504 /* restore the list */
11505 mStorageControllers.rollback();
11506 }
11507
11508 /* rollback any changes to devices after restoring the list */
11509 if (mData->flModifications & IsModified_Storage)
11510 {
11511 StorageControllerList::const_iterator it = mStorageControllers->begin();
11512 while (it != mStorageControllers->end())
11513 {
11514 (*it)->i_rollback();
11515 ++it;
11516 }
11517 }
11518 }
11519
11520 if (!mUSBControllers.isNull())
11521 {
11522 if (mUSBControllers.isBackedUp())
11523 {
11524 /* unitialize all new devices (absent in the backed up list). */
11525 USBControllerList::const_iterator it = mUSBControllers->begin();
11526 USBControllerList *backedList = mUSBControllers.backedUpData();
11527 while (it != mUSBControllers->end())
11528 {
11529 if ( std::find(backedList->begin(), backedList->end(), *it)
11530 == backedList->end()
11531 )
11532 {
11533 (*it)->uninit();
11534 }
11535 ++it;
11536 }
11537
11538 /* restore the list */
11539 mUSBControllers.rollback();
11540 }
11541
11542 /* rollback any changes to devices after restoring the list */
11543 if (mData->flModifications & IsModified_USB)
11544 {
11545 USBControllerList::const_iterator it = mUSBControllers->begin();
11546 while (it != mUSBControllers->end())
11547 {
11548 (*it)->i_rollback();
11549 ++it;
11550 }
11551 }
11552 }
11553
11554 mUserData.rollback();
11555
11556 mHWData.rollback();
11557
11558 if (mData->flModifications & IsModified_Storage)
11559 i_rollbackMedia();
11560
11561 if (mBIOSSettings)
11562 mBIOSSettings->i_rollback();
11563
11564 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11565 mVRDEServer->i_rollback();
11566
11567 if (mAudioAdapter)
11568 mAudioAdapter->i_rollback();
11569
11570 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11571 mUSBDeviceFilters->i_rollback();
11572
11573 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11574 mBandwidthControl->i_rollback();
11575
11576 if (!mHWData.isNull())
11577 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11578 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11579 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11580 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11581
11582 if (mData->flModifications & IsModified_NetworkAdapters)
11583 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11584 if ( mNetworkAdapters[slot]
11585 && mNetworkAdapters[slot]->i_isModified())
11586 {
11587 mNetworkAdapters[slot]->i_rollback();
11588 networkAdapters[slot] = mNetworkAdapters[slot];
11589 }
11590
11591 if (mData->flModifications & IsModified_SerialPorts)
11592 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11593 if ( mSerialPorts[slot]
11594 && mSerialPorts[slot]->i_isModified())
11595 {
11596 mSerialPorts[slot]->i_rollback();
11597 serialPorts[slot] = mSerialPorts[slot];
11598 }
11599
11600 if (mData->flModifications & IsModified_ParallelPorts)
11601 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11602 if ( mParallelPorts[slot]
11603 && mParallelPorts[slot]->i_isModified())
11604 {
11605 mParallelPorts[slot]->i_rollback();
11606 parallelPorts[slot] = mParallelPorts[slot];
11607 }
11608
11609 if (aNotify)
11610 {
11611 /* inform the direct session about changes */
11612
11613 ComObjPtr<Machine> that = this;
11614 uint32_t flModifications = mData->flModifications;
11615 alock.release();
11616
11617 if (flModifications & IsModified_SharedFolders)
11618 that->i_onSharedFolderChange();
11619
11620 if (flModifications & IsModified_VRDEServer)
11621 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11622 if (flModifications & IsModified_USB)
11623 that->i_onUSBControllerChange();
11624
11625 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11626 if (networkAdapters[slot])
11627 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11628 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11629 if (serialPorts[slot])
11630 that->i_onSerialPortChange(serialPorts[slot]);
11631 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11632 if (parallelPorts[slot])
11633 that->i_onParallelPortChange(parallelPorts[slot]);
11634
11635 if (flModifications & IsModified_Storage)
11636 that->i_onStorageControllerChange();
11637
11638#if 0
11639 if (flModifications & IsModified_BandwidthControl)
11640 that->onBandwidthControlChange();
11641#endif
11642 }
11643}
11644
11645/**
11646 * Commits all the changes to machine settings.
11647 *
11648 * Note that this operation is supposed to never fail.
11649 *
11650 * @note Locks this object and children for writing.
11651 */
11652void Machine::i_commit()
11653{
11654 AutoCaller autoCaller(this);
11655 AssertComRCReturnVoid(autoCaller.rc());
11656
11657 AutoCaller peerCaller(mPeer);
11658 AssertComRCReturnVoid(peerCaller.rc());
11659
11660 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11661
11662 /*
11663 * use safe commit to ensure Snapshot machines (that share mUserData)
11664 * will still refer to a valid memory location
11665 */
11666 mUserData.commitCopy();
11667
11668 mHWData.commit();
11669
11670 if (mMediaData.isBackedUp())
11671 i_commitMedia(Global::IsOnline(mData->mMachineState));
11672
11673 mBIOSSettings->i_commit();
11674 mVRDEServer->i_commit();
11675 mAudioAdapter->i_commit();
11676 mUSBDeviceFilters->i_commit();
11677 mBandwidthControl->i_commit();
11678
11679 /* Since mNetworkAdapters is a list which might have been changed (resized)
11680 * without using the Backupable<> template we need to handle the copying
11681 * of the list entries manually, including the creation of peers for the
11682 * new objects. */
11683 bool commitNetworkAdapters = false;
11684 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11685 if (mPeer)
11686 {
11687 /* commit everything, even the ones which will go away */
11688 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11689 mNetworkAdapters[slot]->i_commit();
11690 /* copy over the new entries, creating a peer and uninit the original */
11691 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11692 for (size_t slot = 0; slot < newSize; slot++)
11693 {
11694 /* look if this adapter has a peer device */
11695 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11696 if (!peer)
11697 {
11698 /* no peer means the adapter is a newly created one;
11699 * create a peer owning data this data share it with */
11700 peer.createObject();
11701 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11702 }
11703 mPeer->mNetworkAdapters[slot] = peer;
11704 }
11705 /* uninit any no longer needed network adapters */
11706 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11707 mNetworkAdapters[slot]->uninit();
11708 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11709 {
11710 if (mPeer->mNetworkAdapters[slot])
11711 mPeer->mNetworkAdapters[slot]->uninit();
11712 }
11713 /* Keep the original network adapter count until this point, so that
11714 * discarding a chipset type change will not lose settings. */
11715 mNetworkAdapters.resize(newSize);
11716 mPeer->mNetworkAdapters.resize(newSize);
11717 }
11718 else
11719 {
11720 /* we have no peer (our parent is the newly created machine);
11721 * just commit changes to the network adapters */
11722 commitNetworkAdapters = true;
11723 }
11724 if (commitNetworkAdapters)
11725 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11726 mNetworkAdapters[slot]->i_commit();
11727
11728 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11729 mSerialPorts[slot]->i_commit();
11730 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11731 mParallelPorts[slot]->i_commit();
11732
11733 bool commitStorageControllers = false;
11734
11735 if (mStorageControllers.isBackedUp())
11736 {
11737 mStorageControllers.commit();
11738
11739 if (mPeer)
11740 {
11741 /* Commit all changes to new controllers (this will reshare data with
11742 * peers for those who have peers) */
11743 StorageControllerList *newList = new StorageControllerList();
11744 StorageControllerList::const_iterator it = mStorageControllers->begin();
11745 while (it != mStorageControllers->end())
11746 {
11747 (*it)->i_commit();
11748
11749 /* look if this controller has a peer device */
11750 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11751 if (!peer)
11752 {
11753 /* no peer means the device is a newly created one;
11754 * create a peer owning data this device share it with */
11755 peer.createObject();
11756 peer->init(mPeer, *it, true /* aReshare */);
11757 }
11758 else
11759 {
11760 /* remove peer from the old list */
11761 mPeer->mStorageControllers->remove(peer);
11762 }
11763 /* and add it to the new list */
11764 newList->push_back(peer);
11765
11766 ++it;
11767 }
11768
11769 /* uninit old peer's controllers that are left */
11770 it = mPeer->mStorageControllers->begin();
11771 while (it != mPeer->mStorageControllers->end())
11772 {
11773 (*it)->uninit();
11774 ++it;
11775 }
11776
11777 /* attach new list of controllers to our peer */
11778 mPeer->mStorageControllers.attach(newList);
11779 }
11780 else
11781 {
11782 /* we have no peer (our parent is the newly created machine);
11783 * just commit changes to devices */
11784 commitStorageControllers = true;
11785 }
11786 }
11787 else
11788 {
11789 /* the list of controllers itself is not changed,
11790 * just commit changes to controllers themselves */
11791 commitStorageControllers = true;
11792 }
11793
11794 if (commitStorageControllers)
11795 {
11796 StorageControllerList::const_iterator it = mStorageControllers->begin();
11797 while (it != mStorageControllers->end())
11798 {
11799 (*it)->i_commit();
11800 ++it;
11801 }
11802 }
11803
11804 bool commitUSBControllers = false;
11805
11806 if (mUSBControllers.isBackedUp())
11807 {
11808 mUSBControllers.commit();
11809
11810 if (mPeer)
11811 {
11812 /* Commit all changes to new controllers (this will reshare data with
11813 * peers for those who have peers) */
11814 USBControllerList *newList = new USBControllerList();
11815 USBControllerList::const_iterator it = mUSBControllers->begin();
11816 while (it != mUSBControllers->end())
11817 {
11818 (*it)->i_commit();
11819
11820 /* look if this controller has a peer device */
11821 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11822 if (!peer)
11823 {
11824 /* no peer means the device is a newly created one;
11825 * create a peer owning data this device share it with */
11826 peer.createObject();
11827 peer->init(mPeer, *it, true /* aReshare */);
11828 }
11829 else
11830 {
11831 /* remove peer from the old list */
11832 mPeer->mUSBControllers->remove(peer);
11833 }
11834 /* and add it to the new list */
11835 newList->push_back(peer);
11836
11837 ++it;
11838 }
11839
11840 /* uninit old peer's controllers that are left */
11841 it = mPeer->mUSBControllers->begin();
11842 while (it != mPeer->mUSBControllers->end())
11843 {
11844 (*it)->uninit();
11845 ++it;
11846 }
11847
11848 /* attach new list of controllers to our peer */
11849 mPeer->mUSBControllers.attach(newList);
11850 }
11851 else
11852 {
11853 /* we have no peer (our parent is the newly created machine);
11854 * just commit changes to devices */
11855 commitUSBControllers = true;
11856 }
11857 }
11858 else
11859 {
11860 /* the list of controllers itself is not changed,
11861 * just commit changes to controllers themselves */
11862 commitUSBControllers = true;
11863 }
11864
11865 if (commitUSBControllers)
11866 {
11867 USBControllerList::const_iterator it = mUSBControllers->begin();
11868 while (it != mUSBControllers->end())
11869 {
11870 (*it)->i_commit();
11871 ++it;
11872 }
11873 }
11874
11875 if (i_isSessionMachine())
11876 {
11877 /* attach new data to the primary machine and reshare it */
11878 mPeer->mUserData.attach(mUserData);
11879 mPeer->mHWData.attach(mHWData);
11880 /* mMediaData is reshared by fixupMedia */
11881 // mPeer->mMediaData.attach(mMediaData);
11882 Assert(mPeer->mMediaData.data() == mMediaData.data());
11883 }
11884}
11885
11886/**
11887 * Copies all the hardware data from the given machine.
11888 *
11889 * Currently, only called when the VM is being restored from a snapshot. In
11890 * particular, this implies that the VM is not running during this method's
11891 * call.
11892 *
11893 * @note This method must be called from under this object's lock.
11894 *
11895 * @note This method doesn't call #commit(), so all data remains backed up and
11896 * unsaved.
11897 */
11898void Machine::i_copyFrom(Machine *aThat)
11899{
11900 AssertReturnVoid(!i_isSnapshotMachine());
11901 AssertReturnVoid(aThat->i_isSnapshotMachine());
11902
11903 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11904
11905 mHWData.assignCopy(aThat->mHWData);
11906
11907 // create copies of all shared folders (mHWData after attaching a copy
11908 // contains just references to original objects)
11909 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11910 it != mHWData->mSharedFolders.end();
11911 ++it)
11912 {
11913 ComObjPtr<SharedFolder> folder;
11914 folder.createObject();
11915 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11916 AssertComRC(rc);
11917 *it = folder;
11918 }
11919
11920 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11921 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11922 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11923 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11924 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11925
11926 /* create private copies of all controllers */
11927 mStorageControllers.backup();
11928 mStorageControllers->clear();
11929 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11930 it != aThat->mStorageControllers->end();
11931 ++it)
11932 {
11933 ComObjPtr<StorageController> ctrl;
11934 ctrl.createObject();
11935 ctrl->initCopy(this, *it);
11936 mStorageControllers->push_back(ctrl);
11937 }
11938
11939 /* create private copies of all USB controllers */
11940 mUSBControllers.backup();
11941 mUSBControllers->clear();
11942 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11943 it != aThat->mUSBControllers->end();
11944 ++it)
11945 {
11946 ComObjPtr<USBController> ctrl;
11947 ctrl.createObject();
11948 ctrl->initCopy(this, *it);
11949 mUSBControllers->push_back(ctrl);
11950 }
11951
11952 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11953 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11954 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11955 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11956 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11957 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11958 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11959}
11960
11961/**
11962 * Returns whether the given storage controller is hotplug capable.
11963 *
11964 * @returns true if the controller supports hotplugging
11965 * false otherwise.
11966 * @param enmCtrlType The controller type to check for.
11967 */
11968bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11969{
11970 ComPtr<ISystemProperties> systemProperties;
11971 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11972 if (FAILED(rc))
11973 return false;
11974
11975 BOOL aHotplugCapable = FALSE;
11976 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11977
11978 return RT_BOOL(aHotplugCapable);
11979}
11980
11981#ifdef VBOX_WITH_RESOURCE_USAGE_API
11982
11983void Machine::i_getDiskList(MediaList &list)
11984{
11985 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11986 it != mMediaData->mAttachments.end();
11987 ++it)
11988 {
11989 MediumAttachment* pAttach = *it;
11990 /* just in case */
11991 AssertStmt(pAttach, continue);
11992
11993 AutoCaller localAutoCallerA(pAttach);
11994 if (FAILED(localAutoCallerA.rc())) continue;
11995
11996 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11997
11998 if (pAttach->i_getType() == DeviceType_HardDisk)
11999 list.push_back(pAttach->i_getMedium());
12000 }
12001}
12002
12003void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12004{
12005 AssertReturnVoid(isWriteLockOnCurrentThread());
12006 AssertPtrReturnVoid(aCollector);
12007
12008 pm::CollectorHAL *hal = aCollector->getHAL();
12009 /* Create sub metrics */
12010 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12011 "Percentage of processor time spent in user mode by the VM process.");
12012 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12013 "Percentage of processor time spent in kernel mode by the VM process.");
12014 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12015 "Size of resident portion of VM process in memory.");
12016 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12017 "Actual size of all VM disks combined.");
12018 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12019 "Network receive rate.");
12020 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12021 "Network transmit rate.");
12022 /* Create and register base metrics */
12023 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12024 cpuLoadUser, cpuLoadKernel);
12025 aCollector->registerBaseMetric(cpuLoad);
12026 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12027 ramUsageUsed);
12028 aCollector->registerBaseMetric(ramUsage);
12029 MediaList disks;
12030 i_getDiskList(disks);
12031 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12032 diskUsageUsed);
12033 aCollector->registerBaseMetric(diskUsage);
12034
12035 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12036 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12037 new pm::AggregateAvg()));
12038 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12039 new pm::AggregateMin()));
12040 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12041 new pm::AggregateMax()));
12042 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12043 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12044 new pm::AggregateAvg()));
12045 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12046 new pm::AggregateMin()));
12047 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12048 new pm::AggregateMax()));
12049
12050 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12051 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12052 new pm::AggregateAvg()));
12053 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12054 new pm::AggregateMin()));
12055 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12056 new pm::AggregateMax()));
12057
12058 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12059 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12060 new pm::AggregateAvg()));
12061 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12062 new pm::AggregateMin()));
12063 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12064 new pm::AggregateMax()));
12065
12066
12067 /* Guest metrics collector */
12068 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12069 aCollector->registerGuest(mCollectorGuest);
12070 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12071 this, __PRETTY_FUNCTION__, mCollectorGuest));
12072
12073 /* Create sub metrics */
12074 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12075 "Percentage of processor time spent in user mode as seen by the guest.");
12076 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12077 "Percentage of processor time spent in kernel mode as seen by the guest.");
12078 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12079 "Percentage of processor time spent idling as seen by the guest.");
12080
12081 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12082 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12083 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12084 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12085 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12086 pm::SubMetric *guestMemCache = new pm::SubMetric(
12087 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12088
12089 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12090 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12091
12092 /* Create and register base metrics */
12093 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12094 machineNetRx, machineNetTx);
12095 aCollector->registerBaseMetric(machineNetRate);
12096
12097 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12098 guestLoadUser, guestLoadKernel, guestLoadIdle);
12099 aCollector->registerBaseMetric(guestCpuLoad);
12100
12101 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12102 guestMemTotal, guestMemFree,
12103 guestMemBalloon, guestMemShared,
12104 guestMemCache, guestPagedTotal);
12105 aCollector->registerBaseMetric(guestCpuMem);
12106
12107 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12108 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12109 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12110 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12111
12112 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12113 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12114 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12115 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12116
12117 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12118 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12119 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12120 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12121
12122 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12123 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12124 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12125 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12126
12127 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12128 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12129 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12130 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12131
12132 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12133 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12134 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12135 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12136
12137 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12138 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12139 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12140 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12141
12142 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12143 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12144 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12145 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12146
12147 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12148 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12149 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12150 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12151
12152 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12153 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12154 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12155 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12156
12157 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12158 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12159 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12160 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12161}
12162
12163void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12164{
12165 AssertReturnVoid(isWriteLockOnCurrentThread());
12166
12167 if (aCollector)
12168 {
12169 aCollector->unregisterMetricsFor(aMachine);
12170 aCollector->unregisterBaseMetricsFor(aMachine);
12171 }
12172}
12173
12174#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12175
12176
12177////////////////////////////////////////////////////////////////////////////////
12178
12179DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12180
12181HRESULT SessionMachine::FinalConstruct()
12182{
12183 LogFlowThisFunc(("\n"));
12184
12185 mClientToken = NULL;
12186
12187 return BaseFinalConstruct();
12188}
12189
12190void SessionMachine::FinalRelease()
12191{
12192 LogFlowThisFunc(("\n"));
12193
12194 Assert(!mClientToken);
12195 /* paranoia, should not hang around any more */
12196 if (mClientToken)
12197 {
12198 delete mClientToken;
12199 mClientToken = NULL;
12200 }
12201
12202 uninit(Uninit::Unexpected);
12203
12204 BaseFinalRelease();
12205}
12206
12207/**
12208 * @note Must be called only by Machine::LockMachine() from its own write lock.
12209 */
12210HRESULT SessionMachine::init(Machine *aMachine)
12211{
12212 LogFlowThisFuncEnter();
12213 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12214
12215 AssertReturn(aMachine, E_INVALIDARG);
12216
12217 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12218
12219 /* Enclose the state transition NotReady->InInit->Ready */
12220 AutoInitSpan autoInitSpan(this);
12221 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12222
12223 HRESULT rc = S_OK;
12224
12225 /* create the machine client token */
12226 try
12227 {
12228 mClientToken = new ClientToken(aMachine, this);
12229 if (!mClientToken->isReady())
12230 {
12231 delete mClientToken;
12232 mClientToken = NULL;
12233 rc = E_FAIL;
12234 }
12235 }
12236 catch (std::bad_alloc &)
12237 {
12238 rc = E_OUTOFMEMORY;
12239 }
12240 if (FAILED(rc))
12241 return rc;
12242
12243 /* memorize the peer Machine */
12244 unconst(mPeer) = aMachine;
12245 /* share the parent pointer */
12246 unconst(mParent) = aMachine->mParent;
12247
12248 /* take the pointers to data to share */
12249 mData.share(aMachine->mData);
12250 mSSData.share(aMachine->mSSData);
12251
12252 mUserData.share(aMachine->mUserData);
12253 mHWData.share(aMachine->mHWData);
12254 mMediaData.share(aMachine->mMediaData);
12255
12256 mStorageControllers.allocate();
12257 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12258 it != aMachine->mStorageControllers->end();
12259 ++it)
12260 {
12261 ComObjPtr<StorageController> ctl;
12262 ctl.createObject();
12263 ctl->init(this, *it);
12264 mStorageControllers->push_back(ctl);
12265 }
12266
12267 mUSBControllers.allocate();
12268 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12269 it != aMachine->mUSBControllers->end();
12270 ++it)
12271 {
12272 ComObjPtr<USBController> ctl;
12273 ctl.createObject();
12274 ctl->init(this, *it);
12275 mUSBControllers->push_back(ctl);
12276 }
12277
12278 unconst(mBIOSSettings).createObject();
12279 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12280 /* create another VRDEServer object that will be mutable */
12281 unconst(mVRDEServer).createObject();
12282 mVRDEServer->init(this, aMachine->mVRDEServer);
12283 /* create another audio adapter object that will be mutable */
12284 unconst(mAudioAdapter).createObject();
12285 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12286 /* create a list of serial ports that will be mutable */
12287 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12288 {
12289 unconst(mSerialPorts[slot]).createObject();
12290 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12291 }
12292 /* create a list of parallel ports that will be mutable */
12293 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12294 {
12295 unconst(mParallelPorts[slot]).createObject();
12296 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12297 }
12298
12299 /* create another USB device filters object that will be mutable */
12300 unconst(mUSBDeviceFilters).createObject();
12301 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12302
12303 /* create a list of network adapters that will be mutable */
12304 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12305 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12306 {
12307 unconst(mNetworkAdapters[slot]).createObject();
12308 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12309 }
12310
12311 /* create another bandwidth control object that will be mutable */
12312 unconst(mBandwidthControl).createObject();
12313 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12314
12315 /* default is to delete saved state on Saved -> PoweredOff transition */
12316 mRemoveSavedState = true;
12317
12318 /* Confirm a successful initialization when it's the case */
12319 autoInitSpan.setSucceeded();
12320
12321 miNATNetworksStarted = 0;
12322
12323 LogFlowThisFuncLeave();
12324 return rc;
12325}
12326
12327/**
12328 * Uninitializes this session object. If the reason is other than
12329 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12330 * or the client watcher code.
12331 *
12332 * @param aReason uninitialization reason
12333 *
12334 * @note Locks mParent + this object for writing.
12335 */
12336void SessionMachine::uninit(Uninit::Reason aReason)
12337{
12338 LogFlowThisFuncEnter();
12339 LogFlowThisFunc(("reason=%d\n", aReason));
12340
12341 /*
12342 * Strongly reference ourselves to prevent this object deletion after
12343 * mData->mSession.mMachine.setNull() below (which can release the last
12344 * reference and call the destructor). Important: this must be done before
12345 * accessing any members (and before AutoUninitSpan that does it as well).
12346 * This self reference will be released as the very last step on return.
12347 */
12348 ComObjPtr<SessionMachine> selfRef = this;
12349
12350 /* Enclose the state transition Ready->InUninit->NotReady */
12351 AutoUninitSpan autoUninitSpan(this);
12352 if (autoUninitSpan.uninitDone())
12353 {
12354 LogFlowThisFunc(("Already uninitialized\n"));
12355 LogFlowThisFuncLeave();
12356 return;
12357 }
12358
12359 if (autoUninitSpan.initFailed())
12360 {
12361 /* We've been called by init() because it's failed. It's not really
12362 * necessary (nor it's safe) to perform the regular uninit sequence
12363 * below, the following is enough.
12364 */
12365 LogFlowThisFunc(("Initialization failed.\n"));
12366 /* destroy the machine client token */
12367 if (mClientToken)
12368 {
12369 delete mClientToken;
12370 mClientToken = NULL;
12371 }
12372 uninitDataAndChildObjects();
12373 mData.free();
12374 unconst(mParent) = NULL;
12375 unconst(mPeer) = NULL;
12376 LogFlowThisFuncLeave();
12377 return;
12378 }
12379
12380 MachineState_T lastState;
12381 {
12382 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12383 lastState = mData->mMachineState;
12384 }
12385 NOREF(lastState);
12386
12387#ifdef VBOX_WITH_USB
12388 // release all captured USB devices, but do this before requesting the locks below
12389 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12390 {
12391 /* Console::captureUSBDevices() is called in the VM process only after
12392 * setting the machine state to Starting or Restoring.
12393 * Console::detachAllUSBDevices() will be called upon successful
12394 * termination. So, we need to release USB devices only if there was
12395 * an abnormal termination of a running VM.
12396 *
12397 * This is identical to SessionMachine::DetachAllUSBDevices except
12398 * for the aAbnormal argument. */
12399 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12400 AssertComRC(rc);
12401 NOREF(rc);
12402
12403 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12404 if (service)
12405 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12406 }
12407#endif /* VBOX_WITH_USB */
12408
12409 // we need to lock this object in uninit() because the lock is shared
12410 // with mPeer (as well as data we modify below). mParent lock is needed
12411 // by several calls to it, and USB needs host lock.
12412 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12413
12414#ifdef VBOX_WITH_RESOURCE_USAGE_API
12415 /*
12416 * It is safe to call Machine::i_unregisterMetrics() here because
12417 * PerformanceCollector::samplerCallback no longer accesses guest methods
12418 * holding the lock.
12419 */
12420 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12421 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12422 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12423 this, __PRETTY_FUNCTION__, mCollectorGuest));
12424 if (mCollectorGuest)
12425 {
12426 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12427 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12428 mCollectorGuest = NULL;
12429 }
12430#endif
12431
12432 if (aReason == Uninit::Abnormal)
12433 {
12434 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12435 Global::IsOnlineOrTransient(lastState)));
12436
12437 /* reset the state to Aborted */
12438 if (mData->mMachineState != MachineState_Aborted)
12439 i_setMachineState(MachineState_Aborted);
12440 }
12441
12442 // any machine settings modified?
12443 if (mData->flModifications)
12444 {
12445 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12446 i_rollback(false /* aNotify */);
12447 }
12448
12449 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12450 || !mConsoleTaskData.mSnapshot);
12451 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12452 {
12453 LogWarningThisFunc(("canceling failed save state request!\n"));
12454 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12455 }
12456 else if (!mConsoleTaskData.mSnapshot.isNull())
12457 {
12458 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12459
12460 /* delete all differencing hard disks created (this will also attach
12461 * their parents back by rolling back mMediaData) */
12462 i_rollbackMedia();
12463
12464 // delete the saved state file (it might have been already created)
12465 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12466 // think it's still in use
12467 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12468 mConsoleTaskData.mSnapshot->uninit();
12469 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12470 }
12471
12472 mData->mSession.mPID = NIL_RTPROCESS;
12473
12474 if (aReason == Uninit::Unexpected)
12475 {
12476 /* Uninitialization didn't come from #checkForDeath(), so tell the
12477 * client watcher thread to update the set of machines that have open
12478 * sessions. */
12479 mParent->i_updateClientWatcher();
12480 }
12481
12482 /* uninitialize all remote controls */
12483 if (mData->mSession.mRemoteControls.size())
12484 {
12485 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12486 mData->mSession.mRemoteControls.size()));
12487
12488 Data::Session::RemoteControlList::iterator it =
12489 mData->mSession.mRemoteControls.begin();
12490 while (it != mData->mSession.mRemoteControls.end())
12491 {
12492 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12493 HRESULT rc = (*it)->Uninitialize();
12494 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12495 if (FAILED(rc))
12496 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12497 ++it;
12498 }
12499 mData->mSession.mRemoteControls.clear();
12500 }
12501
12502 /* Remove all references to the NAT network service. The service will stop
12503 * if all references (also from other VMs) are removed. */
12504 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12505 {
12506 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12507 {
12508 NetworkAttachmentType_T type;
12509 HRESULT hrc;
12510
12511 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12512 if ( SUCCEEDED(hrc)
12513 && type == NetworkAttachmentType_NATNetwork)
12514 {
12515 Bstr name;
12516 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12517 if (SUCCEEDED(hrc))
12518 {
12519 multilock.release();
12520 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12521 mUserData->s.strName.c_str(), name.raw()));
12522 mParent->i_natNetworkRefDec(name.raw());
12523 multilock.acquire();
12524 }
12525 }
12526 }
12527 }
12528
12529 /*
12530 * An expected uninitialization can come only from #checkForDeath().
12531 * Otherwise it means that something's gone really wrong (for example,
12532 * the Session implementation has released the VirtualBox reference
12533 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12534 * etc). However, it's also possible, that the client releases the IPC
12535 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12536 * but the VirtualBox release event comes first to the server process.
12537 * This case is practically possible, so we should not assert on an
12538 * unexpected uninit, just log a warning.
12539 */
12540
12541 if ((aReason == Uninit::Unexpected))
12542 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12543
12544 if (aReason != Uninit::Normal)
12545 {
12546 mData->mSession.mDirectControl.setNull();
12547 }
12548 else
12549 {
12550 /* this must be null here (see #OnSessionEnd()) */
12551 Assert(mData->mSession.mDirectControl.isNull());
12552 Assert(mData->mSession.mState == SessionState_Unlocking);
12553 Assert(!mData->mSession.mProgress.isNull());
12554 }
12555 if (mData->mSession.mProgress)
12556 {
12557 if (aReason == Uninit::Normal)
12558 mData->mSession.mProgress->i_notifyComplete(S_OK);
12559 else
12560 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12561 COM_IIDOF(ISession),
12562 getComponentName(),
12563 tr("The VM session was aborted"));
12564 mData->mSession.mProgress.setNull();
12565 }
12566
12567 /* remove the association between the peer machine and this session machine */
12568 Assert( (SessionMachine*)mData->mSession.mMachine == this
12569 || aReason == Uninit::Unexpected);
12570
12571 /* reset the rest of session data */
12572 mData->mSession.mMachine.setNull();
12573 mData->mSession.mState = SessionState_Unlocked;
12574 mData->mSession.mType.setNull();
12575
12576 /* destroy the machine client token before leaving the exclusive lock */
12577 if (mClientToken)
12578 {
12579 delete mClientToken;
12580 mClientToken = NULL;
12581 }
12582
12583 /* fire an event */
12584 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12585
12586 uninitDataAndChildObjects();
12587
12588 /* free the essential data structure last */
12589 mData.free();
12590
12591 /* release the exclusive lock before setting the below two to NULL */
12592 multilock.release();
12593
12594 unconst(mParent) = NULL;
12595 unconst(mPeer) = NULL;
12596
12597 LogFlowThisFuncLeave();
12598}
12599
12600// util::Lockable interface
12601////////////////////////////////////////////////////////////////////////////////
12602
12603/**
12604 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12605 * with the primary Machine instance (mPeer).
12606 */
12607RWLockHandle *SessionMachine::lockHandle() const
12608{
12609 AssertReturn(mPeer != NULL, NULL);
12610 return mPeer->lockHandle();
12611}
12612
12613// IInternalMachineControl methods
12614////////////////////////////////////////////////////////////////////////////////
12615
12616/**
12617 * Passes collected guest statistics to performance collector object
12618 */
12619HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12620 ULONG aCpuKernel, ULONG aCpuIdle,
12621 ULONG aMemTotal, ULONG aMemFree,
12622 ULONG aMemBalloon, ULONG aMemShared,
12623 ULONG aMemCache, ULONG aPageTotal,
12624 ULONG aAllocVMM, ULONG aFreeVMM,
12625 ULONG aBalloonedVMM, ULONG aSharedVMM,
12626 ULONG aVmNetRx, ULONG aVmNetTx)
12627{
12628#ifdef VBOX_WITH_RESOURCE_USAGE_API
12629 if (mCollectorGuest)
12630 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12631 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12632 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12633 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12634
12635 return S_OK;
12636#else
12637 NOREF(aValidStats);
12638 NOREF(aCpuUser);
12639 NOREF(aCpuKernel);
12640 NOREF(aCpuIdle);
12641 NOREF(aMemTotal);
12642 NOREF(aMemFree);
12643 NOREF(aMemBalloon);
12644 NOREF(aMemShared);
12645 NOREF(aMemCache);
12646 NOREF(aPageTotal);
12647 NOREF(aAllocVMM);
12648 NOREF(aFreeVMM);
12649 NOREF(aBalloonedVMM);
12650 NOREF(aSharedVMM);
12651 NOREF(aVmNetRx);
12652 NOREF(aVmNetTx);
12653 return E_NOTIMPL;
12654#endif
12655}
12656
12657/**
12658 * @note Locks this object for writing.
12659 */
12660HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12661{
12662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12663
12664 mRemoveSavedState = RT_BOOL(aRemove);
12665
12666 return S_OK;
12667}
12668
12669/**
12670 * @note Locks the same as #i_setMachineState() does.
12671 */
12672HRESULT SessionMachine::updateState(MachineState_T aState)
12673{
12674 return i_setMachineState(aState);
12675}
12676
12677/**
12678 * @note Locks this object for writing.
12679 */
12680HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12681{
12682 IProgress* pProgress(aProgress);
12683
12684 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12685
12686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12687
12688 if (mData->mSession.mState != SessionState_Locked)
12689 return VBOX_E_INVALID_OBJECT_STATE;
12690
12691 if (!mData->mSession.mProgress.isNull())
12692 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12693
12694 /* If we didn't reference the NAT network service yet, add a reference to
12695 * force a start */
12696 if (miNATNetworksStarted < 1)
12697 {
12698 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12699 {
12700 NetworkAttachmentType_T type;
12701 HRESULT hrc;
12702 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12703 if ( SUCCEEDED(hrc)
12704 && type == NetworkAttachmentType_NATNetwork)
12705 {
12706 Bstr name;
12707 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12708 if (SUCCEEDED(hrc))
12709 {
12710 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12711 mUserData->s.strName.c_str(), name.raw()));
12712 mPeer->lockHandle()->unlockWrite();
12713 mParent->i_natNetworkRefInc(name.raw());
12714#ifdef RT_LOCK_STRICT
12715 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12716#else
12717 mPeer->lockHandle()->lockWrite();
12718#endif
12719 }
12720 }
12721 }
12722 miNATNetworksStarted++;
12723 }
12724
12725 LogFlowThisFunc(("returns S_OK.\n"));
12726 return S_OK;
12727}
12728
12729/**
12730 * @note Locks this object for writing.
12731 */
12732HRESULT SessionMachine::endPowerUp(LONG aResult)
12733{
12734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12735
12736 if (mData->mSession.mState != SessionState_Locked)
12737 return VBOX_E_INVALID_OBJECT_STATE;
12738
12739 /* Finalize the LaunchVMProcess progress object. */
12740 if (mData->mSession.mProgress)
12741 {
12742 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12743 mData->mSession.mProgress.setNull();
12744 }
12745
12746 if (SUCCEEDED((HRESULT)aResult))
12747 {
12748#ifdef VBOX_WITH_RESOURCE_USAGE_API
12749 /* The VM has been powered up successfully, so it makes sense
12750 * now to offer the performance metrics for a running machine
12751 * object. Doing it earlier wouldn't be safe. */
12752 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12753 mData->mSession.mPID);
12754#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12755 }
12756
12757 return S_OK;
12758}
12759
12760/**
12761 * @note Locks this object for writing.
12762 */
12763HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12764{
12765 LogFlowThisFuncEnter();
12766
12767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12768
12769 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12770 E_FAIL);
12771
12772 /* create a progress object to track operation completion */
12773 ComObjPtr<Progress> pProgress;
12774 pProgress.createObject();
12775 pProgress->init(i_getVirtualBox(),
12776 static_cast<IMachine *>(this) /* aInitiator */,
12777 Bstr(tr("Stopping the virtual machine")).raw(),
12778 FALSE /* aCancelable */);
12779
12780 /* fill in the console task data */
12781 mConsoleTaskData.mLastState = mData->mMachineState;
12782 mConsoleTaskData.mProgress = pProgress;
12783
12784 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12785 i_setMachineState(MachineState_Stopping);
12786
12787 pProgress.queryInterfaceTo(aProgress.asOutParam());
12788
12789 return S_OK;
12790}
12791
12792/**
12793 * @note Locks this object for writing.
12794 */
12795HRESULT SessionMachine::endPoweringDown(LONG aResult,
12796 const com::Utf8Str &aErrMsg)
12797{
12798 LogFlowThisFuncEnter();
12799
12800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12801
12802 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12803 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12804 && mConsoleTaskData.mLastState != MachineState_Null,
12805 E_FAIL);
12806
12807 /*
12808 * On failure, set the state to the state we had when BeginPoweringDown()
12809 * was called (this is expected by Console::PowerDown() and the associated
12810 * task). On success the VM process already changed the state to
12811 * MachineState_PoweredOff, so no need to do anything.
12812 */
12813 if (FAILED(aResult))
12814 i_setMachineState(mConsoleTaskData.mLastState);
12815
12816 /* notify the progress object about operation completion */
12817 Assert(mConsoleTaskData.mProgress);
12818 if (SUCCEEDED(aResult))
12819 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12820 else
12821 {
12822 if (aErrMsg.length())
12823 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12824 COM_IIDOF(ISession),
12825 getComponentName(),
12826 aErrMsg.c_str());
12827 else
12828 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12829 }
12830
12831 /* clear out the temporary saved state data */
12832 mConsoleTaskData.mLastState = MachineState_Null;
12833 mConsoleTaskData.mProgress.setNull();
12834
12835 LogFlowThisFuncLeave();
12836 return S_OK;
12837}
12838
12839
12840/**
12841 * Goes through the USB filters of the given machine to see if the given
12842 * device matches any filter or not.
12843 *
12844 * @note Locks the same as USBController::hasMatchingFilter() does.
12845 */
12846HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12847 BOOL *aMatched,
12848 ULONG *aMaskedInterfaces)
12849{
12850 LogFlowThisFunc(("\n"));
12851
12852#ifdef VBOX_WITH_USB
12853 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12854#else
12855 NOREF(aDevice);
12856 NOREF(aMaskedInterfaces);
12857 *aMatched = FALSE;
12858#endif
12859
12860 return S_OK;
12861}
12862
12863/**
12864 * @note Locks the same as Host::captureUSBDevice() does.
12865 */
12866HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
12867{
12868 LogFlowThisFunc(("\n"));
12869
12870#ifdef VBOX_WITH_USB
12871 /* if captureDeviceForVM() fails, it must have set extended error info */
12872 clearError();
12873 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12874 if (FAILED(rc)) return rc;
12875
12876 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12877 AssertReturn(service, E_FAIL);
12878 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
12879#else
12880 NOREF(aId);
12881 return E_NOTIMPL;
12882#endif
12883}
12884
12885/**
12886 * @note Locks the same as Host::detachUSBDevice() does.
12887 */
12888HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12889 BOOL aDone)
12890{
12891 LogFlowThisFunc(("\n"));
12892
12893#ifdef VBOX_WITH_USB
12894 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12895 AssertReturn(service, E_FAIL);
12896 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12897#else
12898 NOREF(aId);
12899 NOREF(aDone);
12900 return E_NOTIMPL;
12901#endif
12902}
12903
12904/**
12905 * Inserts all machine filters to the USB proxy service and then calls
12906 * Host::autoCaptureUSBDevices().
12907 *
12908 * Called by Console from the VM process upon VM startup.
12909 *
12910 * @note Locks what called methods lock.
12911 */
12912HRESULT SessionMachine::autoCaptureUSBDevices()
12913{
12914 LogFlowThisFunc(("\n"));
12915
12916#ifdef VBOX_WITH_USB
12917 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12918 AssertComRC(rc);
12919 NOREF(rc);
12920
12921 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12922 AssertReturn(service, E_FAIL);
12923 return service->autoCaptureDevicesForVM(this);
12924#else
12925 return S_OK;
12926#endif
12927}
12928
12929/**
12930 * Removes all machine filters from the USB proxy service and then calls
12931 * Host::detachAllUSBDevices().
12932 *
12933 * Called by Console from the VM process upon normal VM termination or by
12934 * SessionMachine::uninit() upon abnormal VM termination (from under the
12935 * Machine/SessionMachine lock).
12936 *
12937 * @note Locks what called methods lock.
12938 */
12939HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12940{
12941 LogFlowThisFunc(("\n"));
12942
12943#ifdef VBOX_WITH_USB
12944 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12945 AssertComRC(rc);
12946 NOREF(rc);
12947
12948 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12949 AssertReturn(service, E_FAIL);
12950 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12951#else
12952 NOREF(aDone);
12953 return S_OK;
12954#endif
12955}
12956
12957/**
12958 * @note Locks this object for writing.
12959 */
12960HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12961 ComPtr<IProgress> &aProgress)
12962{
12963 LogFlowThisFuncEnter();
12964
12965 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12966 /*
12967 * We don't assert below because it might happen that a non-direct session
12968 * informs us it is closed right after we've been uninitialized -- it's ok.
12969 */
12970
12971 /* get IInternalSessionControl interface */
12972 ComPtr<IInternalSessionControl> control(aSession);
12973
12974 ComAssertRet(!control.isNull(), E_INVALIDARG);
12975
12976 /* Creating a Progress object requires the VirtualBox lock, and
12977 * thus locking it here is required by the lock order rules. */
12978 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12979
12980 if (control == mData->mSession.mDirectControl)
12981 {
12982 /* The direct session is being normally closed by the client process
12983 * ----------------------------------------------------------------- */
12984
12985 /* go to the closing state (essential for all open*Session() calls and
12986 * for #checkForDeath()) */
12987 Assert(mData->mSession.mState == SessionState_Locked);
12988 mData->mSession.mState = SessionState_Unlocking;
12989
12990 /* set direct control to NULL to release the remote instance */
12991 mData->mSession.mDirectControl.setNull();
12992 LogFlowThisFunc(("Direct control is set to NULL\n"));
12993
12994 if (mData->mSession.mProgress)
12995 {
12996 /* finalize the progress, someone might wait if a frontend
12997 * closes the session before powering on the VM. */
12998 mData->mSession.mProgress->notifyComplete(E_FAIL,
12999 COM_IIDOF(ISession),
13000 getComponentName(),
13001 tr("The VM session was closed before any attempt to power it on"));
13002 mData->mSession.mProgress.setNull();
13003 }
13004
13005 /* Create the progress object the client will use to wait until
13006 * #checkForDeath() is called to uninitialize this session object after
13007 * it releases the IPC semaphore.
13008 * Note! Because we're "reusing" mProgress here, this must be a proxy
13009 * object just like for LaunchVMProcess. */
13010 Assert(mData->mSession.mProgress.isNull());
13011 ComObjPtr<ProgressProxy> progress;
13012 progress.createObject();
13013 ComPtr<IUnknown> pPeer(mPeer);
13014 progress->init(mParent, pPeer,
13015 Bstr(tr("Closing session")).raw(),
13016 FALSE /* aCancelable */);
13017 progress.queryInterfaceTo(aProgress.asOutParam());
13018 mData->mSession.mProgress = progress;
13019 }
13020 else
13021 {
13022 /* the remote session is being normally closed */
13023 Data::Session::RemoteControlList::iterator it =
13024 mData->mSession.mRemoteControls.begin();
13025 while (it != mData->mSession.mRemoteControls.end())
13026 {
13027 if (control == *it)
13028 break;
13029 ++it;
13030 }
13031 BOOL found = it != mData->mSession.mRemoteControls.end();
13032 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13033 E_INVALIDARG);
13034 // This MUST be erase(it), not remove(*it) as the latter triggers a
13035 // very nasty use after free due to the place where the value "lives".
13036 mData->mSession.mRemoteControls.erase(it);
13037 }
13038
13039 /* signal the client watcher thread, because the client is going away */
13040 mParent->i_updateClientWatcher();
13041
13042 LogFlowThisFuncLeave();
13043 return S_OK;
13044}
13045
13046/**
13047 * @note Locks this object for writing.
13048 */
13049HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
13050 com::Utf8Str &aStateFilePath)
13051{
13052 LogFlowThisFuncEnter();
13053
13054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13055
13056 AssertReturn( mData->mMachineState == MachineState_Paused
13057 && mConsoleTaskData.mLastState == MachineState_Null
13058 && mConsoleTaskData.strStateFilePath.isEmpty(),
13059 E_FAIL);
13060
13061 /* create a progress object to track operation completion */
13062 ComObjPtr<Progress> pProgress;
13063 pProgress.createObject();
13064 pProgress->init(i_getVirtualBox(),
13065 static_cast<IMachine *>(this) /* aInitiator */,
13066 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13067 FALSE /* aCancelable */);
13068
13069 /* stateFilePath is null when the machine is not running */
13070 if (mData->mMachineState == MachineState_Paused)
13071 i_composeSavedStateFilename(aStateFilePath);
13072
13073 /* fill in the console task data */
13074 mConsoleTaskData.mLastState = mData->mMachineState;
13075 mConsoleTaskData.strStateFilePath = aStateFilePath;
13076 mConsoleTaskData.mProgress = pProgress;
13077
13078 /* set the state to Saving (this is expected by Console::SaveState()) */
13079 i_setMachineState(MachineState_Saving);
13080
13081 pProgress.queryInterfaceTo(aProgress.asOutParam());
13082
13083 return S_OK;
13084}
13085
13086/**
13087 * @note Locks mParent + this object for writing.
13088 */
13089HRESULT SessionMachine::endSavingState(LONG aResult,
13090 const com::Utf8Str &aErrMsg)
13091{
13092 LogFlowThisFunc(("\n"));
13093
13094 /* endSavingState() need mParent lock */
13095 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13096
13097 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13098 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13099 && mConsoleTaskData.mLastState != MachineState_Null
13100 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13101 E_FAIL);
13102
13103 /*
13104 * On failure, set the state to the state we had when BeginSavingState()
13105 * was called (this is expected by Console::SaveState() and the associated
13106 * task). On success the VM process already changed the state to
13107 * MachineState_Saved, so no need to do anything.
13108 */
13109 if (FAILED(aResult))
13110 i_setMachineState(mConsoleTaskData.mLastState);
13111
13112 return i_endSavingState(aResult, aErrMsg);
13113}
13114
13115/**
13116 * @note Locks this object for writing.
13117 */
13118HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13119{
13120 LogFlowThisFunc(("\n"));
13121
13122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13123
13124 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13125 || mData->mMachineState == MachineState_Teleported
13126 || mData->mMachineState == MachineState_Aborted
13127 , E_FAIL); /** @todo setError. */
13128
13129 com::Utf8Str stateFilePathFull;
13130 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13131 if (RT_FAILURE(vrc))
13132 return setError(VBOX_E_FILE_ERROR,
13133 tr("Invalid saved state file path '%s' (%Rrc)"),
13134 aSavedStateFile.c_str(),
13135 vrc);
13136
13137 mSSData->strStateFilePath = stateFilePathFull;
13138
13139 /* The below i_setMachineState() will detect the state transition and will
13140 * update the settings file */
13141
13142 return i_setMachineState(MachineState_Saved);
13143}
13144
13145HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13146 std::vector<com::Utf8Str> &aValues,
13147 std::vector<LONG64> &aTimestamps,
13148 std::vector<com::Utf8Str> &aFlags)
13149{
13150 LogFlowThisFunc(("\n"));
13151
13152#ifdef VBOX_WITH_GUEST_PROPS
13153 using namespace guestProp;
13154
13155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13156
13157 size_t cEntries = mHWData->mGuestProperties.size();
13158 aNames.resize(cEntries);
13159 aValues.resize(cEntries);
13160 aTimestamps.resize(cEntries);
13161 aFlags.resize(cEntries);
13162
13163 size_t i = 0;
13164 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13165 it != mHWData->mGuestProperties.end();
13166 ++it, ++i)
13167 {
13168 char szFlags[MAX_FLAGS_LEN + 1];
13169 aNames[i] = it->first;
13170 aValues[i] = it->second.strValue;
13171 aTimestamps[i] = it->second.mTimestamp;
13172
13173 /* If it is NULL, keep it NULL. */
13174 if (it->second.mFlags)
13175 {
13176 writeFlags(it->second.mFlags, szFlags);
13177 aFlags[i] = szFlags;
13178 }
13179 else
13180 aFlags[i] = "";
13181 }
13182 return S_OK;
13183#else
13184 ReturnComNotImplemented();
13185#endif
13186}
13187
13188HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13189 const com::Utf8Str &aValue,
13190 LONG64 aTimestamp,
13191 const com::Utf8Str &aFlags)
13192{
13193 LogFlowThisFunc(("\n"));
13194
13195#ifdef VBOX_WITH_GUEST_PROPS
13196 using namespace guestProp;
13197
13198 try
13199 {
13200 /*
13201 * Convert input up front.
13202 */
13203 uint32_t fFlags = NILFLAG;
13204 if (aFlags.length())
13205 {
13206 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13207 AssertRCReturn(vrc, E_INVALIDARG);
13208 }
13209
13210 /*
13211 * Now grab the object lock, validate the state and do the update.
13212 */
13213
13214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13215
13216 switch (mData->mMachineState)
13217 {
13218 case MachineState_Paused:
13219 case MachineState_Running:
13220 case MachineState_Teleporting:
13221 case MachineState_TeleportingPausedVM:
13222 case MachineState_LiveSnapshotting:
13223 case MachineState_DeletingSnapshotOnline:
13224 case MachineState_DeletingSnapshotPaused:
13225 case MachineState_Saving:
13226 case MachineState_Stopping:
13227 break;
13228
13229 default:
13230 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13231 VBOX_E_INVALID_VM_STATE);
13232 }
13233
13234 i_setModified(IsModified_MachineData);
13235 mHWData.backup();
13236
13237 bool fDelete = !aValue.length();
13238 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13239 if (it != mHWData->mGuestProperties.end())
13240 {
13241 if (!fDelete)
13242 {
13243 it->second.strValue = aValue;
13244 it->second.mTimestamp = aTimestamp;
13245 it->second.mFlags = fFlags;
13246 }
13247 else
13248 mHWData->mGuestProperties.erase(it);
13249
13250 mData->mGuestPropertiesModified = TRUE;
13251 }
13252 else if (!fDelete)
13253 {
13254 HWData::GuestProperty prop;
13255 prop.strValue = aValue;
13256 prop.mTimestamp = aTimestamp;
13257 prop.mFlags = fFlags;
13258
13259 mHWData->mGuestProperties[aName] = prop;
13260 mData->mGuestPropertiesModified = TRUE;
13261 }
13262
13263 /*
13264 * Send a callback notification if appropriate
13265 */
13266 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13267 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13268 RTSTR_MAX,
13269 aName.c_str(),
13270 RTSTR_MAX, NULL)
13271 )
13272 {
13273 alock.release();
13274
13275 mParent->i_onGuestPropertyChange(mData->mUuid,
13276 Bstr(aName).raw(),
13277 Bstr(aValue).raw(),
13278 Bstr(aFlags).raw());
13279 }
13280 }
13281 catch (...)
13282 {
13283 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13284 }
13285 return S_OK;
13286#else
13287 ReturnComNotImplemented();
13288#endif
13289}
13290
13291
13292HRESULT SessionMachine::lockMedia()
13293{
13294 AutoMultiWriteLock2 alock(this->lockHandle(),
13295 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13296
13297 AssertReturn( mData->mMachineState == MachineState_Starting
13298 || mData->mMachineState == MachineState_Restoring
13299 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13300
13301 clearError();
13302 alock.release();
13303 return i_lockMedia();
13304}
13305
13306HRESULT SessionMachine::unlockMedia()
13307{
13308 HRESULT hrc = i_unlockMedia();
13309 return hrc;
13310}
13311
13312HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13313 ComPtr<IMediumAttachment> &aNewAttachment)
13314{
13315 // request the host lock first, since might be calling Host methods for getting host drives;
13316 // next, protect the media tree all the while we're in here, as well as our member variables
13317 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13318 this->lockHandle(),
13319 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13320
13321 IMediumAttachment *iAttach = aAttachment;
13322 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13323
13324 Bstr ctrlName;
13325 LONG lPort;
13326 LONG lDevice;
13327 bool fTempEject;
13328 {
13329 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13330
13331 /* Need to query the details first, as the IMediumAttachment reference
13332 * might be to the original settings, which we are going to change. */
13333 ctrlName = pAttach->i_getControllerName();
13334 lPort = pAttach->i_getPort();
13335 lDevice = pAttach->i_getDevice();
13336 fTempEject = pAttach->i_getTempEject();
13337 }
13338
13339 if (!fTempEject)
13340 {
13341 /* Remember previously mounted medium. The medium before taking the
13342 * backup is not necessarily the same thing. */
13343 ComObjPtr<Medium> oldmedium;
13344 oldmedium = pAttach->i_getMedium();
13345
13346 i_setModified(IsModified_Storage);
13347 mMediaData.backup();
13348
13349 // The backup operation makes the pAttach reference point to the
13350 // old settings. Re-get the correct reference.
13351 pAttach = i_findAttachment(mMediaData->mAttachments,
13352 ctrlName.raw(),
13353 lPort,
13354 lDevice);
13355
13356 {
13357 AutoCaller autoAttachCaller(this);
13358 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13359
13360 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13361 if (!oldmedium.isNull())
13362 oldmedium->i_removeBackReference(mData->mUuid);
13363
13364 pAttach->i_updateMedium(NULL);
13365 pAttach->i_updateEjected();
13366 }
13367
13368 i_setModified(IsModified_Storage);
13369 }
13370 else
13371 {
13372 {
13373 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13374 pAttach->i_updateEjected();
13375 }
13376 }
13377
13378 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13379
13380 return S_OK;
13381}
13382
13383// public methods only for internal purposes
13384/////////////////////////////////////////////////////////////////////////////
13385
13386#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13387/**
13388 * Called from the client watcher thread to check for expected or unexpected
13389 * death of the client process that has a direct session to this machine.
13390 *
13391 * On Win32 and on OS/2, this method is called only when we've got the
13392 * mutex (i.e. the client has either died or terminated normally) so it always
13393 * returns @c true (the client is terminated, the session machine is
13394 * uninitialized).
13395 *
13396 * On other platforms, the method returns @c true if the client process has
13397 * terminated normally or abnormally and the session machine was uninitialized,
13398 * and @c false if the client process is still alive.
13399 *
13400 * @note Locks this object for writing.
13401 */
13402bool SessionMachine::i_checkForDeath()
13403{
13404 Uninit::Reason reason;
13405 bool terminated = false;
13406
13407 /* Enclose autoCaller with a block because calling uninit() from under it
13408 * will deadlock. */
13409 {
13410 AutoCaller autoCaller(this);
13411 if (!autoCaller.isOk())
13412 {
13413 /* return true if not ready, to cause the client watcher to exclude
13414 * the corresponding session from watching */
13415 LogFlowThisFunc(("Already uninitialized!\n"));
13416 return true;
13417 }
13418
13419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13420
13421 /* Determine the reason of death: if the session state is Closing here,
13422 * everything is fine. Otherwise it means that the client did not call
13423 * OnSessionEnd() before it released the IPC semaphore. This may happen
13424 * either because the client process has abnormally terminated, or
13425 * because it simply forgot to call ISession::Close() before exiting. We
13426 * threat the latter also as an abnormal termination (see
13427 * Session::uninit() for details). */
13428 reason = mData->mSession.mState == SessionState_Unlocking ?
13429 Uninit::Normal :
13430 Uninit::Abnormal;
13431
13432 if (mClientToken)
13433 terminated = mClientToken->release();
13434 } /* AutoCaller block */
13435
13436 if (terminated)
13437 uninit(reason);
13438
13439 return terminated;
13440}
13441
13442void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13443{
13444 LogFlowThisFunc(("\n"));
13445
13446 strTokenId.setNull();
13447
13448 AutoCaller autoCaller(this);
13449 AssertComRCReturnVoid(autoCaller.rc());
13450
13451 Assert(mClientToken);
13452 if (mClientToken)
13453 mClientToken->getId(strTokenId);
13454}
13455#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13456IToken *SessionMachine::i_getToken()
13457{
13458 LogFlowThisFunc(("\n"));
13459
13460 AutoCaller autoCaller(this);
13461 AssertComRCReturn(autoCaller.rc(), NULL);
13462
13463 Assert(mClientToken);
13464 if (mClientToken)
13465 return mClientToken->getToken();
13466 else
13467 return NULL;
13468}
13469#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13470
13471Machine::ClientToken *SessionMachine::i_getClientToken()
13472{
13473 LogFlowThisFunc(("\n"));
13474
13475 AutoCaller autoCaller(this);
13476 AssertComRCReturn(autoCaller.rc(), NULL);
13477
13478 return mClientToken;
13479}
13480
13481
13482/**
13483 * @note Locks this object for reading.
13484 */
13485HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13486{
13487 LogFlowThisFunc(("\n"));
13488
13489 AutoCaller autoCaller(this);
13490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13491
13492 ComPtr<IInternalSessionControl> directControl;
13493 {
13494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13495 directControl = mData->mSession.mDirectControl;
13496 }
13497
13498 /* ignore notifications sent after #OnSessionEnd() is called */
13499 if (!directControl)
13500 return S_OK;
13501
13502 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13503}
13504
13505/**
13506 * @note Locks this object for reading.
13507 */
13508HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13509 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13510 IN_BSTR aGuestIp, LONG aGuestPort)
13511{
13512 LogFlowThisFunc(("\n"));
13513
13514 AutoCaller autoCaller(this);
13515 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13516
13517 ComPtr<IInternalSessionControl> directControl;
13518 {
13519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13520 directControl = mData->mSession.mDirectControl;
13521 }
13522
13523 /* ignore notifications sent after #OnSessionEnd() is called */
13524 if (!directControl)
13525 return S_OK;
13526 /*
13527 * instead acting like callback we ask IVirtualBox deliver corresponding event
13528 */
13529
13530 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13531 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13532 return S_OK;
13533}
13534
13535/**
13536 * @note Locks this object for reading.
13537 */
13538HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13539{
13540 LogFlowThisFunc(("\n"));
13541
13542 AutoCaller autoCaller(this);
13543 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13544
13545 ComPtr<IInternalSessionControl> directControl;
13546 {
13547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13548 directControl = mData->mSession.mDirectControl;
13549 }
13550
13551 /* ignore notifications sent after #OnSessionEnd() is called */
13552 if (!directControl)
13553 return S_OK;
13554
13555 return directControl->OnSerialPortChange(serialPort);
13556}
13557
13558/**
13559 * @note Locks this object for reading.
13560 */
13561HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13562{
13563 LogFlowThisFunc(("\n"));
13564
13565 AutoCaller autoCaller(this);
13566 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13567
13568 ComPtr<IInternalSessionControl> directControl;
13569 {
13570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13571 directControl = mData->mSession.mDirectControl;
13572 }
13573
13574 /* ignore notifications sent after #OnSessionEnd() is called */
13575 if (!directControl)
13576 return S_OK;
13577
13578 return directControl->OnParallelPortChange(parallelPort);
13579}
13580
13581/**
13582 * @note Locks this object for reading.
13583 */
13584HRESULT SessionMachine::i_onStorageControllerChange()
13585{
13586 LogFlowThisFunc(("\n"));
13587
13588 AutoCaller autoCaller(this);
13589 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13590
13591 ComPtr<IInternalSessionControl> directControl;
13592 {
13593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13594 directControl = mData->mSession.mDirectControl;
13595 }
13596
13597 /* ignore notifications sent after #OnSessionEnd() is called */
13598 if (!directControl)
13599 return S_OK;
13600
13601 return directControl->OnStorageControllerChange();
13602}
13603
13604/**
13605 * @note Locks this object for reading.
13606 */
13607HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13608{
13609 LogFlowThisFunc(("\n"));
13610
13611 AutoCaller autoCaller(this);
13612 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13613
13614 ComPtr<IInternalSessionControl> directControl;
13615 {
13616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13617 directControl = mData->mSession.mDirectControl;
13618 }
13619
13620 /* ignore notifications sent after #OnSessionEnd() is called */
13621 if (!directControl)
13622 return S_OK;
13623
13624 return directControl->OnMediumChange(aAttachment, aForce);
13625}
13626
13627/**
13628 * @note Locks this object for reading.
13629 */
13630HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13631{
13632 LogFlowThisFunc(("\n"));
13633
13634 AutoCaller autoCaller(this);
13635 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13636
13637 ComPtr<IInternalSessionControl> directControl;
13638 {
13639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13640 directControl = mData->mSession.mDirectControl;
13641 }
13642
13643 /* ignore notifications sent after #OnSessionEnd() is called */
13644 if (!directControl)
13645 return S_OK;
13646
13647 return directControl->OnCPUChange(aCPU, aRemove);
13648}
13649
13650HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13651{
13652 LogFlowThisFunc(("\n"));
13653
13654 AutoCaller autoCaller(this);
13655 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13656
13657 ComPtr<IInternalSessionControl> directControl;
13658 {
13659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13660 directControl = mData->mSession.mDirectControl;
13661 }
13662
13663 /* ignore notifications sent after #OnSessionEnd() is called */
13664 if (!directControl)
13665 return S_OK;
13666
13667 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13668}
13669
13670/**
13671 * @note Locks this object for reading.
13672 */
13673HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13674{
13675 LogFlowThisFunc(("\n"));
13676
13677 AutoCaller autoCaller(this);
13678 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13679
13680 ComPtr<IInternalSessionControl> directControl;
13681 {
13682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13683 directControl = mData->mSession.mDirectControl;
13684 }
13685
13686 /* ignore notifications sent after #OnSessionEnd() is called */
13687 if (!directControl)
13688 return S_OK;
13689
13690 return directControl->OnVRDEServerChange(aRestart);
13691}
13692
13693/**
13694 * @note Locks this object for reading.
13695 */
13696HRESULT SessionMachine::i_onVideoCaptureChange()
13697{
13698 LogFlowThisFunc(("\n"));
13699
13700 AutoCaller autoCaller(this);
13701 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13702
13703 ComPtr<IInternalSessionControl> directControl;
13704 {
13705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13706 directControl = mData->mSession.mDirectControl;
13707 }
13708
13709 /* ignore notifications sent after #OnSessionEnd() is called */
13710 if (!directControl)
13711 return S_OK;
13712
13713 return directControl->OnVideoCaptureChange();
13714}
13715
13716/**
13717 * @note Locks this object for reading.
13718 */
13719HRESULT SessionMachine::i_onUSBControllerChange()
13720{
13721 LogFlowThisFunc(("\n"));
13722
13723 AutoCaller autoCaller(this);
13724 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13725
13726 ComPtr<IInternalSessionControl> directControl;
13727 {
13728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13729 directControl = mData->mSession.mDirectControl;
13730 }
13731
13732 /* ignore notifications sent after #OnSessionEnd() is called */
13733 if (!directControl)
13734 return S_OK;
13735
13736 return directControl->OnUSBControllerChange();
13737}
13738
13739/**
13740 * @note Locks this object for reading.
13741 */
13742HRESULT SessionMachine::i_onSharedFolderChange()
13743{
13744 LogFlowThisFunc(("\n"));
13745
13746 AutoCaller autoCaller(this);
13747 AssertComRCReturnRC(autoCaller.rc());
13748
13749 ComPtr<IInternalSessionControl> directControl;
13750 {
13751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13752 directControl = mData->mSession.mDirectControl;
13753 }
13754
13755 /* ignore notifications sent after #OnSessionEnd() is called */
13756 if (!directControl)
13757 return S_OK;
13758
13759 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13760}
13761
13762/**
13763 * @note Locks this object for reading.
13764 */
13765HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13766{
13767 LogFlowThisFunc(("\n"));
13768
13769 AutoCaller autoCaller(this);
13770 AssertComRCReturnRC(autoCaller.rc());
13771
13772 ComPtr<IInternalSessionControl> directControl;
13773 {
13774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13775 directControl = mData->mSession.mDirectControl;
13776 }
13777
13778 /* ignore notifications sent after #OnSessionEnd() is called */
13779 if (!directControl)
13780 return S_OK;
13781
13782 return directControl->OnClipboardModeChange(aClipboardMode);
13783}
13784
13785/**
13786 * @note Locks this object for reading.
13787 */
13788HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13789{
13790 LogFlowThisFunc(("\n"));
13791
13792 AutoCaller autoCaller(this);
13793 AssertComRCReturnRC(autoCaller.rc());
13794
13795 ComPtr<IInternalSessionControl> directControl;
13796 {
13797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13798 directControl = mData->mSession.mDirectControl;
13799 }
13800
13801 /* ignore notifications sent after #OnSessionEnd() is called */
13802 if (!directControl)
13803 return S_OK;
13804
13805 return directControl->OnDnDModeChange(aDnDMode);
13806}
13807
13808/**
13809 * @note Locks this object for reading.
13810 */
13811HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
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 directControl = mData->mSession.mDirectControl;
13822 }
13823
13824 /* ignore notifications sent after #OnSessionEnd() is called */
13825 if (!directControl)
13826 return S_OK;
13827
13828 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13829}
13830
13831/**
13832 * @note Locks this object for reading.
13833 */
13834HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13835{
13836 LogFlowThisFunc(("\n"));
13837
13838 AutoCaller autoCaller(this);
13839 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13840
13841 ComPtr<IInternalSessionControl> directControl;
13842 {
13843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13844 directControl = mData->mSession.mDirectControl;
13845 }
13846
13847 /* ignore notifications sent after #OnSessionEnd() is called */
13848 if (!directControl)
13849 return S_OK;
13850
13851 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13852}
13853
13854/**
13855 * Returns @c true if this machine's USB controller reports it has a matching
13856 * filter for the given USB device and @c false otherwise.
13857 *
13858 * @note locks this object for reading.
13859 */
13860bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13861{
13862 AutoCaller autoCaller(this);
13863 /* silently return if not ready -- this method may be called after the
13864 * direct machine session has been called */
13865 if (!autoCaller.isOk())
13866 return false;
13867
13868#ifdef VBOX_WITH_USB
13869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13870
13871 switch (mData->mMachineState)
13872 {
13873 case MachineState_Starting:
13874 case MachineState_Restoring:
13875 case MachineState_TeleportingIn:
13876 case MachineState_Paused:
13877 case MachineState_Running:
13878 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13879 * elsewhere... */
13880 alock.release();
13881 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13882 default: break;
13883 }
13884#else
13885 NOREF(aDevice);
13886 NOREF(aMaskedIfs);
13887#endif
13888 return false;
13889}
13890
13891/**
13892 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13893 */
13894HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13895 IVirtualBoxErrorInfo *aError,
13896 ULONG aMaskedIfs,
13897 const com::Utf8Str &aCaptureFilename)
13898{
13899 LogFlowThisFunc(("\n"));
13900
13901 AutoCaller autoCaller(this);
13902
13903 /* This notification may happen after the machine object has been
13904 * uninitialized (the session was closed), so don't assert. */
13905 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13906
13907 ComPtr<IInternalSessionControl> directControl;
13908 {
13909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13910 directControl = mData->mSession.mDirectControl;
13911 }
13912
13913 /* fail on notifications sent after #OnSessionEnd() is called, it is
13914 * expected by the caller */
13915 if (!directControl)
13916 return E_FAIL;
13917
13918 /* No locks should be held at this point. */
13919 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13920 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13921
13922 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
13923}
13924
13925/**
13926 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13927 */
13928HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13929 IVirtualBoxErrorInfo *aError)
13930{
13931 LogFlowThisFunc(("\n"));
13932
13933 AutoCaller autoCaller(this);
13934
13935 /* This notification may happen after the machine object has been
13936 * uninitialized (the session was closed), so don't assert. */
13937 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13938
13939 ComPtr<IInternalSessionControl> directControl;
13940 {
13941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13942 directControl = mData->mSession.mDirectControl;
13943 }
13944
13945 /* fail on notifications sent after #OnSessionEnd() is called, it is
13946 * expected by the caller */
13947 if (!directControl)
13948 return E_FAIL;
13949
13950 /* No locks should be held at this point. */
13951 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13952 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13953
13954 return directControl->OnUSBDeviceDetach(aId, aError);
13955}
13956
13957// protected methods
13958/////////////////////////////////////////////////////////////////////////////
13959
13960/**
13961 * Helper method to finalize saving the state.
13962 *
13963 * @note Must be called from under this object's lock.
13964 *
13965 * @param aRc S_OK if the snapshot has been taken successfully
13966 * @param aErrMsg human readable error message for failure
13967 *
13968 * @note Locks mParent + this objects for writing.
13969 */
13970HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13971{
13972 LogFlowThisFuncEnter();
13973
13974 AutoCaller autoCaller(this);
13975 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13976
13977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13978
13979 HRESULT rc = S_OK;
13980
13981 if (SUCCEEDED(aRc))
13982 {
13983 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13984
13985 /* save all VM settings */
13986 rc = i_saveSettings(NULL);
13987 // no need to check whether VirtualBox.xml needs saving also since
13988 // we can't have a name change pending at this point
13989 }
13990 else
13991 {
13992 // delete the saved state file (it might have been already created);
13993 // we need not check whether this is shared with a snapshot here because
13994 // we certainly created this saved state file here anew
13995 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13996 }
13997
13998 /* notify the progress object about operation completion */
13999 Assert(mConsoleTaskData.mProgress);
14000 if (SUCCEEDED(aRc))
14001 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
14002 else
14003 {
14004 if (aErrMsg.length())
14005 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
14006 COM_IIDOF(ISession),
14007 getComponentName(),
14008 aErrMsg.c_str());
14009 else
14010 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
14011 }
14012
14013 /* clear out the temporary saved state data */
14014 mConsoleTaskData.mLastState = MachineState_Null;
14015 mConsoleTaskData.strStateFilePath.setNull();
14016 mConsoleTaskData.mProgress.setNull();
14017
14018 LogFlowThisFuncLeave();
14019 return rc;
14020}
14021
14022/**
14023 * Deletes the given file if it is no longer in use by either the current machine state
14024 * (if the machine is "saved") or any of the machine's snapshots.
14025 *
14026 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14027 * but is different for each SnapshotMachine. When calling this, the order of calling this
14028 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14029 * is therefore critical. I know, it's all rather messy.
14030 *
14031 * @param strStateFile
14032 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14033 * the test for whether the saved state file is in use.
14034 */
14035void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14036 Snapshot *pSnapshotToIgnore)
14037{
14038 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14039 if ( (strStateFile.isNotEmpty())
14040 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14041 )
14042 // ... and it must also not be shared with other snapshots
14043 if ( !mData->mFirstSnapshot
14044 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14045 // this checks the SnapshotMachine's state file paths
14046 )
14047 RTFileDelete(strStateFile.c_str());
14048}
14049
14050/**
14051 * Locks the attached media.
14052 *
14053 * All attached hard disks are locked for writing and DVD/floppy are locked for
14054 * reading. Parents of attached hard disks (if any) are locked for reading.
14055 *
14056 * This method also performs accessibility check of all media it locks: if some
14057 * media is inaccessible, the method will return a failure and a bunch of
14058 * extended error info objects per each inaccessible medium.
14059 *
14060 * Note that this method is atomic: if it returns a success, all media are
14061 * locked as described above; on failure no media is locked at all (all
14062 * succeeded individual locks will be undone).
14063 *
14064 * The caller is responsible for doing the necessary state sanity checks.
14065 *
14066 * The locks made by this method must be undone by calling #unlockMedia() when
14067 * no more needed.
14068 */
14069HRESULT SessionMachine::i_lockMedia()
14070{
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14073
14074 AutoMultiWriteLock2 alock(this->lockHandle(),
14075 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14076
14077 /* bail out if trying to lock things with already set up locking */
14078 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14079
14080 MultiResult mrc(S_OK);
14081
14082 /* Collect locking information for all medium objects attached to the VM. */
14083 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14084 it != mMediaData->mAttachments.end();
14085 ++it)
14086 {
14087 MediumAttachment* pAtt = *it;
14088 DeviceType_T devType = pAtt->i_getType();
14089 Medium *pMedium = pAtt->i_getMedium();
14090
14091 MediumLockList *pMediumLockList(new MediumLockList());
14092 // There can be attachments without a medium (floppy/dvd), and thus
14093 // it's impossible to create a medium lock list. It still makes sense
14094 // to have the empty medium lock list in the map in case a medium is
14095 // attached later.
14096 if (pMedium != NULL)
14097 {
14098 MediumType_T mediumType = pMedium->i_getType();
14099 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14100 || mediumType == MediumType_Shareable;
14101 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14102
14103 alock.release();
14104 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14105 !fIsReadOnlyLock /* fMediumLockWrite */,
14106 false /* fMediumLockWriteAll */,
14107 NULL,
14108 *pMediumLockList);
14109 alock.acquire();
14110 if (FAILED(mrc))
14111 {
14112 delete pMediumLockList;
14113 mData->mSession.mLockedMedia.Clear();
14114 break;
14115 }
14116 }
14117
14118 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14119 if (FAILED(rc))
14120 {
14121 mData->mSession.mLockedMedia.Clear();
14122 mrc = setError(rc,
14123 tr("Collecting locking information for all attached media failed"));
14124 break;
14125 }
14126 }
14127
14128 if (SUCCEEDED(mrc))
14129 {
14130 /* Now lock all media. If this fails, nothing is locked. */
14131 alock.release();
14132 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14133 alock.acquire();
14134 if (FAILED(rc))
14135 {
14136 mrc = setError(rc,
14137 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14138 }
14139 }
14140
14141 return mrc;
14142}
14143
14144/**
14145 * Undoes the locks made by by #lockMedia().
14146 */
14147HRESULT SessionMachine::i_unlockMedia()
14148{
14149 AutoCaller autoCaller(this);
14150 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14151
14152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14153
14154 /* we may be holding important error info on the current thread;
14155 * preserve it */
14156 ErrorInfoKeeper eik;
14157
14158 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14159 AssertComRC(rc);
14160 return rc;
14161}
14162
14163/**
14164 * Helper to change the machine state (reimplementation).
14165 *
14166 * @note Locks this object for writing.
14167 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14168 * it can cause crashes in random places due to unexpectedly committing
14169 * the current settings. The caller is responsible for that. The call
14170 * to saveStateSettings is fine, because this method does not commit.
14171 */
14172HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14173{
14174 LogFlowThisFuncEnter();
14175 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14176
14177 AutoCaller autoCaller(this);
14178 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14179
14180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14181
14182 MachineState_T oldMachineState = mData->mMachineState;
14183
14184 AssertMsgReturn(oldMachineState != aMachineState,
14185 ("oldMachineState=%s, aMachineState=%s\n",
14186 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14187 E_FAIL);
14188
14189 HRESULT rc = S_OK;
14190
14191 int stsFlags = 0;
14192 bool deleteSavedState = false;
14193
14194 /* detect some state transitions */
14195
14196 if ( ( oldMachineState == MachineState_Saved
14197 && aMachineState == MachineState_Restoring)
14198 || ( ( oldMachineState == MachineState_PoweredOff
14199 || oldMachineState == MachineState_Teleported
14200 || oldMachineState == MachineState_Aborted
14201 )
14202 && ( aMachineState == MachineState_TeleportingIn
14203 || aMachineState == MachineState_Starting
14204 )
14205 )
14206 )
14207 {
14208 /* The EMT thread is about to start */
14209
14210 /* Nothing to do here for now... */
14211
14212 /// @todo NEWMEDIA don't let mDVDDrive and other children
14213 /// change anything when in the Starting/Restoring state
14214 }
14215 else if ( ( oldMachineState == MachineState_Running
14216 || oldMachineState == MachineState_Paused
14217 || oldMachineState == MachineState_Teleporting
14218 || oldMachineState == MachineState_LiveSnapshotting
14219 || oldMachineState == MachineState_Stuck
14220 || oldMachineState == MachineState_Starting
14221 || oldMachineState == MachineState_Stopping
14222 || oldMachineState == MachineState_Saving
14223 || oldMachineState == MachineState_Restoring
14224 || oldMachineState == MachineState_TeleportingPausedVM
14225 || oldMachineState == MachineState_TeleportingIn
14226 )
14227 && ( aMachineState == MachineState_PoweredOff
14228 || aMachineState == MachineState_Saved
14229 || aMachineState == MachineState_Teleported
14230 || aMachineState == MachineState_Aborted
14231 )
14232 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14233 * snapshot */
14234 && ( mConsoleTaskData.mSnapshot.isNull()
14235 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14236 )
14237 )
14238 {
14239 /* The EMT thread has just stopped, unlock attached media. Note that as
14240 * opposed to locking that is done from Console, we do unlocking here
14241 * because the VM process may have aborted before having a chance to
14242 * properly unlock all media it locked. */
14243
14244 unlockMedia();
14245 }
14246
14247 if (oldMachineState == MachineState_Restoring)
14248 {
14249 if (aMachineState != MachineState_Saved)
14250 {
14251 /*
14252 * delete the saved state file once the machine has finished
14253 * restoring from it (note that Console sets the state from
14254 * Restoring to Saved if the VM couldn't restore successfully,
14255 * to give the user an ability to fix an error and retry --
14256 * we keep the saved state file in this case)
14257 */
14258 deleteSavedState = true;
14259 }
14260 }
14261 else if ( oldMachineState == MachineState_Saved
14262 && ( aMachineState == MachineState_PoweredOff
14263 || aMachineState == MachineState_Aborted
14264 || aMachineState == MachineState_Teleported
14265 )
14266 )
14267 {
14268 /*
14269 * delete the saved state after Console::ForgetSavedState() is called
14270 * or if the VM process (owning a direct VM session) crashed while the
14271 * VM was Saved
14272 */
14273
14274 /// @todo (dmik)
14275 // Not sure that deleting the saved state file just because of the
14276 // client death before it attempted to restore the VM is a good
14277 // thing. But when it crashes we need to go to the Aborted state
14278 // which cannot have the saved state file associated... The only
14279 // way to fix this is to make the Aborted condition not a VM state
14280 // but a bool flag: i.e., when a crash occurs, set it to true and
14281 // change the state to PoweredOff or Saved depending on the
14282 // saved state presence.
14283
14284 deleteSavedState = true;
14285 mData->mCurrentStateModified = TRUE;
14286 stsFlags |= SaveSTS_CurStateModified;
14287 }
14288
14289 if ( aMachineState == MachineState_Starting
14290 || aMachineState == MachineState_Restoring
14291 || aMachineState == MachineState_TeleportingIn
14292 )
14293 {
14294 /* set the current state modified flag to indicate that the current
14295 * state is no more identical to the state in the
14296 * current snapshot */
14297 if (!mData->mCurrentSnapshot.isNull())
14298 {
14299 mData->mCurrentStateModified = TRUE;
14300 stsFlags |= SaveSTS_CurStateModified;
14301 }
14302 }
14303
14304 if (deleteSavedState)
14305 {
14306 if (mRemoveSavedState)
14307 {
14308 Assert(!mSSData->strStateFilePath.isEmpty());
14309
14310 // it is safe to delete the saved state file if ...
14311 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14312 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14313 // ... none of the snapshots share the saved state file
14314 )
14315 RTFileDelete(mSSData->strStateFilePath.c_str());
14316 }
14317
14318 mSSData->strStateFilePath.setNull();
14319 stsFlags |= SaveSTS_StateFilePath;
14320 }
14321
14322 /* redirect to the underlying peer machine */
14323 mPeer->i_setMachineState(aMachineState);
14324
14325 if ( aMachineState == MachineState_PoweredOff
14326 || aMachineState == MachineState_Teleported
14327 || aMachineState == MachineState_Aborted
14328 || aMachineState == MachineState_Saved)
14329 {
14330 /* the machine has stopped execution
14331 * (or the saved state file was adopted) */
14332 stsFlags |= SaveSTS_StateTimeStamp;
14333 }
14334
14335 if ( ( oldMachineState == MachineState_PoweredOff
14336 || oldMachineState == MachineState_Aborted
14337 || oldMachineState == MachineState_Teleported
14338 )
14339 && aMachineState == MachineState_Saved)
14340 {
14341 /* the saved state file was adopted */
14342 Assert(!mSSData->strStateFilePath.isEmpty());
14343 stsFlags |= SaveSTS_StateFilePath;
14344 }
14345
14346#ifdef VBOX_WITH_GUEST_PROPS
14347 if ( aMachineState == MachineState_PoweredOff
14348 || aMachineState == MachineState_Aborted
14349 || aMachineState == MachineState_Teleported)
14350 {
14351 /* Make sure any transient guest properties get removed from the
14352 * property store on shutdown. */
14353 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14354
14355 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14356 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14357 while (it != llGuestProperties.end())
14358 {
14359 const settings::GuestProperty &prop = *it;
14360 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14361 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14362 {
14363 it = llGuestProperties.erase(it);
14364 fNeedsSaving = true;
14365 }
14366 else
14367 {
14368 ++it;
14369 }
14370 }
14371
14372 if (fNeedsSaving)
14373 {
14374 mData->mCurrentStateModified = TRUE;
14375 stsFlags |= SaveSTS_CurStateModified;
14376 }
14377 }
14378#endif /* VBOX_WITH_GUEST_PROPS */
14379
14380 rc = i_saveStateSettings(stsFlags);
14381
14382 if ( ( oldMachineState != MachineState_PoweredOff
14383 && oldMachineState != MachineState_Aborted
14384 && oldMachineState != MachineState_Teleported
14385 )
14386 && ( aMachineState == MachineState_PoweredOff
14387 || aMachineState == MachineState_Aborted
14388 || aMachineState == MachineState_Teleported
14389 )
14390 )
14391 {
14392 /* we've been shut down for any reason */
14393 /* no special action so far */
14394 }
14395
14396 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14397 LogFlowThisFuncLeave();
14398 return rc;
14399}
14400
14401/**
14402 * Sends the current machine state value to the VM process.
14403 *
14404 * @note Locks this object for reading, then calls a client process.
14405 */
14406HRESULT SessionMachine::i_updateMachineStateOnClient()
14407{
14408 AutoCaller autoCaller(this);
14409 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14410
14411 ComPtr<IInternalSessionControl> directControl;
14412 {
14413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14414 AssertReturn(!!mData, E_FAIL);
14415 directControl = mData->mSession.mDirectControl;
14416
14417 /* directControl may be already set to NULL here in #OnSessionEnd()
14418 * called too early by the direct session process while there is still
14419 * some operation (like deleting the snapshot) in progress. The client
14420 * process in this case is waiting inside Session::close() for the
14421 * "end session" process object to complete, while #uninit() called by
14422 * #checkForDeath() on the Watcher thread is waiting for the pending
14423 * operation to complete. For now, we accept this inconsistent behavior
14424 * and simply do nothing here. */
14425
14426 if (mData->mSession.mState == SessionState_Unlocking)
14427 return S_OK;
14428
14429 AssertReturn(!directControl.isNull(), E_FAIL);
14430 }
14431
14432 return directControl->UpdateMachineState(mData->mMachineState);
14433}
14434
14435HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14436{
14437 NOREF(aRemove);
14438 ReturnComNotImplemented();
14439}
14440
14441HRESULT Machine::updateState(MachineState_T aState)
14442{
14443 NOREF(aState);
14444 ReturnComNotImplemented();
14445}
14446
14447HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14448{
14449 NOREF(aProgress);
14450 ReturnComNotImplemented();
14451}
14452
14453HRESULT Machine::endPowerUp(LONG aResult)
14454{
14455 NOREF(aResult);
14456 ReturnComNotImplemented();
14457}
14458
14459HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14460{
14461 NOREF(aProgress);
14462 ReturnComNotImplemented();
14463}
14464
14465HRESULT Machine::endPoweringDown(LONG aResult,
14466 const com::Utf8Str &aErrMsg)
14467{
14468 NOREF(aResult);
14469 NOREF(aErrMsg);
14470 ReturnComNotImplemented();
14471}
14472
14473HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14474 BOOL *aMatched,
14475 ULONG *aMaskedInterfaces)
14476{
14477 NOREF(aDevice);
14478 NOREF(aMatched);
14479 NOREF(aMaskedInterfaces);
14480 ReturnComNotImplemented();
14481
14482}
14483
14484HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14485{
14486 NOREF(aId); NOREF(aCaptureFilename);
14487 ReturnComNotImplemented();
14488}
14489
14490HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14491 BOOL aDone)
14492{
14493 NOREF(aId);
14494 NOREF(aDone);
14495 ReturnComNotImplemented();
14496}
14497
14498HRESULT Machine::autoCaptureUSBDevices()
14499{
14500 ReturnComNotImplemented();
14501}
14502
14503HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14504{
14505 NOREF(aDone);
14506 ReturnComNotImplemented();
14507}
14508
14509HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14510 ComPtr<IProgress> &aProgress)
14511{
14512 NOREF(aSession);
14513 NOREF(aProgress);
14514 ReturnComNotImplemented();
14515}
14516
14517HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14518 com::Utf8Str &aStateFilePath)
14519{
14520 NOREF(aProgress);
14521 NOREF(aStateFilePath);
14522 ReturnComNotImplemented();
14523}
14524
14525HRESULT Machine::endSavingState(LONG aResult,
14526 const com::Utf8Str &aErrMsg)
14527{
14528 NOREF(aResult);
14529 NOREF(aErrMsg);
14530 ReturnComNotImplemented();
14531}
14532
14533HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14534{
14535 NOREF(aSavedStateFile);
14536 ReturnComNotImplemented();
14537}
14538
14539HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14540 const com::Utf8Str &aName,
14541 const com::Utf8Str &aDescription,
14542 const ComPtr<IProgress> &aConsoleProgress,
14543 BOOL aFTakingSnapshotOnline,
14544 com::Utf8Str &aStateFilePath)
14545{
14546 NOREF(aInitiator);
14547 NOREF(aName);
14548 NOREF(aDescription);
14549 NOREF(aConsoleProgress);
14550 NOREF(aFTakingSnapshotOnline);
14551 NOREF(aStateFilePath);
14552 ReturnComNotImplemented();
14553}
14554
14555HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14556{
14557 NOREF(aSuccess);
14558 ReturnComNotImplemented();
14559}
14560
14561HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14562 const com::Guid &aStartId,
14563 const com::Guid &aEndId,
14564 BOOL aDeleteAllChildren,
14565 MachineState_T *aMachineState,
14566 ComPtr<IProgress> &aProgress)
14567{
14568 NOREF(aInitiator);
14569 NOREF(aStartId);
14570 NOREF(aEndId);
14571 NOREF(aDeleteAllChildren);
14572 NOREF(aMachineState);
14573 NOREF(aProgress);
14574 ReturnComNotImplemented();
14575}
14576
14577HRESULT Machine::finishOnlineMergeMedium()
14578{
14579 ReturnComNotImplemented();
14580}
14581
14582HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14583 const ComPtr<ISnapshot> &aSnapshot,
14584 MachineState_T *aMachineState,
14585 ComPtr<IProgress> &aProgress)
14586{
14587 NOREF(aInitiator);
14588 NOREF(aSnapshot);
14589 NOREF(aMachineState);
14590 NOREF(aProgress);
14591 ReturnComNotImplemented();
14592}
14593
14594HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14595 std::vector<com::Utf8Str> &aValues,
14596 std::vector<LONG64> &aTimestamps,
14597 std::vector<com::Utf8Str> &aFlags)
14598{
14599 NOREF(aNames);
14600 NOREF(aValues);
14601 NOREF(aTimestamps);
14602 NOREF(aFlags);
14603 ReturnComNotImplemented();
14604}
14605
14606HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14607 const com::Utf8Str &aValue,
14608 LONG64 aTimestamp,
14609 const com::Utf8Str &aFlags)
14610{
14611 NOREF(aName);
14612 NOREF(aValue);
14613 NOREF(aTimestamp);
14614 NOREF(aFlags);
14615 ReturnComNotImplemented();
14616}
14617
14618HRESULT Machine::lockMedia()
14619{
14620 ReturnComNotImplemented();
14621}
14622
14623HRESULT Machine::unlockMedia()
14624{
14625 ReturnComNotImplemented();
14626}
14627
14628HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14629 ComPtr<IMediumAttachment> &aNewAttachment)
14630{
14631 NOREF(aAttachment);
14632 NOREF(aNewAttachment);
14633 ReturnComNotImplemented();
14634}
14635
14636HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14637 ULONG aCpuUser,
14638 ULONG aCpuKernel,
14639 ULONG aCpuIdle,
14640 ULONG aMemTotal,
14641 ULONG aMemFree,
14642 ULONG aMemBalloon,
14643 ULONG aMemShared,
14644 ULONG aMemCache,
14645 ULONG aPagedTotal,
14646 ULONG aMemAllocTotal,
14647 ULONG aMemFreeTotal,
14648 ULONG aMemBalloonTotal,
14649 ULONG aMemSharedTotal,
14650 ULONG aVmNetRx,
14651 ULONG aVmNetTx)
14652{
14653 NOREF(aValidStats);
14654 NOREF(aCpuUser);
14655 NOREF(aCpuKernel);
14656 NOREF(aCpuIdle);
14657 NOREF(aMemTotal);
14658 NOREF(aMemFree);
14659 NOREF(aMemBalloon);
14660 NOREF(aMemShared);
14661 NOREF(aMemCache);
14662 NOREF(aPagedTotal);
14663 NOREF(aMemAllocTotal);
14664 NOREF(aMemFreeTotal);
14665 NOREF(aMemBalloonTotal);
14666 NOREF(aMemSharedTotal);
14667 NOREF(aVmNetRx);
14668 NOREF(aVmNetTx);
14669 ReturnComNotImplemented();
14670}
Note: See TracBrowser for help on using the repository browser.

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